bbstrader 0.2.4__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 +18 -0
- bbstrader/btengine/__init__.py +54 -0
- bbstrader/btengine/backtest.py +360 -0
- bbstrader/btengine/data.py +712 -0
- bbstrader/btengine/event.py +221 -0
- bbstrader/btengine/execution.py +251 -0
- bbstrader/btengine/performance.py +347 -0
- bbstrader/btengine/portfolio.py +406 -0
- bbstrader/btengine/strategy.py +779 -0
- bbstrader/config.py +133 -0
- 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 +6 -0
- bbstrader/metatrader/account.py +1488 -0
- bbstrader/metatrader/rates.py +579 -0
- bbstrader/metatrader/risk.py +702 -0
- bbstrader/metatrader/trade.py +1690 -0
- bbstrader/metatrader/utils.py +641 -0
- bbstrader/models/__init__.py +10 -0
- bbstrader/models/factors.py +312 -0
- bbstrader/models/ml.py +1264 -0
- bbstrader/models/optimization.py +182 -0
- bbstrader/models/portfolio.py +223 -0
- bbstrader/models/risk.py +398 -0
- bbstrader/trading/__init__.py +11 -0
- bbstrader/trading/execution.py +726 -0
- bbstrader/trading/scripts.py +67 -0
- bbstrader/trading/strategies.py +860 -0
- bbstrader/tseries.py +1816 -0
- bbstrader-0.2.4.dist-info/LICENSE +21 -0
- bbstrader-0.2.4.dist-info/METADATA +174 -0
- bbstrader-0.2.4.dist-info/RECORD +37 -0
- bbstrader-0.2.4.dist-info/WHEEL +5 -0
- bbstrader-0.2.4.dist-info/top_level.txt +1 -0
bbstrader/__ini__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simplified Investment & Trading Toolkit
|
|
3
|
+
=======================================
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
__author__ = "Bertin Balouki SIMYELI"
|
|
7
|
+
__copyright__ = "2023-2025 Bertin Balouki SIMYELI"
|
|
8
|
+
__email__ = "bertin@bbstrader.com"
|
|
9
|
+
__license__ = "MIT"
|
|
10
|
+
__version__ = '0.2.0'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from bbstrader import btengine # noqa: F401
|
|
14
|
+
from bbstrader import metatrader # noqa: F401
|
|
15
|
+
from bbstrader import models # noqa: F401
|
|
16
|
+
from bbstrader import trading # noqa: F401
|
|
17
|
+
from bbstrader import tseries # noqa: F401
|
|
18
|
+
from .config import config_logger # noqa: F401
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Overview
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
This Backtesting Module provides a comprehensive suite of tools to test trading strategies in an event-driven system.
|
|
6
|
+
It simulates the execution of trades in historical market conditions to evaluate the performance of trading strategies
|
|
7
|
+
before applying them in live trading environments. Designed with modularity and extensibility in mind, it caters to
|
|
8
|
+
both novices and experts in algorithmic trading.
|
|
9
|
+
|
|
10
|
+
Features
|
|
11
|
+
========
|
|
12
|
+
|
|
13
|
+
- **Event-Driven Architecture**: Processes market data, generates signals, executes orders, and manages portfolio updates in response to events, closely mimicking live trading environments.
|
|
14
|
+
- **Historical Market Data Support**: Utilizes historical OHLCV data from CSV files, Yahoo finance and MT5 terminal allowing for the testing of strategies over various market conditions and time frames.
|
|
15
|
+
- **Performance Metrics Calculation**: Includes tools for calculating key performance indicators, such as `Sharpe Ratio`, `Sortino Ratio`, and `drawdowns`, to evaluate the effectiveness of trading strategies.
|
|
16
|
+
- **Visualization**: Generates plots of the `equity curve`, `returns`, `drawdowns`, and other metrics for comprehensive strategy `performance analysis`.
|
|
17
|
+
|
|
18
|
+
Components
|
|
19
|
+
==========
|
|
20
|
+
|
|
21
|
+
- **BacktestEgine**: Orchestrates the backtesting process, managing events and invoking components.
|
|
22
|
+
- **Event**: Abstract class for events, with implementations for market data, signals, fill and order events.
|
|
23
|
+
- **DataHandler**: Abstract class for market data handling, with an implementation for `CSVDataHandler`, `MT5DataHandler`, `YFDataHandler`. We will add another data handling in the future such as MacroEconomic Data, Fundamental Data, TICK Data and Real-time Data.
|
|
24
|
+
- **Portfolio**: Manages positions and calculates performance metrics, responding to market data and signals.
|
|
25
|
+
- **ExecutionHandler**: Abstract class for order execution, with a simulated execution handler provided with an implementation for `SimExecutionHandler`.
|
|
26
|
+
- **Performance**: Utility functions for calculating performance metrics and visualizing strategy performance.
|
|
27
|
+
|
|
28
|
+
Examples
|
|
29
|
+
========
|
|
30
|
+
|
|
31
|
+
>>> from bbstrader.btengine import run_backtest
|
|
32
|
+
>>> from datetime import datetime
|
|
33
|
+
>>> run_backtest(
|
|
34
|
+
... symbol_list=['AAPL', 'GOOG'],
|
|
35
|
+
... start_date=datetime(2020, 1, 1),
|
|
36
|
+
... data_handler=DataHandler,
|
|
37
|
+
... strategy=Strategy,
|
|
38
|
+
... exc_handler=ExecutionHandler,
|
|
39
|
+
... initial_capital=500000.0,
|
|
40
|
+
... heartbeat=1.0
|
|
41
|
+
... )
|
|
42
|
+
|
|
43
|
+
Notes
|
|
44
|
+
=====
|
|
45
|
+
|
|
46
|
+
See `bbstrader.btengine.backtest.run_backtest` for more details on the backtesting process and its parameters.
|
|
47
|
+
"""
|
|
48
|
+
from bbstrader.btengine.backtest import * # noqa: F403
|
|
49
|
+
from bbstrader.btengine.data import * # noqa: F403
|
|
50
|
+
from bbstrader.btengine.event import * # noqa: F403
|
|
51
|
+
from bbstrader.btengine.execution import * # noqa: F403
|
|
52
|
+
from bbstrader.btengine.performance import * # noqa: F403
|
|
53
|
+
from bbstrader.btengine.portfolio import * # noqa: F403
|
|
54
|
+
from bbstrader.btengine.strategy import * # noqa: F403
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import queue
|
|
2
|
+
import time
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import List, Literal, Optional
|
|
5
|
+
|
|
6
|
+
from tabulate import tabulate
|
|
7
|
+
|
|
8
|
+
from bbstrader.btengine.data import DataHandler
|
|
9
|
+
from bbstrader.btengine.execution import ExecutionHandler, SimExecutionHandler
|
|
10
|
+
from bbstrader.btengine.portfolio import Portfolio
|
|
11
|
+
from bbstrader.btengine.strategy import Strategy
|
|
12
|
+
|
|
13
|
+
__all__ = ["Backtest", "BacktestEngine", "run_backtest"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Backtest(object):
|
|
17
|
+
"""
|
|
18
|
+
The `Backtest()` object encapsulates the event-handling logic and essentially
|
|
19
|
+
ties together all of the other classes.
|
|
20
|
+
|
|
21
|
+
The Backtest object is designed to carry out a nested while-loop event-driven system
|
|
22
|
+
in order to handle the events placed on the `Event` Queue object.
|
|
23
|
+
The outer while-loop is known as the "heartbeat loop" and decides the temporal resolution of
|
|
24
|
+
the backtesting system. In a live environment this value will be a positive number,
|
|
25
|
+
such as 600 seconds (every ten minutes). Thus the market data and positions
|
|
26
|
+
will only be updated on this timeframe.
|
|
27
|
+
|
|
28
|
+
For the backtester described here the "heartbeat" can be set to zero,
|
|
29
|
+
irrespective of the strategy frequency, since the data is already available by virtue of
|
|
30
|
+
the fact it is historical! We can run the backtest at whatever speed we like,
|
|
31
|
+
since the event-driven system is agnostic to when the data became available,
|
|
32
|
+
so long as it has an associated timestamp.
|
|
33
|
+
|
|
34
|
+
The inner while-loop actually processes the signals and sends them to the correct
|
|
35
|
+
component depending upon the event type. Thus the Event Queue is continually being
|
|
36
|
+
populated and depopulated with events. This is what it means for a system to be event-driven.
|
|
37
|
+
|
|
38
|
+
The initialisation of the Backtest object requires the full `symbol list` of traded symbols,
|
|
39
|
+
the `initial capital`, the `heartbeat` time in milliseconds, the `start datetime` stamp
|
|
40
|
+
of the backtest as well as the `DataHandler`, `ExecutionHandler`, `Strategy` objects
|
|
41
|
+
and additionnal `kwargs` based on the `ExecutionHandler`, the `DataHandler`, and the `Strategy` used.
|
|
42
|
+
|
|
43
|
+
A Queue is used to hold the events. The signals, orders and fills are counted.
|
|
44
|
+
For a `MarketEvent`, the `Strategy` object is told to recalculate new signals,
|
|
45
|
+
while the `Portfolio` object is told to reindex the time. If a `SignalEvent`
|
|
46
|
+
object is received the `Portfolio` is told to handle the new signal and convert it into a
|
|
47
|
+
set of `OrderEvents`, if appropriate. If an `OrderEvent` is received the `ExecutionHandler`
|
|
48
|
+
is sent the order to be transmitted to the broker (if in a real trading setting).
|
|
49
|
+
Finally, if a `FillEvent` is received, the Portfolio will update itself to be aware of
|
|
50
|
+
the new positions.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class BacktestEngine(Backtest):
|
|
58
|
+
__doc__ = Backtest.__doc__
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
symbol_list: List[str],
|
|
63
|
+
initial_capital: float,
|
|
64
|
+
heartbeat: float,
|
|
65
|
+
start_date: datetime,
|
|
66
|
+
data_handler: DataHandler,
|
|
67
|
+
execution_handler: ExecutionHandler,
|
|
68
|
+
strategy: Strategy,
|
|
69
|
+
/,
|
|
70
|
+
**kwargs,
|
|
71
|
+
):
|
|
72
|
+
"""
|
|
73
|
+
Initialises the backtest.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
symbol_list (List[str]): The list of symbol strings.
|
|
77
|
+
intial_capital (float): The starting capital for the portfolio.
|
|
78
|
+
heartbeat (float): Backtest "heartbeat" in seconds
|
|
79
|
+
start_date (datetime): The start datetime of the strategy.
|
|
80
|
+
data_handler (DataHandler) : Handles the market data feed.
|
|
81
|
+
execution_handler (ExecutionHandler) : Handles the orders/fills for trades.
|
|
82
|
+
strategy (Strategy): Generates signals based on market data.
|
|
83
|
+
kwargs : Additional parameters based on the `ExecutionHandler`,
|
|
84
|
+
the `DataHandler`, the `Strategy` used and the `Portfolio`.
|
|
85
|
+
- show_equity (bool): Show the equity curve of the portfolio.
|
|
86
|
+
- stats_file (str): File to save the summary stats.
|
|
87
|
+
"""
|
|
88
|
+
self.symbol_list = symbol_list
|
|
89
|
+
self.initial_capital = initial_capital
|
|
90
|
+
self.heartbeat = heartbeat
|
|
91
|
+
self.start_date = start_date
|
|
92
|
+
|
|
93
|
+
self.dh_cls = data_handler
|
|
94
|
+
self.eh_cls = execution_handler
|
|
95
|
+
self.strategy_cls = strategy
|
|
96
|
+
self.kwargs = kwargs
|
|
97
|
+
|
|
98
|
+
self.events = queue.Queue()
|
|
99
|
+
|
|
100
|
+
self.signals = 0
|
|
101
|
+
self.orders = 0
|
|
102
|
+
self.fills = 0
|
|
103
|
+
|
|
104
|
+
self._generate_trading_instances()
|
|
105
|
+
self.show_equity = kwargs.get("show_equity", False)
|
|
106
|
+
self.stats_file = kwargs.get("stats_file", None)
|
|
107
|
+
|
|
108
|
+
def _generate_trading_instances(self):
|
|
109
|
+
"""
|
|
110
|
+
Generates the trading instance objects from
|
|
111
|
+
their class types.
|
|
112
|
+
"""
|
|
113
|
+
print(
|
|
114
|
+
f"\n[======= STARTING BACKTEST =======]\n"
|
|
115
|
+
f"START DATE: {self.start_date} \n"
|
|
116
|
+
f"INITIAL CAPITAL: {self.initial_capital}\n"
|
|
117
|
+
)
|
|
118
|
+
self.data_handler: DataHandler = self.dh_cls(
|
|
119
|
+
self.events, self.symbol_list, **self.kwargs
|
|
120
|
+
)
|
|
121
|
+
self.strategy: Strategy = self.strategy_cls(
|
|
122
|
+
bars=self.data_handler, events=self.events, **self.kwargs
|
|
123
|
+
)
|
|
124
|
+
self.portfolio = Portfolio(
|
|
125
|
+
self.data_handler,
|
|
126
|
+
self.events,
|
|
127
|
+
self.start_date,
|
|
128
|
+
self.initial_capital,
|
|
129
|
+
**self.kwargs,
|
|
130
|
+
)
|
|
131
|
+
self.execution_handler: ExecutionHandler = self.eh_cls(
|
|
132
|
+
self.events, self.data_handler, **self.kwargs
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def _run_backtest(self):
|
|
136
|
+
"""
|
|
137
|
+
Executes the backtest.
|
|
138
|
+
"""
|
|
139
|
+
i = 0
|
|
140
|
+
while True:
|
|
141
|
+
i += 1
|
|
142
|
+
value = self.portfolio.all_holdings[-1]["Total"]
|
|
143
|
+
if self.data_handler.continue_backtest is True:
|
|
144
|
+
# Update the market bars
|
|
145
|
+
self.data_handler.update_bars()
|
|
146
|
+
self.strategy.check_pending_orders()
|
|
147
|
+
self.strategy.get_update_from_portfolio(
|
|
148
|
+
self.portfolio.current_positions, self.portfolio.current_holdings
|
|
149
|
+
)
|
|
150
|
+
self.strategy.cash = value
|
|
151
|
+
else:
|
|
152
|
+
print("\n[======= BACKTEST COMPLETED =======]")
|
|
153
|
+
print(
|
|
154
|
+
f"END DATE: {self.data_handler.get_latest_bar_datetime(self.symbol_list[0])}"
|
|
155
|
+
)
|
|
156
|
+
print(f"TOTAL BARS: {i} ")
|
|
157
|
+
print(f"PORFOLIO VALUE: {round(value, 2)}")
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
# Handle the events
|
|
161
|
+
while True:
|
|
162
|
+
try:
|
|
163
|
+
event = self.events.get(False)
|
|
164
|
+
except queue.Empty:
|
|
165
|
+
break
|
|
166
|
+
else:
|
|
167
|
+
if event is not None:
|
|
168
|
+
if event.type == "MARKET":
|
|
169
|
+
self.strategy.calculate_signals(event)
|
|
170
|
+
self.portfolio.update_timeindex(event)
|
|
171
|
+
|
|
172
|
+
elif event.type == "SIGNAL":
|
|
173
|
+
self.signals += 1
|
|
174
|
+
self.portfolio.update_signal(event)
|
|
175
|
+
|
|
176
|
+
elif event.type == "ORDER":
|
|
177
|
+
self.orders += 1
|
|
178
|
+
self.execution_handler.execute_order(event)
|
|
179
|
+
|
|
180
|
+
elif event.type == "FILL":
|
|
181
|
+
self.fills += 1
|
|
182
|
+
self.portfolio.update_fill(event)
|
|
183
|
+
self.strategy.update_trades_from_fill(event)
|
|
184
|
+
|
|
185
|
+
time.sleep(self.heartbeat)
|
|
186
|
+
|
|
187
|
+
def _output_performance(self):
|
|
188
|
+
"""
|
|
189
|
+
Outputs the strategy performance from the backtest.
|
|
190
|
+
"""
|
|
191
|
+
self.portfolio.create_equity_curve_dataframe()
|
|
192
|
+
|
|
193
|
+
print("\nCreating summary stats...")
|
|
194
|
+
stats = self.portfolio.output_summary_stats()
|
|
195
|
+
print("[======= Summary Stats =======]")
|
|
196
|
+
stat2 = {}
|
|
197
|
+
stat2["Signals"] = self.signals
|
|
198
|
+
stat2["Orders"] = self.orders
|
|
199
|
+
stat2["Fills"] = self.fills
|
|
200
|
+
stats.extend(stat2.items())
|
|
201
|
+
tab_stats = tabulate(stats, headers=["Metric", "Value"], tablefmt="outline")
|
|
202
|
+
print(tab_stats, "\n")
|
|
203
|
+
if self.stats_file:
|
|
204
|
+
with open(self.stats_file, "a") as f:
|
|
205
|
+
f.write("\n[======= Summary Stats =======]\n")
|
|
206
|
+
f.write(tab_stats)
|
|
207
|
+
f.write("\n")
|
|
208
|
+
|
|
209
|
+
if self.show_equity:
|
|
210
|
+
print("\nCreating equity curve...")
|
|
211
|
+
print("\n[======= PORTFOLIO SUMMARY =======]")
|
|
212
|
+
print(
|
|
213
|
+
tabulate(
|
|
214
|
+
self.portfolio.equity_curve.tail(10),
|
|
215
|
+
headers="keys",
|
|
216
|
+
tablefmt="outline",
|
|
217
|
+
),
|
|
218
|
+
"\n",
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def simulate_trading(self):
|
|
222
|
+
"""
|
|
223
|
+
Simulates the backtest and outputs portfolio performance.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
pd.DataFrame: The portfilio values over time (capital, equity, returns etc.)
|
|
227
|
+
"""
|
|
228
|
+
self._run_backtest()
|
|
229
|
+
self._output_performance()
|
|
230
|
+
return self.portfolio.equity_curve
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def run_backtest(
|
|
234
|
+
symbol_list: List[str],
|
|
235
|
+
start_date: datetime,
|
|
236
|
+
data_handler: DataHandler,
|
|
237
|
+
strategy: Strategy,
|
|
238
|
+
exc_handler: Optional[ExecutionHandler] = None,
|
|
239
|
+
initial_capital: float = 100000.0,
|
|
240
|
+
heartbeat: float = 0.0,
|
|
241
|
+
**kwargs,
|
|
242
|
+
):
|
|
243
|
+
"""
|
|
244
|
+
Runs a backtest simulation based on a `DataHandler`, `Strategy`, and `ExecutionHandler`.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
symbol_list (List[str]): List of symbol strings for the assets to be backtested.
|
|
248
|
+
|
|
249
|
+
start_date (datetime): Start date of the backtest.
|
|
250
|
+
|
|
251
|
+
data_handler (DataHandler): An instance of the `DataHandler` class, responsible for managing
|
|
252
|
+
and processing market data. Available options include `CSVDataHandler`,
|
|
253
|
+
`MT5DataHandler`, and `YFDataHandler`. Ensure that the `DataHandler`
|
|
254
|
+
instance is initialized before passing it to the function.
|
|
255
|
+
|
|
256
|
+
strategy (Strategy): The trading strategy to be employed during the backtest.
|
|
257
|
+
The strategy must be an instance of `Strategy` and should include the following attributes:
|
|
258
|
+
- `bars` (DataHandler): The `DataHandler` instance for the strategy.
|
|
259
|
+
- `events` (Queue): Queue instance for managing events.
|
|
260
|
+
- `symbol_list` (List[str]): List of symbols to trade.
|
|
261
|
+
- `mode` (str): 'live' or 'backtest'.
|
|
262
|
+
|
|
263
|
+
Additional parameters specific to the strategy should be passed in `**kwargs`.
|
|
264
|
+
The strategy class must implement a `calculate_signals` method to generate `SignalEvent`.
|
|
265
|
+
|
|
266
|
+
exc_handler (ExecutionHandler, optional): The execution handler for managing order executions.
|
|
267
|
+
If not provided, a `SimulatedExecutionHandler` will be used by default. This handler must
|
|
268
|
+
implement an `execute_order` method to process `OrderEvent` in the `Backtest` class.
|
|
269
|
+
|
|
270
|
+
initial_capital (float, optional): The initial capital for the portfolio in the backtest.
|
|
271
|
+
Default is 100,000.
|
|
272
|
+
|
|
273
|
+
heartbeat (float, optional): Time delay (in seconds) between iterations of the event-driven
|
|
274
|
+
backtest loop. Default is 0.0, allowing the backtest to run as fast as possible. This could
|
|
275
|
+
also be used as a time frame in live trading (e.g., 1m, 5m, 15m) with a live `DataHandler`.
|
|
276
|
+
|
|
277
|
+
**kwargs: Additional parameters passed to the `Backtest` instance, which may include strategy-specific,
|
|
278
|
+
data handler, portfolio, or execution handler options.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
pd.DataFrame: The portfolio values over time (capital, equities, returns etc.).
|
|
282
|
+
|
|
283
|
+
Notes:
|
|
284
|
+
This function generates three outputs:
|
|
285
|
+
- A performance summary saved as an HTML file.
|
|
286
|
+
- An equity curve of the portfolio saved as a CSV file.
|
|
287
|
+
- Monthly returns saved as a PNG image.
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
>>> from bbstrader.trading.strategies import StockIndexSTBOTrading
|
|
291
|
+
>>> from bbstrader.metatrader.utils import config_logger
|
|
292
|
+
>>> from bbstrader.datahandlers import MT5DataHandler
|
|
293
|
+
>>> from bbstrader.execution import MT5ExecutionHandler
|
|
294
|
+
>>> from datetime import datetime
|
|
295
|
+
>>>
|
|
296
|
+
>>> logger = config_logger('index_trade.log', console_log=True)
|
|
297
|
+
>>> symbol_list = ['[SP500]', 'GERMANY40', '[DJI30]', '[NQ100]']
|
|
298
|
+
>>> start = datetime(2010, 6, 1, 2, 0, 0)
|
|
299
|
+
>>> kwargs = {
|
|
300
|
+
... 'expected_returns': {'[NQ100]': 1.5, '[SP500]': 1.5, '[DJI30]': 1.0, 'GERMANY40': 1.0},
|
|
301
|
+
... 'quantities': {'[NQ100]': 15, '[SP500]': 30, '[DJI30]': 5, 'GERMANY40': 10},
|
|
302
|
+
... 'max_trades': {'[NQ100]': 3, '[SP500]': 3, '[DJI30]': 3, 'GERMANY40': 3},
|
|
303
|
+
... 'mt5_start': start,
|
|
304
|
+
... 'time_frame': '15m',
|
|
305
|
+
... 'strategy_name': 'SISTBO',
|
|
306
|
+
... }
|
|
307
|
+
>>> run_backtest(
|
|
308
|
+
... symbol_list=symbol_list,
|
|
309
|
+
... start_date=start,
|
|
310
|
+
... data_handler=MT5DataHandler(),
|
|
311
|
+
... strategy=StockIndexSTBOTrading(),
|
|
312
|
+
... exc_handler=MT5ExecutionHandler(),
|
|
313
|
+
... initial_capital=100000.0,
|
|
314
|
+
... heartbeat=0.0,
|
|
315
|
+
... **kwargs
|
|
316
|
+
... )
|
|
317
|
+
"""
|
|
318
|
+
if exc_handler is None:
|
|
319
|
+
execution_handler = SimExecutionHandler
|
|
320
|
+
else:
|
|
321
|
+
execution_handler = exc_handler
|
|
322
|
+
engine = BacktestEngine(
|
|
323
|
+
symbol_list,
|
|
324
|
+
initial_capital,
|
|
325
|
+
heartbeat,
|
|
326
|
+
start_date,
|
|
327
|
+
data_handler,
|
|
328
|
+
execution_handler,
|
|
329
|
+
strategy,
|
|
330
|
+
**kwargs,
|
|
331
|
+
)
|
|
332
|
+
portfolio = engine.simulate_trading()
|
|
333
|
+
return portfolio
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class CerebroEngine: ...
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class ZiplineEngine: ...
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwargs):
|
|
343
|
+
""" """
|
|
344
|
+
if engine == "bbstrader":
|
|
345
|
+
return run_backtest(
|
|
346
|
+
symbol_list=kwargs.get("symbol_list"),
|
|
347
|
+
start_date=kwargs.get("start_date"),
|
|
348
|
+
data_handler=kwargs.get("data_handler"),
|
|
349
|
+
strategy=kwargs.get("strategy"),
|
|
350
|
+
exc_handler=kwargs.get("exc_handler"),
|
|
351
|
+
initial_capital=kwargs.get("initial_capital", 100000.0),
|
|
352
|
+
heartbeat=kwargs.get("heartbeat", 0.0),
|
|
353
|
+
**kwargs,
|
|
354
|
+
)
|
|
355
|
+
elif engine == "cerebro":
|
|
356
|
+
# TODO:
|
|
357
|
+
pass
|
|
358
|
+
elif engine == "zipline":
|
|
359
|
+
# TODO:
|
|
360
|
+
pass
|