I realise that I've never actually sat down and described my fully automated futures trading system in all it's detail; despite having runit for around 7.5 years now. That isn't because I want to keep it a secret - far from it! I've blogged or written books about all the various components of the system. Since I've made a fair few changes to my over the last year or so, it would seem to make sense to set down what the system looks like as of today.
You'll find reading this post much easier if you've already read my first book, "Systematic Trading". I'm expecting a christmas rush of book orders - I've bought all of my relatives copies, which I know they will be pleased to receive... for the 7th year in a row [Hope none of them are reading this, to avoid spoiling the 'surprise'!].
I'll be using snippets of code and configuration from my open source backtesting and trading engine, pysystemtrade. But you don't need to be a python or pystemtrade expert to follow along. Of course if you are, then you can find the full .yaml configuration of my system here. This code will run the system. Note that this won't work with the standard .csv supplied data files, since these don't include the 114 or so instruments in the config. But you can edit the instrument weights in the config to suit the markets you can actually trade.
In much of this post I'll also be linking to other blog posts I've written - no need to repeat myself ad infinitum.
I'll also be mentioning various data providers and brokers in this post. It's important to note that none of them are paying me to endorse them, and I have no connection with them apart from being a (reasonably) satisfied customer. In fact I'm paying them.... sadly.
Update: 5th January 2022, added many more instruments
Research summary
- Heuristic dynamic optimisation
- Dynamic optimisation using a grid search
- Fitting forecast weight by instrument and asset class (too complex!)
- Setting risk using drawdown levels (too simple!)
- A risk overlay (actually was in my old system, but not yet in my new system - a change I plan to make soon)
- Kurtosis as a trading rule (too complex and didn't work as well as expected)
- Dynamic optimisation using minimum tracking error (here and here)
- A systematic method for choosing instruments to trade or not
- Changing behaviour of rules when vol changes
- Handcrafting in it's automated variant
- Improving handcrafting correlation estimate adjustments and Sharpe Ratio estimate adjustments
- Estimating vol using a partially mean reverting method
- Skew as a trading rule
- Various other trading rules (no blog posts - read the book I'm currently writing and hope will come out next year!)
Which markets should we sample / trade
- Periodically survey the list of markets offered by my broker, interactivebrokers
- Create a list of markets I want to add data for. Gradually work my way through them (as of writing this post, my wish list has 64 instruments in it!)
- Regardless of whether I think I can actually trade them (see below), backfill their data using historic data from barchart.com
- Add them to my system, where I will start sampling their prices
- At this stage I won't be trading them until they're manually added to my system configuration
Because of the dynamic optimisation that my system uses, it's possible for me to calculate optimal positions for instruments that I can't / won't actually trade. And in any case, one might want to include such markets when backtesting - more data is always better!
I do however ignore the following markets when backtesting:
- Markets for which I have a duplicate (normally a different sized contract eg SP500 emini and micro; but could be a market traded in a different exchange) and where the duplicate is better. See this report.
- A couple of other markets where my data is just a bit unreliable (I might delete these instruments at some point unless things improve)
- The odd market which is so expensive I can't even just hold a constant position (i.e. the rolling costs alone are too much)
Then for trading purposes I ignore:
- Markets for which there are legal restrictions on me trading (for me right now, US equity sector futures; but could be eg Bitcoin for UK traders who aren't considered MiFID professionals)
- Markets which don't meet my requirements for costs and liqiuidity (again see here). This is why I sample markets for a while before trading them, to get an idea of their likely bid-ask spreads and hence trading costs
Which trading rules to use
- Momentum - EWMAC (See my first or third book)
- Breakout (blogpost)
- Relative (cross sectional within asset class) momentum (blogpost)
- Assettrend: asset class momentum (blogpost)
- Normalised momentum (blogpost)
- Acceleration
Trading rule performance
Here are the crude Sharpe Ratios for each trading rule:
breakout10 -1.19
breakout20 0.06
breakout40 0.57
breakout80 0.79
breakout160 0.79
breakout320 0.77
relmomentum10 -1.86
relmomentum20 -0.46
relmomentum40 0.01
relmomentum80 0.13
mrinasset160 -0.63
carry10 0.90
carry30 0.92
carry60 0.95
carry125 0.93
assettrend2 -0.94
assettrend4 -0.15
assettrend8 0.33
assettrend16 0.65
assettrend32 0.70
assettrend64 0.62
normmom2 -1.23
normmom4 -0.22
normmom8 0.41
normmom16 0.76
normmom32 0.82
normmom64 0.75
momentum4 -0.17
momentum8 0.46
momentum16 0.75
momentum32 0.78
momentum64 0.73
relcarry 0.37
skewabs365 0.52
skewabs180 0.42
skewrv365 0.22
skewrv180 0.33
accel16 -0.08
accel32 0.06
accel64 0.18
mrwrings4 -0.94
I say crude, because I've just taken the average performance weighting all instruments equally. In particular that means we might have a poor performance for a rule that trades quickly because I've used the performance from many expensive instruments which wouldn't actually have an allocation to that rule at all. I've highlighted these in italics. In bold are the rules that are genuine money losers:
- mean reversion in the wings
- mean reversion across assset classes
Vol attenuation
As discussed here for momentum like trading rules we see much worse performance when volatility rises. For these rules, I reduce the size of the forecast if volatility is higher than it's historic levels. The code that does that is here.
Forecast weights
weights = dict(
trendy = 0.6,
other = 0.4)
weights = dict(
assettrend= 0.15,
relmomentum= 0.12,
breakout= 0.12,
momentum= 0.10,
normmom2= 0.11,
skew= 0.04,
carry= 0.18,
relcarry= 0.08,
mr= 0.04
accel= 0.06)
weights = dict(
assettrend2= 0.015,
assettrend4= 0.015,
assettrend16= 0.03,
assettrend32= 0.03,
assettrend64= 0.03,
assettrend8= 0.03,
relmomentum10= 0.02,
relmomentum20= 0.02,
relmomentum40= 0.04,
relmomentum80= 0.04,
breakout10= 0.01,
breakout20= 0.01,
breakout40= 0.02,
breakout80= 0.02,
breakout160= 0.03,
breakout320= 0.03,
momentum4= 0.005,
momentum8= 0.015,
momentum16= 0.02,
momentum32= 0.03,
momentum64= 0.03,
normmom2= 0.01,
normmom4= 0.01,
normmom8= 0.02,
normmom16= 0.02,
normmom32= 0.02,
normmom64= 0.03,
skewabs180= 0.01,
skewabs365= 0.01,
skewrv180= 0.01,
skewrv365= 0.01,
carry10= 0.04,
carry125= 0.05,
carry30= 0.04,
carry60= 0.05,
relcarry= 0.08,
mrinasset160= 0.02,
mrwrings4= 0.02,
accel16= 0.02,
accel32= 0.02,
accel64= 0.02
)
Next all I have to do is exclude any rules which a particular instrument can't trade because the costs exceed my 'speed limit' of 0.01 SR units. So here for example are the weights for an expensive instrument, Eurodollar with zeros removed:
EDOLLAR:
assettrend32: 0.048
assettrend64: 0.048
breakout160: 0.048
breakout320: 0.048
breakout80: 0.032
carry10: 0.063
carry125: 0.079
carry30: 0.063
carry60: 0.079
momentum32: 0.048
momentum64: 0.048
mrinasset160: 0.032
mrwrings4: 0.032
normmom32: 0.032
normmom64: 0.048
relcarry: 0.127
relmomentum80: 0.063
skewabs180: 0.016
skewabs365: 0.016
skewrv180: 0.016
skewrv365: 0.016
Forecast diversification multipliers will obviously be different for each instrument, and these are estimated using by standard method.
Position scaling: volatility calculation
Instrument performance and characteristics
Instrument weights
{'Ags': 0.15,
'Bond & STIR': 0.19,
'Equity': 0.22,
'FX': 0.13,
'Metals & Crypto': 0.13,
'OilGas': 0.13,
'Vol': 0.05}
Dynamic optimisation
- Markets for which there are legal restrictions on me trading (for me right now, US equity sector futures; but could be eg Bitcoin for UK traders who aren't considered MiFID professionals)
- Markets which don't meet my requirements for costs and liqiuidity (again see here)
Backtest properties
('min', '-13.32'), ('max', '11.26'), ('median', '0.09578'), ('mean', '0.1177'), ('std', '1.44'),
('ann_mean', '30.11'), ('ann_std', '23.04'),
('sharpe', '1.307'), ('sortino', '1.814'), ('avg_drawdown', '-9.205'),
('time_in_drawdown', '0.9126'), ('calmar', '0.7017'), ('avg_return_to_drawdown', '3.271'),
('avg_loss', '-0.9803'), ('avg_gain', '1.06'), ('gaintolossratio', '1.081'),
('profitfactor', '1.26'), ('hitrate', '0.5383'),
('t_stat', '9.508'), ('p_value', '2.257e-21')
[[('min', '-21.84'), ('max', '45.79'), ('median', '2.175'), ('mean', '2.55'),
('std', '7.768'), ('skew', '0.9368'), ('ann_mean', '30.55'), ('ann_std', '26.91'),
('sharpe', '1.135'), ('sortino', '2.177'), ('avg_drawdown', '-5.831'),
('time_in_drawdown', '0.6485'), ('calmar', '0.8371'),
('avg_return_to_drawdown', '5.239'), ('avg_loss', '-4.465'), ('avg_gain', '6.741'),
('gaintolossratio', '1.51'), ('profitfactor', '2.527'),
('hitrate', '0.626'), ('t_stat', '8.193'), ('p_value', '1.457e-15')]
A great and generous post - and to think, I was just about to buy the first book. No need now surely!!
ReplyDelete"the costs exceed my 'speed limit' of 0.01 SR units".
(0.1?)
limit of 0.01 SR per trade
DeleteIn the book - as you will know when you've bought it - I suggest a maximum of 0.13 SR per year. So 13 trades a year including rolls - I'd suggest you couldn't go much slower.
I've contemplated many thanks. A full quant options trading system would be a lot of work (buy/sell options and delta hedge forecasting vol direction). An integrated options/futures system would be even more work (forecast both vol direction and price direction, trade futures and/or options accordingly). Selling options for premium based on futures forecasts would be less work, but without some kind of overlay that knows about gamma would be crazy risky (I assume no delta hedging implied in your question).
ReplyDeleteHello, Mr. Carver!
ReplyDeleteI studied your first and third books with pleasure (to be honest, I hardly understood the first one, but the third one clarified a lot).
More than a month ago, I run my own trading system entirely based on the Starting System, improved using the ideas of the third and fourth chapters (let's call it FISS - Fully Improved Starting System). Now I have 8 instruments, 13 rules and non-binary trading.
My system works stably (in my humble opinion). Therefore, I began to study additional information to understand what I am doing after all)) And, perhaps, to improve my strategy.
I noticed that you are using at your own system (as opposed to FISS):
1) for the carry rule you use not raw carry, but smoothed EMA90
2) the daily volatility (risk) of the instrument is calculated not as std(25), but (0.7 * std (30) + 0.3 * std (2560)) / 2
Please advise whether I should make these changes to FISS or pay attention to something else?
Many thanks!
These changes will reduce turnover and therefore costs, as you're doing non binary trading they're probably worth doing as simple changes. You already have 8 instruments so you've already done the most important improvement of all.
DeleteIs the forecast scalar for the smoothed carry rule the same like for the raw carry rule (around 30)? I know, that I need run my own backtest to answer this question, but I don't ready for now. Thank you.
DeleteApplying a moving average smooth will not affect the distributional properties of a series, except at the extremes. So yes, same scalar.
DeleteHi Rob,
ReplyDeleteI just purchased your book and am loving it so far. Do you have code in your github for the system backtest you shared in this post?
Thanks!
https://github.com/robcarver17/pysystemtrade/blob/master/systems/provided/rob_system/run_system.py
DeleteThanks Rob. I went through the installation guide and set everything, but when I run this system nothing seems to happen and all I get is the following output:
Delete(pysystemtrade) C:\Users\armar\PycharmProjects\pysystemtrade\systems\provided\rob_system>python run_system.py
Private configuration private.private_config.yaml does not exist; no problem if running in sim mode
C:\Users\armar\anaconda3\envs\pysystemtrade\lib\site-packages\arctic-1.79.2-py3.8.egg\arctic\_util.py:6: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
C:\Users\armar\anaconda3\envs\pysystemtrade\lib\site-packages\arctic-1.79.2-py3.8.egg\arctic\store\_pandas_ndarray_store.py:6: FutureWarning: The Panel class is removed from pandas. Accessing it from the top-level namespace will also be removed in the next version
Sorry I assumed you were already familiar with pysystemtrade. You will need to read the documentation.
DeleteI note your allocation to vol has decreased a lot to ~5% compared to the space you gave in the books. Can you share what inspired this? Is this due to performance / predictability or having more instruments to trade?
ReplyDeleteMore instruments; 5% for three instruments (only two of which I can actually take positions in) is ~1.67% each, versus an average of ~0.67% so I'm still overweight on an instrument basis, reflecting the fact that I think vol is probably an asset class in itself (https://qoppac.blogspot.com/2016/08/trend-following-and-correlation.html)
DeleteHey rob, have you done a post explaining your "accel" rule? I would be interested if not. Cheers.
ReplyDeleteNo, will be in the new book.
DeleteHi Rob,
ReplyDeleteI'm curious as to why you don't run faster mean reversion in your system (apart from the "mr wings" at the very extremes)?
As I'm sure you know, faster mean reversion is complementary to medium and longer term trend strategies, and should smooth out some of the drawdowns from reversals in extreme trending markets as well as provide some pnl in years when things are mostly just going sideways.
This is actually discussed in the new book. But basically, it will probably be too expensive, unless I trade it in a particular way that requires much more capital than I have.
DeleteHi Rob, great post as always. My question is I want to ask about the past performance of your carry rule in FX. I'm trying to replicate it, but it seems like the returns are not great (could be that I replicated it wrongly, hence the question). A bit surprised to see that you said if you could only trade 1 rule it would be carry. It would be great if you can share your carry rule performance broken down to groups (bonds, FX, equities etc). Thanks!
ReplyDeleteIn the new book :-)
DeleteHi Rob. I was curious about your "Relative (cross sectional within asset class) momentum" but I couldn't find any info in the linked blogpost (in the paragraph about Cross sectional within assets, you write about a mean reversion strategy).
ReplyDeleteI think this strategy is the same as the "rel_mom" rule in pysystemtrade which, from my understanding of the code, it actually is a momentum signal. But in the block comment within the code you mention "Cross sectional mean reversion within asset class" which confuses me.
Thanks
Yes it's cross sectional momentum, but it actually uses the old mean reversion code with a minus sign. Described in the new book.
DeleteHi Rob,
ReplyDeleteI was doing some testing with normally distributed fake data to check forecast scalars on your EWMAC rules, and I kept getting significantly higher scalars than are mentioned in the book, “leveraged trading”. Then, when I tried to determine scalars based on the absolute value of close minus slow moving average (instead of fast moving average minus slow moving average), I began getting very similar results to yours. When determining the scalar, do you use the average for distance between close and slow MA, even though the actual rule is regarding a fast minus a slow. If so, why? (Also perfectly possible that I messed up somewhere in determining average scalars).
Thank you!
"When determining the scalar, do you use the average for distance between close and slow MA, even though the actual rule is regarding a fast minus a slow." .... no that would make no sense at all! How did you generate the fake data? Does it have trends in it, or is it just a random walk? If the latter, I would expect significantly higher scalars.
DeleteOk, I see. It's a random walk. (I'm a beginner in this).
DeleteThanks Rob!
One more thing. . . Just want to make sure I'm calculating the EWMA properly. Currently using excel, and there's no built-in function for it. Let's say I'm calculating the 4 span EWMA. I start with a simple MA of the first four days just to get a number to do further calculations on. Then, on day 5, the formula would be:
Delete(2 / [4 + 1]) * current close + (1 - (2 / [4 + 1])) * previous average.
Then I'd fill down that formula for the rest of the column.
Is this more or less correct?
Hi Rob,
ReplyDeleteWhen calculating EWMAC on etfs as opposed to futures, the forecast usually ends up lower. (For example, the current forecast for the SPY etf [using an average of the 32, 64, 128, and 256 crossovers] is 7.2, while the forecast for ES futures is only 4. This is similarly true for the U.S. ten year future and its corresponding etf, as well as many others). I assume this is because the futures price is affected by the risk free rate while the etf isn't.
What would be the best way to adjust for this?
Thanks
"I assume this is because the futures price is affected by the risk free rate while the etf isn't." not quite it's because a backadjusted futures price is a total return series which includes carry eg dividend yield or interest less funding cost. Like for like you should do your trend signal on a cumulated total return series for the ETF; otherwise increase the % of carry in your system to compensate if you want to match.
DeleteHi Rob,
ReplyDeleteI might be missing something obvious here but how did you go from the handcrafted weights to the EDOLLAR example weights by just removing expensive trading rule variations?