bbstrader 0.1.5__tar.gz → 0.1.7__tar.gz

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.

Files changed (39) hide show
  1. {bbstrader-0.1.5 → bbstrader-0.1.7}/PKG-INFO +15 -7
  2. {bbstrader-0.1.5 → bbstrader-0.1.7}/README.md +11 -6
  3. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/__ini__.py +0 -1
  4. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/__init__.py +12 -9
  5. bbstrader-0.1.7/bbstrader/btengine/backtest.py +298 -0
  6. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/data.py +25 -12
  7. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/event.py +18 -11
  8. bbstrader-0.1.7/bbstrader/btengine/execution.py +143 -0
  9. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/performance.py +34 -1
  10. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/portfolio.py +24 -14
  11. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/btengine/strategy.py +4 -3
  12. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/account.py +18 -6
  13. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/rates.py +35 -12
  14. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/trade.py +54 -38
  15. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/utils.py +3 -2
  16. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/models/risk.py +39 -2
  17. bbstrader-0.1.7/bbstrader/trading/__init__.py +11 -0
  18. bbstrader-0.1.7/bbstrader/trading/execution.py +386 -0
  19. bbstrader-0.1.7/bbstrader/trading/strategies.py +838 -0
  20. bbstrader-0.1.7/bbstrader/tseries.py +1176 -0
  21. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader.egg-info/PKG-INFO +15 -7
  22. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader.egg-info/SOURCES.txt +1 -3
  23. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader.egg-info/requires.txt +3 -0
  24. {bbstrader-0.1.5 → bbstrader-0.1.7}/setup.py +3 -2
  25. bbstrader-0.1.5/bbstrader/btengine/backtest.py +0 -900
  26. bbstrader-0.1.5/bbstrader/btengine/execution.py +0 -83
  27. bbstrader-0.1.5/bbstrader/strategies.py +0 -681
  28. bbstrader-0.1.5/bbstrader/trading/__init__.py +0 -4
  29. bbstrader-0.1.5/bbstrader/trading/execution.py +0 -965
  30. bbstrader-0.1.5/bbstrader/trading/run.py +0 -131
  31. bbstrader-0.1.5/bbstrader/trading/utils.py +0 -153
  32. bbstrader-0.1.5/bbstrader/tseries.py +0 -592
  33. {bbstrader-0.1.5 → bbstrader-0.1.7}/LICENSE +0 -0
  34. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/__init__.py +0 -0
  35. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/metatrader/risk.py +0 -0
  36. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader/models/__init__.py +0 -0
  37. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader.egg-info/dependency_links.txt +0 -0
  38. {bbstrader-0.1.5 → bbstrader-0.1.7}/bbstrader.egg-info/top_level.txt +0 -0
  39. {bbstrader-0.1.5 → bbstrader-0.1.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbstrader
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Simplified Investment & Trading Toolkit
5
5
  Home-page: https://github.com/bbalouki/bbstrader
6
6
  Download-URL: https://pypi.org/project/bbstrader/
@@ -36,6 +36,9 @@ Requires-Dist: matplotlib
36
36
  Requires-Dist: filterpy
37
37
  Requires-Dist: pytest
38
38
  Requires-Dist: CurrencyConverter
39
+ Requires-Dist: tabulate
40
+ Requires-Dist: ipython
41
+ Requires-Dist: quantstats
39
42
  Requires-Dist: Metatrader5
40
43
 
41
44
  # Simplified Investment & Trading Toolkit
@@ -43,6 +46,7 @@ Requires-Dist: Metatrader5
43
46
 
44
47
  [![Documentation Status](https://readthedocs.org/projects/bbstrader/badge/?version=latest)](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
45
48
 
49
+ [Dcoumentation](https://bbstrader.readthedocs.io/en/latest/index.html)
46
50
  ## Overview
47
51
 
48
52
  BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR platforms (comming soon), designed to offer a comprehensive set of tools for developping, backtesting, executing, and managing a wide array of trading strategies. With an emphasis on algorithmic and quantitative trading to provide traders with a robust platform for exploring and deploying sophisticated trading strategies.
@@ -50,7 +54,7 @@ BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR pl
50
54
  `bbstrader` is comprised of several key modules, each focus on specific aspects of trading strategy development and execution:
51
55
 
52
56
  - **Backtesting Module (btengine)** : Enables traders to rigorously test their trading strategies using historical data to evaluate performance before live deployment.
53
- - **Trading Strategies Module**: A collection of predefined trading strategies, including ARIMA+GARCH models, Kalman Filters, Ornstein-Uhlenbeck processes, and Simple Moving Averages, equipped with risk management through Hidden Markov Models.
57
+ - **Trading Strategies Module**: A collection of predefined trading strategies, including ARIMA+GARCH models, Kalman Filters, and Simple Moving Averages, equipped with risk management through Hidden Markov Models.
54
58
  - **MetaTrader5 Module (metatrader)**: Facilitates the direct execution of trading strategies on the MetaTrader 5 platform, supporting real-time trading across multiple financial instruments.
55
59
  - **Modles Module**: Serves as a framework for implementing various types of financial models (risk managment models, Machine learing models etc).
56
60
  - **Time serie Module (tseries)** designed for conducting advanced time series analysis in financial markets.
@@ -78,18 +82,22 @@ Then, you can install `bbstrader` using pip:
78
82
  pip install bbstrader
79
83
  ```
80
84
 
81
-
82
85
  ## Examples
83
86
  ### Backtesting Module
84
87
  ```python
85
- from bbstrader.btengine.backtest import run_backtest
88
+ from bbstrader.trading.strategies import test_strategy
86
89
 
87
90
  if __name__ == '__main__':
88
- # KLF IEI TLT Event Driven backtest using Kalman Filter pairs trading strategy integrating with # Hidden Markov Model (HMM) for risk management on `IEI` and `TLT`.
89
- run_backtest(test_strategy='klf')
91
+ # Run backtesting for Stock Index Short Term Buy Only Strategy
92
+ test_strategy(strategy='sistbo')
93
+
90
94
  ```
91
95
  ### Backtesting Results
92
- ![Backtesting Results](https://github.com/bbalouki/bbstrader/blob/main/assets/backtest_results.jpg?raw=true)
96
+ ![Backtesting Results 1](https://github.com/bbalouki/bbstrader/blob/main/assets/bbs_.png?raw=true)
97
+ ![Backtesting Results 2](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_metrics_1.png?raw=true)
98
+ ![Backtesting Results 3](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_metrics_2.png?raw=true)
99
+ ![Backtesting Results 4](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_plots_1_.png?raw=true)
100
+ ![Backtesting Results 5](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_plots_2_.png?raw=true)
93
101
 
94
102
  ## Customization and Contribution
95
103
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  [![Documentation Status](https://readthedocs.org/projects/bbstrader/badge/?version=latest)](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
5
5
 
6
+ [Dcoumentation](https://bbstrader.readthedocs.io/en/latest/index.html)
6
7
  ## Overview
7
8
 
8
9
  BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR platforms (comming soon), designed to offer a comprehensive set of tools for developping, backtesting, executing, and managing a wide array of trading strategies. With an emphasis on algorithmic and quantitative trading to provide traders with a robust platform for exploring and deploying sophisticated trading strategies.
@@ -10,7 +11,7 @@ BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR pl
10
11
  `bbstrader` is comprised of several key modules, each focus on specific aspects of trading strategy development and execution:
11
12
 
12
13
  - **Backtesting Module (btengine)** : Enables traders to rigorously test their trading strategies using historical data to evaluate performance before live deployment.
13
- - **Trading Strategies Module**: A collection of predefined trading strategies, including ARIMA+GARCH models, Kalman Filters, Ornstein-Uhlenbeck processes, and Simple Moving Averages, equipped with risk management through Hidden Markov Models.
14
+ - **Trading Strategies Module**: A collection of predefined trading strategies, including ARIMA+GARCH models, Kalman Filters, and Simple Moving Averages, equipped with risk management through Hidden Markov Models.
14
15
  - **MetaTrader5 Module (metatrader)**: Facilitates the direct execution of trading strategies on the MetaTrader 5 platform, supporting real-time trading across multiple financial instruments.
15
16
  - **Modles Module**: Serves as a framework for implementing various types of financial models (risk managment models, Machine learing models etc).
16
17
  - **Time serie Module (tseries)** designed for conducting advanced time series analysis in financial markets.
@@ -38,18 +39,22 @@ Then, you can install `bbstrader` using pip:
38
39
  pip install bbstrader
39
40
  ```
40
41
 
41
-
42
42
  ## Examples
43
43
  ### Backtesting Module
44
44
  ```python
45
- from bbstrader.btengine.backtest import run_backtest
45
+ from bbstrader.trading.strategies import test_strategy
46
46
 
47
47
  if __name__ == '__main__':
48
- # KLF IEI TLT Event Driven backtest using Kalman Filter pairs trading strategy integrating with # Hidden Markov Model (HMM) for risk management on `IEI` and `TLT`.
49
- run_backtest(test_strategy='klf')
48
+ # Run backtesting for Stock Index Short Term Buy Only Strategy
49
+ test_strategy(strategy='sistbo')
50
+
50
51
  ```
51
52
  ### Backtesting Results
52
- ![Backtesting Results](https://github.com/bbalouki/bbstrader/blob/main/assets/backtest_results.jpg?raw=true)
53
+ ![Backtesting Results 1](https://github.com/bbalouki/bbstrader/blob/main/assets/bbs_.png?raw=true)
54
+ ![Backtesting Results 2](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_metrics_1.png?raw=true)
55
+ ![Backtesting Results 3](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_metrics_2.png?raw=true)
56
+ ![Backtesting Results 4](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_plots_1_.png?raw=true)
57
+ ![Backtesting Results 5](https://github.com/bbalouki/bbstrader/blob/main/assets/qs_plots_2_.png?raw=true)
53
58
 
54
59
  ## Customization and Contribution
55
60
 
@@ -13,5 +13,4 @@ from bbstrader import btengine
13
13
  from bbstrader import metatrader
14
14
  from bbstrader import models
15
15
  from bbstrader import trading
16
- from bbstrader import strategies
17
16
  from bbstrader import tseries
@@ -12,7 +12,6 @@ Features
12
12
 
13
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
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
- - **Strategy Implementation Flexibility**: Abstract base classes for strategies and other components enable users to define custom trading logic and data handling processes.
16
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.
17
16
  - **Visualization**: Generates plots of the `equity curve`, `returns`, `drawdowns`, and other metrics for comprehensive strategy `performance analysis`.
18
17
 
@@ -22,24 +21,29 @@ Components
22
21
  - **Backtest**: Orchestrates the backtesting process, managing events and invoking components.
23
22
  - **Event**: Abstract class for events, with implementations for market data, signals, fill and order events.
24
23
  - **DataHandler**: Abstract class for market data handling, with an implementation for `HistoricalCSVHandler`, `MT5HistoricDataHandler`, `YFHistoricDataHandler`. We will add another data handling in the future such as MacroEconomic Data, Fundamental Data, TICK Data and Real-time Data.
25
- - **Strategy**: Abstract class for trading strategies, allowing for custom signal generation logic.
26
24
  - **Portfolio**: Manages positions and calculates performance metrics, responding to market data and signals.
27
25
  - **ExecutionHandler**: Abstract class for order execution, with a simulated execution handler provided with an implementation for `SimulatedExecutionHandler`.
28
26
  - **Performance**: Utility functions for calculating performance metrics and visualizing strategy performance.
29
27
 
30
- Examples:
31
- >>> from bbstrader.btengine import run_backtest
32
- >>> run_backtest(test_mode=True, test_strategy='ou', test_quantity=2000)
28
+ Examples
29
+ ========
33
30
 
31
+ >>> from bbstrader.btengine import run_backtest
32
+ >>> from datetime import datetime
34
33
  >>> run_backtest(
35
34
  ... symbol_list=['AAPL', 'GOOG'],
36
35
  ... start_date=datetime(2020, 1, 1),
37
- ... data_handler=CustomDataHandler(),
38
- ... strategy=MovingAverageStrategy(),
39
- ... exc_handler=CustomExecutionHandler(),
36
+ ... data_handler=DataHandler,
37
+ ... strategy=Strategy,
38
+ ... exc_handler=ExecutionHandler,
40
39
  ... initial_capital=500000.0,
41
40
  ... heartbeat=1.0
42
41
  ... )
42
+
43
+ Notes
44
+ =====
45
+
46
+ See `bbstrader.btengine.backtest.run_backtest` for more details on the backtesting process and its parameters.
43
47
  """
44
48
  from bbstrader.btengine.data import *
45
49
  from bbstrader.btengine.event import *
@@ -47,4 +51,3 @@ from bbstrader.btengine.execution import *
47
51
  from bbstrader.btengine.performance import *
48
52
  from bbstrader.btengine.backtest import *
49
53
  from bbstrader.btengine.portfolio import Portfolio
50
- from bbstrader.btengine.strategy import Strategy
@@ -0,0 +1,298 @@
1
+ import pprint
2
+ import queue
3
+ import time
4
+ import yfinance as yf
5
+ from queue import Queue
6
+ from datetime import datetime
7
+ from bbstrader.btengine.data import *
8
+ from bbstrader.btengine.execution import *
9
+ from bbstrader.btengine.portfolio import Portfolio
10
+ from bbstrader.btengine.event import SignalEvent
11
+ from bbstrader.btengine.strategy import Strategy
12
+ from typing import Literal, Optional, List
13
+ from tabulate import tabulate
14
+
15
+ __all__ = [
16
+ "Backtest",
17
+ "BacktestEngine",
18
+ "run_backtest"
19
+ ]
20
+
21
+ class Backtest(object):
22
+ """
23
+ The `Backtest()` object encapsulates the event-handling logic and essentially
24
+ ties together all of the other classes.
25
+
26
+ The Backtest object is designed to carry out a nested while-loop event-driven system
27
+ in order to handle the events placed on the `Event` Queue object.
28
+ The outer while-loop is known as the "heartbeat loop" and decides the temporal resolution of
29
+ the backtesting system. In a live environment this value will be a positive number,
30
+ such as 600 seconds (every ten minutes). Thus the market data and positions
31
+ will only be updated on this timeframe.
32
+
33
+ For the backtester described here the "heartbeat" can be set to zero,
34
+ irrespective of the strategy frequency, since the data is already available by virtue of
35
+ the fact it is historical! We can run the backtest at whatever speed we like,
36
+ since the event-driven system is agnostic to when the data became available,
37
+ so long as it has an associated timestamp.
38
+
39
+ The inner while-loop actually processes the signals and sends them to the correct
40
+ component depending upon the event type. Thus the Event Queue is continually being
41
+ populated and depopulated with events. This is what it means for a system to be event-driven.
42
+
43
+ The initialisation of the Backtest object requires the full `symbol list` of traded symbols,
44
+ the `initial capital`, the `heartbeat` time in milliseconds, the `start datetime` stamp
45
+ of the backtest as well as the `DataHandler`, `ExecutionHandler`, `Strategy` objects
46
+ and additionnal `kwargs` based on the `ExecutionHandler`, the `DataHandler`, and the `Strategy` used.
47
+
48
+ A Queue is used to hold the events. The signals, orders and fills are counted.
49
+ For a `MarketEvent`, the `Strategy` object is told to recalculate new signals,
50
+ while the `Portfolio` object is told to reindex the time. If a `SignalEvent`
51
+ object is received the `Portfolio` is told to handle the new signal and convert it into a
52
+ set of `OrderEvents`, if appropriate. If an `OrderEvent` is received the `ExecutionHandler`
53
+ is sent the order to be transmitted to the broker (if in a real trading setting).
54
+ Finally, if a `FillEvent` is received, the Portfolio will update itself to be aware of
55
+ the new positions.
56
+
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ symbol_list: List[str],
62
+ initial_capital: float,
63
+ heartbeat: float,
64
+ start_date: datetime,
65
+ data_handler: DataHandler,
66
+ execution_handler: ExecutionHandler,
67
+ strategy: Strategy,
68
+ /,
69
+ **kwargs
70
+ ):
71
+ """
72
+ Initialises the backtest.
73
+
74
+ Args:
75
+ symbol_list (List[str]): The list of symbol strings.
76
+ intial_capital (float): The starting capital for the portfolio.
77
+ heartbeat (float): Backtest "heartbeat" in seconds
78
+ start_date (datetime): The start datetime of the strategy.
79
+ data_handler (DataHandler) : Handles the market data feed.
80
+ execution_handler (ExecutionHandler) : Handles the orders/fills for trades.
81
+ strategy (Strategy): Generates signals based on market data.
82
+ kwargs : Additional parameters based on the `ExecutionHandler`,
83
+ the `DataHandler`, the `Strategy` used and the `Portfolio`.
84
+ """
85
+ self.symbol_list = symbol_list
86
+ self.initial_capital = initial_capital
87
+ self.heartbeat = heartbeat
88
+ self.start_date = start_date
89
+
90
+ self.dh_cls = data_handler
91
+ self.eh_cls = execution_handler
92
+ self.strategy_cls = strategy
93
+ self.kwargs = kwargs
94
+
95
+ self.events = queue.Queue()
96
+
97
+ self.signals = 0
98
+ self.orders = 0
99
+ self.fills = 0
100
+
101
+ self._generate_trading_instances()
102
+
103
+ def _generate_trading_instances(self):
104
+ """
105
+ Generates the trading instance objects from
106
+ their class types.
107
+ """
108
+ print(
109
+ f"\nStarting Backtest on {self.symbol_list} "
110
+ f"with ${self.initial_capital} Initial Capital\n"
111
+ )
112
+ self.data_handler: DataHandler = self.dh_cls(
113
+ self.events, self.symbol_list, **self.kwargs
114
+ )
115
+ self.strategy: Strategy = self.strategy_cls(
116
+ bars=self.data_handler, events=self.events, **self.kwargs
117
+ )
118
+ self.portfolio = Portfolio(
119
+ self.data_handler,
120
+ self.events,
121
+ self.start_date,
122
+ self.initial_capital, **self.kwargs
123
+ )
124
+ self.execution_handler: ExecutionHandler = self.eh_cls(
125
+ self.events, **self.kwargs)
126
+
127
+ def _run_backtest(self):
128
+ """
129
+ Executes the backtest.
130
+ """
131
+ i = 0
132
+ while True:
133
+ i += 1
134
+ print(i)
135
+ # Update the market bars
136
+ if self.data_handler.continue_backtest == True:
137
+ self.data_handler.update_bars()
138
+ else:
139
+ break
140
+
141
+ # Handle the events
142
+ while True:
143
+ try:
144
+ event = self.events.get(False)
145
+ except queue.Empty:
146
+ break
147
+ else:
148
+ if event is not None:
149
+ if event.type == 'MARKET':
150
+ self.strategy.calculate_signals(event)
151
+ self.portfolio.update_timeindex(event)
152
+
153
+ elif event.type == 'SIGNAL':
154
+ self.signals += 1
155
+ self.portfolio.update_signal(event)
156
+
157
+ elif event.type == 'ORDER':
158
+ self.orders += 1
159
+ self.execution_handler.execute_order(event)
160
+
161
+ elif event.type == 'FILL':
162
+ self.fills += 1
163
+ self.portfolio.update_fill(event)
164
+
165
+ time.sleep(self.heartbeat)
166
+
167
+ def _output_performance(self):
168
+ """
169
+ Outputs the strategy performance from the backtest.
170
+ """
171
+ self.portfolio.create_equity_curve_dataframe()
172
+
173
+ print("\nCreating summary stats...")
174
+ stats = self.portfolio.output_summary_stats()
175
+ print("[======= Summary Stats =======]")
176
+ stat2 = {}
177
+ stat2['Signals'] = self.signals
178
+ stat2['Orders'] = self.orders
179
+ stat2['Fills'] = self.fills
180
+ stats.extend(stat2.items())
181
+ print(
182
+ tabulate(
183
+ stats,
184
+ headers=["Metric", "Value"],
185
+ tablefmt="outline"),
186
+ "\n"
187
+ )
188
+
189
+ print("\nCreating equity curve...")
190
+ print("\n[======= EQUITY CURVE =======]")
191
+ print(
192
+ tabulate(
193
+ self.portfolio.equity_curve.tail(10),
194
+ headers="keys",
195
+ tablefmt="outline"),
196
+ "\n"
197
+ )
198
+
199
+ def simulate_trading(self):
200
+ """
201
+ Simulates the backtest and outputs portfolio performance.
202
+ """
203
+ self._run_backtest()
204
+ self._output_performance()
205
+
206
+ BacktestEngine = Backtest
207
+
208
+ def run_backtest(
209
+ symbol_list: List[str],
210
+ start_date: datetime,
211
+ data_handler: DataHandler,
212
+ strategy: Strategy,
213
+ exc_handler: Optional[ExecutionHandler] = None,
214
+ initial_capital: float = 100000.0,
215
+ heartbeat: float = 0.0,
216
+ **kwargs
217
+ ):
218
+ """
219
+ Runs a backtest simulation based on a `DataHandler`, `Strategy`, and `ExecutionHandler`.
220
+
221
+ Args:
222
+ symbol_list (List[str]): List of symbol strings for the assets to be backtested.
223
+
224
+ start_date (datetime): Start date of the backtest.
225
+
226
+ data_handler (DataHandler): An instance of the `DataHandler` class, responsible for managing
227
+ and processing market data. Available options include `HistoricCSVDataHandler`,
228
+ `MT5HistoricDataHandler`, and `YFHistoricDataHandler`. Ensure that the `DataHandler`
229
+ instance is initialized before passing it to the function.
230
+
231
+ strategy (Strategy): The trading strategy to be employed during the backtest.
232
+ The strategy must be an instance of `Strategy` and should include the following attributes:
233
+ - `bars` (DataHandler): The `DataHandler` instance for the strategy.
234
+ - `events` (Queue): Queue instance for managing events.
235
+ - `symbol_list` (List[str]): List of symbols to trade.
236
+ - `mode` (str): 'live' or 'backtest'.
237
+
238
+ Additional parameters specific to the strategy should be passed in `**kwargs`.
239
+ The strategy class must implement a `calculate_signals` method to generate `SignalEvent`.
240
+
241
+ exc_handler (ExecutionHandler, optional): The execution handler for managing order executions.
242
+ If not provided, a `SimulatedExecutionHandler` will be used by default. This handler must
243
+ implement an `execute_order` method to process `OrderEvent` in the `Backtest` class.
244
+
245
+ initial_capital (float, optional): The initial capital for the portfolio in the backtest.
246
+ Default is 100,000.
247
+
248
+ heartbeat (float, optional): Time delay (in seconds) between iterations of the event-driven
249
+ backtest loop. Default is 0.0, allowing the backtest to run as fast as possible. This could
250
+ also be used as a time frame in live trading (e.g., 1m, 5m, 15m) with a live `DataHandler`.
251
+
252
+ **kwargs: Additional parameters passed to the `Backtest` instance, which may include strategy-specific,
253
+ data handler, portfolio, or execution handler options.
254
+
255
+ Notes:
256
+ This function generates three outputs:
257
+ - A performance summary saved as an HTML file.
258
+ - An equity curve of the portfolio saved as a CSV file.
259
+ - Monthly returns saved as a PNG image.
260
+
261
+ Example:
262
+ >>> from bbstrader.trading.strategies import StockIndexSTBOTrading
263
+ >>> from bbstrader.metatrader.utils import config_logger
264
+ >>> from bbstrader.datahandlers import MT5HistoricDataHandler
265
+ >>> from bbstrader.execution import MT5ExecutionHandler
266
+ >>> from datetime import datetime
267
+ >>>
268
+ >>> logger = config_logger('index_trade.log', console_log=True)
269
+ >>> symbol_list = ['[SP500]', 'GERMANY40', '[DJI30]', '[NQ100]']
270
+ >>> start = datetime(2010, 6, 1, 2, 0, 0)
271
+ >>> kwargs = {
272
+ ... 'expected_returns': {'[NQ100]': 1.5, '[SP500]': 1.5, '[DJI30]': 1.0, 'GERMANY40': 1.0},
273
+ ... 'quantities': {'[NQ100]': 15, '[SP500]': 30, '[DJI30]': 5, 'GERMANY40': 10},
274
+ ... 'max_trades': {'[NQ100]': 3, '[SP500]': 3, '[DJI30]': 3, 'GERMANY40': 3},
275
+ ... 'mt5_start': start,
276
+ ... 'time_frame': '15m',
277
+ ... 'strategy_name': 'SISTBO',
278
+ ... }
279
+ >>> run_backtest(
280
+ ... symbol_list=symbol_list,
281
+ ... start_date=start,
282
+ ... data_handler=MT5HistoricDataHandler(),
283
+ ... strategy=StockIndexSTBOTrading(),
284
+ ... exc_handler=MT5ExecutionHandler(),
285
+ ... initial_capital=100000.0,
286
+ ... heartbeat=0.0,
287
+ ... **kwargs
288
+ ... )
289
+ """
290
+ if exc_handler is None:
291
+ execution_handler = SimulatedExecutionHandler
292
+ else:
293
+ execution_handler = exc_handler
294
+ engine = BacktestEngine(
295
+ symbol_list, initial_capital, heartbeat, start_date,
296
+ data_handler, execution_handler, strategy, **kwargs
297
+ )
298
+ engine.simulate_trading()
@@ -8,6 +8,7 @@ from queue import Queue
8
8
  from abc import ABCMeta, abstractmethod
9
9
  from bbstrader.metatrader.rates import Rates
10
10
  from bbstrader.btengine.event import MarketEvent
11
+ from datetime import datetime
11
12
 
12
13
 
13
14
  __all__ = [
@@ -260,7 +261,7 @@ class HistoricCSVDataHandler(BaseCSVDataHandler):
260
261
  csv_dir = kwargs.get("csv_dir")
261
262
  super().__init__(events, symbol_list, csv_dir)
262
263
 
263
- MAX_BARS = 10_000_000
264
+
264
265
  class MT5HistoricDataHandler(BaseCSVDataHandler):
265
266
  """
266
267
  Downloads historical data from MetaTrader 5 (MT5) and provides
@@ -281,19 +282,16 @@ class MT5HistoricDataHandler(BaseCSVDataHandler):
281
282
  symbol_list (List[str]): A list of symbol strings to download data for.
282
283
  **kwargs: Keyword arguments for data retrieval:
283
284
  time_frame (str): MT5 time frame (e.g., 'D1' for daily).
284
- max_bars (int): Maximum number of bars to download per symbol.
285
- start_pos (int | str, optional): Starting bar position (default: 0).
286
- If it set to `str`, it must be in 'YYYY-MM-DD' format and
287
- session_duration (int | float): Number of trading hours per day.
285
+ mt5_start (datetime): Start date for historical data.
286
+ mt5_end (datetime): End date for historical data.
288
287
  mt5_data (str): Directory for storing data (default: 'mt5_data').
289
288
 
290
289
  Note:
291
290
  Requires a working connection to an MT5 terminal.
292
291
  """
293
292
  self.tf = kwargs.get('time_frame', 'D1')
294
- self.start_pos = kwargs.get('start_pos', 0)
295
- self.max_bars = kwargs.get('max_bars', MAX_BARS)
296
- self.sd = kwargs.get('session_duration', 6.5)
293
+ self.start = kwargs.get('mt5_start')
294
+ self.end = kwargs.get('mt5_end', datetime.now())
297
295
  self.data_dir = kwargs.get('mt5_data', 'mt5_data')
298
296
  self.symbol_list = symbol_list
299
297
  csv_dir = self._download_data(self.data_dir)
@@ -304,8 +302,10 @@ class MT5HistoricDataHandler(BaseCSVDataHandler):
304
302
  data_dir.mkdir(parents=True, exist_ok=True)
305
303
  for symbol in self.symbol_list:
306
304
  try:
307
- rate = Rates(symbol, self.tf, self.start_pos, self.max_bars, self.sd)
308
- data = rate.get_rates_from_pos()
305
+ rate = Rates(symbol=symbol, time_frame=self.tf)
306
+ data = rate.get_historical_data(
307
+ date_from=self.start, date_to=self.end
308
+ )
309
309
  if data is None:
310
310
  raise ValueError(f"No data found for {symbol}")
311
311
  data.to_csv(data_dir / f'{symbol}.csv')
@@ -359,16 +359,29 @@ class YFHistoricDataHandler(BaseCSVDataHandler):
359
359
  return cache_dir
360
360
 
361
361
 
362
- # TODO # Get data from FinancialModelingPrep ()
362
+ # TODO # Get data from EODHD
363
+ # https://eodhd.com/
364
+ class EODHDHistoricDataHandler(BaseCSVDataHandler):
365
+ ...
366
+
367
+ # TODO # Get data from FMP using Financialtoolkit API
368
+ # https://github.com/bbalouki/FinanceToolkit
363
369
  class FMPHistoricDataHandler(BaseCSVDataHandler):
364
370
  ...
365
371
 
366
372
 
367
373
  class BaseFMPDataHanler(object):
374
+ """
375
+ This will serve as the base class for all other FMP data
376
+ that is not historical data and does not have an OHLC structure.
377
+ """
368
378
  ...
369
379
 
370
380
 
371
381
  class FMPFundamentalDataHandler(BaseFMPDataHanler):
372
382
  ...
373
383
 
374
- # TODO Add other Handlers
384
+ # TODO Add other Handlers for FMP
385
+
386
+
387
+ # TODO Add data Handlers for Interactive Brokers
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime
2
+ from typing import Literal
2
3
 
3
4
  __all__ = [
4
5
  "Event",
@@ -53,9 +54,10 @@ class SignalEvent(Event):
53
54
  strategy_id: int,
54
55
  symbol: str,
55
56
  datetime: datetime,
56
- signal_type: str,
57
+ signal_type: Literal['LONG', 'SHORT', 'EXIT'],
57
58
  quantity: int | float = 100,
58
- strength: int | float = 1.0
59
+ strength: int | float = 1.0,
60
+ price: int | float = None
59
61
  ):
60
62
  """
61
63
  Initialises the SignalEvent.
@@ -66,10 +68,11 @@ class SignalEvent(Event):
66
68
 
67
69
  symbol (str): The ticker symbol, e.g. 'GOOG'.
68
70
  datetime (datetime): The timestamp at which the signal was generated.
69
- signal_type (str): 'LONG' or 'SHORT'.
71
+ signal_type (str): 'LONG' or 'SHORT' or 'EXIT'.
70
72
  quantity (int | float): An optional integer (or float) representing the order size.
71
73
  strength (int | float): An adjustment factor "suggestion" used to scale
72
74
  quantity at the portfolio level. Useful for pairs strategies.
75
+ price (int | float): An optional price to be used when the signal is generated.
73
76
  """
74
77
  self.type = 'SIGNAL'
75
78
  self.strategy_id = strategy_id
@@ -78,6 +81,7 @@ class SignalEvent(Event):
78
81
  self.signal_type = signal_type
79
82
  self.quantity = quantity
80
83
  self.strength = strength
84
+ self.price = price
81
85
 
82
86
 
83
87
  class OrderEvent(Event):
@@ -95,9 +99,10 @@ class OrderEvent(Event):
95
99
 
96
100
  def __init__(self,
97
101
  symbol: str,
98
- order_type: str,
102
+ order_type: Literal['MKT', 'LMT'],
99
103
  quantity: int | float,
100
- direction: str
104
+ direction: Literal['BUY', 'SELL'],
105
+ price: int | float = None
101
106
  ):
102
107
  """
103
108
  Initialises the order type, setting whether it is
@@ -109,20 +114,22 @@ class OrderEvent(Event):
109
114
  order_type (str): 'MKT' or 'LMT' for Market or Limit.
110
115
  quantity (int | float): Non-negative number for quantity.
111
116
  direction (str): 'BUY' or 'SELL' for long or short.
117
+ price (int | float): The price at which to order.
112
118
  """
113
119
  self.type = 'ORDER'
114
120
  self.symbol = symbol
115
121
  self.order_type = order_type
116
122
  self.quantity = quantity
117
123
  self.direction = direction
124
+ self.price = price
118
125
 
119
126
  def print_order(self):
120
127
  """
121
128
  Outputs the values within the Order.
122
129
  """
123
130
  print(
124
- "Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s" %
125
- (self.symbol, self.order_type, self.quantity, self.direction)
131
+ "Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s, Price=%s" %
132
+ (self.symbol, self.order_type, self.quantity, self.direction, self.price)
126
133
  )
127
134
 
128
135
 
@@ -150,8 +157,8 @@ class FillEvent(Event):
150
157
  symbol: str,
151
158
  exchange: str,
152
159
  quantity: int | float,
153
- direction: str,
154
- fill_cost: int | float,
160
+ direction: Literal['BUY', 'SELL'],
161
+ fill_cost: int | float | None,
155
162
  commission: float | None = None
156
163
  ):
157
164
  """
@@ -168,8 +175,8 @@ class FillEvent(Event):
168
175
  symbol (str): The instrument which was filled.
169
176
  exchange (str): The exchange where the order was filled.
170
177
  quantity (int | float): The filled quantity.
171
- direction (str): The direction of fill ('BUY' or 'SELL')
172
- fill_cost (int | float): The holdings value in dollars.
178
+ direction (str): The direction of fill `('LONG', 'SHORT', 'EXIT')`
179
+ fill_cost (int | float): Price of the shares when filled.
173
180
  commission (float | None): An optional commission sent from IB.
174
181
  """
175
182
  self.type = 'FILL'