The platform is custom backtesting framework based on open-source bt for Python. We picked it for its simplicity, and its focus on quantitative trading approaches. The latter unlocks limitless potential for Wall St. like trading such as factor trading, pair trading, portfolio optimization, and ai trading.
Simple Moving Average Strategy
In this example, we build a strategy for a simple moving average.
Breaking it down
The algorithm must have a config variable conforming to Config type and a function which defines the strategy as new_frontier.
The new_frontier object must return a strategy of bt.Strategy type. It is fed in with the data that was requested prior in the config symbols.
A strategy is defined by a user defined name, and a list or stack of algos. In this case, we are defining 3 algos.
- SelectWhere takes in a signal of True or False on a per interval basis to be hold or not hold.
- WeightEqually tells the strategy to set the weight equally across all the asset selected in the prior step. In this case, it’s only 1, so the weight is either 1 or 0.
- Rebalance adjusts the portfolio to the target weight based by buying or sell the differences.
The raw data is fed into the new_frontier function as a multi-level pandas DataFrame.
> data.head() BTC/USDT Open High Low Close Volume Timestamp 2018-01-01 13715.65 13818.55 12750.00 13380.00 8609.915844 2018-01-02 13382.16 15473.49 12890.02 14675.11 20078.092111 2018-01-03 14690.00 15307.56 14150.00 14919.51 15905.667639 2018-01-04 14919.51 15280.00 13918.04 15059.54 21329.649574 2018-01-05 15059.56 17176.24 14600.00 16960.39 23251.491125
Next, we cross-select all the symbols with the open price, then compute a rolling mean with a window of 50 day.
> data.xs('Open', level=1, axis=1).rolling(window=50).mean() BTC/USDT Timestamp 2018-01-01 NaN 2018-01-02 NaN 2018-01-03 NaN 2018-01-04 NaN 2018-01-05 NaN 2018-01-06 NaN 2018-01-07 15113.865714 2018-01-08 15471.465714 2018-01-09 15688.662857
Of course, the first 7 days do not have enough data to compute the moving average, yielding NaN. Next, we compute the boolean signal by select True whenever the SMA is higher than price.
> data.xs('Open', level=1, axis=1).rolling(window=7).mean() > data.xs('Open', level=1, axis=1) BTC/USDT Timestamp 2018-01-01 False 2018-01-02 False 2018-01-03 False 2018-01-04 False 2018-01-05 False 2018-01-06 False 2018-01-07 False 2018-01-08 False 2018-01-09 True 2018-01-10 True 2018-01-11 True
Muti-asset Simple Moving Average Strategy
With a simple change to the configuration symbols from just BTC, we pick BTC, and ETH – we can begin to see the hidden potential the framework.
This change will feed in both BTC & ETH into the data flow. The pandas Dataframe compute moving average both assets (and potentially as many as you want) instantly. The end result is you have a moving average data signal for each asset.
> data.xs('Open', level=1, axis=1).rolling(window=7).mean() > data.xs('Open', level=1, axis=1) BTC/USDT ETH/USDT Timestamp 2018-01-01 False False 2018-01-02 False False 2018-01-03 False False 2018-01-04 False False 2018-01-05 False False 2018-01-06 False False 2018-01-07 False False 2018-01-08 False False 2018-01-09 True False 2018-01-10 True False
When the signal is True, False , the allocation is 1.0, 0. When allocation is True, True, allocation is 0.5 and 0.5 for BTC, and ETH respectively.