fa-quant 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. fa_quant-1.2.1.dist-info/METADATA +236 -0
  2. fa_quant-1.2.1.dist-info/RECORD +106 -0
  3. fa_quant-1.2.1.dist-info/WHEEL +4 -0
  4. fa_quant-1.2.1.dist-info/entry_points.txt +2 -0
  5. fa_quant-1.2.1.dist-info/licenses/LICENSE +21 -0
  6. financial_agent/__init__.py +1 -0
  7. financial_agent/agents/__init__.py +0 -0
  8. financial_agent/agents/claude/__init__.py +0 -0
  9. financial_agent/agents/gpt/__init__.py +0 -0
  10. financial_agent/agents/grok/__init__.py +0 -0
  11. financial_agent/dl/__init__.py +0 -0
  12. financial_agent/dl/models/__init__.py +0 -0
  13. financial_agent/dl/models/baseline_model.py +23 -0
  14. financial_agent/dl/preprocessing/__init__.py +0 -0
  15. financial_agent/dl/preprocessing/data_windowing.py +135 -0
  16. financial_agent/harness/__init__.py +73 -0
  17. financial_agent/harness/__main__.py +430 -0
  18. financial_agent/harness/_system/factor_scoring.py +175 -0
  19. financial_agent/harness/_system/settlement_ledger.py +1222 -0
  20. financial_agent/harness/audit.py +393 -0
  21. financial_agent/harness/equity_policy.toml +179 -0
  22. financial_agent/harness/fingerprint.py +55 -0
  23. financial_agent/harness/gates.py +522 -0
  24. financial_agent/harness/handlers.py +77 -0
  25. financial_agent/harness/helpers.py +47 -0
  26. financial_agent/harness/kernels.py +696 -0
  27. financial_agent/harness/ledger.py +325 -0
  28. financial_agent/harness/lifecycle.py +127 -0
  29. financial_agent/harness/manifest.py +113 -0
  30. financial_agent/harness/market_calendar.py +50 -0
  31. financial_agent/harness/mcp.py +505 -0
  32. financial_agent/harness/policy.py +248 -0
  33. financial_agent/harness/runner.py +275 -0
  34. financial_agent/harness/schema.py +451 -0
  35. financial_agent/harness/scoring.py +363 -0
  36. financial_agent/harness/skills.py +458 -0
  37. financial_agent/quant/__init__.py +1 -0
  38. financial_agent/quant/__main__.py +506 -0
  39. financial_agent/quant/backtest/__init__.py +16 -0
  40. financial_agent/quant/backtest/config.py +74 -0
  41. financial_agent/quant/backtest/costs.py +62 -0
  42. financial_agent/quant/backtest/engine.py +248 -0
  43. financial_agent/quant/backtest/metrics.py +102 -0
  44. financial_agent/quant/backtest/result.py +51 -0
  45. financial_agent/quant/data/__init__.py +21 -0
  46. financial_agent/quant/data/bars.py +103 -0
  47. financial_agent/quant/data/cache.py +168 -0
  48. financial_agent/quant/data/providers.py +138 -0
  49. financial_agent/quant/data/quality.py +104 -0
  50. financial_agent/quant/data/synthetic.py +67 -0
  51. financial_agent/quant/execution/__init__.py +24 -0
  52. financial_agent/quant/execution/alpaca.py +293 -0
  53. financial_agent/quant/execution/broker.py +98 -0
  54. financial_agent/quant/execution/ibkr.py +634 -0
  55. financial_agent/quant/execution/journal.py +139 -0
  56. financial_agent/quant/execution/paper.py +254 -0
  57. financial_agent/quant/execution/reconcile.py +35 -0
  58. financial_agent/quant/mcp.py +210 -0
  59. financial_agent/quant/portfolio.py +56 -0
  60. financial_agent/quant/quant_policy.toml +40 -0
  61. financial_agent/quant/risk/__init__.py +21 -0
  62. financial_agent/quant/risk/killswitch.py +61 -0
  63. financial_agent/quant/risk/limits.py +109 -0
  64. financial_agent/quant/risk/orders.py +91 -0
  65. financial_agent/quant/risk/pretrade.py +157 -0
  66. financial_agent/quant/risk/state.py +86 -0
  67. financial_agent/quant/runner/__init__.py +20 -0
  68. financial_agent/quant/runner/config.py +254 -0
  69. financial_agent/quant/runner/promotion.py +277 -0
  70. financial_agent/quant/runner/report.py +231 -0
  71. financial_agent/quant/runner/session.py +705 -0
  72. financial_agent/quant/strategies/__init__.py +19 -0
  73. financial_agent/quant/strategies/base.py +44 -0
  74. financial_agent/quant/strategies/benchmarks.py +54 -0
  75. financial_agent/quant/strategies/momentum.py +80 -0
  76. financial_agent/quant/strategies/registry.py +65 -0
  77. financial_agent/quant/strategies/research.py +226 -0
  78. financial_agent/quant/strategies/trend.py +91 -0
  79. financial_agent/quant/validation/__init__.py +41 -0
  80. financial_agent/quant/validation/bootstrap.py +39 -0
  81. financial_agent/quant/validation/certificate.py +189 -0
  82. financial_agent/quant/validation/gate.py +304 -0
  83. financial_agent/quant/validation/overfitting.py +148 -0
  84. financial_agent/quant/validation/stress.py +33 -0
  85. financial_agent/quant/validation/walkforward.py +157 -0
  86. financial_agent/rl/__init__.py +13 -0
  87. financial_agent/rl/agent.py +112 -0
  88. financial_agent/rl/environment.py +177 -0
  89. financial_agent/rl/trainer.py +142 -0
  90. financial_agent/trading/__init__.py +0 -0
  91. financial_agent/trading/autotrain/__init__.py +0 -0
  92. financial_agent/trading/autotrain/training.py +92 -0
  93. financial_agent/trading/backtest.py +118 -0
  94. financial_agent/trading/config/__init__.py +0 -0
  95. financial_agent/trading/config/config.py +1587 -0
  96. financial_agent/trading/marketdata/__init__.py +0 -0
  97. financial_agent/trading/marketdata/yahoodownloader.py +99 -0
  98. financial_agent/trading/model/__init__.py +0 -0
  99. financial_agent/trading/model/models.py +466 -0
  100. financial_agent/trading/preprocessing/__init__.py +0 -0
  101. financial_agent/trading/preprocessing/data.py +32 -0
  102. financial_agent/trading/preprocessing/preprocessors.py +162 -0
  103. financial_agent/trading/utils/__init__.py +0 -0
  104. financial_agent/trading/utils/gpu_config.py +213 -0
  105. financial_agent/trading/utils/main.py +52 -0
  106. financial_agent/trading/utils/setup_gpu_for_notebooks.py +130 -0
