bbstrader 0.1.6__tar.gz → 0.1.8__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.
- {bbstrader-0.1.6 → bbstrader-0.1.8}/PKG-INFO +16 -7
- {bbstrader-0.1.6 → bbstrader-0.1.8}/README.md +12 -6
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/__ini__.py +0 -1
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/__init__.py +13 -9
- bbstrader-0.1.8/bbstrader/btengine/backtest.py +300 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/data.py +3 -1
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/event.py +15 -9
- bbstrader-0.1.8/bbstrader/btengine/execution.py +143 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/performance.py +32 -20
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/portfolio.py +33 -15
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/btengine/strategy.py +1 -2
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/account.py +15 -8
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/rates.py +10 -6
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/risk.py +1 -2
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/trade.py +307 -239
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/utils.py +37 -29
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/models/risk.py +39 -2
- bbstrader-0.1.8/bbstrader/trading/__init__.py +11 -0
- bbstrader-0.1.8/bbstrader/trading/execution.py +423 -0
- bbstrader-0.1.8/bbstrader/trading/strategies.py +840 -0
- bbstrader-0.1.8/bbstrader/tseries.py +1182 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader.egg-info/PKG-INFO +16 -7
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader.egg-info/SOURCES.txt +1 -3
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader.egg-info/requires.txt +3 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/setup.py +4 -3
- bbstrader-0.1.6/bbstrader/btengine/backtest.py +0 -903
- bbstrader-0.1.6/bbstrader/btengine/execution.py +0 -83
- bbstrader-0.1.6/bbstrader/strategies.py +0 -681
- bbstrader-0.1.6/bbstrader/trading/__init__.py +0 -4
- bbstrader-0.1.6/bbstrader/trading/execution.py +0 -977
- bbstrader-0.1.6/bbstrader/trading/run.py +0 -131
- bbstrader-0.1.6/bbstrader/trading/utils.py +0 -153
- bbstrader-0.1.6/bbstrader/tseries.py +0 -592
- {bbstrader-0.1.6 → bbstrader-0.1.8}/LICENSE +0 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/metatrader/__init__.py +0 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader/models/__init__.py +0 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader.egg-info/dependency_links.txt +0 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/bbstrader.egg-info/top_level.txt +0 -0
- {bbstrader-0.1.6 → bbstrader-0.1.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
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,8 @@ Requires-Dist: Metatrader5
|
|
|
43
46
|
|
|
44
47
|
[](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
|
|
45
48
|
|
|
49
|
+
[Dcoumentation](https://bbstrader.readthedocs.io/en/latest/index.html)
|
|
50
|
+
|
|
46
51
|
## Overview
|
|
47
52
|
|
|
48
53
|
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 +55,7 @@ BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR pl
|
|
|
50
55
|
`bbstrader` is comprised of several key modules, each focus on specific aspects of trading strategy development and execution:
|
|
51
56
|
|
|
52
57
|
- **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,
|
|
58
|
+
- **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
59
|
- **MetaTrader5 Module (metatrader)**: Facilitates the direct execution of trading strategies on the MetaTrader 5 platform, supporting real-time trading across multiple financial instruments.
|
|
55
60
|
- **Modles Module**: Serves as a framework for implementing various types of financial models (risk managment models, Machine learing models etc).
|
|
56
61
|
- **Time serie Module (tseries)** designed for conducting advanced time series analysis in financial markets.
|
|
@@ -78,18 +83,22 @@ Then, you can install `bbstrader` using pip:
|
|
|
78
83
|
pip install bbstrader
|
|
79
84
|
```
|
|
80
85
|
|
|
81
|
-
|
|
82
86
|
## Examples
|
|
83
87
|
### Backtesting Module
|
|
84
88
|
```python
|
|
85
|
-
from bbstrader.
|
|
89
|
+
from bbstrader.trading.strategies import test_strategy
|
|
86
90
|
|
|
87
91
|
if __name__ == '__main__':
|
|
88
|
-
#
|
|
89
|
-
|
|
92
|
+
# Run backtesting for Stock Index Short Term Buy Only Strategy
|
|
93
|
+
test_strategy(strategy='sistbo')
|
|
94
|
+
|
|
90
95
|
```
|
|
91
96
|
### Backtesting Results
|
|
92
|
-

|
|
98
|
+

|
|
99
|
+

|
|
100
|
+

|
|
101
|
+

|
|
93
102
|
|
|
94
103
|
## Customization and Contribution
|
|
95
104
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
[](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
|
|
5
5
|
|
|
6
|
+
[Dcoumentation](https://bbstrader.readthedocs.io/en/latest/index.html)
|
|
7
|
+
|
|
6
8
|
## Overview
|
|
7
9
|
|
|
8
10
|
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 +12,7 @@ BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR pl
|
|
|
10
12
|
`bbstrader` is comprised of several key modules, each focus on specific aspects of trading strategy development and execution:
|
|
11
13
|
|
|
12
14
|
- **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,
|
|
15
|
+
- **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
16
|
- **MetaTrader5 Module (metatrader)**: Facilitates the direct execution of trading strategies on the MetaTrader 5 platform, supporting real-time trading across multiple financial instruments.
|
|
15
17
|
- **Modles Module**: Serves as a framework for implementing various types of financial models (risk managment models, Machine learing models etc).
|
|
16
18
|
- **Time serie Module (tseries)** designed for conducting advanced time series analysis in financial markets.
|
|
@@ -38,18 +40,22 @@ Then, you can install `bbstrader` using pip:
|
|
|
38
40
|
pip install bbstrader
|
|
39
41
|
```
|
|
40
42
|
|
|
41
|
-
|
|
42
43
|
## Examples
|
|
43
44
|
### Backtesting Module
|
|
44
45
|
```python
|
|
45
|
-
from bbstrader.
|
|
46
|
+
from bbstrader.trading.strategies import test_strategy
|
|
46
47
|
|
|
47
48
|
if __name__ == '__main__':
|
|
48
|
-
#
|
|
49
|
-
|
|
49
|
+
# Run backtesting for Stock Index Short Term Buy Only Strategy
|
|
50
|
+
test_strategy(strategy='sistbo')
|
|
51
|
+
|
|
50
52
|
```
|
|
51
53
|
### Backtesting Results
|
|
52
|
-

|
|
55
|
+

|
|
56
|
+

|
|
57
|
+

|
|
58
|
+

|
|
53
59
|
|
|
54
60
|
## Customization and Contribution
|
|
55
61
|
|
|
@@ -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,29 +21,34 @@ 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
|
-
|
|
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=
|
|
38
|
-
... strategy=
|
|
39
|
-
... exc_handler=
|
|
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 *
|
|
46
50
|
from bbstrader.btengine.execution import *
|
|
47
51
|
from bbstrader.btengine.performance import *
|
|
48
52
|
from bbstrader.btengine.backtest import *
|
|
49
|
-
from bbstrader.btengine.portfolio import Portfolio
|
|
50
53
|
from bbstrader.btengine.strategy import Strategy
|
|
54
|
+
from bbstrader.btengine.portfolio import Portfolio
|
|
@@ -0,0 +1,300 @@
|
|
|
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
|
+
self.show_equity = kwargs.get("show_equity", False)
|
|
103
|
+
|
|
104
|
+
def _generate_trading_instances(self):
|
|
105
|
+
"""
|
|
106
|
+
Generates the trading instance objects from
|
|
107
|
+
their class types.
|
|
108
|
+
"""
|
|
109
|
+
print(
|
|
110
|
+
f"\nStarting Backtest on {self.symbol_list} "
|
|
111
|
+
f"with ${self.initial_capital} Initial Capital\n"
|
|
112
|
+
)
|
|
113
|
+
self.data_handler: DataHandler = self.dh_cls(
|
|
114
|
+
self.events, self.symbol_list, **self.kwargs
|
|
115
|
+
)
|
|
116
|
+
self.strategy: Strategy = self.strategy_cls(
|
|
117
|
+
bars=self.data_handler, events=self.events, **self.kwargs
|
|
118
|
+
)
|
|
119
|
+
self.portfolio = Portfolio(
|
|
120
|
+
self.data_handler,
|
|
121
|
+
self.events,
|
|
122
|
+
self.start_date,
|
|
123
|
+
self.initial_capital, **self.kwargs
|
|
124
|
+
)
|
|
125
|
+
self.execution_handler: ExecutionHandler = self.eh_cls(
|
|
126
|
+
self.events, **self.kwargs)
|
|
127
|
+
|
|
128
|
+
def _run_backtest(self):
|
|
129
|
+
"""
|
|
130
|
+
Executes the backtest.
|
|
131
|
+
"""
|
|
132
|
+
i = 0
|
|
133
|
+
while True:
|
|
134
|
+
i += 1
|
|
135
|
+
print(i)
|
|
136
|
+
# Update the market bars
|
|
137
|
+
if self.data_handler.continue_backtest == True:
|
|
138
|
+
self.data_handler.update_bars()
|
|
139
|
+
else:
|
|
140
|
+
break
|
|
141
|
+
|
|
142
|
+
# Handle the events
|
|
143
|
+
while True:
|
|
144
|
+
try:
|
|
145
|
+
event = self.events.get(False)
|
|
146
|
+
except queue.Empty:
|
|
147
|
+
break
|
|
148
|
+
else:
|
|
149
|
+
if event is not None:
|
|
150
|
+
if event.type == 'MARKET':
|
|
151
|
+
self.strategy.calculate_signals(event)
|
|
152
|
+
self.portfolio.update_timeindex(event)
|
|
153
|
+
|
|
154
|
+
elif event.type == 'SIGNAL':
|
|
155
|
+
self.signals += 1
|
|
156
|
+
self.portfolio.update_signal(event)
|
|
157
|
+
|
|
158
|
+
elif event.type == 'ORDER':
|
|
159
|
+
self.orders += 1
|
|
160
|
+
self.execution_handler.execute_order(event)
|
|
161
|
+
|
|
162
|
+
elif event.type == 'FILL':
|
|
163
|
+
self.fills += 1
|
|
164
|
+
self.portfolio.update_fill(event)
|
|
165
|
+
|
|
166
|
+
time.sleep(self.heartbeat)
|
|
167
|
+
|
|
168
|
+
def _output_performance(self):
|
|
169
|
+
"""
|
|
170
|
+
Outputs the strategy performance from the backtest.
|
|
171
|
+
"""
|
|
172
|
+
self.portfolio.create_equity_curve_dataframe()
|
|
173
|
+
|
|
174
|
+
print("\nCreating summary stats...")
|
|
175
|
+
stats = self.portfolio.output_summary_stats()
|
|
176
|
+
print("[======= Summary Stats =======]")
|
|
177
|
+
stat2 = {}
|
|
178
|
+
stat2['Signals'] = self.signals
|
|
179
|
+
stat2['Orders'] = self.orders
|
|
180
|
+
stat2['Fills'] = self.fills
|
|
181
|
+
stats.extend(stat2.items())
|
|
182
|
+
print(
|
|
183
|
+
tabulate(
|
|
184
|
+
stats,
|
|
185
|
+
headers=["Metric", "Value"],
|
|
186
|
+
tablefmt="outline"),
|
|
187
|
+
"\n"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if self.show_equity:
|
|
191
|
+
print("\nCreating equity curve...")
|
|
192
|
+
print("\n[======= EQUITY CURVE =======]")
|
|
193
|
+
print(
|
|
194
|
+
tabulate(
|
|
195
|
+
self.portfolio.equity_curve.tail(10),
|
|
196
|
+
headers="keys",
|
|
197
|
+
tablefmt="outline"),
|
|
198
|
+
"\n"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def simulate_trading(self):
|
|
202
|
+
"""
|
|
203
|
+
Simulates the backtest and outputs portfolio performance.
|
|
204
|
+
"""
|
|
205
|
+
self._run_backtest()
|
|
206
|
+
self._output_performance()
|
|
207
|
+
|
|
208
|
+
BacktestEngine = Backtest
|
|
209
|
+
|
|
210
|
+
def run_backtest(
|
|
211
|
+
symbol_list: List[str],
|
|
212
|
+
start_date: datetime,
|
|
213
|
+
data_handler: DataHandler,
|
|
214
|
+
strategy: Strategy,
|
|
215
|
+
exc_handler: Optional[ExecutionHandler] = None,
|
|
216
|
+
initial_capital: float = 100000.0,
|
|
217
|
+
heartbeat: float = 0.0,
|
|
218
|
+
**kwargs
|
|
219
|
+
):
|
|
220
|
+
"""
|
|
221
|
+
Runs a backtest simulation based on a `DataHandler`, `Strategy`, and `ExecutionHandler`.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
symbol_list (List[str]): List of symbol strings for the assets to be backtested.
|
|
225
|
+
|
|
226
|
+
start_date (datetime): Start date of the backtest.
|
|
227
|
+
|
|
228
|
+
data_handler (DataHandler): An instance of the `DataHandler` class, responsible for managing
|
|
229
|
+
and processing market data. Available options include `HistoricCSVDataHandler`,
|
|
230
|
+
`MT5HistoricDataHandler`, and `YFHistoricDataHandler`. Ensure that the `DataHandler`
|
|
231
|
+
instance is initialized before passing it to the function.
|
|
232
|
+
|
|
233
|
+
strategy (Strategy): The trading strategy to be employed during the backtest.
|
|
234
|
+
The strategy must be an instance of `Strategy` and should include the following attributes:
|
|
235
|
+
- `bars` (DataHandler): The `DataHandler` instance for the strategy.
|
|
236
|
+
- `events` (Queue): Queue instance for managing events.
|
|
237
|
+
- `symbol_list` (List[str]): List of symbols to trade.
|
|
238
|
+
- `mode` (str): 'live' or 'backtest'.
|
|
239
|
+
|
|
240
|
+
Additional parameters specific to the strategy should be passed in `**kwargs`.
|
|
241
|
+
The strategy class must implement a `calculate_signals` method to generate `SignalEvent`.
|
|
242
|
+
|
|
243
|
+
exc_handler (ExecutionHandler, optional): The execution handler for managing order executions.
|
|
244
|
+
If not provided, a `SimulatedExecutionHandler` will be used by default. This handler must
|
|
245
|
+
implement an `execute_order` method to process `OrderEvent` in the `Backtest` class.
|
|
246
|
+
|
|
247
|
+
initial_capital (float, optional): The initial capital for the portfolio in the backtest.
|
|
248
|
+
Default is 100,000.
|
|
249
|
+
|
|
250
|
+
heartbeat (float, optional): Time delay (in seconds) between iterations of the event-driven
|
|
251
|
+
backtest loop. Default is 0.0, allowing the backtest to run as fast as possible. This could
|
|
252
|
+
also be used as a time frame in live trading (e.g., 1m, 5m, 15m) with a live `DataHandler`.
|
|
253
|
+
|
|
254
|
+
**kwargs: Additional parameters passed to the `Backtest` instance, which may include strategy-specific,
|
|
255
|
+
data handler, portfolio, or execution handler options.
|
|
256
|
+
|
|
257
|
+
Notes:
|
|
258
|
+
This function generates three outputs:
|
|
259
|
+
- A performance summary saved as an HTML file.
|
|
260
|
+
- An equity curve of the portfolio saved as a CSV file.
|
|
261
|
+
- Monthly returns saved as a PNG image.
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
>>> from bbstrader.trading.strategies import StockIndexSTBOTrading
|
|
265
|
+
>>> from bbstrader.metatrader.utils import config_logger
|
|
266
|
+
>>> from bbstrader.datahandlers import MT5HistoricDataHandler
|
|
267
|
+
>>> from bbstrader.execution import MT5ExecutionHandler
|
|
268
|
+
>>> from datetime import datetime
|
|
269
|
+
>>>
|
|
270
|
+
>>> logger = config_logger('index_trade.log', console_log=True)
|
|
271
|
+
>>> symbol_list = ['[SP500]', 'GERMANY40', '[DJI30]', '[NQ100]']
|
|
272
|
+
>>> start = datetime(2010, 6, 1, 2, 0, 0)
|
|
273
|
+
>>> kwargs = {
|
|
274
|
+
... 'expected_returns': {'[NQ100]': 1.5, '[SP500]': 1.5, '[DJI30]': 1.0, 'GERMANY40': 1.0},
|
|
275
|
+
... 'quantities': {'[NQ100]': 15, '[SP500]': 30, '[DJI30]': 5, 'GERMANY40': 10},
|
|
276
|
+
... 'max_trades': {'[NQ100]': 3, '[SP500]': 3, '[DJI30]': 3, 'GERMANY40': 3},
|
|
277
|
+
... 'mt5_start': start,
|
|
278
|
+
... 'time_frame': '15m',
|
|
279
|
+
... 'strategy_name': 'SISTBO',
|
|
280
|
+
... }
|
|
281
|
+
>>> run_backtest(
|
|
282
|
+
... symbol_list=symbol_list,
|
|
283
|
+
... start_date=start,
|
|
284
|
+
... data_handler=MT5HistoricDataHandler(),
|
|
285
|
+
... strategy=StockIndexSTBOTrading(),
|
|
286
|
+
... exc_handler=MT5ExecutionHandler(),
|
|
287
|
+
... initial_capital=100000.0,
|
|
288
|
+
... heartbeat=0.0,
|
|
289
|
+
... **kwargs
|
|
290
|
+
... )
|
|
291
|
+
"""
|
|
292
|
+
if exc_handler is None:
|
|
293
|
+
execution_handler = SimulatedExecutionHandler
|
|
294
|
+
else:
|
|
295
|
+
execution_handler = exc_handler
|
|
296
|
+
engine = BacktestEngine(
|
|
297
|
+
symbol_list, initial_capital, heartbeat, start_date,
|
|
298
|
+
data_handler, execution_handler, strategy, **kwargs
|
|
299
|
+
)
|
|
300
|
+
engine.simulate_trading()
|
|
@@ -360,10 +360,12 @@ class YFHistoricDataHandler(BaseCSVDataHandler):
|
|
|
360
360
|
|
|
361
361
|
|
|
362
362
|
# TODO # Get data from EODHD
|
|
363
|
+
# https://eodhd.com/
|
|
363
364
|
class EODHDHistoricDataHandler(BaseCSVDataHandler):
|
|
364
365
|
...
|
|
365
366
|
|
|
366
|
-
# TODO # Get data from
|
|
367
|
+
# TODO # Get data from FMP using Financialtoolkit API
|
|
368
|
+
# https://github.com/bbalouki/FinanceToolkit
|
|
367
369
|
class FMPHistoricDataHandler(BaseCSVDataHandler):
|
|
368
370
|
...
|
|
369
371
|
|
|
@@ -54,9 +54,10 @@ class SignalEvent(Event):
|
|
|
54
54
|
strategy_id: int,
|
|
55
55
|
symbol: str,
|
|
56
56
|
datetime: datetime,
|
|
57
|
-
signal_type:
|
|
57
|
+
signal_type: Literal['LONG', 'SHORT', 'EXIT'],
|
|
58
58
|
quantity: int | float = 100,
|
|
59
|
-
strength: int | float = 1.0
|
|
59
|
+
strength: int | float = 1.0,
|
|
60
|
+
price: int | float = None
|
|
60
61
|
):
|
|
61
62
|
"""
|
|
62
63
|
Initialises the SignalEvent.
|
|
@@ -67,10 +68,11 @@ class SignalEvent(Event):
|
|
|
67
68
|
|
|
68
69
|
symbol (str): The ticker symbol, e.g. 'GOOG'.
|
|
69
70
|
datetime (datetime): The timestamp at which the signal was generated.
|
|
70
|
-
signal_type (str): 'LONG' or 'SHORT'.
|
|
71
|
+
signal_type (str): 'LONG' or 'SHORT' or 'EXIT'.
|
|
71
72
|
quantity (int | float): An optional integer (or float) representing the order size.
|
|
72
73
|
strength (int | float): An adjustment factor "suggestion" used to scale
|
|
73
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.
|
|
74
76
|
"""
|
|
75
77
|
self.type = 'SIGNAL'
|
|
76
78
|
self.strategy_id = strategy_id
|
|
@@ -79,6 +81,7 @@ class SignalEvent(Event):
|
|
|
79
81
|
self.signal_type = signal_type
|
|
80
82
|
self.quantity = quantity
|
|
81
83
|
self.strength = strength
|
|
84
|
+
self.price = price
|
|
82
85
|
|
|
83
86
|
|
|
84
87
|
class OrderEvent(Event):
|
|
@@ -96,9 +99,10 @@ class OrderEvent(Event):
|
|
|
96
99
|
|
|
97
100
|
def __init__(self,
|
|
98
101
|
symbol: str,
|
|
99
|
-
order_type:
|
|
102
|
+
order_type: Literal['MKT', 'LMT'],
|
|
100
103
|
quantity: int | float,
|
|
101
|
-
direction:
|
|
104
|
+
direction: Literal['BUY', 'SELL'],
|
|
105
|
+
price: int | float = None
|
|
102
106
|
):
|
|
103
107
|
"""
|
|
104
108
|
Initialises the order type, setting whether it is
|
|
@@ -110,20 +114,22 @@ class OrderEvent(Event):
|
|
|
110
114
|
order_type (str): 'MKT' or 'LMT' for Market or Limit.
|
|
111
115
|
quantity (int | float): Non-negative number for quantity.
|
|
112
116
|
direction (str): 'BUY' or 'SELL' for long or short.
|
|
117
|
+
price (int | float): The price at which to order.
|
|
113
118
|
"""
|
|
114
119
|
self.type = 'ORDER'
|
|
115
120
|
self.symbol = symbol
|
|
116
121
|
self.order_type = order_type
|
|
117
122
|
self.quantity = quantity
|
|
118
123
|
self.direction = direction
|
|
124
|
+
self.price = price
|
|
119
125
|
|
|
120
126
|
def print_order(self):
|
|
121
127
|
"""
|
|
122
128
|
Outputs the values within the Order.
|
|
123
129
|
"""
|
|
124
130
|
print(
|
|
125
|
-
"Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s" %
|
|
126
|
-
(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)
|
|
127
133
|
)
|
|
128
134
|
|
|
129
135
|
|
|
@@ -151,7 +157,7 @@ class FillEvent(Event):
|
|
|
151
157
|
symbol: str,
|
|
152
158
|
exchange: str,
|
|
153
159
|
quantity: int | float,
|
|
154
|
-
direction: Literal['
|
|
160
|
+
direction: Literal['BUY', 'SELL'],
|
|
155
161
|
fill_cost: int | float | None,
|
|
156
162
|
commission: float | None = None
|
|
157
163
|
):
|
|
@@ -170,7 +176,7 @@ class FillEvent(Event):
|
|
|
170
176
|
exchange (str): The exchange where the order was filled.
|
|
171
177
|
quantity (int | float): The filled quantity.
|
|
172
178
|
direction (str): The direction of fill `('LONG', 'SHORT', 'EXIT')`
|
|
173
|
-
fill_cost (int | float):
|
|
179
|
+
fill_cost (int | float): Price of the shares when filled.
|
|
174
180
|
commission (float | None): An optional commission sent from IB.
|
|
175
181
|
"""
|
|
176
182
|
self.type = 'FILL'
|