PYTHON TOOLBOX
This tutorial explains the use of the Quantiacs Python 3.7 Toolbox.
The easiest way to get going with python is to use the Anaconda as dependencies will be automatically managed and you will be able to work in a dedicated environment for Quantiacs.
For installing Anaconda on Windows, MacOS and Linux please see the Anaconda page.
INSTALLING THE TOOLBOX
Once Anaconda is installed you can create an environment for Quantiacs as follows:
conda create --name quantiacsbox
Then, activate the environment using:
conda activate quantiacsbox
and install the Quantiacs toolbox:
conda install -c quantiacspkg quantiacstoolbox
Anaconda will take care of installing the required dependencies.
Installing the toolbox can be also done via the pip installer in Python. Package is available here.
The source code is hosted on github and, once downloaded, can by installed using setuptools.
If you have any issues write us to
info@quantiacs.com and we’ll respond ASAP.
RUNNING A SYSTEM
Once the toolbox is installed, you can run any strategy defined in a file called 'strategy.py' by typing:
python strategy.py
You can find several examples at the Quantiacs github repository. You can also copy and paste with your editor the simple strategy we provide at Trading System 101.
DEVELOPING YOUR OWN SYSTEM
Your trading system only has to define allocations or the percentage of your capital you wish to apply to each equity in your market list. When defining your trading system, you must label the function definition as “myTradingSystem”. A typical function definition is shown below.
def myTradingSystem(DATE, OPEN, HIGH, CLOSE, VOL, exposure, equity, settings):
Within 'myTradingSystem' you can use any prediction method and mathematical technique imaginable.
PACKAGES
At the moment Quantiacs supports:
-
keras
-
lightgbm
-
matplotlib
-
scikit-learn
-
scipy
-
seaborn
-
statsmodels
-
TA-lib
-
tensorflow
-
torch
-
xgboost
THE DATA
Data can be requested for your trading system through the arguments of the function definition. The current data types provided are the primary market data, 'DATE', 'OPEN', 'HIGH', 'LOW', 'CLOSE' and 'VOL'. These should be written in uppercase letters.
'DATE' is a column vector of size ['nLookback' x 1] that contains the ascending dates of the last nLookback trading days in the format YYYYMMDD. The most recent data is at the end of the vector.
'OPEN', 'HIGH', 'LOW', 'CLOSE', 'VOL' are matrices of size ['nLookback' x 'nMarkets']. E.g. 'OPEN' has the last 504 open values (rows) for the 100 markets (columns) in our portfolio.
'exposure' and 'equity' are also matrices of size ['nLookback' x 'nMarkets']. 'exposure' stores the last 'nLookback' values of realized exposure of your trading system. These are the quantities you held in your simulated broker account. 'equity' is the cumulated sum of your holding quantities times the change in price minus trading costs. Or simply put: It is what you made with each market over your lookback period.
'settings' contain your environment variables and can be defined as described below.
SYSTEM SETTINGS
For evaluation of a trading system, there must also be a settings function. This defines the markets you wish to trade, your budget, and market model parameters. This function is declared as seen below:
def mySettings():
within mySettings the market list and evaluation settings must be defined. The function should look something like this:
def mySettings():
settings['markets'] = ['F_AD', 'F_BO', 'F_BP', 'F_C', 'F_CD', 'F_CL', 'F_DX', 'F_EC', 'F_ES', 'F_FV', 'F_GC', 'F_HG', 'F_HO', 'F_LC', 'F_LN', 'F_NG', 'F_NQ', 'F_RB', 'F_S', 'F_SF', 'F_SI', 'F_SM', 'F_TY', 'F_US', 'F_W', 'F_YM']
settings['lookback']=504
settings['budget']=10**6
settings['slippage']=0.05
return settings
settings['markets'] Defines the markets you wish to trade. Any market on the markets page can be defined in this list.
settings['lookback'] An int definining the length of historical data passed to myTradingSystem .
settings['budget'] An int defining the initial budget of your simulated brokerage account.
settings['slippage'] A float defining slippage
settings['beginInSample'] A string that defines the beginning of the time period over which you wish to evaluate your period. Must be in the form of YYYYMMDD
settings['endInSample] A string that defines the end date over which your system will be evaluated. Must be in the form YYYYMMDD
EVALUATING YOUR SYSTEM
If you have already developed your own system, you can run it by importing the toolbox and running the runts function with the file path to your system. Evaluation follows from:
if __name__ == "__main__":
import quantiacsToolbox
returnDict = quantiacsToolbox.runts(__file__)
quantiacsToolbox.runts will evaluate your system on your settings and return a dictionary of performance values and a plot of the your system’s performance.
returnDict contains the following:
returnDict['tsName'] a string that contains the name of your trading system
returnDict['fundDate'] a list of the dates available to your trading system
returnDict['returns'] a list of the returns your trading system.
returnDict['fundEquity'] a list defining the equity of your portfolio
returnDict['marketEquity'] a list defining the equity earned in each market.
returnDict['marketExposure'] a list containing the equity made from each market in settings[‘markets’]
returnDict['errorLog'] a list describing any errors made in evaluation
returnDict['runtime'] a float containing the time required to evaluate your system.
returnDict['evalDate'] an int that describes the last date of evaluation for your system (in YYYYMMDD format)
returnDict['stats'] a dict containing the performance statistics of your system
returnDict['settings'] a dict containting the settings defined in mySettings
SUBMITTING YOUR SYSTEM
To submit a system to the Quantiacs server, simply run the submit function with the filepath to your system and the name you wish to use for your system. Submission must look like this:
import quantiacsToolbox
quantiacsToolbox.submit(‘/Path/to/your/TradingSystem.py’,’mySystemName’)
You can also upload your file using the upload form in your area, or pressing the Submit button in the GUI.
MATLAB TOOLBOX
The Toolbox contains
-
End-of-day data of the Stocks and Futures.
-
sample trading system functions trendfollowing and meanreversion
-
A function runts that runs and evaluates your tradingsystem
-
A function submit that uploads your tradingsystem to Quantiacs
PACKAGES
We support the following Matlab ® Toolboxes:
-
Curve Fitting
-
Deep Learning
-
Econometrics
-
Financial
-
Global Optimization
-
Optimization
-
Signal Processing
-
Statistics and Machine Learning
WRITE YOUR TRADINGSYSTEM
Write a Matlab ® function with the following prototype:
function [p, settings] = ts(DATE, OPEN, HIGH, LOW, CLOSE, VOL, exposure, equity, settings)
Your function will be called each day of the backtesting period with the most recent data as arguments. You return your desired exposure p for the next day.
THE ARGUMENTS
OPEN, HIGH, LOW, CLOSE, VOL, exposure and equity are each matrices of size [lookback x markets], where lookback represents the maximum lookback period in days and markets the number of markets.
If a market is not defined, its values are set to NaN. If data is missing within the series (for example due to a holiday) the missing data is replaced by the last known value with zero volume.
ARGUMENT DESCRIPTION
-
DATE
-
a date integer in the format YYYYMMDD
-
OPEN
-
the first price of the session
-
HIGH
-
the highest price in session
-
LOW
-
the lowest price in session
-
CLOSE
-
the last price of the session
-
VOL
-
the number of stocks traded that day
-
exposure
-
the realized quantities of your tradingsystem
-
equity
-
the cumulated performance of each market
-
settings
-
a structure that stores all relevant settings for the simulation.
For the contests your algorithm will always be called with the following settings:
settings.lookback = 504;
settings.budget = 1000000;
settings.slippage = 0.05;
THE RETURN VALUES
p is a vector of size 1 x markets with your desired holding quantities for the next day.
-
pi = 0
-
you don't hold asset i in your portfolio
-
pi > 0
-
you are invested long in asset i with weight p
-
pi < 0
-
you are invested short in asset i with weight p
You can modify the settings to fit your needs. But please note, that your algorithm will be called with the default settings for the contest.
TEST YOUR TRADINGSYSTEM
You can run your tradingsystem by typing
runts('tsFilename')
at the Matlab prompt. runts loads the market data and calls your tradingsystem for each day of the backtest with the most recent market data. It simulates the equity curve for your output values p. runts returns a struct with the exposures and the equity curves of the trading system. It computes the performance numbers of your system and plots its Factsheet.
DEVELOP A TREND FOLLOWING SYSTEM
In this tutorial we write a simple trend following algorithm for Futures.
We will create step by step the following Matlab® function:
function [p, settings] = trendfollowing(DATE, OPEN, HIGH, LOW, CLOSE, VOL, exposure, equity, settings)
settings.markets = {'F_AD', 'F_BO', 'F_BP', 'F_C', 'F_CD', 'F_CL', 'F_DX', 'F_EC', 'F_ES', 'F_FV', 'F_GC', 'F_HG', 'F_HO', 'F_LC', 'F_LN', 'F_NG', 'F_NQ', 'F_RB', 'F_S', 'F_SF', 'F_SI', 'F_SM', 'F_TY', 'F_US', 'F_W', 'F_YM'};
settings.sampleend = 20121231;
settings.lookback = 504;
nMarkets = size(CLOSE,2);
periodLong = 200;
periodRecent = 40;
smaLong = sum(CLOSE(end-periodLong+1:end,:)) /periodLong;
smaRecent = sum(CLOSE(end-periodRecent+1:end,:)) /periodRecent;
long = smaRecent >= smaLong;
p = ones(1, nMarkets);
p(~long) = -1;
end
We implement a trend following classic: we compare two moving averages of the same data series. A moving average is the average price of an asset over the last n days. So we simply compare two differently averaged prices of the same stock.
We call the market bullish and expect rising prices, if a recent average is higher than a long term average. If for example the average price of a stock in the last 40 trading days was higher than the average price of the same stock in the last 200 tradingdays, prices went up recently. Since we try to follow the trend, we want to be invested long in that situation.
On the other hand, if the average price of the last 40 trading days dropped below the average price of the last 200 trading days, we assume the market is bearish. We expect further falling prices, and we want to be invested short.
In short, we measure in a very simple way what happened with the market recently, and assume a continuation of that price move.
The function template looks like this:
function [p, settings] = trendfollowing(DATE, OPEN, HIGH, LOW, CLOSE, VOL, exposure, equity, settings)
end
The arguments
DATE, OPEN, HIGH, LOW, CLOSE and
VOL are primary market variables, and thus written in upcase letters.
DATE is a column vector of size [504x1] that contains the ascending dates of the last 504 tradingdays in the format YYYYMMDD. The most recent data is at the end of the vector.
OPEN, HIGH, LOW, CLOSE, VOL are matrices of size [lookback x markets]. E.g.
OPEN has in our example the last 504 open values (rows) for the markets (columns) in our portfolio.
exposure and
equity are also matrices of size [
lookback x
markets].
exposure stores the last 504 values of realized exposure of your tradingsystem. These are the percentage allocations for each asset you held in your simulated broker account the last two years.
equity is the cumulated sum of your holding quanities times the change in price minus trading costs. Or simply put: It is what you made with each asset in the last 2 years.
The settings struct stores all relevant settings. We could change the lookback with a different value for
settings.lookback or the portfolio composition with another
settings.markets list. But for this tutorial we’ll go with the defaults: A lookback length of 504 trading days, which covers approximately the last two years of data for Futures.
First we define three variables:
nMarkets = size(CLOSE,2);
periodLong = 200;
periodRecent = 40;
nMarkets stores the number of markets, by measuring how many columns the Matrix
CLOSE has.
periodLong and
periodRecent are the two lookback periods, that we want to use to compute the average price.
Next we compute the two average prices for each market, and store the results in vectors of size [1xnMarkets]:
smaLong = sum(CLOSE(end-periodLong+1:end,:)) / periodLong;
smaRecent = sum(CLOSE(end-periodRecent+1:end,:)) / periodRecent;
What happened here? Let’s start with
CLOSE(end-periodLong+1:end,:). It uses the convenient way Matlab® provides to address only a part of a larger matrix.
Generally you can address specific values of a matrix if you use their coordinates:
CLOSE(504,nMarkets) returns you the last
CLOSE of market 'nMarkets'.
end can be used to address the last element of a row or column.
CLOSE(end,end) gives you the last
CLOSE of the last market. In this case
CLOSE(end, end) is equivalent to
CLOSE(504,nMarkets).
Not only can you address single numbers from a matrix, but you can also address arbitrary sub-matrices:
CLOSE(1:10,2) returns you the first 10 values of
CLOSE of the second market.
The
: returns all rows/columns. It is equivalent to
1:end. In our case it returns all market columns.
So what we were doing with
CLOSE(end-periodLong+1:end,:) was simply returning a sub-matrix of size [200xnMarkets] from
CLOSE of the last 200 days, and all markets.
We then use
sum to sum up the sub-matrix column wise. Thus we get a vector of size [1xnMarkets], containing the sum of the last 200 values of
CLOSE for each market.
Since we don’t want the sum but an average price, we have to divide each element of the vector by 200. The result of this computation is assigned to the variable
smaLong, a vector of size [1xnMarkets].
We compute
smaRecent the same way.
Now we can compare the two vectors and assign the result to a vector
long:
long = smaRecent >= smaLong;
smaRecent and
smaLong have the same size, [1xnMarkets]. That’s why we can perform an element-wise comparison, and get a boolean vector
long. It is 1, when
smaRecent is greater or equal to
smaLong and 0 otherwise.
Our trading system is almost complete. Now we just have to allocate the proper percentages. We start by defining our output value
p as a row vector of 'nMarkets' ones. Then we replace the 1 with -1 wherever our field
long is false, since we want to be short in the market if there is no long signal:
p = ones(1, nMarkets);
p(~long) = -1;
The trading system allocates the same weight, either 1 or -1 to every market. Every market is traded with 1/nMarkets of the available capital.
That’s it, the first tradingsystem is ready. If you type runts('trendfollowing') at the Matlab® prompt, and hit return you see the poor result.
Surely you can do better than that.
OPTIMIZATION
In the developing tutorial, we chose our parameters periodLong and periodRecent at 200 and 40 in trendfollowing simply because it appeared reasonable. That choice was somewhat arbitrary. Maybe there is a better set of parameters for our system?
To compute several different parameterizations of the same system we can use the optimize function. To use this feature place a comment next to the parameter you wish to scan. Within the comment place the parameter domain and step-size between two pound signs (similar to the Matlab vector notation). For our example system, we will scan over periodLong and periodRecent:
periodLong = 200; %#[150:10:300]#
periodRecent = 40; %#[20:5:60]#
With these settings we choose to vary periodLong from 150 to 300 in steps of 10, and periodRecent from 20 to 60 in steps of 5. To evaluate the entire parameter space, input the following into the Matlab prompt.
res = optimize('trendfollowing')
Press return to compute all instances of that parameter space. The result of the optimization is stored in the struct res. It contains the statistics of each instance--Sharpe Ratio, average yearly performance, average volatility, etc.
To find the parameters of the system with the best Sharpe Ratio, enter into the prompt:
[m mIx] = max(res.sharpe)
Next we just only need look up the parameter values for the optimal instance. To find the parameter values, we find the parameter value at the optimal index, mIx. To do this type the following at the Matlab prompt:
res.periodLong(mIx)
res.periodRecent(mIx)
SUBMITTING
Type
submit('TSFile', 'nameTS') at the Matlab prompt.
TSFile is the name of the .m file on your computer,
nameTS is the name that you want to give your Tradingsystem on Quantiacs. If you don’t enter a name, the filename will be used as default nameTS.
You will be prompted to log in to complete the upload.
Alternatively you can upload a tradingsystem via the Upload button on the Website, or from the GUI.
TRAIN YOUR MATLAB SKILLS
If you want to improve your Matlab skills we recommend Mathwork's Cody. It's a good problem solving challenge that features a lot of small programming problems with gradually increasing difficulty.
If you are already familiar with Matlab and want to improve your skills to expert level we recommend you solve a problem on Project Euler... or write an algorithm on Quantiacs!