bbstrader 0.1.4__py3-none-any.whl → 0.1.6__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/btengine/backtest.py +10 -7
- bbstrader/btengine/data.py +22 -11
- bbstrader/btengine/event.py +4 -3
- bbstrader/btengine/execution.py +1 -1
- bbstrader/btengine/strategy.py +3 -1
- bbstrader/metatrader/account.py +46 -2
- bbstrader/metatrader/rates.py +41 -12
- bbstrader/metatrader/risk.py +52 -30
- bbstrader/metatrader/trade.py +16 -13
- bbstrader/metatrader/utils.py +3 -2
- bbstrader/trading/execution.py +24 -12
- {bbstrader-0.1.4.dist-info → bbstrader-0.1.6.dist-info}/METADATA +4 -1
- bbstrader-0.1.6.dist-info/RECORD +28 -0
- {bbstrader-0.1.4.dist-info → bbstrader-0.1.6.dist-info}/WHEEL +1 -1
- bbstrader-0.1.4.dist-info/RECORD +0 -28
- {bbstrader-0.1.4.dist-info → bbstrader-0.1.6.dist-info}/LICENSE +0 -0
- {bbstrader-0.1.4.dist-info → bbstrader-0.1.6.dist-info}/top_level.txt +0 -0
bbstrader/btengine/backtest.py
CHANGED
|
@@ -6,7 +6,6 @@ import pandas as pd
|
|
|
6
6
|
import yfinance as yf
|
|
7
7
|
from queue import Queue
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from seaborn import saturate
|
|
10
9
|
from bbstrader.btengine.data import *
|
|
11
10
|
from bbstrader.btengine.execution import *
|
|
12
11
|
from bbstrader.btengine.portfolio import Portfolio
|
|
@@ -132,7 +131,8 @@ class Backtest(object):
|
|
|
132
131
|
self.start_date,
|
|
133
132
|
self.initial_capital, **self.kwargs
|
|
134
133
|
)
|
|
135
|
-
self.execution_handler: ExecutionHandler = self.eh_cls(
|
|
134
|
+
self.execution_handler: ExecutionHandler = self.eh_cls(
|
|
135
|
+
self.events, **self.kwargs)
|
|
136
136
|
|
|
137
137
|
def _run_backtest(self):
|
|
138
138
|
"""
|
|
@@ -172,7 +172,7 @@ class Backtest(object):
|
|
|
172
172
|
self.fills += 1
|
|
173
173
|
self.portfolio.update_fill(event)
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
time.sleep(self.heartbeat)
|
|
176
176
|
|
|
177
177
|
def _output_performance(self):
|
|
178
178
|
"""
|
|
@@ -788,10 +788,10 @@ def _run_sma_backtest(
|
|
|
788
788
|
"quantity": quantity,
|
|
789
789
|
"hmm_end": "2009-12-31",
|
|
790
790
|
"hmm_tiker": "^GSPC",
|
|
791
|
-
"yf_start": "2010-01-
|
|
791
|
+
"yf_start": "2010-01-04",
|
|
792
792
|
"hmm_start": "1990-01-01",
|
|
793
|
-
"
|
|
794
|
-
"
|
|
793
|
+
"mt5_start": datetime(2010, 1, 4),
|
|
794
|
+
"mt5_end": datetime(2023, 1, 1),
|
|
795
795
|
"backtester_class": SMAStrategyBacktester,
|
|
796
796
|
"data_handler": MT5HistoricDataHandler
|
|
797
797
|
}
|
|
@@ -892,7 +892,10 @@ def run_backtest(
|
|
|
892
892
|
quantity=test_quantity
|
|
893
893
|
)
|
|
894
894
|
else:
|
|
895
|
-
|
|
895
|
+
if exc_handler is None:
|
|
896
|
+
execution_handler = SimulatedExecutionHandler
|
|
897
|
+
else:
|
|
898
|
+
execution_handler = exc_handler
|
|
896
899
|
engine = Backtest(
|
|
897
900
|
symbol_list, initial_capital, heartbeat, start_date,
|
|
898
901
|
data_handler, execution_handler, strategy, **kwargs
|
bbstrader/btengine/data.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
285
|
-
|
|
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.
|
|
295
|
-
self.
|
|
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
|
|
308
|
-
data = rate.
|
|
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,27 @@ class YFHistoricDataHandler(BaseCSVDataHandler):
|
|
|
359
359
|
return cache_dir
|
|
360
360
|
|
|
361
361
|
|
|
362
|
+
# TODO # Get data from EODHD
|
|
363
|
+
class EODHDHistoricDataHandler(BaseCSVDataHandler):
|
|
364
|
+
...
|
|
365
|
+
|
|
362
366
|
# TODO # Get data from FinancialModelingPrep ()
|
|
363
367
|
class FMPHistoricDataHandler(BaseCSVDataHandler):
|
|
364
368
|
...
|
|
365
369
|
|
|
366
370
|
|
|
367
371
|
class BaseFMPDataHanler(object):
|
|
372
|
+
"""
|
|
373
|
+
This will serve as the base class for all other FMP data
|
|
374
|
+
that is not historical data and does not have an OHLC structure.
|
|
375
|
+
"""
|
|
368
376
|
...
|
|
369
377
|
|
|
370
378
|
|
|
371
379
|
class FMPFundamentalDataHandler(BaseFMPDataHanler):
|
|
372
380
|
...
|
|
373
381
|
|
|
374
|
-
# TODO Add other Handlers
|
|
382
|
+
# TODO Add other Handlers for FMP
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# TODO Add data Handlers for Interactive Brokers
|
bbstrader/btengine/event.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
from typing import Literal
|
|
2
3
|
|
|
3
4
|
__all__ = [
|
|
4
5
|
"Event",
|
|
@@ -150,8 +151,8 @@ class FillEvent(Event):
|
|
|
150
151
|
symbol: str,
|
|
151
152
|
exchange: str,
|
|
152
153
|
quantity: int | float,
|
|
153
|
-
direction:
|
|
154
|
-
fill_cost: int | float,
|
|
154
|
+
direction: Literal['LONG', 'SHORT', 'EXIT'],
|
|
155
|
+
fill_cost: int | float | None,
|
|
155
156
|
commission: float | None = None
|
|
156
157
|
):
|
|
157
158
|
"""
|
|
@@ -168,7 +169,7 @@ class FillEvent(Event):
|
|
|
168
169
|
symbol (str): The instrument which was filled.
|
|
169
170
|
exchange (str): The exchange where the order was filled.
|
|
170
171
|
quantity (int | float): The filled quantity.
|
|
171
|
-
direction (str): The direction of fill ('
|
|
172
|
+
direction (str): The direction of fill `('LONG', 'SHORT', 'EXIT')`
|
|
172
173
|
fill_cost (int | float): The holdings value in dollars.
|
|
173
174
|
commission (float | None): An optional commission sent from IB.
|
|
174
175
|
"""
|
bbstrader/btengine/execution.py
CHANGED
bbstrader/btengine/strategy.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABCMeta, abstractmethod
|
|
2
|
+
from typing import Dict, Union
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class Strategy(metaclass=ABCMeta):
|
|
@@ -22,9 +23,10 @@ class Strategy(metaclass=ABCMeta):
|
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
@abstractmethod
|
|
25
|
-
def calculate_signals(self):
|
|
26
|
+
def calculate_signals(self, *args, **kwargs) -> Dict[str, Union[str, None]]:
|
|
26
27
|
"""
|
|
27
28
|
Provides the mechanisms to calculate the list of signals.
|
|
29
|
+
This methods should return a dictionary of symbols and their respective signals.
|
|
28
30
|
"""
|
|
29
31
|
raise NotImplementedError(
|
|
30
32
|
"Should implement calculate_signals()"
|
bbstrader/metatrader/account.py
CHANGED
|
@@ -555,13 +555,19 @@ class Account(object):
|
|
|
555
555
|
|
|
556
556
|
Raises:
|
|
557
557
|
MT5TerminalError: A specific exception based on the error code.
|
|
558
|
+
|
|
559
|
+
Notes:
|
|
560
|
+
The `time` property is converted to a `datetime` object using Broker server time.
|
|
558
561
|
"""
|
|
559
562
|
try:
|
|
560
563
|
symbol_info = mt5.symbol_info(symbol)
|
|
561
564
|
if symbol_info is None:
|
|
562
565
|
return None
|
|
563
566
|
else:
|
|
564
|
-
|
|
567
|
+
symbol_info_dict = symbol_info._asdict()
|
|
568
|
+
time = datetime.fromtimestamp(symbol_info.time)
|
|
569
|
+
symbol_info_dict['time'] = time
|
|
570
|
+
return SymbolInfo(**symbol_info_dict)
|
|
565
571
|
except Exception as e:
|
|
566
572
|
msg = self._symbol_info_msg(symbol)
|
|
567
573
|
raise_mt5_error(message=f"{e+msg}")
|
|
@@ -595,13 +601,19 @@ class Account(object):
|
|
|
595
601
|
|
|
596
602
|
Raises:
|
|
597
603
|
MT5TerminalError: A specific exception based on the error code.
|
|
604
|
+
|
|
605
|
+
Notes:
|
|
606
|
+
The `time` property is converted to a `datetime` object using Broker server time.
|
|
598
607
|
"""
|
|
599
608
|
try:
|
|
600
609
|
tick_info = mt5.symbol_info_tick(symbol)
|
|
601
610
|
if tick_info is None:
|
|
602
611
|
return None
|
|
603
612
|
else:
|
|
604
|
-
|
|
613
|
+
info_dict = tick_info._asdict()
|
|
614
|
+
time = datetime.fromtimestamp(tick_info.time)
|
|
615
|
+
info_dict['time'] = time
|
|
616
|
+
return TickInfo(**info_dict)
|
|
605
617
|
except Exception as e:
|
|
606
618
|
msg = self._symbol_info_msg(symbol)
|
|
607
619
|
raise_mt5_error(message=f"{e+msg}")
|
|
@@ -615,6 +627,38 @@ class Account(object):
|
|
|
615
627
|
"""
|
|
616
628
|
self._show_info(self.get_tick_info, "tick", symbol=symbol)
|
|
617
629
|
|
|
630
|
+
def calculate_margin(self,
|
|
631
|
+
action: Literal['buy', 'sell'],
|
|
632
|
+
symbol: str,
|
|
633
|
+
lot: float,
|
|
634
|
+
price: float) -> float:
|
|
635
|
+
"""
|
|
636
|
+
Calculate margin required for an order.
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
action (str): The trading action, either 'buy' or 'sell'.
|
|
640
|
+
symbol (str): The symbol of the financial instrument.
|
|
641
|
+
lot (float): The lot size of the order.
|
|
642
|
+
price (float): The price of the order.
|
|
643
|
+
|
|
644
|
+
Returns:
|
|
645
|
+
float: The margin required for the order.
|
|
646
|
+
|
|
647
|
+
Raises:
|
|
648
|
+
MT5TerminalError: A specific exception based on the error code.
|
|
649
|
+
"""
|
|
650
|
+
_action = {
|
|
651
|
+
'buy': mt5.ORDER_TYPE_BUY,
|
|
652
|
+
'sell': mt5.ORDER_TYPE_SELL
|
|
653
|
+
}
|
|
654
|
+
try:
|
|
655
|
+
margin = mt5.order_calc_margin(_action[action], symbol, lot, price)
|
|
656
|
+
if margin is None:
|
|
657
|
+
return None
|
|
658
|
+
return margin
|
|
659
|
+
except Exception as e:
|
|
660
|
+
raise_mt5_error(e)
|
|
661
|
+
|
|
618
662
|
def check_order(self,
|
|
619
663
|
request: Dict[str, Any]) -> OrderCheckResult:
|
|
620
664
|
"""
|
bbstrader/metatrader/rates.py
CHANGED
|
@@ -21,6 +21,10 @@ class Rates(object):
|
|
|
21
21
|
flexibility in retrieving data either by specifying a starting position
|
|
22
22
|
and count of bars or by providing a specific date range.
|
|
23
23
|
|
|
24
|
+
Notes:
|
|
25
|
+
The `get_open, get_high, get_low, get_close, get_adj_close, get_returns,
|
|
26
|
+
get_volume` properties return data in Broker's timezone.
|
|
27
|
+
|
|
24
28
|
Example:
|
|
25
29
|
>>> rates = Rates("EURUSD", "1h")
|
|
26
30
|
>>> df = rates.get_historical_data(
|
|
@@ -62,8 +66,14 @@ class Rates(object):
|
|
|
62
66
|
self.start_pos = self._get_start_pos(start_pos, time_frame)
|
|
63
67
|
self.count = count
|
|
64
68
|
self._mt5_initialized()
|
|
65
|
-
self.
|
|
69
|
+
self.__data = self.get_rates_from_pos()
|
|
70
|
+
|
|
66
71
|
|
|
72
|
+
def _mt5_initialized(self):
|
|
73
|
+
"""Ensures the MetaTrader 5 Terminal is initialized."""
|
|
74
|
+
if not Mt5.initialize():
|
|
75
|
+
raise_mt5_error(message=INIT_MSG)
|
|
76
|
+
|
|
67
77
|
def _get_start_pos(self, index, time_frame):
|
|
68
78
|
if isinstance(index, int):
|
|
69
79
|
start_pos = index
|
|
@@ -112,11 +122,6 @@ class Rates(object):
|
|
|
112
122
|
)
|
|
113
123
|
return TIMEFRAMES[time_frame]
|
|
114
124
|
|
|
115
|
-
def _mt5_initialized(self):
|
|
116
|
-
"""Ensures the MetaTrader 5 Terminal is initialized."""
|
|
117
|
-
if not Mt5.initialize():
|
|
118
|
-
raise_mt5_error(message=INIT_MSG)
|
|
119
|
-
|
|
120
125
|
def _fetch_data(
|
|
121
126
|
self, start: Union[int, datetime],
|
|
122
127
|
count: Union[int, datetime]
|
|
@@ -160,6 +165,13 @@ class Rates(object):
|
|
|
160
165
|
Returns:
|
|
161
166
|
Union[pd.DataFrame, None]: A DataFrame containing historical
|
|
162
167
|
data if successful, otherwise None.
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
ValueError: If `start_pos` or `count` is not provided during
|
|
171
|
+
initialization.
|
|
172
|
+
|
|
173
|
+
Notes:
|
|
174
|
+
The Datetime for this method is in Broker's timezone.
|
|
163
175
|
"""
|
|
164
176
|
if self.start_pos is None or self.count is None:
|
|
165
177
|
raise ValueError(
|
|
@@ -171,27 +183,38 @@ class Rates(object):
|
|
|
171
183
|
|
|
172
184
|
@property
|
|
173
185
|
def get_open(self):
|
|
174
|
-
return self.
|
|
186
|
+
return self.__data['Open']
|
|
175
187
|
|
|
176
188
|
@property
|
|
177
189
|
def get_high(self):
|
|
178
|
-
return self.
|
|
190
|
+
return self.__data['High']
|
|
179
191
|
|
|
180
192
|
@property
|
|
181
193
|
def get_low(self):
|
|
182
|
-
return self.
|
|
194
|
+
return self.__data['Low']
|
|
183
195
|
|
|
184
196
|
@property
|
|
185
197
|
def get_close(self):
|
|
186
|
-
return self.
|
|
198
|
+
return self.__data['Close']
|
|
187
199
|
|
|
188
200
|
@property
|
|
189
201
|
def get_adj_close(self):
|
|
190
|
-
return self.
|
|
202
|
+
return self.__data['Adj Close']
|
|
191
203
|
|
|
192
204
|
@property
|
|
193
205
|
def get_returns(self):
|
|
194
|
-
|
|
206
|
+
"""
|
|
207
|
+
Fractional change between the current and a prior element.
|
|
208
|
+
|
|
209
|
+
Computes the fractional change from the immediately previous row by default.
|
|
210
|
+
This is useful in comparing the fraction of change in a time series of elements.
|
|
211
|
+
|
|
212
|
+
Note
|
|
213
|
+
----
|
|
214
|
+
It calculates fractional change (also known as `per unit change or relative change`)
|
|
215
|
+
and `not percentage change`. If you need the percentage change, multiply these values by 100.
|
|
216
|
+
"""
|
|
217
|
+
data = self.__data.copy()
|
|
195
218
|
data['Returns'] = data['Adj Close'].pct_change()
|
|
196
219
|
data = data.dropna()
|
|
197
220
|
return data['Returns']
|
|
@@ -219,6 +242,12 @@ class Rates(object):
|
|
|
219
242
|
Returns:
|
|
220
243
|
Union[pd.DataFrame, None]: A DataFrame containing historical data
|
|
221
244
|
if successful, otherwise None.
|
|
245
|
+
|
|
246
|
+
Raises:
|
|
247
|
+
ValueError: If the starting date is greater than the ending date.
|
|
248
|
+
|
|
249
|
+
Notes:
|
|
250
|
+
The Datetime for this method is in UTC timezone.
|
|
222
251
|
"""
|
|
223
252
|
df = self._fetch_data(date_from, date_to)
|
|
224
253
|
if save_csv and df is not None:
|
bbstrader/metatrader/risk.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import random
|
|
2
|
+
import re
|
|
2
3
|
import numpy as np
|
|
3
4
|
from scipy.stats import norm
|
|
4
5
|
from datetime import datetime
|
|
@@ -67,6 +68,8 @@ class RiskManagement(Account):
|
|
|
67
68
|
max_trades: Optional[int] = None,
|
|
68
69
|
std_stop: bool = False,
|
|
69
70
|
pchange_sl: Optional[float] = None,
|
|
71
|
+
var_level: float = 0.95,
|
|
72
|
+
var_time_frame: TimeFrame = 'D1',
|
|
70
73
|
account_leverage: bool = True,
|
|
71
74
|
time_frame: TimeFrame = 'D1',
|
|
72
75
|
start_time: str = "1:00",
|
|
@@ -91,6 +94,9 @@ class RiskManagement(Account):
|
|
|
91
94
|
On `historical volatility` of the trading instrument. Defaults to False.
|
|
92
95
|
pchange_sl (float, optional): If set, the Stop loss is calculated based
|
|
93
96
|
On `percentage change` of the trading instrument.
|
|
97
|
+
var_level (float, optional): Confidence level for Value-at-Risk,e.g., 0.99 for 99% confidence interval.
|
|
98
|
+
The default is 0.95.
|
|
99
|
+
var_time_frame (str, optional): Time frame to use to calculate the VaR.
|
|
94
100
|
account_leverage (bool, optional): If set to True the account leverage will be used
|
|
95
101
|
In risk management setting. Defaults to False.
|
|
96
102
|
time_frame (str, optional): The time frame on which the program is working
|
|
@@ -118,7 +124,9 @@ class RiskManagement(Account):
|
|
|
118
124
|
if be is not None and (not isinstance(be, int) or be <= 0):
|
|
119
125
|
raise ValueError("be must be a positive integer number")
|
|
120
126
|
if time_frame not in TIMEFRAMES:
|
|
121
|
-
raise ValueError("Unsupported time frame")
|
|
127
|
+
raise ValueError("Unsupported time frame {}".format(time_frame))
|
|
128
|
+
if var_time_frame not in TIMEFRAMES:
|
|
129
|
+
raise ValueError("Unsupported time frame {}".format(var_time_frame))
|
|
122
130
|
|
|
123
131
|
self.symbol = symbol
|
|
124
132
|
self.start_time = start_time
|
|
@@ -126,6 +134,8 @@ class RiskManagement(Account):
|
|
|
126
134
|
self.max_trades = max_trades
|
|
127
135
|
self.std = std_stop
|
|
128
136
|
self.pchange = pchange_sl
|
|
137
|
+
self.var_level = var_level
|
|
138
|
+
self.var_tf = var_time_frame
|
|
129
139
|
self.daily_dd = daily_risk
|
|
130
140
|
self.max_risk = max_risk
|
|
131
141
|
self.rr = rr
|
|
@@ -136,8 +146,21 @@ class RiskManagement(Account):
|
|
|
136
146
|
self.account_leverage = account_leverage
|
|
137
147
|
self.symbol_info = super().get_symbol_info(self.symbol)
|
|
138
148
|
|
|
139
|
-
self.
|
|
140
|
-
|
|
149
|
+
self._tf = time_frame
|
|
150
|
+
|
|
151
|
+
def _convert_time_frame(self, tf: str) -> int:
|
|
152
|
+
"""Convert time frame to minutes"""
|
|
153
|
+
if tf == 'D1':
|
|
154
|
+
tf_int = self.get_minutes()
|
|
155
|
+
elif 'm' in tf:
|
|
156
|
+
tf_int = TIMEFRAMES[tf]
|
|
157
|
+
elif 'h' in tf:
|
|
158
|
+
tf_int = int(tf[0])*60
|
|
159
|
+
elif tf == 'W1':
|
|
160
|
+
tf_int = self.get_minutes() * 5
|
|
161
|
+
elif tf == 'MN1':
|
|
162
|
+
tf_int = self.get_minutes() * 22
|
|
163
|
+
return tf_int
|
|
141
164
|
|
|
142
165
|
def risk_level(self) -> float:
|
|
143
166
|
"""
|
|
@@ -194,10 +217,11 @@ class RiskManagement(Account):
|
|
|
194
217
|
def max_trade(self) -> int:
|
|
195
218
|
"""calculates the maximum number of trades allowed"""
|
|
196
219
|
minutes = self.get_minutes()
|
|
220
|
+
tf_int = self._convert_time_frame(self._tf)
|
|
197
221
|
if self.max_trades is not None:
|
|
198
222
|
max_trades = self.max_trades
|
|
199
223
|
else:
|
|
200
|
-
max_trades = round(minutes /
|
|
224
|
+
max_trades = round(minutes / tf_int)
|
|
201
225
|
return max(max_trades, 1)
|
|
202
226
|
|
|
203
227
|
def get_minutes(self) -> int:
|
|
@@ -217,24 +241,22 @@ class RiskManagement(Account):
|
|
|
217
241
|
|
|
218
242
|
return hours
|
|
219
243
|
|
|
220
|
-
def get_std_stop(self
|
|
244
|
+
def get_std_stop(self):
|
|
221
245
|
"""
|
|
222
246
|
Calculate the standard deviation-based stop loss level
|
|
223
247
|
for a given financial instrument.
|
|
224
248
|
|
|
225
|
-
Args:
|
|
226
|
-
tf (str): Timeframe for data, default is 'D1' (Daily).
|
|
227
|
-
interval (int): Number of historical data points to consider
|
|
228
|
-
for calculating standard deviation, default is 252.
|
|
229
|
-
|
|
230
249
|
Returns:
|
|
231
250
|
- Standard deviation-based stop loss level, rounded to the nearest point.
|
|
232
251
|
- 0 if the calculated stop loss is less than or equal to 0.
|
|
233
252
|
"""
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
253
|
+
minutes = self.get_minutes()
|
|
254
|
+
tf_int = self._convert_time_frame(self._tf)
|
|
255
|
+
interval = round((minutes / tf_int) * 252)
|
|
256
|
+
|
|
257
|
+
rate = Rates(self.symbol, self._tf, 0, interval)
|
|
258
|
+
returns = rate.get_returns*100
|
|
259
|
+
std = returns.std()
|
|
238
260
|
point = self.get_symbol_info(self.symbol).point
|
|
239
261
|
av_price = (self.symbol_info.bid + self.symbol_info.ask)/2
|
|
240
262
|
price_interval = av_price * ((100-std))/100
|
|
@@ -271,26 +293,27 @@ class RiskManagement(Account):
|
|
|
271
293
|
# Use std as default pchange
|
|
272
294
|
return self.get_std_stop()
|
|
273
295
|
|
|
274
|
-
def calculate_var(self, tf: TimeFrame = 'D1',
|
|
296
|
+
def calculate_var(self, tf: TimeFrame = 'D1', c=0.95):
|
|
275
297
|
"""
|
|
276
298
|
Calculate Value at Risk (VaR) for a given portfolio.
|
|
277
299
|
|
|
278
300
|
Args:
|
|
279
301
|
tf (str): Time frame to use to calculate volatility.
|
|
280
|
-
interval (int): How many periods to use based on time frame.
|
|
281
302
|
c (float): Confidence level for VaR calculation (default is 95%).
|
|
282
303
|
|
|
283
304
|
Returns:
|
|
284
305
|
- VaR value
|
|
285
306
|
"""
|
|
307
|
+
minutes = self.get_minutes()
|
|
308
|
+
tf_int = self._convert_time_frame(tf)
|
|
309
|
+
interval = round((minutes / tf_int) * 252)
|
|
310
|
+
|
|
286
311
|
rate = Rates(self.symbol, tf, 0, interval)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
sigma = np.std(prices['return'])
|
|
293
|
-
var = self.var_cov_var(P, c, mu, sigma)
|
|
312
|
+
returns = rate.get_returns*100
|
|
313
|
+
p = self.get_account_info().margin_free
|
|
314
|
+
mu = returns.mean()
|
|
315
|
+
sigma = returns.std()
|
|
316
|
+
var = self.var_cov_var(p, c, mu, sigma)
|
|
294
317
|
return var
|
|
295
318
|
|
|
296
319
|
def var_cov_var(self, P: float, c: float, mu: float, sigma: float):
|
|
@@ -312,11 +335,15 @@ class RiskManagement(Account):
|
|
|
312
335
|
def var_loss_value(self):
|
|
313
336
|
"""
|
|
314
337
|
Calculate the stop-loss level based on VaR.
|
|
338
|
+
|
|
339
|
+
Notes:
|
|
340
|
+
The Var is Estimated using the Variance-Covariance method on the daily returns.
|
|
341
|
+
If you want to use the VaR for a different time frame .
|
|
315
342
|
"""
|
|
316
343
|
P = self.get_account_info().margin_free
|
|
317
344
|
trade_risk = self.get_trade_risk()
|
|
318
345
|
loss_allowed = P * trade_risk
|
|
319
|
-
var = self.calculate_var()
|
|
346
|
+
var = self.calculate_var(c=self.var_level, tf=self.var_tf)
|
|
320
347
|
return min(var, loss_allowed)
|
|
321
348
|
|
|
322
349
|
def get_take_profit(self) -> int:
|
|
@@ -480,13 +507,8 @@ class RiskManagement(Account):
|
|
|
480
507
|
if self.get_symbol_type(self.symbol) == 'IDX':
|
|
481
508
|
rates = self.get_currency_rates(self.symbol)
|
|
482
509
|
if rates['mc'] == rates['pc'] == 'JPY':
|
|
483
|
-
if self.std:
|
|
484
|
-
raise ValueError(
|
|
485
|
-
f"""Please Set std=False or use pchange_sl=True
|
|
486
|
-
or set sl=value or use the default method calculation for {self.symbol}
|
|
487
|
-
Currency risk"""
|
|
488
|
-
)
|
|
489
510
|
lot = lot * contract_size
|
|
511
|
+
lot = self._check_lot(lot)
|
|
490
512
|
volume = round(lot * av_price * contract_size)
|
|
491
513
|
if contract_size == 1:
|
|
492
514
|
volume = round(lot * av_price)
|
bbstrader/metatrader/trade.py
CHANGED
|
@@ -14,7 +14,7 @@ from bbstrader.metatrader.utils import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
# Configure the logger
|
|
17
|
-
logger = config_logger('trade.log', console_log=
|
|
17
|
+
logger = config_logger('trade.log', console_log=False)
|
|
18
18
|
|
|
19
19
|
class Trade(RiskManagement):
|
|
20
20
|
"""
|
|
@@ -160,7 +160,7 @@ class Trade(RiskManagement):
|
|
|
160
160
|
print()
|
|
161
161
|
self.risk_managment()
|
|
162
162
|
print(
|
|
163
|
-
f">>> Everything is OK, @{self.expert_name} is Running
|
|
163
|
+
f">>> Everything is OK, @{self.expert_name} is Running ...>>>\n")
|
|
164
164
|
|
|
165
165
|
def initialize(self):
|
|
166
166
|
"""
|
|
@@ -416,7 +416,7 @@ class Trade(RiskManagement):
|
|
|
416
416
|
request["action"] = Mt5.TRADE_ACTION_PENDING
|
|
417
417
|
request["type"] = self._order_type()[action][0]
|
|
418
418
|
|
|
419
|
-
self.break_even(
|
|
419
|
+
self.break_even(mm=mm)
|
|
420
420
|
if self.check(comment):
|
|
421
421
|
self.request_result(_price, request, action),
|
|
422
422
|
|
|
@@ -555,7 +555,7 @@ class Trade(RiskManagement):
|
|
|
555
555
|
check_result = self.check_order(request)
|
|
556
556
|
result = self.send_order(request)
|
|
557
557
|
except Exception as e:
|
|
558
|
-
print(f"{self.
|
|
558
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
559
559
|
trade_retcode_message(
|
|
560
560
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
561
561
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
@@ -571,7 +571,7 @@ class Trade(RiskManagement):
|
|
|
571
571
|
check_result = self.check_order(request)
|
|
572
572
|
result = self.send_order(request)
|
|
573
573
|
except Exception as e:
|
|
574
|
-
print(f"{self.
|
|
574
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
575
575
|
trade_retcode_message(
|
|
576
576
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
577
577
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
@@ -767,7 +767,7 @@ class Trade(RiskManagement):
|
|
|
767
767
|
return True
|
|
768
768
|
return False
|
|
769
769
|
|
|
770
|
-
def break_even(self, id: Optional[int] = None):
|
|
770
|
+
def break_even(self, mm=True, id: Optional[int] = None):
|
|
771
771
|
"""
|
|
772
772
|
Checks if it's time to put the break even,
|
|
773
773
|
if so , it will sets the break even ,and if the break even was already set,
|
|
@@ -776,8 +776,11 @@ class Trade(RiskManagement):
|
|
|
776
776
|
|
|
777
777
|
Args:
|
|
778
778
|
id (int): The strategy Id or Expert Id
|
|
779
|
+
mm (bool): Weither to manage the position or not
|
|
779
780
|
"""
|
|
780
781
|
time.sleep(0.1)
|
|
782
|
+
if not mm:
|
|
783
|
+
return
|
|
781
784
|
Id = id if id is not None else self.expert_id
|
|
782
785
|
positions = self.get_positions(symbol=self.symbol)
|
|
783
786
|
be = self.get_break_even()
|
|
@@ -900,7 +903,7 @@ class Trade(RiskManagement):
|
|
|
900
903
|
check_result = self.check_order(request)
|
|
901
904
|
result = self.send_order(request)
|
|
902
905
|
except Exception as e:
|
|
903
|
-
print(f"{self.
|
|
906
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
904
907
|
trade_retcode_message(
|
|
905
908
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
906
909
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
@@ -917,7 +920,7 @@ class Trade(RiskManagement):
|
|
|
917
920
|
check_result = self.check_order(request)
|
|
918
921
|
result = self.send_order(request)
|
|
919
922
|
except Exception as e:
|
|
920
|
-
print(f"{self.
|
|
923
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
921
924
|
trade_retcode_message(
|
|
922
925
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
923
926
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
@@ -1041,7 +1044,7 @@ class Trade(RiskManagement):
|
|
|
1041
1044
|
check_result = self.check_order(request)
|
|
1042
1045
|
result = self.send_order(request)
|
|
1043
1046
|
except Exception as e:
|
|
1044
|
-
print(f"{self.
|
|
1047
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
1045
1048
|
trade_retcode_message(
|
|
1046
1049
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
1047
1050
|
if result.retcode != Mt5.TRADE_RETCODE_DONE:
|
|
@@ -1055,7 +1058,7 @@ class Trade(RiskManagement):
|
|
|
1055
1058
|
check_result = self.check_order(request)
|
|
1056
1059
|
result = self.send_order(request)
|
|
1057
1060
|
except Exception as e:
|
|
1058
|
-
print(f"{self.
|
|
1061
|
+
print(f"{self.current_datetime()} -", end=' ')
|
|
1059
1062
|
trade_retcode_message(
|
|
1060
1063
|
result.retcode, display=True, add_msg=f"{e}{addtionnal}")
|
|
1061
1064
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
@@ -1111,13 +1114,13 @@ class Trade(RiskManagement):
|
|
|
1111
1114
|
|
|
1112
1115
|
if len(tickets) == 0:
|
|
1113
1116
|
logger.info(
|
|
1114
|
-
f"ALL {
|
|
1117
|
+
f"ALL {pos_type.upper()} Positions closed, SYMBOL={self.symbol}.")
|
|
1115
1118
|
else:
|
|
1116
1119
|
logger.info(
|
|
1117
|
-
f"{len(tickets)} {
|
|
1120
|
+
f"{len(tickets)} {pos_type.upper()} Positions not closed, SYMBOL={self.symbol}")
|
|
1118
1121
|
else:
|
|
1119
1122
|
logger.info(
|
|
1120
|
-
f"No {
|
|
1123
|
+
f"No {pos_type.upper()} Positions to close, SYMBOL={self.symbol}.")
|
|
1121
1124
|
|
|
1122
1125
|
def get_stats(self) -> Tuple[Dict[str, Any]]:
|
|
1123
1126
|
"""
|
bbstrader/metatrader/utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
import MetaTrader5 as MT5
|
|
2
3
|
import logging
|
|
3
4
|
from typing import List, NamedTuple, Optional
|
|
@@ -216,7 +217,7 @@ class SymbolInfo(NamedTuple):
|
|
|
216
217
|
volume: int
|
|
217
218
|
volumehigh: int
|
|
218
219
|
volumelow: int
|
|
219
|
-
time:
|
|
220
|
+
time: datetime
|
|
220
221
|
digits: int
|
|
221
222
|
spread: int
|
|
222
223
|
spread_float: bool
|
|
@@ -316,7 +317,7 @@ class TickInfo(NamedTuple):
|
|
|
316
317
|
* flags: Tick flags
|
|
317
318
|
* volume_real: Volume for the current Last price with greater accuracy
|
|
318
319
|
"""
|
|
319
|
-
time:
|
|
320
|
+
time: datetime
|
|
320
321
|
bid: float
|
|
321
322
|
ask: float
|
|
322
323
|
last: float
|
bbstrader/trading/execution.py
CHANGED
|
@@ -165,10 +165,13 @@ def sma_trading(
|
|
|
165
165
|
time.sleep((60 * iter_time) - 1.5)
|
|
166
166
|
if iter_time == 1:
|
|
167
167
|
time_intervals += 1
|
|
168
|
-
elif iter_time ==
|
|
169
|
-
time_intervals +=
|
|
168
|
+
elif trade_time % iter_time == 0:
|
|
169
|
+
time_intervals += iter_time
|
|
170
170
|
else:
|
|
171
|
-
|
|
171
|
+
raise ValueError(
|
|
172
|
+
f"iter_time must be a multiple of the {tf} !!!"
|
|
173
|
+
f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
|
|
174
|
+
)
|
|
172
175
|
if period.lower() == 'month':
|
|
173
176
|
if trade.days_end() and today != 'Friday':
|
|
174
177
|
sleep_time = trade.sleep_time()
|
|
@@ -484,10 +487,13 @@ def pair_trading(
|
|
|
484
487
|
|
|
485
488
|
if iter_time == 1:
|
|
486
489
|
time_intervals += 1
|
|
487
|
-
elif iter_time ==
|
|
488
|
-
time_intervals +=
|
|
490
|
+
elif trade_time % iter_time == 0:
|
|
491
|
+
time_intervals += iter_time
|
|
489
492
|
else:
|
|
490
|
-
|
|
493
|
+
raise ValueError(
|
|
494
|
+
f"iter_time must be a multiple of the {tf} !!!"
|
|
495
|
+
f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
|
|
496
|
+
)
|
|
491
497
|
|
|
492
498
|
if period.lower() == 'month':
|
|
493
499
|
if p0.days_end() and today != 'Friday':
|
|
@@ -706,10 +712,13 @@ def ou_trading(
|
|
|
706
712
|
time.sleep((60 * iter_time) - 1.5)
|
|
707
713
|
if iter_time == 1:
|
|
708
714
|
time_intervals += 1
|
|
709
|
-
elif iter_time ==
|
|
710
|
-
time_intervals +=
|
|
715
|
+
elif trade_time % iter_time == 0:
|
|
716
|
+
time_intervals += iter_time
|
|
711
717
|
else:
|
|
712
|
-
|
|
718
|
+
raise ValueError(
|
|
719
|
+
f"iter_time must be a multiple of the {tf} !!!"
|
|
720
|
+
f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
|
|
721
|
+
)
|
|
713
722
|
|
|
714
723
|
if period.lower() == 'month':
|
|
715
724
|
if trade.days_end() and today != 'Friday':
|
|
@@ -918,10 +927,13 @@ def arch_trading(
|
|
|
918
927
|
time.sleep((60 * iter_time) - 1.5)
|
|
919
928
|
if iter_time == 1:
|
|
920
929
|
time_intervals += 1
|
|
921
|
-
elif iter_time ==
|
|
922
|
-
time_intervals +=
|
|
930
|
+
elif trade_time % iter_time == 0:
|
|
931
|
+
time_intervals += iter_time
|
|
923
932
|
else:
|
|
924
|
-
|
|
933
|
+
raise ValueError(
|
|
934
|
+
f"iter_time must be a multiple of the {tf} !!!"
|
|
935
|
+
f"(e.g; if time_frame is 15m, iter_time must be 1.5, 3, 3, 15 etc)"
|
|
936
|
+
)
|
|
925
937
|
|
|
926
938
|
if period.lower() == 'month':
|
|
927
939
|
if trade.days_end() and today != 'Friday':
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Simplified Investment & Trading Toolkit
|
|
5
5
|
Home-page: https://github.com/bbalouki/bbstrader
|
|
6
|
+
Download-URL: https://pypi.org/project/bbstrader/
|
|
6
7
|
Author: Bertin Balouki SIMYELI
|
|
7
8
|
Author-email: <bertin@bbstrader.com>
|
|
8
9
|
Maintainer: Bertin Balouki SIMYELI
|
|
9
10
|
License: The MIT License (MIT)
|
|
11
|
+
Project-URL: Documentation, https://bbstrader.readthedocs.io/en/latest/
|
|
12
|
+
Project-URL: Source Code, https://github.com/bbalouki/bbstrader
|
|
10
13
|
Keywords: Finance,Toolkit,Financial,Analysis,Fundamental,Quantitative,Database,Equities,Currencies,Economics,ETFs,Funds,Indices,Moneymarkets,Commodities,Futures,CFDs,Derivatives,Trading,Investing,Portfolio,Optimization,Performance
|
|
11
14
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
bbstrader/__ini__.py,sha256=pXy9hM6Yh9hvIHRQS56TzEfYj97YYoc48oiYuYOfns4,478
|
|
2
|
+
bbstrader/strategies.py,sha256=6USRp43fsLLe2rT608Glz6lmea6bvjKx8fQEgOWyUGI,24510
|
|
3
|
+
bbstrader/tseries.py,sha256=M9x5jcdvIiAAAv1V2PSldguOHT4K6tmR2EYW_IswqI0,21779
|
|
4
|
+
bbstrader/btengine/__init__.py,sha256=oGyNWUAqzpounayQ5ptww6vFUp58J-af-ooCMf9hFV0,3156
|
|
5
|
+
bbstrader/btengine/backtest.py,sha256=eqeoNfm2_d0gEvSoXTqbNnEkLCq76UQEQqslL31iwSI,36744
|
|
6
|
+
bbstrader/btengine/data.py,sha256=q8DQpdknbzLfFKIso5sRyKw8w8clH-uDxs_UqLKTT2g,13933
|
|
7
|
+
bbstrader/btengine/event.py,sha256=quqNzvfvqtGYGVg9id3uNHb3EhzH5FCWYdeCn2KZ9sc,7894
|
|
8
|
+
bbstrader/btengine/execution.py,sha256=g5p_7XXU7fgxkzoXNd5SiHITUWcURpd9ViYumtlxF0g,2646
|
|
9
|
+
bbstrader/btengine/performance.py,sha256=Pz-qZb7EglPt8TMKs5ZCgo-af54UCOfqWKpEH8B19rQ,10211
|
|
10
|
+
bbstrader/btengine/portfolio.py,sha256=RXGJTmg1NIq0MSkMB6v6hmCG4xfdwLo9rO1_UCAX7As,14008
|
|
11
|
+
bbstrader/btengine/strategy.py,sha256=G9_vcAzH-oT3vk1t3espXP-snrN2uXTEZh3sVCyFzWM,1554
|
|
12
|
+
bbstrader/metatrader/__init__.py,sha256=y4JLz05esm3PKerHMgtd3dmRpa7yUvWVuj_xOnlhXSA,261
|
|
13
|
+
bbstrader/metatrader/account.py,sha256=qbkcPbIbuld-ZVrNY8zW8cTSpPPdKMw9zKk5HQD108g,44301
|
|
14
|
+
bbstrader/metatrader/rates.py,sha256=Ro4NIAjR_cjMivrfvixsEtivNAx0ACvPoBosYTCuzZg,9383
|
|
15
|
+
bbstrader/metatrader/risk.py,sha256=qMDLqrwD8foKOW8jCnhwt0Lvxgtl0uwKYgRqhSbNeh8,25827
|
|
16
|
+
bbstrader/metatrader/trade.py,sha256=Y0WPJC2rVETx5wmZUpRzVRj42gQhkh-8LNcOBYlpzdg,57390
|
|
17
|
+
bbstrader/metatrader/utils.py,sha256=UaxsQ2myvpPGSvO_Xs3T9cuT9rwGRUhDE2YyQA4u05I,18963
|
|
18
|
+
bbstrader/models/__init__.py,sha256=6tAj9V9vgwesgPVMKznwRB3k8-Ec8Q73Di5p2UO0qlA,274
|
|
19
|
+
bbstrader/models/risk.py,sha256=2fFBqsY2v8Kd6oU8t8h2h-Sp8joVqetA-t21rjkT9U4,13593
|
|
20
|
+
bbstrader/trading/__init__.py,sha256=yZ85EALV2sSCzCPxaeFYlk1JJhYQq2C-xSd5EEQYvI8,82
|
|
21
|
+
bbstrader/trading/execution.py,sha256=Ru1TiVrxCVr9YOXpweb1KiqHMANeTsKqfsth8Ho-nbg,47288
|
|
22
|
+
bbstrader/trading/run.py,sha256=UA5Sn5nWOqsezZVGCM_kjOPPsu_IxMdSABUBnBAJA-k,3474
|
|
23
|
+
bbstrader/trading/utils.py,sha256=Eu_cBnfPcOGel4Lj6Dc-UCl-h7NqR8Rfv9RZtaxNRos,7711
|
|
24
|
+
bbstrader-0.1.6.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
|
|
25
|
+
bbstrader-0.1.6.dist-info/METADATA,sha256=an7eJE6u3flDzS0irxAmooZX5IEf2t8l12GnnT7rGE0,9218
|
|
26
|
+
bbstrader-0.1.6.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
|
|
27
|
+
bbstrader-0.1.6.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
|
|
28
|
+
bbstrader-0.1.6.dist-info/RECORD,,
|
bbstrader-0.1.4.dist-info/RECORD
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
bbstrader/__ini__.py,sha256=pXy9hM6Yh9hvIHRQS56TzEfYj97YYoc48oiYuYOfns4,478
|
|
2
|
-
bbstrader/strategies.py,sha256=6USRp43fsLLe2rT608Glz6lmea6bvjKx8fQEgOWyUGI,24510
|
|
3
|
-
bbstrader/tseries.py,sha256=M9x5jcdvIiAAAv1V2PSldguOHT4K6tmR2EYW_IswqI0,21779
|
|
4
|
-
bbstrader/btengine/__init__.py,sha256=oGyNWUAqzpounayQ5ptww6vFUp58J-af-ooCMf9hFV0,3156
|
|
5
|
-
bbstrader/btengine/backtest.py,sha256=eXKTIQxPWFN3RdTXWQTE2ybc4YI0svoWobN6Cz2xR14,36656
|
|
6
|
-
bbstrader/btengine/data.py,sha256=-8k3tBf-sHnV7GU26xxaGusK_SYxm6TuIvCTMcR_zY4,13810
|
|
7
|
-
bbstrader/btengine/event.py,sha256=k27f8pMnF8iCiLT748Zcpc_nUnfL5QbxkGvefIdEqPA,7820
|
|
8
|
-
bbstrader/btengine/execution.py,sha256=0U4ukV8a5G1eJTvyXHxkdT6E5Jk2UoDUIok0SGdRfWk,2636
|
|
9
|
-
bbstrader/btengine/performance.py,sha256=Pz-qZb7EglPt8TMKs5ZCgo-af54UCOfqWKpEH8B19rQ,10211
|
|
10
|
-
bbstrader/btengine/portfolio.py,sha256=RXGJTmg1NIq0MSkMB6v6hmCG4xfdwLo9rO1_UCAX7As,14008
|
|
11
|
-
bbstrader/btengine/strategy.py,sha256=tY5ek37NxLrYftgzJ4CqMGDFv5oMkvMsBbOkkOGghKw,1384
|
|
12
|
-
bbstrader/metatrader/__init__.py,sha256=y4JLz05esm3PKerHMgtd3dmRpa7yUvWVuj_xOnlhXSA,261
|
|
13
|
-
bbstrader/metatrader/account.py,sha256=V5VbkUP2OawEuvRSGAAr_L0gXXdiwiACBD5hDXm-cbE,42651
|
|
14
|
-
bbstrader/metatrader/rates.py,sha256=tGcWh2t9Yilik8YDMN2_VBeWV4VNa4zHABmOT98rW8g,8286
|
|
15
|
-
bbstrader/metatrader/risk.py,sha256=tGpmP6pOtbn4iWtX4P6Gl2Z5YuP2jRI5g7Z8pybsO58,25134
|
|
16
|
-
bbstrader/metatrader/trade.py,sha256=szLRhWKM7Vh0pxPXNi0mRq0bSoHpETRXOwWGPVdbW28,57296
|
|
17
|
-
bbstrader/metatrader/utils.py,sha256=Tw98T4iBqlMtm9jVnwsf9mjKxZkD_rA0u8xJLqoHl0Q,18922
|
|
18
|
-
bbstrader/models/__init__.py,sha256=6tAj9V9vgwesgPVMKznwRB3k8-Ec8Q73Di5p2UO0qlA,274
|
|
19
|
-
bbstrader/models/risk.py,sha256=2fFBqsY2v8Kd6oU8t8h2h-Sp8joVqetA-t21rjkT9U4,13593
|
|
20
|
-
bbstrader/trading/__init__.py,sha256=yZ85EALV2sSCzCPxaeFYlk1JJhYQq2C-xSd5EEQYvI8,82
|
|
21
|
-
bbstrader/trading/execution.py,sha256=geiIiR9ldTJvM6rapa66Xug-gRcA_YqfA6kCr54lnRw,46708
|
|
22
|
-
bbstrader/trading/run.py,sha256=UA5Sn5nWOqsezZVGCM_kjOPPsu_IxMdSABUBnBAJA-k,3474
|
|
23
|
-
bbstrader/trading/utils.py,sha256=Eu_cBnfPcOGel4Lj6Dc-UCl-h7NqR8Rfv9RZtaxNRos,7711
|
|
24
|
-
bbstrader-0.1.4.dist-info/LICENSE,sha256=1EudjwwP2oTJy8Vh0e-Kzv8VZZU95y-t6c3DYhR51uc,1115
|
|
25
|
-
bbstrader-0.1.4.dist-info/METADATA,sha256=L-rGeXHa2gllmmh4q8OHkmD514Lb6zpwkVn35YTX5NM,9029
|
|
26
|
-
bbstrader-0.1.4.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
|
|
27
|
-
bbstrader-0.1.4.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
|
|
28
|
-
bbstrader-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|