File without changes
@@ -0,0 +1,99 @@
1
+ """Contains methods and classes to collect data from
2
+ Yahoo Finance API
3
+ """
4
+
5
+ import pandas as pd
6
+ import yfinance as yf
7
+
8
+
9
+ class YahooDownloader:
10
+ """Provides methods for retrieving daily stock data from
11
+ Yahoo Finance API
12
+
13
+ Attributes
14
+ ----------
15
+ start_date : str
16
+ start date of the data (modified from config.py)
17
+ end_date : str
18
+ end date of the data (modified from config.py)
19
+ ticker_list : list
20
+ a list of stock tickers (modified from config.py)
21
+
22
+ Methods
23
+ -------
24
+ fetch_data()
25
+ Fetches data from yahoo API
26
+
27
+ """
28
+
29
+ def __init__(self, start_date: str, end_date: str, ticker_list: list):
30
+
31
+ self.start_date = start_date
32
+ self.end_date = end_date
33
+ self.ticker_list = ticker_list
34
+
35
+ def fetch_data(self) -> pd.DataFrame:
36
+ """Fetches data from Yahoo API
37
+ Parameters
38
+ ----------
39
+
40
+ Returns
41
+ -------
42
+ `pd.DataFrame`
43
+ 7 columns: A date, open, high, low, close, volume and tick symbol
44
+ for the specified stock ticker
45
+ """
46
+ # Download and save the data in a pandas DataFrame:
47
+ data_df = pd.DataFrame()
48
+ for tic in self.ticker_list:
49
+ temp_df = yf.download(tic, start=self.start_date, end=self.end_date, auto_adjust=False, multi_level_index=False)
50
+ temp_df["tic"] = tic
51
+ data_df = pd.concat([data_df, temp_df])
52
+
53
+ # reset the index, we want to use numbers as index instead of dates
54
+ data_df = data_df.reset_index()
55
+ try:
56
+ # convert the column names to standardized names
57
+
58
+ mapping = {
59
+ "Date": "date",
60
+ "Adj Close": "adjcp",
61
+ "Close": "close",
62
+ "High": "high",
63
+ "Low": "low",
64
+ "Open": "open",
65
+ "Volume": "volume",
66
+ "tic": "tic"
67
+ }
68
+ data_df = data_df.rename(columns=mapping)
69
+
70
+ # use adjusted close price instead of close price
71
+ data_df["close"] = data_df["adjcp"]
72
+ # drop the adjusted close price column
73
+ data_df = data_df.drop(['adjcp'], axis=1)
74
+ except NotImplementedError:
75
+ print("the features are not supported currently")
76
+ # create day of the week column (monday = 0)
77
+ data_df["day"] = data_df["date"].dt.dayofweek
78
+ # convert date to standard string format, easy to filter
79
+ data_df["date"] = data_df.date.apply(lambda x: x.strftime("%Y-%m-%d"))
80
+ # drop missing data
81
+ data_df = data_df.dropna()
82
+ data_df = data_df.reset_index(drop=True)
83
+ print("Shape of DataFrame: ", data_df.shape)
84
+ # print("Display DataFrame: ", data_df.head())
85
+
86
+ data_df = data_df.sort_values(by=['date','tic']).reset_index(drop=True)
87
+
88
+ return data_df
89
+
90
+ def select_equal_rows_stock(self, df):
91
+ df_check = df.tic.value_counts()
92
+ df_check = pd.DataFrame(df_check).reset_index()
93
+ df_check.columns = ["tic", "counts"]
94
+ mean_df = df_check.counts.mean()
95
+ equal_list = list(df.tic.value_counts() >= mean_df)
96
+ names = df.tic.value_counts().index
97
+ select_stocks_list = list(names[equal_list])
98
+ df = df[df.tic.isin(select_stocks_list)]
99
+ return df
File without changes
@@ -0,0 +1,466 @@
1
+ # common library
2
+ import pandas as pd
3
+ import numpy as np
4
+ import time
5
+ import gymnasium as gym
6
+
7
+ # RL models from stable-baselines
8
+ # from stable_baselines import SAC
9
+ # from stable_baselines import TD3
10
+
11
+ from stable_baselines3.ppo import MlpPolicy
12
+ from stable_baselines3.common.vec_env import DummyVecEnv
13
+
14
+ from stable_baselines3 import DDPG
15
+ from stable_baselines3.common.noise import (
16
+ NormalActionNoise,
17
+ OrnsteinUhlenbeckActionNoise,
18
+ )
19
+
20
+ from ..config import config
21
+ from ..preprocessing.data import data_split
22
+ from ..env.env_stocktrading import StockTradingEnv
23
+
24
+ from stable_baselines3 import A2C
25
+ from stable_baselines3 import PPO
26
+ from stable_baselines3 import TD3
27
+ from stable_baselines3.td3.policies import MlpPolicy
28
+ from stable_baselines3.common.noise import (
29
+ NormalActionNoise,
30
+ OrnsteinUhlenbeckActionNoise,
31
+ )
32
+
33
+ from stable_baselines3 import SAC
34
+
35
+
36
+ MODELS = {"a2c": A2C, "ddpg": DDPG, "td3": TD3, "sac": SAC, "ppo": PPO}
37
+
38
+ MODEL_KWARGS = {x: config.__dict__[f"{x.upper()}_PARAMS"] for x in MODELS.keys()}
39
+
40
+ NOISE = {
41
+ "normal": NormalActionNoise,
42
+ "ornstein_uhlenbeck": OrnsteinUhlenbeckActionNoise,
43
+ }
44
+
45
+
46
+ class DRLAgent:
47
+ """Provides implementations for DRL algorithms
48
+
49
+ Attributes
50
+ ----------
51
+ env: gym environment class
52
+ user-defined class
53
+
54
+ Methods
55
+ -------
56
+ train_PPO()
57
+ the implementation for PPO algorithm
58
+ train_A2C()
59
+ the implementation for A2C algorithm
60
+ train_DDPG()
61
+ the implementation for DDPG algorithm
62
+ train_TD3()
63
+ the implementation for TD3 algorithm
64
+ train_SAC()
65
+ the implementation for SAC algorithm
66
+ DRL_prediction()
67
+ make a prediction in a test dataset and get results
68
+ """
69
+
70
+ @staticmethod
71
+ def DRL_prediction(model, environment):
72
+ test_env, test_obs = environment.get_sb_env()
73
+ """make a prediction"""
74
+ account_memory = []
75
+ actions_memory = []
76
+ test_obs = test_env.reset()
77
+ for i in range(len(environment.df.index.unique())):
78
+ action, _states = model.predict(test_obs)
79
+ #account_memory = test_env.env_method(method_name="save_asset_memory")
80
+ #actions_memory = test_env.env_method(method_name="save_action_memory")
81
+ test_obs, rewards, dones, info = test_env.step(action)
82
+ if i == (len(environment.df.index.unique()) - 2):
83
+ account_memory = test_env.env_method(method_name="save_asset_memory")
84
+ actions_memory = test_env.env_method(method_name="save_action_memory")
85
+ if dones[0]:
86
+ print("hit end!")
87
+ break
88
+ return account_memory[0], actions_memory[0]
89
+
90
+ def __init__(self, env):
91
+ self.env = env
92
+
93
+ def get_model(
94
+ self,
95
+ model_name,
96
+ policy="MlpPolicy",
97
+ policy_kwargs=None,
98
+ model_kwargs=None,
99
+ verbose=1,
100
+ ):
101
+ if model_name not in MODELS:
102
+ raise NotImplementedError("NotImplementedError")
103
+
104
+ if model_kwargs is None:
105
+ model_kwargs = MODEL_KWARGS[model_name]
106
+
107
+ if "action_noise" in model_kwargs:
108
+ n_actions = self.env.action_space.shape[-1]
109
+ model_kwargs["action_noise"] = NOISE[model_kwargs["action_noise"]](
110
+ mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions)
111
+ )
112
+ print(model_kwargs)
113
+ model = MODELS[model_name](
114
+ policy=policy,
115
+ env=self.env,
116
+ tensorboard_log=f"{config.TENSORBOARD_LOG_DIR}/{model_name}",
117
+ verbose=verbose,
118
+ policy_kwargs=policy_kwargs,
119
+ **model_kwargs,
120
+ )
121
+ return model
122
+
123
+ def train_model(self, model, tb_log_name, total_timesteps=5000):
124
+ model = model.learn(total_timesteps=total_timesteps, tb_log_name=tb_log_name)
125
+ return model
126
+
127
+
128
+ class DRLEnsembleAgent:
129
+ @staticmethod
130
+ def get_model(model_name,
131
+ env,
132
+ policy="MlpPolicy",
133
+ policy_kwargs=None,
134
+ model_kwargs=None,
135
+ verbose=1):
136
+
137
+ if model_name not in MODELS:
138
+ raise NotImplementedError("NotImplementedError")
139
+
140
+ if model_kwargs is None:
141
+ temp_model_kwargs = MODEL_KWARGS[model_name]
142
+ else:
143
+ temp_model_kwargs = model_kwargs.copy()
144
+
145
+ if "action_noise" in temp_model_kwargs:
146
+ n_actions = env.action_space.shape[-1]
147
+ temp_model_kwargs["action_noise"] = NOISE[temp_model_kwargs["action_noise"]](
148
+ mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions)
149
+ )
150
+ print(temp_model_kwargs)
151
+ model = MODELS[model_name](
152
+ policy=policy,
153
+ env=env,
154
+ tensorboard_log=f"{config.TENSORBOARD_LOG_DIR}/{model_name}",
155
+ verbose=verbose,
156
+ policy_kwargs=policy_kwargs,
157
+ **temp_model_kwargs,
158
+ )
159
+ return model
160
+
161
+ @staticmethod
162
+ def train_model(model, model_name, tb_log_name, iter_num, total_timesteps=5000):
163
+ model = model.learn(total_timesteps=total_timesteps, tb_log_name=tb_log_name)
164
+ model.save(f"{config.TRAINED_MODEL_DIR}/{model_name.upper()}_{total_timesteps//1000}k_{iter_num}")
165
+ return model
166
+
167
+ @staticmethod
168
+ def get_validation_sharpe(iteration,model_name):
169
+ ###Calculate Sharpe ratio based on validation results###
170
+ df_total_value = pd.read_csv('results/account_value_validation_{}_{}.csv'.format(model_name,iteration))
171
+ sharpe = (4 ** 0.5) * df_total_value['daily_return'].mean() / \
172
+ df_total_value['daily_return'].std()
173
+ return sharpe
174
+
175
+ def __init__(self,df,
176
+ train_period,val_test_period,
177
+ rebalance_window, validation_window,
178
+ stock_dim,
179
+ hmax,
180
+ initial_amount,
181
+ buy_cost_pct,
182
+ sell_cost_pct,
183
+ reward_scaling,
184
+ state_space,
185
+ action_space,
186
+ tech_indicator_list,
187
+ print_verbosity):
188
+
189
+ self.df=df
190
+ self.train_period = train_period
191
+ self.val_test_period = val_test_period
192
+
193
+ self.unique_trade_date = df[(df.date > val_test_period[0])&(df.date <= val_test_period[1])].date.unique()
194
+ self.rebalance_window = rebalance_window
195
+ self.validation_window = validation_window
196
+
197
+ self.stock_dim = stock_dim
198
+ self.hmax = hmax
199
+ self.initial_amount = initial_amount
200
+ self.buy_cost_pct = buy_cost_pct
201
+ self.sell_cost_pct = sell_cost_pct
202
+ self.reward_scaling = reward_scaling
203
+ self.state_space = state_space
204
+ self.action_space = action_space
205
+ self.tech_indicator_list = tech_indicator_list
206
+ self.print_verbosity = print_verbosity
207
+
208
+
209
+ def DRL_validation(self, model, test_data, test_env, test_obs):
210
+ ###validation process###
211
+ for i in range(len(test_data.index.unique())):
212
+ action, _states = model.predict(test_obs)
213
+ test_obs, rewards, dones, info = test_env.step(action)
214
+
215
+ def DRL_prediction(self,model,name,last_state,iter_num,turbulence_threshold,initial):
216
+ ### make a prediction based on trained model###
217
+
218
+ ## trading env
219
+ trade_data = data_split(self.df, start=self.unique_trade_date[iter_num - self.rebalance_window], end=self.unique_trade_date[iter_num])
220
+ trade_env = DummyVecEnv([lambda: StockTradingEnv(trade_data,
221
+ self.stock_dim,
222
+ self.hmax,
223
+ self.initial_amount,
224
+ self.buy_cost_pct,
225
+ self.sell_cost_pct,
226
+ self.reward_scaling,
227
+ self.state_space,
228
+ self.action_space,
229
+ self.tech_indicator_list,
230
+ turbulence_threshold=turbulence_threshold,
231
+ initial=initial,
232
+ previous_state=last_state,
233
+ model_name=name,
234
+ mode='trade',
235
+ iteration=iter_num,
236
+ print_verbosity=self.print_verbosity)])
237
+
238
+ trade_obs = trade_env.reset()
239
+
240
+ for i in range(len(trade_data.index.unique())):
241
+ action, _states = model.predict(trade_obs)
242
+ trade_obs, rewards, dones, info = trade_env.step(action)
243
+ if i == (len(trade_data.index.unique()) - 2):
244
+ # print(env_test.render())
245
+ last_state = trade_env.render()
246
+
247
+ df_last_state = pd.DataFrame({'last_state': last_state})
248
+ df_last_state.to_csv('results/last_state_{}_{}.csv'.format(name, i), index=False)
249
+ return last_state
250
+
251
+ def run_ensemble_strategy(self,A2C_model_kwargs,PPO_model_kwargs,DDPG_model_kwargs,timesteps_dict):
252
+ """Ensemble Strategy that combines PPO, A2C and DDPG"""
253
+ print("============Start Ensemble Strategy============")
254
+ # for ensemble model, it's necessary to feed the last state
255
+ # of the previous model to the current model as the initial state
256
+ last_state_ensemble = []
257
+
258
+ ppo_sharpe_list = []
259
+ ddpg_sharpe_list = []
260
+ a2c_sharpe_list = []
261
+
262
+ model_use = []
263
+ validation_start_date_list = []
264
+ validation_end_date_list = []
265
+ iteration_list = []
266
+
267
+ insample_turbulence = self.df[(self.df.date<self.train_period[1]) & (self.df.date>=self.train_period[0])]
268
+ insample_turbulence_threshold = np.quantile(insample_turbulence.turbulence.values, .90)
269
+
270
+ start = time.time()
271
+ for i in range(self.rebalance_window + self.validation_window, len(self.unique_trade_date), self.rebalance_window):
272
+ validation_start_date = self.unique_trade_date[i - self.rebalance_window - self.validation_window]
273
+ validation_end_date = self.unique_trade_date[i - self.rebalance_window]
274
+
275
+ validation_start_date_list.append(validation_start_date)
276
+ validation_end_date_list.append(validation_end_date)
277
+ iteration_list.append(i)
278
+
279
+ print("============================================")
280
+ ## initial state is empty
281
+ if i - self.rebalance_window - self.validation_window == 0:
282
+ # inital state
283
+ initial = True
284
+ else:
285
+ # previous state
286
+ initial = False
287
+
288
+ # Tuning trubulence index based on historical data
289
+ # Turbulence lookback window is one quarter (63 days)
290
+ end_date_index = self.df.index[self.df["date"] == self.unique_trade_date[i - self.rebalance_window - self.validation_window]].to_list()[-1]
291
+ start_date_index = end_date_index - 63 + 1
292
+
293
+ historical_turbulence = self.df.iloc[start_date_index:(end_date_index + 1), :]
294
+
295
+ historical_turbulence = historical_turbulence.drop_duplicates(subset=['date'])
296
+
297
+ historical_turbulence_mean = np.mean(historical_turbulence.turbulence.values)
298
+
299
+ print(historical_turbulence_mean)
300
+
301
+ if historical_turbulence_mean > insample_turbulence_threshold:
302
+ # if the mean of the historical data is greater than the 90% quantile of insample turbulence data
303
+ # then we assume that the current market is volatile,
304
+ # therefore we set the 90% quantile of insample turbulence data as the turbulence threshold
305
+ # meaning the current turbulence can't exceed the 90% quantile of insample turbulence data
306
+ turbulence_threshold = insample_turbulence_threshold
307
+ else:
308
+ # if the mean of the historical data is less than the 90% quantile of insample turbulence data
309
+ # then we tune up the turbulence_threshold, meaning we lower the risk
310
+ turbulence_threshold = np.quantile(insample_turbulence.turbulence.values, 1)
311
+ print("turbulence_threshold: ", turbulence_threshold)
312
+
313
+ ############## Environment Setup starts ##############
314
+ ## training env
315
+ train = data_split(self.df, start=self.train_period[0], end=self.unique_trade_date[i - self.rebalance_window - self.validation_window])
316
+ self.train_env = DummyVecEnv([lambda: StockTradingEnv(train,
317
+ self.stock_dim,
318
+ self.hmax,
319
+ self.initial_amount,
320
+ self.buy_cost_pct,
321
+ self.sell_cost_pct,
322
+ self.reward_scaling,
323
+ self.state_space,
324
+ self.action_space,
325
+ self.tech_indicator_list,
326
+ print_verbosity=self.print_verbosity)])
327
+
328
+ validation = data_split(self.df, start=self.unique_trade_date[i - self.rebalance_window - self.validation_window],
329
+ end=self.unique_trade_date[i - self.rebalance_window])
330
+ ############## Environment Setup ends ##############
331
+
332
+ ############## Training and Validation starts ##############
333
+ print("======Model training from: ", self.train_period[0], "to ",
334
+ self.unique_trade_date[i - self.rebalance_window - self.validation_window])
335
+ # print("training: ",len(data_split(df, start=20090000, end=test.datadate.unique()[i-rebalance_window]) ))
336
+ # print("==============Model Training===========")
337
+ print("======A2C Training========")
338
+ model_a2c = self.get_model("a2c",self.train_env,policy="MlpPolicy",model_kwargs=A2C_model_kwargs)
339
+ model_a2c = self.train_model(model_a2c, "a2c", tb_log_name="a2c_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['a2c']) #100_000
340
+
341
+ print("======A2C Validation from: ", validation_start_date, "to ",validation_end_date)
342
+ val_env_a2c = DummyVecEnv([lambda: StockTradingEnv(validation,
343
+ self.stock_dim,
344
+ self.hmax,
345
+ self.initial_amount,
346
+ self.buy_cost_pct,
347
+ self.sell_cost_pct,
348
+ self.reward_scaling,
349
+ self.state_space,
350
+ self.action_space,
351
+ self.tech_indicator_list,
352
+ turbulence_threshold=turbulence_threshold,
353
+ iteration=i,
354
+ model_name='A2C',
355
+ mode='validation',
356
+ print_verbosity=self.print_verbosity)])
357
+ val_obs_a2c = val_env_a2c.reset()
358
+ self.DRL_validation(model=model_a2c,test_data=validation,test_env=val_env_a2c,test_obs=val_obs_a2c)
359
+ sharpe_a2c = self.get_validation_sharpe(i,model_name="A2C")
360
+ print("A2C Sharpe Ratio: ", sharpe_a2c)
361
+
362
+ print("======PPO Training========")
363
+ model_ppo = self.get_model("ppo",self.train_env,policy="MlpPolicy",model_kwargs=PPO_model_kwargs)
364
+ model_ppo = self.train_model(model_ppo, "ppo", tb_log_name="ppo_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['ppo']) #100_000
365
+ print("======PPO Validation from: ", validation_start_date, "to ",validation_end_date)
366
+ val_env_ppo = DummyVecEnv([lambda: StockTradingEnv(validation,
367
+ self.stock_dim,
368
+ self.hmax,
369
+ self.initial_amount,
370
+ self.buy_cost_pct,
371
+ self.sell_cost_pct,
372
+ self.reward_scaling,
373
+ self.state_space,
374
+ self.action_space,
375
+ self.tech_indicator_list,
376
+ turbulence_threshold=turbulence_threshold,
377
+ iteration=i,
378
+ model_name='PPO',
379
+ mode='validation',
380
+ print_verbosity=self.print_verbosity)])
381
+ val_obs_ppo = val_env_ppo.reset()
382
+ self.DRL_validation(model=model_ppo,test_data=validation,test_env=val_env_ppo,test_obs=val_obs_ppo)
383
+ sharpe_ppo = self.get_validation_sharpe(i,model_name="PPO")
384
+ print("PPO Sharpe Ratio: ", sharpe_ppo)
385
+
386
+ print("======DDPG Training========")
387
+ model_ddpg = self.get_model("ddpg",self.train_env,policy="MlpPolicy",model_kwargs=DDPG_model_kwargs)
388
+ model_ddpg = self.train_model(model_ddpg, "ddpg", tb_log_name="ddpg_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['ddpg']) #50_000
389
+ print("======DDPG Validation from: ", validation_start_date, "to ",validation_end_date)
390
+ val_env_ddpg = DummyVecEnv([lambda: StockTradingEnv(validation,
391
+ self.stock_dim,
392
+ self.hmax,
393
+ self.initial_amount,
394
+ self.buy_cost_pct,
395
+ self.sell_cost_pct,
396
+ self.reward_scaling,
397
+ self.state_space,
398
+ self.action_space,
399
+ self.tech_indicator_list,
400
+ turbulence_threshold=turbulence_threshold,
401
+ iteration=i,
402
+ model_name='DDPG',
403
+ mode='validation',
404
+ print_verbosity=self.print_verbosity)])
405
+ val_obs_ddpg = val_env_ddpg.reset()
406
+ self.DRL_validation(model=model_ddpg,test_data=validation,test_env=val_env_ddpg,test_obs=val_obs_ddpg)
407
+ sharpe_ddpg = self.get_validation_sharpe(i,model_name="DDPG")
408
+
409
+ ppo_sharpe_list.append(sharpe_ppo)
410
+ a2c_sharpe_list.append(sharpe_a2c)
411
+ ddpg_sharpe_list.append(sharpe_ddpg)
412
+
413
+ print("======Best Model Retraining from: ", self.train_period[0], "to ",
414
+ self.unique_trade_date[i - self.rebalance_window])
415
+ # Environment setup for model retraining up to first trade date
416
+ train_full = data_split(self.df, start=self.train_period[0], end=self.unique_trade_date[i - self.rebalance_window])
417
+ self.train_full_env = DummyVecEnv([lambda: StockTradingEnv(train_full,
418
+ self.stock_dim,
419
+ self.hmax,
420
+ self.initial_amount,
421
+ self.buy_cost_pct,
422
+ self.sell_cost_pct,
423
+ self.reward_scaling,
424
+ self.state_space,
425
+ self.action_space,
426
+ self.tech_indicator_list,
427
+ print_verbosity=self.print_verbosity)])
428
+ # Model Selection based on sharpe ratio
429
+ if (sharpe_ppo >= sharpe_a2c) & (sharpe_ppo >= sharpe_ddpg):
430
+ model_use.append('PPO')
431
+
432
+ model_ensemble = self.get_model("ppo",self.train_full_env,policy="MlpPolicy",model_kwargs=PPO_model_kwargs)
433
+ model_ensemble = self.train_model(model_ensemble, "ensemble", tb_log_name="ensemble_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['ppo']) #100_000
434
+ elif (sharpe_a2c > sharpe_ppo) & (sharpe_a2c > sharpe_ddpg):
435
+ model_use.append('A2C')
436
+
437
+ model_ensemble = self.get_model("a2c",self.train_full_env,policy="MlpPolicy",model_kwargs=A2C_model_kwargs)
438
+ model_ensemble = self.train_model(model_ensemble, "ensemble", tb_log_name="ensemble_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['a2c']) #100_000
439
+ else:
440
+ model_use.append('DDPG')
441
+
442
+ model_ensemble = self.get_model("ddpg",self.train_full_env,policy="MlpPolicy",model_kwargs=DDPG_model_kwargs)
443
+ model_ensemble = self.train_model(model_ensemble, "ensemble", tb_log_name="ensemble_{}".format(i), iter_num = i, total_timesteps=timesteps_dict['ddpg']) #50_000
444
+
445
+ ############## Training and Validation ends ##############
446
+
447
+ ############## Trading starts ##############
448
+ print("======Trading from: ", self.unique_trade_date[i - self.rebalance_window], "to ", self.unique_trade_date[i])
449
+ #print("Used Model: ", model_ensemble)
450
+ last_state_ensemble = self.DRL_prediction(model=model_ensemble, name="ensemble",
451
+ last_state=last_state_ensemble, iter_num=i,
452
+ turbulence_threshold = turbulence_threshold,
453
+ initial=initial)
454
+ ############## Trading ends ##############
455
+
456
+ end = time.time()
457
+ print("Ensemble Strategy took: ", (end - start) / 60, " minutes")
458
+
459
+ df_summary = pd.DataFrame([iteration_list,validation_start_date_list,validation_end_date_list,model_use,a2c_sharpe_list,ppo_sharpe_list,ddpg_sharpe_list]).T
460
+ df_summary.columns = ['Iter','Val Start','Val End','Model Used','A2C Sharpe','PPO Sharpe','DDPG Sharpe']
461
+
462
+ return df_summary
463
+
464
+
465
+
466
+
File without changes
@@ -0,0 +1,32 @@
1
+ from __future__ import division, absolute_import, print_function
2
+ import datetime
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+
7
+ def load_dataset(*, file_name: str) -> pd.DataFrame:
8
+ """
9
+ load csv dataset from path
10
+ :return: (df) pandas dataframe
11
+ """
12
+ # _data = pd.read_csv(f"{config.DATASET_DIR}/{file_name}")
13
+ _data = pd.read_csv(file_name)
14
+ return _data
15
+
16
+
17
+ def data_split(df, start, end):
18
+ """
19
+ split the dataset into training or testing using date
20
+ :param data: (df) pandas dataframe, start, end
21
+ :return: (df) pandas dataframe
22
+ """
23
+ data = df[(df.date >= start) & (df.date < end)]
24
+ data = data.sort_values(["date", "tic"], ignore_index=True)
25
+ data.index = data.date.factorize()[0]
26
+ return data
27
+
28
+
29
+ def convert_to_datetime(time):
30
+ time_fmt = "%Y-%m-%dT%H:%M:%S"
31
+ if isinstance(time, str):
32
+ return datetime.datetime.strptime(time, time_fmt)