This document describes how you'd make changes to strategies in production: adding new strategies, or replacing existing ones.
It is fairly 'bare bones', and you should have read and understand the production documentation first.
It's important that the following steps are followed, in order.
- Set up any new instruments and get their prices sampling
- Ensure you have a working backtest, and create a production configuration .yaml file
- Create custom classes to run the strategy
- Make a backup of your data
- Stop any processes running
- Update private_config.yaml and private_control_config.yaml
- Update strategy capital
- Check the production backtest will run
- Transfer positions between strategies
- Run any strategy backtests that will be closing or reducing in capital
- Ensure position and trade limits are appropriate
- Manually generate instrument orders
- Run reports
- Restart processes
- Delete replaced strategies from configuration files
- Clean up
Not all the steps are described in detail in this document, see the production documents for details.
If your new strategy has new instruments, set these up first. You'll need to have their sampled data to do your backtests.
If you are using a completely vanilla 'out of the box' strategy that can be run with the default provided stages then this isn't necessary. For clarity these are the default strategies:
- Classic system
- Dynamic system (as described here)
Otherwise you will need to create your own runSystem class. There is an example here. Notice that it overrides system_method
to create a different system from the default; that in turn is built up from some custom stages.
You may also need to override the run_backtest
method if you need your strategy to do something different in terms of saving optimised positions (again that is the principal difference between the classic and dynamic systems).
If you are using a completely vanilla 'out of the box' strategy that can be run with the default order creation functions then this isn't necessary. For clarity these are the default strategies:
- Classic system
- Dynamic system (as described here)
Otherwise you'll need to write an order management function. You will need to overwrite the method get_required_orders
.
If you are using a completely vanilla 'out of the box' strategy that can be run with the default reporting functions then this isn't necessary. For clarity these are the default strategies:
- Classic system
- Dynamic system (as described here)
Otherwise you'll need to write a reporting function. If your strategy is similar enough to the 'classic' system, you can reuse the code as the dynamic system does.
Even if you're replacing a strategy I strongly advise that you initially add your new strategies to configuration files, keeping the old strategies in place for the time being.
In your private_control_config.yaml
file, for example:
process_configuration_methods:
run_systems:
... existing systems ...
dynamic_TF_carry:
max_executions: 1
object: private.systems.dynamic_carry_trend.run_system.runMySystemCarryTrendDynamic
backtest_config_filename: private.systems.dynamic_carry_trend.production.yaml
run_strategy_order_generator:
... existing systems ...
dynamic_TF_carry:
object: sysexecution.strategies.dynamic_optimised_positions.orderGeneratorForDynamicPositions
max_executions: 1
Notice that we refer to the custom run_system and order_generator classes that we've created, or the provided classes. We also refer to the production.yaml backtest configuration file.
In your private_config.yaml
file, for example:
strategy_list:
... existing systems ...
dynamic_TF_carry:
load_backtests:
object: private.systems.dynamic_carry_trend.run_system.runMySystemCarryTrendDynamic
function: system_method
reporting_code:
function: sysproduction.strategy_code.report_system_dynamic_optimised.report_system_dynamic
strategy_capital_allocation:
function: sysproduction.strategy_code.strategy_allocation.weighted_strategy_allocation
strategy_weights:
dynamic_TF_carry: 99.99
medium_speed_TF_carry: 0.01
Again we refer to the custom run_system and reporting classes, or to default provided classes. Notice that the contents of strategy_weights will depend on exactly what you'd like to do. Here we're replacing medium_speed_TF_carry
with dynamic_TF_carry
. Of course we could also want to do this more gradually starting with just a small notional amount of capital to check everything is working, or only allocate part of our capital to the new strategy rather than all of it.
Next we need to update the strategy capital (using the provided script update_strategy_capital
). This is required to ensure the backtest will actually run. You should do this even if you're going to start with a nominal amount of capital just to make sure everything works.
Using the update_system_backtests
script make sure that the production system runs okay, and generates optimal positions.
If you are replacing a strategy, wholly or partially, then it makes sense to transfer the positions across. Otherwise you'll do costly trading as one strategy closes it's positions, and the other opens up new ones.
There is a script that does this by generating pseudo instrument orders at the current market price. For example (python):
from sysinit.futures.strategy_transfer import *
transfer_positions_between_strategies('medium_speed_TF_carry', 'dynamic_TF_carry')
This tidying up stage ensures that the optimal positions for existing strategies are correctly adjusted for the new capital, stops position breaks appearing, and ensures that we won't generate unnecessary orders for the old strategy.
Using the interactive_controls script, you may want to create strategy specific limits or tweak existing limits.
I'd recommend running the update_strategy_orders
script. You can run these for both the new and existing strategies. If you're replacing an existing strategy, all the optimal positions should be zero, and if you've transferred all the positions it shouldn't generate any traders.
It may be worth running interactive_order_stack
and looking at both positions and the instrument order stack to check all is as expected.
I'd recommend running the full suite of reports to make sure everything works, and also to check that all is as it should be (the strategy report should be especially interesting in this respect).
You can now restart your running processes.
Check that any new instruments can actually trade, i.e. you don't have any unforseen regulatory issues.
FIXME: To do
In this section of the document I describe how to implement specific provided strategies.
FIX ME
This is the strategy as described here.
Under ignore_instruments in the .yaml configuration, I suggest you include only instruments which are duplicated for different multipliers (i.e. we should have only one of SP500 and SP500_micro). Do not include here untradeable instruments. This also means that for instrument weight calculations we'll include potentially untradeable instruments.
The shadow cost is a key variable which is set in the private_config.yaml file (not the backtest configuration file, since it is used 'outside' the backtest in the strategy order generation). The default value is 10, but you may want to initially begin with a very large value (eg 500) and gradually reduce it over the first few days. This will produce a more gradual adjustment from old to new strategy positions, although bear in mind that any strategy position with the wrong sign will immediately be closed regardless of the shadow_cost value unless you set this instrument to don't trade.
The optimal positions are output from the strategy using a special '-raw' suffix which means they won't normally be displayed. However they are shown in strategy reports.
It's particularly important to ensure position limits are in place, because these are used by the dynamic optimisation. I'd suggest using the auto-populate function from the interactive_controls script.
It's vital that you set don't trade or reduce only trade overrides for instruments that you don't wish to trade (these are set in interactive_controls). Reasons for this can include:
- Too expensive (see the costs report)
- Not enough contracts traded (see liquidity report)
- Not enough risk traded (see the liquidity report)
- Regulatory issues (eg Reg 871, or UK restrictions on crypto derivatives for non MFID professional clients)
- Tick data not available / too expensive (if you're using another source to get daily data)
Obviously if you have no existing position then it doesn't matter if you use don't trade or reduce only flags.
The actual optimal positions, used to generate instrument orders, are of simple integer values. In addition they also contain a lot of hidden diagnostic information about the optimisation, which is displayed by the strategy report and also by the interactive_diagnostics function to display optimal positions.