Translating code from Quantiacs Legacy¶
Technical changes¶
The new version of Quantiacs addresses several issues:
it includes a private user space for developing code online and test ideas for forecasting global financial markets based on Jupyter Notebook and JupyterLab;
you can install in your private area any Python library you need for developing and submitting strategies;
you have access to historical data for futures, Bitcoin futures and cryptocurrencies;
we updated open-source libraries for algorithm development, which has become more efficient.
Example¶
A distinctive feature of the new version of Quantiacs is the simplification of the strategy code, which became much easier to read and use. Let us consider a simple strategy based on a crossing of moving averages.
Quantiacs Legacy¶
In Quantiacs Legacy you would write a function defining weights and a settings function on the following lines:
def myTradingSystem(DATE, OPEN, HIGH, LOW, CLOSE, VOL, exposure, equity, settings):
nMarkets = CLOSE.shape[1]
perL = 200
perS = 40
smaLong = numpy.nansum(CLOSE[-perL:, :], axis=0) / perL
smaRecent = numpy.nansum(CLOSE[-perS:, :], axis=0) / perS
longEquity = smaRecent > smaLong
shortEquity = ~longEquity
pos = numpy.zeros(nMarkets)
pos[longEquity] = 1
pos[shortEquity] = -1
weights = pos / numpy.nansum(abs(pos))
return weights, settings
def mySettings():
settings = {}
settings['markets'] = ['CASH', 'F_AD', 'F_BO', 'F_BP', 'F_C']
settings['beginInSample'] = '20120506'
settings['endInSample'] = '20150506'
settings['lookback'] = 504
settings['budget'] = 10 ** 6
settings['slippage'] = 0.05
return settings
if __name__ == '__main__':
import quantiacsToolbox
results = quantiacsToolbox.runts(__file__)
Quantiacs: Single-Pass Mode¶
A similar logic in the new Quantiacs can be implemented using the following compact single-pass implementation. By single-pass implementation we mean an implementation where the complete time series of data is constantly accessible at any step of the evaluation:
import xarray as xr
import qnt.ta as qnta
import qnt.data as qndata
import qnt.output as qnout
data = qndata.futures_load_data(
assets=["F_AD", "F_BO", "F_BP", "F_C"],
min_date="2006-01-01")
close = data.sel(field="close")
sma_long = qnta.sma(close, 200)
sma_short = qnta.sma(close, 40)
weights = xr.where(sma_short > sma_long, 1, -1)
weights = weights / abs(weights).sum("asset")
weights = qnout.clean(weights, data, "futures")
qnout.check(weights, data, "futures")
qnout.write(weights)
The single-pass implementation is very fast because it uses fast bulk operations on the full time series. On the other hand, it is possible that implicit looking-forward is taking place. To verify the results of your single-pass strategy you can use the file precheck.ipynb (in the root directory of your strategy) and read the warnings.
Quantiacs: Multi-Pass Mode¶
As an alternative, you can use the function qnt.backtester.backtest and perform a slower multi-pass simulation which is looking-forward free. Note that we will check your In-Sample Sharpe ratio at submission time using a multi-pass implementation which will reveal looking-forward issues.
This is an example of multi-pass implementation where at timestamp “t” only data until timestamp “t” are available by construction:
import xarray as xr
import qnt.ta as qnta
import qnt.backtester as qnbt
import qnt.data as qndata
def load_data(period):
return qndata.futures_load_data(
assets=["F_AD", "F_BO", "F_BP", "F_C"],
tail=period)
def strategy(data):
close = data.sel(field="close")
sma_long = qnta.sma(close, 200).isel(time=-1)
sma_short = qnta.sma(close, 40).isel(time=-1)
pos = xr.where(sma_short > sma_long, 1, -1)
return pos / abs(pos).sum("asset")
weights = qnbt.backtest(
competition_type = "futures",
load_data = load_data,
lookback_period = 365,
test_period = 365 * 15,
strategy = strategy)
Here we have used the function qnt.backtester.backtest whose details can be found in your private area in the /qnt/backtester.py file.
The function requires the following input:
competition type: “futures” for the futures contest or “cryptofutures” for the Bitcoin futures contest;
load data: the pre-defined load_data function. The period passed to load_data is given by test_period + lookback_period;
lookback_period: the maximal lookback period in calendar days used for building indicators. In this case, as we use 200 trading days for defining the largest simple moving average, 1 year is fine;
test_period, in calendar days, is the In-Sample period used for the simulation. Here we use 15 years of data;
strategy: the pre-defined strategy function which should return allocation weights for all assets at a fixed point in time (note that in strategy we select the last index, isel(time=-1)).
The function returns the time series weights for all assets. It performs automatically the last 3 operations of the single-pass evaluation:
weights = qnout.clean(weights, data, "futures")
qnout.check(weights, data, "futures")
qnout.write(weights)