bbstrader 0.1.8__py3-none-any.whl → 0.1.91__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 +4 -2
- bbstrader/btengine/__init__.py +5 -5
- bbstrader/btengine/backtest.py +51 -10
- bbstrader/btengine/data.py +147 -55
- bbstrader/btengine/event.py +4 -1
- bbstrader/btengine/execution.py +125 -23
- bbstrader/btengine/performance.py +4 -7
- bbstrader/btengine/portfolio.py +34 -13
- bbstrader/btengine/strategy.py +466 -6
- bbstrader/config.py +111 -0
- bbstrader/metatrader/__init__.py +4 -4
- bbstrader/metatrader/account.py +357 -55
- bbstrader/metatrader/rates.py +234 -31
- bbstrader/metatrader/risk.py +35 -24
- bbstrader/metatrader/trade.py +361 -173
- bbstrader/metatrader/utils.py +2 -53
- bbstrader/models/factors.py +0 -0
- bbstrader/models/ml.py +0 -0
- bbstrader/models/optimization.py +0 -0
- bbstrader/trading/__init__.py +1 -1
- bbstrader/trading/execution.py +329 -215
- bbstrader/trading/scripts.py +57 -0
- bbstrader/trading/strategies.py +49 -71
- bbstrader/tseries.py +274 -39
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/METADATA +11 -3
- bbstrader-0.1.91.dist-info/RECORD +31 -0
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/WHEEL +1 -1
- bbstrader-0.1.8.dist-info/RECORD +0 -26
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/LICENSE +0 -0
- {bbstrader-0.1.8.dist-info → bbstrader-0.1.91.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from telegram import Bot
|
|
3
|
+
from notifypy import Notify
|
|
4
|
+
from telegram.error import TelegramError
|
|
5
|
+
|
|
6
|
+
__all__ = ['send_telegram_message', 'send_notification', 'send_message']
|
|
7
|
+
|
|
8
|
+
async def send_telegram_message(token, chat_id, text=''):
|
|
9
|
+
"""
|
|
10
|
+
Send a message to a telegram chat
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
token: str: Telegram bot token
|
|
14
|
+
chat_id: int or str or list: Chat id or list of chat ids
|
|
15
|
+
text: str: Message to send
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
bot = Bot(token=token)
|
|
19
|
+
if isinstance(chat_id, (int, str)):
|
|
20
|
+
chat_id = [chat_id]
|
|
21
|
+
for id in chat_id:
|
|
22
|
+
await bot.send_message(chat_id=id, text=text)
|
|
23
|
+
except TelegramError as e:
|
|
24
|
+
print(f"Error sending message: {e}")
|
|
25
|
+
|
|
26
|
+
def send_notification(title, message=''):
|
|
27
|
+
"""
|
|
28
|
+
Send a desktop notification
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
title: str: Title of the notification
|
|
32
|
+
message: str: Message of the notification
|
|
33
|
+
"""
|
|
34
|
+
notification = Notify(default_notification_application_name='bbstrading')
|
|
35
|
+
notification.title = title
|
|
36
|
+
notification.message = message
|
|
37
|
+
notification.send()
|
|
38
|
+
|
|
39
|
+
def send_message(title='SIGNAL', message='New signal',
|
|
40
|
+
notify_me=False, telegram=False, token=None, chat_id=None):
|
|
41
|
+
"""
|
|
42
|
+
Send a message to the user
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
title: str: Title of the message
|
|
46
|
+
message: str: Message of the message
|
|
47
|
+
notify_me: bool: Send a desktop notification
|
|
48
|
+
telegram: bool: Send a telegram message
|
|
49
|
+
token: str: Telegram bot token
|
|
50
|
+
chat_id: int or str or list: Chat id or list of chat ids
|
|
51
|
+
"""
|
|
52
|
+
if notify_me:
|
|
53
|
+
send_notification(title, message=message)
|
|
54
|
+
if telegram:
|
|
55
|
+
if token is None or chat_id is None:
|
|
56
|
+
raise ValueError('Token and chat_id must be provided')
|
|
57
|
+
asyncio.run(send_telegram_message(token, chat_id, text=message))
|
bbstrader/trading/strategies.py
CHANGED
|
@@ -7,7 +7,6 @@ import pandas as pd
|
|
|
7
7
|
from queue import Queue
|
|
8
8
|
import yfinance as yf
|
|
9
9
|
from datetime import datetime
|
|
10
|
-
from typing import List, Literal, Dict, Union, Optional
|
|
11
10
|
from bbstrader.metatrader.rates import Rates
|
|
12
11
|
from bbstrader.metatrader.account import Account
|
|
13
12
|
from bbstrader.btengine.event import SignalEvent
|
|
@@ -16,26 +15,29 @@ from bbstrader.models.risk import HMMRiskManager
|
|
|
16
15
|
from bbstrader.models.risk import build_hmm_models
|
|
17
16
|
from bbstrader.btengine.backtest import BacktestEngine
|
|
18
17
|
from bbstrader.btengine.strategy import Strategy
|
|
18
|
+
from bbstrader.btengine.strategy import MT5Strategy
|
|
19
19
|
from bbstrader.btengine.execution import *
|
|
20
20
|
from bbstrader.btengine.data import *
|
|
21
|
-
from bbstrader.tseries import
|
|
22
|
-
|
|
21
|
+
from bbstrader.tseries import KalmanFilterModel, ArimaGarchModel
|
|
22
|
+
from typing import Union, Optional, Literal, Dict, List
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
25
|
'SMAStrategy',
|
|
26
26
|
'ArimaGarchStrategy',
|
|
27
27
|
'KalmanFilterStrategy',
|
|
28
28
|
'StockIndexSTBOTrading',
|
|
29
|
-
'test_strategy'
|
|
29
|
+
'test_strategy',
|
|
30
|
+
'get_quantities'
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
def
|
|
34
|
+
def get_quantities(quantities, symbol_list):
|
|
34
35
|
if isinstance(quantities, dict):
|
|
35
36
|
return quantities
|
|
36
37
|
elif isinstance(quantities, int):
|
|
37
38
|
return {symbol: quantities for symbol in symbol_list}
|
|
38
39
|
|
|
40
|
+
|
|
39
41
|
class SMAStrategy(Strategy):
|
|
40
42
|
"""
|
|
41
43
|
Carries out a basic Moving Average Crossover strategy bactesting with a
|
|
@@ -75,16 +77,13 @@ class SMAStrategy(Strategy):
|
|
|
75
77
|
"""
|
|
76
78
|
self.bars = bars
|
|
77
79
|
self.events = events
|
|
78
|
-
|
|
79
|
-
self.symbol_list = symbol_list
|
|
80
|
-
else:
|
|
81
|
-
self.symbol_list = self.bars.symbol_list
|
|
80
|
+
self.symbol_list = symbol_list or self.bars.symbol_list
|
|
82
81
|
self.mode = mode
|
|
83
82
|
|
|
84
83
|
self.short_window = kwargs.get("short_window", 50)
|
|
85
84
|
self.long_window = kwargs.get("long_window", 200)
|
|
86
85
|
self.tf = kwargs.get("time_frame", 'D1')
|
|
87
|
-
self.qty =
|
|
86
|
+
self.qty = get_quantities(
|
|
88
87
|
kwargs.get('quantities', 100), self.symbol_list)
|
|
89
88
|
self.sd = kwargs.get("session_duration", 23.0)
|
|
90
89
|
self.risk_models = build_hmm_models(self.symbol_list, **kwargs)
|
|
@@ -101,12 +100,13 @@ class SMAStrategy(Strategy):
|
|
|
101
100
|
def get_backtest_data(self):
|
|
102
101
|
symbol_data = {symbol: None for symbol in self.symbol_list}
|
|
103
102
|
for s in self.symbol_list:
|
|
103
|
+
latest_bars = self.bars.get_latest_bars(s, N=self.long_window)
|
|
104
104
|
bar_date = self.bars.get_latest_bar_datetime(s)
|
|
105
105
|
bars = self.bars.get_latest_bars_values(
|
|
106
|
-
s, "
|
|
106
|
+
s, "adj_close", N=self.long_window
|
|
107
107
|
)
|
|
108
108
|
returns_val = self.bars.get_latest_bars_values(
|
|
109
|
-
s, "
|
|
109
|
+
s, "returns", N=self.risk_window
|
|
110
110
|
)
|
|
111
111
|
if len(bars) >= self.long_window and len(returns_val) >= self.risk_window:
|
|
112
112
|
regime = self.risk_models[s].which_trade_allowed(returns_val)
|
|
@@ -123,7 +123,7 @@ class SMAStrategy(Strategy):
|
|
|
123
123
|
for s, data in symbol_data.items():
|
|
124
124
|
signal = None
|
|
125
125
|
if data is not None:
|
|
126
|
-
price = self.bars.get_latest_bar_value(s, "
|
|
126
|
+
price = self.bars.get_latest_bar_value(s, "adj_close")
|
|
127
127
|
short_sma, long_sma, regime, bar_date = data
|
|
128
128
|
dt = bar_date
|
|
129
129
|
if regime == "LONG":
|
|
@@ -158,8 +158,8 @@ class SMAStrategy(Strategy):
|
|
|
158
158
|
symbol_data = {symbol: None for symbol in self.symbol_list}
|
|
159
159
|
for symbol in self.symbol_list:
|
|
160
160
|
sig_rate = Rates(symbol, self.tf, 0, self.risk_window)
|
|
161
|
-
hmm_data = sig_rate.
|
|
162
|
-
prices = sig_rate.
|
|
161
|
+
hmm_data = sig_rate.returns.values
|
|
162
|
+
prices = sig_rate.close.values
|
|
163
163
|
current_regime = self.risk_models[symbol].which_trade_allowed(hmm_data)
|
|
164
164
|
assert len(prices) >= self.long_window and len(hmm_data) >= self.risk_window
|
|
165
165
|
short_sma = np.mean(prices[-self.short_window:])
|
|
@@ -240,13 +240,10 @@ class ArimaGarchStrategy(Strategy):
|
|
|
240
240
|
"""
|
|
241
241
|
self.bars = bars
|
|
242
242
|
self.events = events
|
|
243
|
-
|
|
244
|
-
self.symbol_list = symbol_list
|
|
245
|
-
else:
|
|
246
|
-
self.symbol_list = self.bars.symbol_list
|
|
243
|
+
self.symbol_list = symbol_list or self.bars.symbol_list
|
|
247
244
|
self.mode = mode
|
|
248
245
|
|
|
249
|
-
self.qty =
|
|
246
|
+
self.qty = get_quantities(
|
|
250
247
|
kwargs.get('quantities', 100), self.symbol_list)
|
|
251
248
|
self.arima_window = kwargs.get('arima_window', 252)
|
|
252
249
|
self.tf = kwargs.get('time_frame', 'D1')
|
|
@@ -258,7 +255,6 @@ class ArimaGarchStrategy(Strategy):
|
|
|
258
255
|
self.long_market = {s : False for s in self.symbol_list}
|
|
259
256
|
self.short_market = {s : False for s in self.symbol_list}
|
|
260
257
|
|
|
261
|
-
|
|
262
258
|
def _build_arch_models(self, **kwargs) -> Dict[str, ArimaGarchModel]:
|
|
263
259
|
arch_models = {symbol: None for symbol in self.symbol_list}
|
|
264
260
|
for symbol in self.symbol_list:
|
|
@@ -279,10 +275,10 @@ class ArimaGarchStrategy(Strategy):
|
|
|
279
275
|
N = self.risk_window
|
|
280
276
|
dt = self.bars.get_latest_bar_datetime(symbol)
|
|
281
277
|
bars = self.bars.get_latest_bars_values(
|
|
282
|
-
symbol, "
|
|
278
|
+
symbol, "close", N=self.arima_window
|
|
283
279
|
)
|
|
284
280
|
returns = self.bars.get_latest_bars_values(
|
|
285
|
-
symbol, '
|
|
281
|
+
symbol, 'returns', N=self.risk_window
|
|
286
282
|
)
|
|
287
283
|
df = pd.DataFrame()
|
|
288
284
|
df['Close'] = bars[-M:]
|
|
@@ -302,7 +298,7 @@ class ArimaGarchStrategy(Strategy):
|
|
|
302
298
|
signal = None
|
|
303
299
|
prediction = self.arima_models[symbol].get_prediction(data)
|
|
304
300
|
regime = self.risk_models[symbol].which_trade_allowed(returns)
|
|
305
|
-
price = self.bars.get_latest_bar_value(symbol, "
|
|
301
|
+
price = self.bars.get_latest_bar_value(symbol, "adj_close")
|
|
306
302
|
|
|
307
303
|
# If we are short the market, check for an exit
|
|
308
304
|
if prediction > 0 and self.short_market[symbol]:
|
|
@@ -341,14 +337,15 @@ class ArimaGarchStrategy(Strategy):
|
|
|
341
337
|
rates = arch_data.get_rates_from_pos()
|
|
342
338
|
arch_returns = self.arima_models[symbol].load_and_prepare_data(rates)
|
|
343
339
|
window_data = arch_returns['diff_log_return'].iloc[-self.arima_window:]
|
|
344
|
-
hmm_returns = arch_data.
|
|
340
|
+
hmm_returns = arch_data.returns.values[-self.risk_window:]
|
|
345
341
|
symbol_data[symbol] = (window_data, hmm_returns)
|
|
346
342
|
return symbol_data
|
|
347
343
|
|
|
348
344
|
def create_live_signals(self):
|
|
349
345
|
signals = {symbol: None for symbol in self.symbol_list}
|
|
346
|
+
data = self.get_live_data()
|
|
350
347
|
for symbol in self.symbol_list:
|
|
351
|
-
symbol_data =
|
|
348
|
+
symbol_data = data[symbol]
|
|
352
349
|
if symbol_data is not None:
|
|
353
350
|
window_data, hmm_returns = symbol_data
|
|
354
351
|
prediction = self.arima_models[symbol].get_prediction(window_data)
|
|
@@ -402,14 +399,12 @@ class KalmanFilterStrategy(Strategy):
|
|
|
402
399
|
"""
|
|
403
400
|
self.bars = bars
|
|
404
401
|
self.events_queue = events
|
|
405
|
-
|
|
406
|
-
self.symbol_list = symbol_list
|
|
407
|
-
else:
|
|
408
|
-
self.symbol_list = self.bars.symbol_list
|
|
402
|
+
self.symbol_list = symbol_list or self.bars.symbol_list
|
|
409
403
|
self.mode = mode
|
|
410
404
|
|
|
411
405
|
self.hmm_tiker = kwargs.get("hmm_tiker")
|
|
412
406
|
self._assert_tikers()
|
|
407
|
+
self.account = Account()
|
|
413
408
|
self.hmm_window = kwargs.get("hmm_window", 50)
|
|
414
409
|
self.qty = kwargs.get("quantity", 100)
|
|
415
410
|
self.tf = kwargs.get("time_frame", "D1")
|
|
@@ -436,8 +431,8 @@ class KalmanFilterStrategy(Strategy):
|
|
|
436
431
|
return
|
|
437
432
|
et, sqrt_Qt = etqt
|
|
438
433
|
theta = self.kl_model.theta
|
|
439
|
-
p1 = self.bars.get_latest_bar_value(self.tickers[1], "
|
|
440
|
-
p0 = self.bars.get_latest_bar_value(self.tickers[0], "
|
|
434
|
+
p1 = self.bars.get_latest_bar_value(self.tickers[1], "adj_close")
|
|
435
|
+
p0 = self.bars.get_latest_bar_value(self.tickers[0], "adj_close")
|
|
441
436
|
if et >= -sqrt_Qt and self.long_market:
|
|
442
437
|
print("CLOSING LONG: %s" % dt)
|
|
443
438
|
y_signal = SignalEvent(1, self.tickers[1], dt, "EXIT", price=p1)
|
|
@@ -480,14 +475,9 @@ class KalmanFilterStrategy(Strategy):
|
|
|
480
475
|
|
|
481
476
|
def calculate_livexy(self):
|
|
482
477
|
signals = {symbol: None for symbol in self.symbol_list}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
p0_data = p0_.get_close
|
|
487
|
-
p1_data = p1_.get_close
|
|
488
|
-
prices = np.array(
|
|
489
|
-
[p0_data.values[-1], p1_data.values[-1]]
|
|
490
|
-
)
|
|
478
|
+
p0_price = self.account.get_tick_info(self.tickers[0]).ask
|
|
479
|
+
p1_price = self.account.get_tick_info(self.tickers[1]).ask
|
|
480
|
+
prices = np.array([p0_price, p1_price])
|
|
491
481
|
et_std = self.kl_model.calculate_etqt(prices)
|
|
492
482
|
if et_std is not None:
|
|
493
483
|
et, std = et_std
|
|
@@ -513,19 +503,15 @@ class KalmanFilterStrategy(Strategy):
|
|
|
513
503
|
def calculate_backtest_signals(self):
|
|
514
504
|
p0, p1 = self.tickers[0], self.tickers[1]
|
|
515
505
|
dt = self.bars.get_latest_bar_datetime(p0)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
)
|
|
519
|
-
_y = self.bars.get_latest_bars_values(
|
|
520
|
-
p1, "Close", N=1
|
|
521
|
-
)
|
|
506
|
+
x = self.bars.get_latest_bar_value(p0, "close")
|
|
507
|
+
y = self.bars.get_latest_bar_value(p1, "close")
|
|
522
508
|
returns = self.bars.get_latest_bars_values(
|
|
523
|
-
self.hmm_tiker, "
|
|
509
|
+
self.hmm_tiker, "returns", N=self.hmm_window
|
|
524
510
|
)
|
|
525
511
|
latest_prices = np.array([-1.0, -1.0])
|
|
526
512
|
if len(returns) >= self.hmm_window:
|
|
527
|
-
latest_prices[0] =
|
|
528
|
-
latest_prices[1] =
|
|
513
|
+
latest_prices[0] = x
|
|
514
|
+
latest_prices[1] = y
|
|
529
515
|
et_qt = self.kl_model.calculate_etqt(latest_prices)
|
|
530
516
|
regime = self.risk_model[
|
|
531
517
|
self.hmm_tiker].which_trade_allowed(returns)
|
|
@@ -536,7 +522,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
536
522
|
signals = {symbol: None for symbol in self.symbol_list}
|
|
537
523
|
initial_signals = self.calculate_livexy()
|
|
538
524
|
hmm_data = Rates(self.hmm_ticker, self.tf, 0, self.hmm_window)
|
|
539
|
-
returns = hmm_data.
|
|
525
|
+
returns = hmm_data.returns.values
|
|
540
526
|
current_regime = self.risk_model[
|
|
541
527
|
self.hmm_tiker].which_trade_allowed(returns)
|
|
542
528
|
for symbol in self.symbol_list:
|
|
@@ -557,6 +543,7 @@ class KalmanFilterStrategy(Strategy):
|
|
|
557
543
|
self.calculate_backtest_signals()
|
|
558
544
|
elif self.mode == 'live':
|
|
559
545
|
return self.calculate_live_signals()
|
|
546
|
+
|
|
560
547
|
|
|
561
548
|
class StockIndexSTBOTrading(Strategy):
|
|
562
549
|
"""
|
|
@@ -590,10 +577,7 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
590
577
|
"""
|
|
591
578
|
self.bars = bars
|
|
592
579
|
self.events = events
|
|
593
|
-
|
|
594
|
-
self.symbol_list = symbol_list
|
|
595
|
-
else:
|
|
596
|
-
self.symbol_list = self.bars.symbol_list
|
|
580
|
+
self.symbol_list = symbol_list or self.bars.symbol_list
|
|
597
581
|
self.mode = mode
|
|
598
582
|
|
|
599
583
|
self.account = Account()
|
|
@@ -617,7 +601,7 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
617
601
|
self.lowerst_price = {index: None for index in symbols}
|
|
618
602
|
|
|
619
603
|
if self.mode == 'backtest':
|
|
620
|
-
self.qty =
|
|
604
|
+
self.qty = get_quantities(quantities, symbols)
|
|
621
605
|
self.num_buys = {index: 0 for index in symbols}
|
|
622
606
|
self.buy_prices = {index: [] for index in symbols}
|
|
623
607
|
|
|
@@ -668,7 +652,7 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
668
652
|
def calculate_backtest_signals(self):
|
|
669
653
|
for index in self.symbol_list.copy():
|
|
670
654
|
dt = self.bars.get_latest_bar_datetime(index)
|
|
671
|
-
last_price = self.bars.get_latest_bars_values(index, '
|
|
655
|
+
last_price = self.bars.get_latest_bars_values(index, 'close', N=1)
|
|
672
656
|
|
|
673
657
|
current_price = last_price[-1]
|
|
674
658
|
if self.last_price[index] is None:
|
|
@@ -691,7 +675,6 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
691
675
|
and self.num_buys[index] <= self.max_trades[index]):
|
|
692
676
|
signal = SignalEvent(100, index, dt, 'LONG',
|
|
693
677
|
quantity=self.qty[index], price=current_price)
|
|
694
|
-
print(f'{dt}: LONG {self.qty[index]} units of {index} at {current_price}')
|
|
695
678
|
self.events.put(signal)
|
|
696
679
|
self.num_buys[index] += 1
|
|
697
680
|
self.buy_prices[index].append(current_price)
|
|
@@ -703,12 +686,10 @@ class StockIndexSTBOTrading(Strategy):
|
|
|
703
686
|
if self._calculate_pct_change(
|
|
704
687
|
current_price, av_price) >= (self.expeted_return[index]):
|
|
705
688
|
signal = SignalEvent(100, index, dt, 'EXIT', quantity=qty, price=current_price)
|
|
706
|
-
print(f'{dt}: EXIT {qty} units of {index} at {current_price}')
|
|
707
689
|
self.events.put(signal)
|
|
708
690
|
self.num_buys[index] = 0
|
|
709
691
|
self.buy_prices[index] = []
|
|
710
692
|
|
|
711
|
-
|
|
712
693
|
def calculate_signals(self, event=None) -> Dict[str, Union[str, None]]:
|
|
713
694
|
if self.mode == 'backtest' and event is not None:
|
|
714
695
|
if event.type == 'MARKET':
|
|
@@ -728,13 +709,12 @@ def _run_backtest(
|
|
|
728
709
|
engine = BacktestEngine(
|
|
729
710
|
symbol_list, capital, 0.0, datetime.strptime(
|
|
730
711
|
kwargs['yf_start'], "%Y-%m-%d"),
|
|
731
|
-
kwargs.get("data_handler",
|
|
732
|
-
kwargs.get("exc_handler",
|
|
712
|
+
kwargs.get("data_handler", YFDataHandler),
|
|
713
|
+
kwargs.get("exc_handler", SimExecutionHandler),
|
|
733
714
|
kwargs.pop('backtester_class'), **kwargs
|
|
734
715
|
)
|
|
735
716
|
engine.simulate_trading()
|
|
736
717
|
|
|
737
|
-
|
|
738
718
|
def _run_arch_backtest(
|
|
739
719
|
capital: float = 100000.0,
|
|
740
720
|
quantity: int = 1000
|
|
@@ -746,11 +726,10 @@ def _run_arch_backtest(
|
|
|
746
726
|
"yf_start": "2010-01-04",
|
|
747
727
|
"hmm_data": hmm_data,
|
|
748
728
|
'backtester_class': ArimaGarchStrategy,
|
|
749
|
-
"data_handler":
|
|
729
|
+
"data_handler": YFDataHandler,
|
|
750
730
|
}
|
|
751
731
|
_run_backtest("ARIMA+GARCH & HMM", capital, ["^GSPC"], kwargs)
|
|
752
732
|
|
|
753
|
-
|
|
754
733
|
def _run_kf_backtest(
|
|
755
734
|
capital: float = 100000.0,
|
|
756
735
|
quantity: int = 2000
|
|
@@ -765,11 +744,10 @@ def _run_kf_backtest(
|
|
|
765
744
|
"hmm_tiker": "TLT",
|
|
766
745
|
"session_duration": 6.5,
|
|
767
746
|
'backtester_class': KalmanFilterStrategy,
|
|
768
|
-
"data_handler":
|
|
747
|
+
"data_handler": YFDataHandler
|
|
769
748
|
}
|
|
770
749
|
_run_backtest("Kalman Filter & HMM", capital, symbol_list, kwargs)
|
|
771
750
|
|
|
772
|
-
|
|
773
751
|
def _run_sma_backtest(
|
|
774
752
|
capital: float = 100000.0,
|
|
775
753
|
quantity: int = 1
|
|
@@ -780,10 +758,10 @@ def _run_sma_backtest(
|
|
|
780
758
|
"hmm_end": "2009-12-31",
|
|
781
759
|
"yf_start": "2010-01-04",
|
|
782
760
|
"hmm_data": spx_data,
|
|
783
|
-
"mt5_start": datetime(2010,
|
|
784
|
-
"mt5_end": datetime(2023,
|
|
761
|
+
"mt5_start": datetime(2010,1,1),
|
|
762
|
+
"mt5_end": datetime(2023,1,1),
|
|
785
763
|
"backtester_class": SMAStrategy,
|
|
786
|
-
"data_handler":
|
|
764
|
+
"data_handler": MT5DataHandler,
|
|
787
765
|
"exc_handler": MT5ExecutionHandler
|
|
788
766
|
}
|
|
789
767
|
_run_backtest("SMA & HMM", capital, ["[SP500]"], kwargs)
|
|
@@ -808,7 +786,7 @@ def _run_sistbo_backtest(
|
|
|
808
786
|
'yf_start': start.strftime('%Y-%m-%d'),
|
|
809
787
|
'time_frame': '15m',
|
|
810
788
|
"backtester_class": StockIndexSTBOTrading,
|
|
811
|
-
"data_handler":
|
|
789
|
+
"data_handler": MT5DataHandler,
|
|
812
790
|
"exc_handler": MT5ExecutionHandler
|
|
813
791
|
}
|
|
814
792
|
_run_backtest("Stock Index Short Term Buy Only ", capital, symbol_list, kwargs)
|
|
@@ -837,4 +815,4 @@ def test_strategy(strategy: Literal['sma', 'klf', 'arch', 'sistbo'] = 'sma',
|
|
|
837
815
|
if strategy in _BACKTESTS:
|
|
838
816
|
_BACKTESTS[strategy](quantity=quantity)
|
|
839
817
|
else:
|
|
840
|
-
raise ValueError(f"Unknown strategy: {strategy}")
|
|
818
|
+
raise ValueError(f"Unknown strategy: {strategy}")
|