Work In Progress
This project is a quantitative trading backtest ecosystem.
Melo-fwk and Melo-ql, respectively melo framework and melo query language Mozart is the parallel computation orchestrator
Melo-fwk & Melo-ql form a backtesting ecosystem used for trading simulations, quantitative strategy design and portfolio building. Strategies and trading policies can easily be extended (WIP).
Mozart is an orchestrator for parallel data pipelines and processes.
pip install dependencies then pip install this repo
pandas, matplotlib, yfinance, numpy, pyparse, scikit-optimize, mongodb, scikit-learn, scipy
Tutorial on Mql is cooking
# import necessary packages
from melo_fwk.market_data import CommodityDataLoader
from minimelo.trading_systems import TradingSystem
from minimelo.strategies import EWMAStrategy
from minimelo.pose_size import VolTargetInertiaPolicy
from melo_fwk.plots import TsarPlotter
# fetch product
commo_dl = CommodityDataLoader()
product = commo_dl.commo_data_registry["Gold"]
strat_basket = StratBasket(
strat_list=[
EWMAStrategy(
fast_span=16,
slow_span=64,
scale=16.,
),
],
weights=Weights([1.], 1.)
)
# setup balance and vol target
start_capital = 60000
size_policy = VolTargetInertiaPolicy(
annual_vol_target=25e-2,
trading_capital=start_capital)
# init trading system
trading_subsys = tr(
strat_basket=strat_basket,
size_policy=size_policy
)
# run trading system for each year
# and save result in dictionary
results = {
f"Gold_{year}": trading_subsys.run_product_year(product, year)
for year in product.years()
}
# update balance
balance += np.sum([tsar.annual_delta() for tsar in results.values()])
print(f"Final balance = {balance}")
# save annual metrics plots in export folder (make sure folder exists)
tsar_plotter.save_fig(export_folder="data/residual", mute=True)
To add a new Strategy in the strategy stack, you need to follow a couple of steps. First of all, we define the strategy :
# import base class, dataclass and other necessary packages
from minimelo.strategies import BaseStrategy
from dataclasses import dataclass
import pandas as pd
import numpy as np
# define optimisation search space
# used by StratOptimEstimator to optimize strategies
@dataclass
class StratParamSpace:
fast_span: int
slow_span: int
search_space = {
"fast_span": [i for i in range(4, 60)],
"slow_span": [i for i in range(30, 100)],
}
# define the actual strategy component
# needs to inherit base and search space classes
@dataclass
class NewStrategy(BaseStrategy, StratParamSpace):
# forecast_vect implements the computation
# of all forecast series from price data
def forecast_vect(self, data: pd.Series) -> pd.Series:
pass # do your calculations here
def forecast_df(self, data: pd.DataFrame) -> pd.DataFrame:
pass
This component on it's own is sufficient to be used in a harcoded backtesting scenario. If we want to link it to mql and use it in that context, we need to register the component in the factory
# in melo_fwk.quantfactory_registry
# find register_strats() and register_search_spaces funtions
def register_strats():
# register strategy in strategy stack
quantflow_factory.QuantFlowFactory.register_strategy("NewStrategy", NewStrategy)
def register_search_spaces():
# save strategy's search space in proper register
quantflow_factory.QuantFlowFactory.register_search_space(
"NewStrategy.search_space", NewStrategy.search_space
)
To add a position sizing component, same as with strategies, we need to create the component
We need to inherit the basic vol target component and override position_size_vect
method :
from minimelo.pose_size import BaseSizePolicy
import pandas as pd
class NewVolTargetPolicy(BaseSizePolicy):
def __init__(
self,
annual_vol_target: float,
trading_capital: float
):
super(NewVolTargetPolicy, self).__init__(
annual_vol_target, trading_capital
)
# this is the method we need to override
# computes number of contracts to buy/sell depending on forecast and risk appetite
def position_size_vect(self, forecast: pd.Series, lookback: int = 36) -> pd.Series:
pass
def position_size_df(self, forecast: pd.DataFrame) -> pd.DataFrame:
pass
Then we register the component to the factory in order to use it with mql.
# find this function in melo_fwk.quantfactory_registry
def register_size_policies():
# add this line to register your component to be used with mql
quantflow_factory.QuantFlowFactory.register_size_policy(
"NewVolTargetPolicy", NewVolTargetPolicy
)
Same for estimator as for stategies and size policies. Estimators are used to perform various optimization tasks (strategies hyperparameters, forecast weights, portfolio allocation...) Melo-Fwk implements some estimators already, but if you need to make your own, you need to follow this template
# import necessities
class MyNewEstimator:
def __init__(
self,
products: dict,
time_period: List[int],
strategies: List[BaseStrategy] = None,
forecast_weights: List[int] = None,
size_policy: BaseSizePolicy = None,
estimator_params: List[str] = None
):
pass # initialize the estimator
def run(self):
# every estimator needs to implement a run method
pass # usually returns a result passed to a reporting component
# check melo_fwk.reporters for example
Then register both estimator and associated reporter to the factory melo_fwk.quantfactory_registry
# find register_estimator
# register estimator and reporter
def register_estimator():
quantflow_factory.QuantFlowFactory.register_estimator("MyNewEstimator", MyNewEstimator)
quantflow_factory.QuantFlowFactory.register_reporter("MyNewEstimator", MyNewReporter)