bbstrader 0.1.94__py3-none-any.whl → 0.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.
Potentially problematic release.
This version of bbstrader might be problematic. Click here for more details.
- bbstrader/__ini__.py +9 -9
- bbstrader/btengine/__init__.py +7 -7
- bbstrader/btengine/backtest.py +30 -26
- bbstrader/btengine/data.py +100 -79
- bbstrader/btengine/event.py +2 -1
- bbstrader/btengine/execution.py +18 -16
- bbstrader/btengine/performance.py +11 -7
- bbstrader/btengine/portfolio.py +35 -36
- bbstrader/btengine/strategy.py +119 -94
- bbstrader/config.py +14 -8
- bbstrader/core/__init__.py +0 -0
- bbstrader/core/data.py +22 -0
- bbstrader/core/utils.py +57 -0
- bbstrader/ibkr/__init__.py +0 -0
- bbstrader/ibkr/utils.py +0 -0
- bbstrader/metatrader/__init__.py +5 -5
- bbstrader/metatrader/account.py +117 -121
- bbstrader/metatrader/rates.py +83 -80
- bbstrader/metatrader/risk.py +23 -37
- bbstrader/metatrader/trade.py +169 -140
- bbstrader/metatrader/utils.py +3 -3
- bbstrader/models/__init__.py +5 -5
- bbstrader/models/factors.py +280 -0
- bbstrader/models/ml.py +1092 -0
- bbstrader/models/optimization.py +31 -28
- bbstrader/models/{portfolios.py → portfolio.py} +64 -46
- bbstrader/models/risk.py +15 -9
- bbstrader/trading/__init__.py +2 -2
- bbstrader/trading/execution.py +252 -164
- bbstrader/trading/scripts.py +8 -4
- bbstrader/trading/strategies.py +79 -66
- bbstrader/tseries.py +482 -107
- {bbstrader-0.1.94.dist-info → bbstrader-0.2.1.dist-info}/LICENSE +1 -1
- {bbstrader-0.1.94.dist-info → bbstrader-0.2.1.dist-info}/METADATA +6 -1
- bbstrader-0.2.1.dist-info/RECORD +37 -0
- bbstrader-0.1.94.dist-info/RECORD +0 -32
- {bbstrader-0.1.94.dist-info → bbstrader-0.2.1.dist-info}/WHEEL +0 -0
- {bbstrader-0.1.94.dist-info → bbstrader-0.2.1.dist-info}/top_level.txt +0 -0
bbstrader/trading/strategies.py
CHANGED
|
@@ -2,28 +2,27 @@
|
|
|
2
2
|
Strategies module for trading strategies backtesting and execution.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from queue import Queue
|
|
7
|
+
from typing import Dict, List, Literal, Optional, Union
|
|
8
|
+
|
|
5
9
|
import numpy as np
|
|
6
10
|
import pandas as pd
|
|
7
|
-
from queue import Queue
|
|
8
11
|
import yfinance as yf
|
|
9
|
-
|
|
10
|
-
from bbstrader.metatrader.rates import Rates
|
|
11
|
-
from bbstrader.metatrader.account import Account
|
|
12
|
-
from bbstrader.btengine.event import SignalEvent
|
|
13
|
-
from bbstrader.btengine.data import DataHandler
|
|
14
|
-
from bbstrader.models.risk import HMMRiskManager
|
|
15
|
-
from bbstrader.models.risk import build_hmm_models
|
|
12
|
+
|
|
16
13
|
from bbstrader.btengine.backtest import BacktestEngine
|
|
14
|
+
from bbstrader.btengine.data import DataHandler, MT5DataHandler, YFDataHandler
|
|
15
|
+
from bbstrader.btengine.event import SignalEvent
|
|
16
|
+
from bbstrader.btengine.execution import MT5ExecutionHandler, SimExecutionHandler
|
|
17
17
|
from bbstrader.btengine.strategy import Strategy
|
|
18
|
-
from bbstrader.
|
|
19
|
-
from bbstrader.
|
|
20
|
-
from bbstrader.
|
|
21
|
-
from bbstrader.tseries import
|
|
22
|
-
from typing import Union, Optional, Literal, Dict, List
|
|
18
|
+
from bbstrader.metatrader.account import Account
|
|
19
|
+
from bbstrader.metatrader.rates import Rates
|
|
20
|
+
from bbstrader.models.risk import build_hmm_models
|
|
21
|
+
from bbstrader.tseries import ArimaGarchModel, KalmanFilterModel
|
|
23
22
|
|
|
24
23
|
__all__ = [
|
|
25
|
-
'SMAStrategy',
|
|
26
|
-
'ArimaGarchStrategy',
|
|
24
|
+
'SMAStrategy',
|
|
25
|
+
'ArimaGarchStrategy',
|
|
27
26
|
'KalmanFilterStrategy',
|
|
28
27
|
'StockIndexSTBOTrading',
|
|
29
28
|
'test_strategy',
|
|
@@ -36,7 +35,7 @@ def get_quantities(quantities, symbol_list):
|
|
|
36
35
|
return quantities
|
|
37
36
|
elif isinstance(quantities, int):
|
|
38
37
|
return {symbol: quantities for symbol in symbol_list}
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
|
|
41
40
|
class SMAStrategy(Strategy):
|
|
42
41
|
"""
|
|
@@ -56,9 +55,9 @@ class SMAStrategy(Strategy):
|
|
|
56
55
|
"""
|
|
57
56
|
|
|
58
57
|
def __init__(
|
|
59
|
-
self, bars: DataHandler = None,
|
|
58
|
+
self, bars: DataHandler = None,
|
|
60
59
|
events: Queue = None,
|
|
61
|
-
symbol_list: List[str] = None,
|
|
60
|
+
symbol_list: List[str] = None,
|
|
62
61
|
mode: Literal['backtest', 'live'] = 'backtest',
|
|
63
62
|
**kwargs
|
|
64
63
|
):
|
|
@@ -100,7 +99,6 @@ class SMAStrategy(Strategy):
|
|
|
100
99
|
def get_backtest_data(self):
|
|
101
100
|
symbol_data = {symbol: None for symbol in self.symbol_list}
|
|
102
101
|
for s in self.symbol_list:
|
|
103
|
-
latest_bars = self.bars.get_latest_bars(s, N=self.long_window)
|
|
104
102
|
bar_date = self.bars.get_latest_bar_datetime(s)
|
|
105
103
|
bars = self.bars.get_latest_bars_values(
|
|
106
104
|
s, "adj_close", N=self.long_window
|
|
@@ -157,17 +155,20 @@ class SMAStrategy(Strategy):
|
|
|
157
155
|
def get_live_data(self):
|
|
158
156
|
symbol_data = {symbol: None for symbol in self.symbol_list}
|
|
159
157
|
for symbol in self.symbol_list:
|
|
160
|
-
sig_rate = Rates(symbol, self.tf, 0,
|
|
158
|
+
sig_rate = Rates(symbol, self.tf, 0,
|
|
159
|
+
self.risk_window+2, **self.kwargs)
|
|
161
160
|
hmm_data = sig_rate.returns.values
|
|
162
161
|
prices = sig_rate.close.values
|
|
163
|
-
current_regime = self.risk_models[symbol].which_trade_allowed(
|
|
164
|
-
|
|
162
|
+
current_regime = self.risk_models[symbol].which_trade_allowed(
|
|
163
|
+
hmm_data)
|
|
164
|
+
assert len(prices) >= self.long_window and len(
|
|
165
|
+
hmm_data) >= self.risk_window
|
|
165
166
|
short_sma = np.mean(prices[-self.short_window:])
|
|
166
167
|
long_sma = np.mean(prices[-self.long_window:])
|
|
167
168
|
short_sma, long_sma, current_regime
|
|
168
169
|
symbol_data[symbol] = (short_sma, long_sma, current_regime)
|
|
169
170
|
return symbol_data
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
def create_live_signals(self):
|
|
172
173
|
signals = {symbol: None for symbol in self.symbol_list}
|
|
173
174
|
symbol_data = self.get_live_data()
|
|
@@ -182,7 +183,7 @@ class SMAStrategy(Strategy):
|
|
|
182
183
|
signal = 'SHORT'
|
|
183
184
|
signals[symbol] = signal
|
|
184
185
|
return signals
|
|
185
|
-
|
|
186
|
+
|
|
186
187
|
def calculate_signals(self, event=None):
|
|
187
188
|
if self.mode == 'backtest' and event is not None:
|
|
188
189
|
if event.type == 'MARKET':
|
|
@@ -204,9 +205,9 @@ class ArimaGarchStrategy(Strategy):
|
|
|
204
205
|
Features
|
|
205
206
|
========
|
|
206
207
|
- **ARIMA-GARCH Model**: Utilizes ARIMA for time series forecasting and GARCH for volatility forecasting, aimed at predicting market movements.
|
|
207
|
-
|
|
208
|
+
|
|
208
209
|
- **HMM Risk Management**: Employs a Hidden Markov Model to manage risks, determining safe trading regimes.
|
|
209
|
-
|
|
210
|
+
|
|
210
211
|
- **Event-Driven Backtesting**: Capable of simulating real-time trading conditions by processing market data and signals sequentially.
|
|
211
212
|
|
|
212
213
|
- **Live Trading**: Supports real-time trading by generating signals based on live ARIMA-GARCH predictions and HMM risk management.
|
|
@@ -218,15 +219,15 @@ class ArimaGarchStrategy(Strategy):
|
|
|
218
219
|
- `get_live_data()`: Retrieves live data for real-time trading.
|
|
219
220
|
- `create_live_signals()`: Generates trading signals based on live ARIMA-GARCH predictions and HMM risk management.
|
|
220
221
|
- `calculate_signals()`: Determines the trading signals based on the mode of operation (backtest or live).
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
"""
|
|
223
224
|
|
|
224
|
-
def __init__(self,
|
|
225
|
-
bars: DataHandler = None,
|
|
225
|
+
def __init__(self,
|
|
226
|
+
bars: DataHandler = None,
|
|
226
227
|
events: Queue = None,
|
|
227
228
|
symbol_list: List[str] = None,
|
|
228
229
|
mode: Literal['backtest', 'live'] = 'backtest',
|
|
229
|
-
|
|
230
|
+
**kwargs):
|
|
230
231
|
"""
|
|
231
232
|
Args:
|
|
232
233
|
`bars`: A data handler object that provides market data.
|
|
@@ -242,8 +243,8 @@ class ArimaGarchStrategy(Strategy):
|
|
|
242
243
|
self.events = events
|
|
243
244
|
self.symbol_list = symbol_list or self.bars.symbol_list
|
|
244
245
|
self.mode = mode
|
|
245
|
-
|
|
246
|
-
self.qty =
|
|
246
|
+
|
|
247
|
+
self.qty = get_quantities(
|
|
247
248
|
kwargs.get('quantities', 100), self.symbol_list)
|
|
248
249
|
self.arima_window = kwargs.get('arima_window', 252)
|
|
249
250
|
self.tf = kwargs.get('time_frame', 'D1')
|
|
@@ -251,9 +252,9 @@ class ArimaGarchStrategy(Strategy):
|
|
|
251
252
|
self.risk_window = kwargs.get("hmm_window", 50)
|
|
252
253
|
self.risk_models = build_hmm_models(self.symbol_list, **kwargs)
|
|
253
254
|
self.arima_models = self._build_arch_models(**kwargs)
|
|
254
|
-
|
|
255
|
-
self.long_market = {s
|
|
256
|
-
self.short_market = {s
|
|
255
|
+
|
|
256
|
+
self.long_market = {s: False for s in self.symbol_list}
|
|
257
|
+
self.short_market = {s: False for s in self.symbol_list}
|
|
257
258
|
|
|
258
259
|
def _build_arch_models(self, **kwargs) -> Dict[str, ArimaGarchModel]:
|
|
259
260
|
arch_models = {symbol: None for symbol in self.symbol_list}
|
|
@@ -263,11 +264,11 @@ class ArimaGarchStrategy(Strategy):
|
|
|
263
264
|
data = rates.get_rates_from_pos()
|
|
264
265
|
assert data is not None, f"No data for {symbol}"
|
|
265
266
|
except AssertionError:
|
|
266
|
-
data =
|
|
267
|
+
data = yf.download(symbol, start=kwargs.get('yf_start'))
|
|
267
268
|
arch = ArimaGarchModel(symbol, data, k=self.arima_window)
|
|
268
269
|
arch_models[symbol] = arch
|
|
269
270
|
return arch_models
|
|
270
|
-
|
|
271
|
+
|
|
271
272
|
def get_backtest_data(self):
|
|
272
273
|
symbol_data = {symbol: None for symbol in self.symbol_list}
|
|
273
274
|
for symbol in self.symbol_list:
|
|
@@ -335,12 +336,13 @@ class ArimaGarchStrategy(Strategy):
|
|
|
335
336
|
for symbol in self.symbol_list:
|
|
336
337
|
arch_data = Rates(symbol, self.tf, 0, self.arima_window)
|
|
337
338
|
rates = arch_data.get_rates_from_pos()
|
|
338
|
-
arch_returns = self.arima_models[symbol].load_and_prepare_data(
|
|
339
|
+
arch_returns = self.arima_models[symbol].load_and_prepare_data(
|
|
340
|
+
rates)
|
|
339
341
|
window_data = arch_returns['diff_log_return'].iloc[-self.arima_window:]
|
|
340
342
|
hmm_returns = arch_data.returns.values[-self.risk_window:]
|
|
341
343
|
symbol_data[symbol] = (window_data, hmm_returns)
|
|
342
344
|
return symbol_data
|
|
343
|
-
|
|
345
|
+
|
|
344
346
|
def create_live_signals(self):
|
|
345
347
|
signals = {symbol: None for symbol in self.symbol_list}
|
|
346
348
|
data = self.get_live_data()
|
|
@@ -348,8 +350,10 @@ class ArimaGarchStrategy(Strategy):
|
|
|
348
350
|
symbol_data = data[symbol]
|
|
349
351
|
if symbol_data is not None:
|
|
350
352
|
window_data, hmm_returns = symbol_data
|
|
351
|
-
prediction = self.arima_models[symbol].get_prediction(
|
|
352
|
-
|
|
353
|
+
prediction = self.arima_models[symbol].get_prediction(
|
|
354
|
+
window_data)
|
|
355
|
+
regime = self.risk_models[symbol].which_trade_allowed(
|
|
356
|
+
hmm_returns)
|
|
353
357
|
if regime == "LONG":
|
|
354
358
|
if prediction > 0:
|
|
355
359
|
signals[symbol] = "LONG"
|
|
@@ -357,7 +361,7 @@ class ArimaGarchStrategy(Strategy):
|
|
|
357
361
|
if prediction < 0:
|
|
358
362
|
signals[symbol] = "SHORT"
|
|
359
363
|
return signals
|
|
360
|
-
|
|
364
|
+
|
|
361
365
|
def calculate_signals(self, event=None):
|
|
362
366
|
if self.mode == 'backtest' and event is not None:
|
|
363
367
|
if event.type == 'MARKET':
|
|
@@ -369,7 +373,7 @@ class ArimaGarchStrategy(Strategy):
|
|
|
369
373
|
return self.create_live_signals()
|
|
370
374
|
|
|
371
375
|
|
|
372
|
-
class KalmanFilterStrategy(Strategy):
|
|
376
|
+
class KalmanFilterStrategy(Strategy):
|
|
373
377
|
"""
|
|
374
378
|
The `KalmanFilterStrategy` class implements a backtesting framework for a
|
|
375
379
|
[pairs trading](https://en.wikipedia.org/wiki/Pairs_trade) strategy using
|
|
@@ -378,12 +382,12 @@ class KalmanFilterStrategy(Strategy):
|
|
|
378
382
|
including initialization parameters, main functions, and an example of how to run a backtest.
|
|
379
383
|
"""
|
|
380
384
|
|
|
381
|
-
def __init__(self,
|
|
382
|
-
bars: DataHandler = None,
|
|
385
|
+
def __init__(self,
|
|
386
|
+
bars: DataHandler = None,
|
|
383
387
|
events: Queue = None,
|
|
384
388
|
symbol_list: List[str] = None,
|
|
385
389
|
mode: Literal['backtest', 'live'] = 'backtest',
|
|
386
|
-
|
|
390
|
+
**kwargs):
|
|
387
391
|
"""
|
|
388
392
|
Args:
|
|
389
393
|
`bars`: `DataHandler` for market data handling.
|
|
@@ -404,7 +408,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
404
408
|
|
|
405
409
|
self.hmm_tiker = kwargs.get("hmm_tiker")
|
|
406
410
|
self._assert_tikers()
|
|
407
|
-
self.account = Account()
|
|
411
|
+
self.account = Account(**kwargs)
|
|
408
412
|
self.hmm_window = kwargs.get("hmm_window", 50)
|
|
409
413
|
self.qty = kwargs.get("quantity", 100)
|
|
410
414
|
self.tf = kwargs.get("time_frame", "D1")
|
|
@@ -424,7 +428,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
424
428
|
if self.hmm_tiker is None:
|
|
425
429
|
raise ValueError(
|
|
426
430
|
"You need to provide a ticker used by the HMM for risk management")
|
|
427
|
-
|
|
431
|
+
|
|
428
432
|
def calculate_btxy(self, etqt, regime, dt):
|
|
429
433
|
# Make sure there is no position open
|
|
430
434
|
if etqt is None:
|
|
@@ -499,7 +503,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
499
503
|
signals[self.tickers[0]] = x_signal
|
|
500
504
|
signals[self.tickers[1]] = y_signal
|
|
501
505
|
return signals
|
|
502
|
-
|
|
506
|
+
|
|
503
507
|
def calculate_backtest_signals(self):
|
|
504
508
|
p0, p1 = self.tickers[0], self.tickers[1]
|
|
505
509
|
dt = self.bars.get_latest_bar_datetime(p0)
|
|
@@ -543,7 +547,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
543
547
|
self.calculate_backtest_signals()
|
|
544
548
|
elif self.mode == 'live':
|
|
545
549
|
return self.calculate_live_signals()
|
|
546
|
-
|
|
550
|
+
|
|
547
551
|
|
|
548
552
|
class StockIndexSTBOTrading(Strategy):
|
|
549
553
|
"""
|
|
@@ -554,12 +558,13 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
554
558
|
to recover. It operates in two modes: backtest and live, and it is particularly
|
|
555
559
|
tailored to index trading.
|
|
556
560
|
"""
|
|
557
|
-
|
|
558
|
-
|
|
561
|
+
|
|
562
|
+
def __init__(self,
|
|
563
|
+
bars: DataHandler = None,
|
|
559
564
|
events: Queue = None,
|
|
560
565
|
symbol_list: List[str] = None,
|
|
561
566
|
mode: Literal['backtest', 'live'] = 'backtest',
|
|
562
|
-
|
|
567
|
+
**kwargs):
|
|
563
568
|
"""
|
|
564
569
|
Args:
|
|
565
570
|
`bars`: `DataHandler` for market data handling.
|
|
@@ -599,7 +604,7 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
599
604
|
self.last_price = {index: None for index in symbols}
|
|
600
605
|
self.heightest_price = {index: None for index in symbols}
|
|
601
606
|
self.lowerst_price = {index: None for index in symbols}
|
|
602
|
-
|
|
607
|
+
|
|
603
608
|
if self.mode == 'backtest':
|
|
604
609
|
self.qty = get_quantities(quantities, symbols)
|
|
605
610
|
self.num_buys = {index: 0 for index in symbols}
|
|
@@ -644,7 +649,7 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
644
649
|
current_price, avg_price) >= (self.expeted_return[index]):
|
|
645
650
|
signals[index] = 'EXIT'
|
|
646
651
|
self.logger.info(
|
|
647
|
-
f"SYMBOL={index} - Hp={self.heightest_price[index]} - "
|
|
652
|
+
f"SYMBOL={index} - Hp={self.heightest_price[index]} - "
|
|
648
653
|
f"Lp={self.lowerst_price[index]} - Cp={current_price} - %chg={round(down_change, 2)}"
|
|
649
654
|
)
|
|
650
655
|
return signals
|
|
@@ -672,8 +677,8 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
672
677
|
current_price, self.heightest_price[index])
|
|
673
678
|
|
|
674
679
|
if (down_change <= - (self.expeted_return[index]/self.rr)
|
|
675
|
-
|
|
676
|
-
signal = SignalEvent(100, index, dt, 'LONG',
|
|
680
|
+
and self.num_buys[index] <= self.max_trades[index]):
|
|
681
|
+
signal = SignalEvent(100, index, dt, 'LONG',
|
|
677
682
|
quantity=self.qty[index], price=current_price)
|
|
678
683
|
self.events.put(signal)
|
|
679
684
|
self.num_buys[index] += 1
|
|
@@ -685,7 +690,8 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
685
690
|
qty = self.qty[index] * self.num_buys[index]
|
|
686
691
|
if self._calculate_pct_change(
|
|
687
692
|
current_price, av_price) >= (self.expeted_return[index]):
|
|
688
|
-
signal = SignalEvent(
|
|
693
|
+
signal = SignalEvent(
|
|
694
|
+
100, index, dt, 'EXIT', quantity=qty, price=current_price)
|
|
689
695
|
self.events.put(signal)
|
|
690
696
|
self.num_buys[index] = 0
|
|
691
697
|
self.buy_prices[index] = []
|
|
@@ -709,12 +715,13 @@ def _run_backtest(
|
|
|
709
715
|
engine = BacktestEngine(
|
|
710
716
|
symbol_list, capital, 0.0, datetime.strptime(
|
|
711
717
|
kwargs['yf_start'], "%Y-%m-%d"),
|
|
712
|
-
kwargs.get("data_handler", YFDataHandler),
|
|
713
|
-
kwargs.get("exc_handler", SimExecutionHandler),
|
|
718
|
+
kwargs.get("data_handler", YFDataHandler),
|
|
719
|
+
kwargs.get("exc_handler", SimExecutionHandler),
|
|
714
720
|
kwargs.pop('backtester_class'), **kwargs
|
|
715
721
|
)
|
|
716
722
|
engine.simulate_trading()
|
|
717
723
|
|
|
724
|
+
|
|
718
725
|
def _run_arch_backtest(
|
|
719
726
|
capital: float = 100000.0,
|
|
720
727
|
quantity: int = 1000
|
|
@@ -730,6 +737,7 @@ def _run_arch_backtest(
|
|
|
730
737
|
}
|
|
731
738
|
_run_backtest("ARIMA+GARCH & HMM", capital, ["^GSPC"], kwargs)
|
|
732
739
|
|
|
740
|
+
|
|
733
741
|
def _run_kf_backtest(
|
|
734
742
|
capital: float = 100000.0,
|
|
735
743
|
quantity: int = 2000
|
|
@@ -748,24 +756,26 @@ def _run_kf_backtest(
|
|
|
748
756
|
}
|
|
749
757
|
_run_backtest("Kalman Filter & HMM", capital, symbol_list, kwargs)
|
|
750
758
|
|
|
759
|
+
|
|
751
760
|
def _run_sma_backtest(
|
|
752
761
|
capital: float = 100000.0,
|
|
753
762
|
quantity: int = 1
|
|
754
|
-
):
|
|
755
|
-
spx_data = yf.download("^GSPC",start="1990-01-01", end="2009-12-31")
|
|
763
|
+
):
|
|
764
|
+
spx_data = yf.download("^GSPC", start="1990-01-01", end="2009-12-31")
|
|
756
765
|
kwargs = {
|
|
757
766
|
"quantities": quantity,
|
|
758
767
|
"hmm_end": "2009-12-31",
|
|
759
768
|
"yf_start": "2010-01-04",
|
|
760
769
|
"hmm_data": spx_data,
|
|
761
|
-
"mt5_start": datetime(2010,1,1),
|
|
762
|
-
"mt5_end": datetime(2023,1,1),
|
|
770
|
+
"mt5_start": datetime(2010, 1, 1),
|
|
771
|
+
"mt5_end": datetime(2023, 1, 1),
|
|
763
772
|
"backtester_class": SMAStrategy,
|
|
764
773
|
"data_handler": MT5DataHandler,
|
|
765
774
|
"exc_handler": MT5ExecutionHandler
|
|
766
775
|
}
|
|
767
776
|
_run_backtest("SMA & HMM", capital, ["[SP500]"], kwargs)
|
|
768
777
|
|
|
778
|
+
|
|
769
779
|
def _run_sistbo_backtest(
|
|
770
780
|
capital: float = 100000.0,
|
|
771
781
|
quantity: int = None
|
|
@@ -789,7 +799,9 @@ def _run_sistbo_backtest(
|
|
|
789
799
|
"data_handler": MT5DataHandler,
|
|
790
800
|
"exc_handler": MT5ExecutionHandler
|
|
791
801
|
}
|
|
792
|
-
_run_backtest("Stock Index Short Term Buy Only ",
|
|
802
|
+
_run_backtest("Stock Index Short Term Buy Only ",
|
|
803
|
+
capital, symbol_list, kwargs)
|
|
804
|
+
|
|
793
805
|
|
|
794
806
|
_BACKTESTS = {
|
|
795
807
|
'sma': _run_sma_backtest,
|
|
@@ -798,8 +810,9 @@ _BACKTESTS = {
|
|
|
798
810
|
'sistbo': _run_sistbo_backtest
|
|
799
811
|
}
|
|
800
812
|
|
|
813
|
+
|
|
801
814
|
def test_strategy(strategy: Literal['sma', 'klf', 'arch', 'sistbo'] = 'sma',
|
|
802
|
-
|
|
815
|
+
quantity: Optional[int] = 100):
|
|
803
816
|
"""
|
|
804
817
|
Executes a backtest of the specified strategy
|
|
805
818
|
|