forked from robcarver17/pysystemtrade
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun_system_classic.py
200 lines (148 loc) · 6.42 KB
/
run_system_classic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
this:
- gets capital from the database (earmarked with a strategy name)
- runs a backtest using that capital level, and mongodb data
- gets the final positions and position buffers
- writes these into a table (earmarked with a strategy name)
"""
from syscore.objects import success, arg_not_supplied, missing_data
from sysdata.config.configdata import Config
from sysdata.data_blob import dataBlob
from sysobjects.production.optimal_positions import bufferedOptimalPositions
from sysobjects.production.tradeable_object import instrumentStrategy
from sysproduction.data.currency_data import dataCurrency
from sysproduction.data.capital import dataCapital
from sysproduction.data.contracts import dataContracts
from sysproduction.data.positions import dataOptimalPositions
from sysproduction.data.sim_data import get_sim_data_object_for_production
from sysproduction.data.backtest import store_backtest_state
from syslogdiag.log_to_screen import logtoscreen
from systems.provided.futures_chapter15.basesystem import futures_system
from systems.basesystem import System
class runSystemClassic(object):
def __init__(
self,
data: dataBlob,
strategy_name: str,
backtest_config_filename=arg_not_supplied,
):
if backtest_config_filename is arg_not_supplied:
raise Exception("Need to supply config filename")
self.data = data
self.strategy_name = strategy_name
self.backtest_config_filename = backtest_config_filename
def run_backtest(self):
strategy_name = self.strategy_name
data = self.data
base_currency, notional_trading_capital = self._get_currency_and_capital()
system = self.system_method(
notional_trading_capital=notional_trading_capital,
base_currency=base_currency,
)
updated_buffered_positions(data, strategy_name, system)
store_backtest_state(data, system, strategy_name=strategy_name)
def _get_currency_and_capital(self):
data = self.data
strategy_name = self.strategy_name
capital_data = dataCapital(data)
notional_trading_capital = capital_data.get_capital_for_strategy(strategy_name)
if notional_trading_capital is missing_data:
# critical log will send email
error_msg = (
"Capital data is missing for %s: can't run backtest" % strategy_name
)
data.log.critical(error_msg)
raise Exception(error_msg)
currency_data = dataCurrency(data)
base_currency = currency_data.get_base_currency()
self.data.log.msg("Using capital of %s %.2f" % (base_currency, notional_trading_capital))
return base_currency, notional_trading_capital
# DO NOT CHANGE THE NAME OF THIS FUNCTION; IT IS HARDCODED INTO CONFIGURATION FILES
# BECAUSE IT IS ALSO USED TO LOAD BACKTESTS
def system_method(
self, notional_trading_capital: float = arg_not_supplied, base_currency: str = arg_not_supplied
) -> System:
data = self.data
backtest_config_filename = self.backtest_config_filename
system = production_classic_futures_system(
data,
backtest_config_filename,
log=data.log,
notional_trading_capital=notional_trading_capital,
base_currency=base_currency,
)
return system
def production_classic_futures_system(
data: dataBlob,
config_filename: str,
log=logtoscreen("futures_system"),
notional_trading_capital: float = arg_not_supplied,
base_currency: str = arg_not_supplied,
) -> System:
log_level = "on"
sim_data = get_sim_data_object_for_production(data)
config = set_up_config(data, config_filename)
# Overwrite capital and base currency
if notional_trading_capital is not arg_not_supplied:
config.notional_trading_capital = notional_trading_capital
if base_currency is not arg_not_supplied:
config.base_currency = base_currency
system = futures_system(data=sim_data, config=config)
system._log = log
system.set_logging_level(log_level)
return system
def set_up_config(data: dataBlob, config_filename: str) -> Config:
production_config = data.config
backtest_file_config = Config(config_filename)
# 'later elements overwrite earlier ones'
config = Config([production_config, backtest_file_config])
## this is also done by the system, but more transparent to do it here
config.fill_with_defaults()
return config
def updated_buffered_positions(data: dataBlob, strategy_name: str, system: System):
log = data.log
data_optimal_positions = dataOptimalPositions(data)
list_of_instruments = system.get_instrument_list()
for instrument_code in list_of_instruments:
lower_buffer, upper_buffer = get_position_buffers_from_system(
system, instrument_code
)
position_entry = construct_position_entry(
data=data,
system=system,
instrument_code=instrument_code,
lower_buffer=lower_buffer,
upper_buffer=upper_buffer,
)
instrument_strategy = instrumentStrategy(
instrument_code=instrument_code, strategy_name=strategy_name
)
data_optimal_positions.update_optimal_position_for_instrument_strategy(
instrument_strategy=instrument_strategy, position_entry=position_entry
)
log.msg(
"New buffered positions %.3f %.3f"
% (position_entry.lower_position, position_entry.upper_position),
instrument_code=instrument_code,
)
def get_position_buffers_from_system(system: System, instrument_code: str):
buffers = system.portfolio.get_buffers_for_position(
instrument_code
) # get the upper and lower edges of the buffer
lower_buffer = buffers.iloc[-1].bot_pos
upper_buffer = buffers.iloc[-1].top_pos
return lower_buffer, upper_buffer
def construct_position_entry(
data: dataBlob,
system: System,
instrument_code: str,
lower_buffer: float,
upper_buffer: float,
) -> bufferedOptimalPositions:
diag_contracts = dataContracts(data)
reference_price = system.rawdata.get_daily_prices(instrument_code).iloc[-1]
reference_contract = diag_contracts.get_priced_contract_id(instrument_code)
position_entry = bufferedOptimalPositions(
lower_buffer, upper_buffer, reference_price, reference_contract
)
return position_entry