bbstrader 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bbstrader might be problematic. Click here for more details.
- bbstrader/__ini__.py +6 -5
- bbstrader/btengine/execution.py +1 -1
- bbstrader/btengine/strategy.py +32 -5
- bbstrader/config.py +10 -4
- bbstrader/metatrader/account.py +62 -25
- bbstrader/metatrader/risk.py +40 -6
- bbstrader/metatrader/trade.py +36 -24
- bbstrader/trading/execution.py +24 -21
- {bbstrader-0.2.5.dist-info → bbstrader-0.2.7.dist-info}/METADATA +10 -3
- {bbstrader-0.2.5.dist-info → bbstrader-0.2.7.dist-info}/RECORD +13 -13
- {bbstrader-0.2.5.dist-info → bbstrader-0.2.7.dist-info}/LICENSE +0 -0
- {bbstrader-0.2.5.dist-info → bbstrader-0.2.7.dist-info}/WHEEL +0 -0
- {bbstrader-0.2.5.dist-info → bbstrader-0.2.7.dist-info}/top_level.txt +0 -0
bbstrader/__ini__.py
CHANGED
|
@@ -3,11 +3,12 @@ Simplified Investment & Trading Toolkit
|
|
|
3
3
|
=======================================
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
|
|
7
|
+
__author__ = "Bertin Balouki SIMYELI"
|
|
8
|
+
__copyright__ = "2023-2025 Bertin Balouki SIMYELI"
|
|
9
|
+
__email__ = "bertin@bbstrader.com"
|
|
10
|
+
__license__ = "MIT"
|
|
11
|
+
__version__ = "0.2.0"
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
from bbstrader import btengine # noqa: F401
|
bbstrader/btengine/execution.py
CHANGED
|
@@ -124,7 +124,7 @@ class MT5ExecutionHandler(ExecutionHandler):
|
|
|
124
124
|
self.events = events
|
|
125
125
|
self.bardata = data
|
|
126
126
|
self.logger = kwargs.get("logger")
|
|
127
|
-
self.__account = Account()
|
|
127
|
+
self.__account = Account(**kwargs)
|
|
128
128
|
|
|
129
129
|
def _calculate_lot(self, symbol, quantity, price):
|
|
130
130
|
symbol_type = self.__account.get_symbol_type(symbol)
|
bbstrader/btengine/strategy.py
CHANGED
|
@@ -10,7 +10,11 @@ import pytz
|
|
|
10
10
|
|
|
11
11
|
from bbstrader.btengine.data import DataHandler
|
|
12
12
|
from bbstrader.btengine.event import FillEvent, SignalEvent
|
|
13
|
-
from bbstrader.metatrader.account import
|
|
13
|
+
from bbstrader.metatrader.account import (
|
|
14
|
+
Account,
|
|
15
|
+
AdmiralMarktsGroup,
|
|
16
|
+
PepperstoneGroupLimited,
|
|
17
|
+
)
|
|
14
18
|
from bbstrader.metatrader.rates import Rates
|
|
15
19
|
from bbstrader.models.optimization import optimized_weights
|
|
16
20
|
from bbstrader.core.utils import TradeSignal
|
|
@@ -122,7 +126,7 @@ class MT5Strategy(Strategy):
|
|
|
122
126
|
for asset in self.symbols:
|
|
123
127
|
if asset not in weights:
|
|
124
128
|
raise ValueError(f"Risk budget for asset {asset} is missing.")
|
|
125
|
-
total_risk = sum(weights.values())
|
|
129
|
+
total_risk = float(round(sum(weights.values())))
|
|
126
130
|
if not np.isclose(total_risk, 1.0):
|
|
127
131
|
raise ValueError(f"Risk budget weights must sum to 1. got {total_risk}")
|
|
128
132
|
return weights
|
|
@@ -226,7 +230,7 @@ class MT5Strategy(Strategy):
|
|
|
226
230
|
prices = prices.dropna(axis=0, how="any")
|
|
227
231
|
try:
|
|
228
232
|
weights = optimized_weights(prices=prices, freq=freq, method=optimer)
|
|
229
|
-
return {symbol: weight for symbol, weight in weights.items()}
|
|
233
|
+
return {symbol: abs(weight) for symbol, weight in weights.items()}
|
|
230
234
|
except Exception:
|
|
231
235
|
return {symbol: 0.0 for symbol in symbols}
|
|
232
236
|
|
|
@@ -605,6 +609,7 @@ class MT5Strategy(Strategy):
|
|
|
605
609
|
bars: DataHandler = None,
|
|
606
610
|
mode: Literal["backtest", "live"] = "backtest",
|
|
607
611
|
tf: str = "D1",
|
|
612
|
+
error: Literal["ignore", "raise"] = None,
|
|
608
613
|
) -> Dict[str, np.ndarray | pd.Series] | None:
|
|
609
614
|
"""
|
|
610
615
|
Get the historical OHLCV value or returns or custum value
|
|
@@ -618,6 +623,7 @@ class MT5Strategy(Strategy):
|
|
|
618
623
|
mode : Mode of operation for the strategy.
|
|
619
624
|
window : The lookback period for resquesting the data.
|
|
620
625
|
tf : The time frame for the strategy.
|
|
626
|
+
error : The error handling method for the function.
|
|
621
627
|
|
|
622
628
|
Returns:
|
|
623
629
|
asset_values : Historical values of the assets in the symbol list.
|
|
@@ -651,6 +657,10 @@ class MT5Strategy(Strategy):
|
|
|
651
657
|
if all(len(values) >= window for values in asset_values.values()):
|
|
652
658
|
return {a: v[-window:] for a, v in asset_values.items()}
|
|
653
659
|
else:
|
|
660
|
+
if error == "raise":
|
|
661
|
+
raise ValueError("Not enough data to calculate the values.")
|
|
662
|
+
elif error == "ignore":
|
|
663
|
+
return asset_values
|
|
654
664
|
return None
|
|
655
665
|
|
|
656
666
|
@staticmethod
|
|
@@ -763,8 +773,20 @@ class MT5Strategy(Strategy):
|
|
|
763
773
|
return dt_to
|
|
764
774
|
|
|
765
775
|
@staticmethod
|
|
766
|
-
def get_mt5_equivalent(symbols, type="STK",
|
|
767
|
-
|
|
776
|
+
def get_mt5_equivalent(symbols, type="STK", **kwargs) -> List[str]:
|
|
777
|
+
"""
|
|
778
|
+
Get the MetaTrader 5 equivalent symbols for the symbols in the list.
|
|
779
|
+
This method is used to get the symbols that are available on the MetaTrader 5 platform.
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
symbols : The list of symbols to get the MetaTrader 5 equivalent symbols for.
|
|
783
|
+
type : The type of symbols to get (e.g., STK, CFD, etc.).
|
|
784
|
+
**kwargs : Additional keyword arguments for the `bbstrader.metatrader.Account` object.
|
|
785
|
+
|
|
786
|
+
Returns:
|
|
787
|
+
mt5_equivalent : The MetaTrader 5 equivalent symbols for the symbols in the list.
|
|
788
|
+
"""
|
|
789
|
+
account = Account(**kwargs)
|
|
768
790
|
mt5_symbols = account.get_symbols(symbol_type=type)
|
|
769
791
|
mt5_equivalent = []
|
|
770
792
|
if account.broker == AdmiralMarktsGroup():
|
|
@@ -773,6 +795,11 @@ class MT5Strategy(Strategy):
|
|
|
773
795
|
for symbol in symbols:
|
|
774
796
|
if _s.split(".")[0] == symbol or _s.split("_")[0] == symbol:
|
|
775
797
|
mt5_equivalent.append(s)
|
|
798
|
+
elif account.broker == PepperstoneGroupLimited():
|
|
799
|
+
for s in mt5_symbols:
|
|
800
|
+
for symbol in symbols:
|
|
801
|
+
if s.split(".")[0] == symbol:
|
|
802
|
+
mt5_equivalent.append(s)
|
|
776
803
|
return mt5_equivalent
|
|
777
804
|
|
|
778
805
|
|
bbstrader/config.py
CHANGED
|
@@ -2,16 +2,22 @@ import logging
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
|
|
6
|
+
TERMINAL = "\\terminal64.exe"
|
|
7
|
+
BASE_FOLDER = "C:\\Program Files\\"
|
|
8
|
+
|
|
9
|
+
AMG_PATH = BASE_FOLDER + "Admirals Group MT5 Terminal" + TERMINAL
|
|
10
|
+
XCB_PATH = BASE_FOLDER + "4xCube MT5 Terminal" + TERMINAL
|
|
11
|
+
TML_PATH = BASE_FOLDER + "Trinota Markets MetaTrader 5 Terminal" + TERMINAL
|
|
12
|
+
PGL_PATH = BASE_FOLDER + "Pepperstone MetaTrader 5" + TERMINAL
|
|
13
|
+
FTMO_PATH = BASE_FOLDER + "FTMO MetaTrader 5" + TERMINAL
|
|
9
14
|
|
|
10
15
|
BROKERS_PATHS = {
|
|
11
16
|
"AMG": AMG_PATH,
|
|
12
17
|
"FTMO": FTMO_PATH,
|
|
13
18
|
"XCB": XCB_PATH,
|
|
14
19
|
"TML": TML_PATH,
|
|
20
|
+
"PGL": PGL_PATH,
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
|
bbstrader/metatrader/account.py
CHANGED
|
@@ -28,6 +28,9 @@ __all__ = [
|
|
|
28
28
|
"Broker",
|
|
29
29
|
"AdmiralMarktsGroup",
|
|
30
30
|
"JustGlobalMarkets",
|
|
31
|
+
"TrinotaMarkets",
|
|
32
|
+
"XCubeLimited",
|
|
33
|
+
"PepperstoneGroupLimited",
|
|
31
34
|
"FTMO",
|
|
32
35
|
]
|
|
33
36
|
|
|
@@ -36,7 +39,8 @@ __BROKERS__ = {
|
|
|
36
39
|
"JGM": "Just Global Markets Ltd.",
|
|
37
40
|
"FTMO": "FTMO S.R.O.",
|
|
38
41
|
"XCB": "4xCube Limited",
|
|
39
|
-
"TML": "Trinota Markets (Global) Limited"
|
|
42
|
+
"TML": "Trinota Markets (Global) Limited",
|
|
43
|
+
"PGL": "Pepperstone Group Limited",
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
BROKERS_TIMEZONES = {
|
|
@@ -45,6 +49,7 @@ BROKERS_TIMEZONES = {
|
|
|
45
49
|
"FTMO": "Europe/Helsinki",
|
|
46
50
|
"XCB": "Europe/Helsinki",
|
|
47
51
|
"TML": "Europe/Helsinki",
|
|
52
|
+
"PGL": "Europe/Helsinki",
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
_ADMIRAL_MARKETS_URL_ = (
|
|
@@ -52,8 +57,6 @@ _ADMIRAL_MARKETS_URL_ = (
|
|
|
52
57
|
)
|
|
53
58
|
_JUST_MARKETS_URL_ = "https://one.justmarkets.link/a/tufvj0xugm/registration/trader"
|
|
54
59
|
_FTMO_URL_ = "https://trader.ftmo.com/?affiliates=JGmeuQqepAZLMcdOEQRp"
|
|
55
|
-
_XCB_URL_ = ""
|
|
56
|
-
_TML_URL_ = ""
|
|
57
60
|
_ADMIRAL_MARKETS_PRODUCTS_ = [
|
|
58
61
|
"Stocks",
|
|
59
62
|
"ETFs",
|
|
@@ -72,27 +75,26 @@ INIT_MSG = (
|
|
|
72
75
|
f"* If you want to trade {', '.join(_ADMIRAL_MARKETS_PRODUCTS_)}, See [{_ADMIRAL_MARKETS_URL_}]\n"
|
|
73
76
|
f"* If you want to trade {', '.join(_JUST_MARKETS_PRODUCTS_)}, See [{_JUST_MARKETS_URL_}]\n"
|
|
74
77
|
f"* If you are looking for a prop firm, See [{_FTMO_URL_}]\n"
|
|
75
|
-
f"* You can also look at 4xCube Limited [{_XCB_URL_}] \n and Trinota Markets (Global) Limited [{_TML_URL_}]\n"
|
|
76
78
|
)
|
|
77
79
|
|
|
78
80
|
amg_url = _ADMIRAL_MARKETS_URL_
|
|
79
81
|
jgm_url = _JUST_MARKETS_URL_
|
|
80
82
|
ftmo_url = _FTMO_URL_
|
|
81
|
-
|
|
82
|
-
tml_url = _TML_URL_
|
|
83
|
+
|
|
83
84
|
|
|
84
85
|
_SYMBOLS_TYPE_ = {
|
|
85
|
-
"STK": r"\b(Stocks?|Equities?|Shares?)\b",
|
|
86
86
|
"ETF": r"\b(ETFs?)\b",
|
|
87
|
-
"
|
|
87
|
+
"BOND": r"\b(Treasuries?)\b",
|
|
88
88
|
"FX": r"\b(Forex|Exotics?)\b",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
89
|
+
"FUT": r"\b(Futures?|Forwards)\b",
|
|
90
|
+
"STK": r"\b(Stocks?|Equities?|Shares?)\b",
|
|
91
|
+
"IDX": r"\b(?:Indices?|Cash|Index)\b(?!.*\\(?:UKOIL|USOIL))",
|
|
92
|
+
"COMD": r"\b(Commodity|Commodities?|Metals?|Agricultures?|Energies?|OIL|Oil|USOIL|UKOIL)\b",
|
|
91
93
|
"CRYPTO": r"\b(Cryptos?|Cryptocurrencies|Cryptocurrency)\b",
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
_COUNTRY_MAP_ = {
|
|
95
|
-
"USA": r"\b(US)\b",
|
|
97
|
+
"USA": r"\b(US|USA)\b",
|
|
96
98
|
"AUS": r"\b(Australia)\b",
|
|
97
99
|
"BEL": r"\b(Belgium)\b",
|
|
98
100
|
"DNK": r"\b(Denmark)\b",
|
|
@@ -106,6 +108,9 @@ _COUNTRY_MAP_ = {
|
|
|
106
108
|
"SWE": r"\b(Sweden)\b",
|
|
107
109
|
"GBR": r"\b(UK)\b",
|
|
108
110
|
"CHE": r"\b(Switzerland)\b",
|
|
111
|
+
"HKG": r"\b(Hong Kong)\b",
|
|
112
|
+
"IRL": r"\b(Ireland)\b",
|
|
113
|
+
"AUT": r"\b(Austria)\b",
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
AMG_EXCHANGES = {
|
|
@@ -205,6 +210,9 @@ class Broker(object):
|
|
|
205
210
|
def __ne__(self, orther) -> bool:
|
|
206
211
|
return self.name != orther.name
|
|
207
212
|
|
|
213
|
+
def __repr__(self):
|
|
214
|
+
return f"{self.__class__.__name__}({self.name})"
|
|
215
|
+
|
|
208
216
|
|
|
209
217
|
class AdmiralMarktsGroup(Broker):
|
|
210
218
|
def __init__(self, **kwargs):
|
|
@@ -232,6 +240,7 @@ class FTMO(Broker):
|
|
|
232
240
|
def timezone(self) -> str:
|
|
233
241
|
return BROKERS_TIMEZONES["FTMO"]
|
|
234
242
|
|
|
243
|
+
|
|
235
244
|
class XCubeLimited(Broker):
|
|
236
245
|
def __init__(self, **kwargs):
|
|
237
246
|
super().__init__("4xCube Limited", **kwargs)
|
|
@@ -239,7 +248,8 @@ class XCubeLimited(Broker):
|
|
|
239
248
|
@property
|
|
240
249
|
def timezone(self) -> str:
|
|
241
250
|
return BROKERS_TIMEZONES["XCB"]
|
|
242
|
-
|
|
251
|
+
|
|
252
|
+
|
|
243
253
|
class TrinotaMarkets(Broker):
|
|
244
254
|
def __init__(self, **kwargs):
|
|
245
255
|
super().__init__("Trinota Markets (Global) Limited", **kwargs)
|
|
@@ -249,6 +259,15 @@ class TrinotaMarkets(Broker):
|
|
|
249
259
|
return BROKERS_TIMEZONES["TML"]
|
|
250
260
|
|
|
251
261
|
|
|
262
|
+
class PepperstoneGroupLimited(Broker):
|
|
263
|
+
def __init__(self, **kwargs):
|
|
264
|
+
super().__init__("Pepperstone Group Limited", **kwargs)
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def timezone(self) -> str:
|
|
268
|
+
return BROKERS_TIMEZONES["PGL"]
|
|
269
|
+
|
|
270
|
+
|
|
252
271
|
class AMP(Broker): ...
|
|
253
272
|
|
|
254
273
|
|
|
@@ -258,6 +277,7 @@ BROKERS: Dict[str, Broker] = {
|
|
|
258
277
|
"JGM": JustGlobalMarkets(),
|
|
259
278
|
"XCB": XCubeLimited(),
|
|
260
279
|
"TML": TrinotaMarkets(),
|
|
280
|
+
"PGL": PepperstoneGroupLimited(),
|
|
261
281
|
}
|
|
262
282
|
|
|
263
283
|
|
|
@@ -316,8 +336,6 @@ class Account(object):
|
|
|
316
336
|
f"For {supported['AMG'].name}, See [{amg_url}]\n"
|
|
317
337
|
f"For {supported['JGM'].name}, See [{jgm_url}]\n"
|
|
318
338
|
f"For {supported['FTMO'].name}, See [{ftmo_url}]\n"
|
|
319
|
-
f"For {supported['XCB'].name}, See [{xcb_url}]\n"
|
|
320
|
-
f"For {supported['TML'].name}, See [{tml_url}]\n"
|
|
321
339
|
)
|
|
322
340
|
raise InvalidBroker(message=msg)
|
|
323
341
|
|
|
@@ -584,6 +602,7 @@ class Account(object):
|
|
|
584
602
|
- `COMD`: Commodities (e.g., 'CRUDOIL', 'GOLD')
|
|
585
603
|
- `FUT`: Futures (e.g., 'USTNote_U4'),
|
|
586
604
|
- `CRYPTO`: Cryptocurrencies (e.g., 'BTC', 'ETH')
|
|
605
|
+
- `BOND`: Bonds (e.g., 'USTN10YR')
|
|
587
606
|
|
|
588
607
|
check_etf (bool): If True and symbol_type is 'etf', check if the
|
|
589
608
|
ETF description contains 'ETF'.
|
|
@@ -669,6 +688,7 @@ class Account(object):
|
|
|
669
688
|
"COMD": "Commodities",
|
|
670
689
|
"FUT": "Futures",
|
|
671
690
|
"CRYPTO": "Cryptos Assets",
|
|
691
|
+
"BOND": "Bonds",
|
|
672
692
|
}
|
|
673
693
|
print(f"Total {names[symbol_type]}: {len(symbol_list)}")
|
|
674
694
|
|
|
@@ -684,7 +704,7 @@ class Account(object):
|
|
|
684
704
|
|
|
685
705
|
def get_symbol_type(
|
|
686
706
|
self, symbol: str
|
|
687
|
-
) -> Literal["STK", "ETF", "IDX", "FX", "COMD", "FUT", "CRYPTO", "unknown"]:
|
|
707
|
+
) -> Literal["STK", "ETF", "IDX", "FX", "COMD", "FUT", "CRYPTO", "BOND", "unknown"]:
|
|
688
708
|
"""
|
|
689
709
|
Determines the type of a given financial instrument symbol.
|
|
690
710
|
|
|
@@ -692,7 +712,7 @@ class Account(object):
|
|
|
692
712
|
symbol (str): The symbol of the financial instrument (e.g., `GOOGL`, `EURUSD`).
|
|
693
713
|
|
|
694
714
|
Returns:
|
|
695
|
-
Literal["STK", "ETF", "IDX", "FX", "COMD", "FUT", "CRYPTO", "unknown"]:
|
|
715
|
+
Literal["STK", "ETF", "IDX", "FX", "COMD", "FUT", "CRYPTO", "BOND", "unknown"]:
|
|
696
716
|
The type of the financial instrument, one of the following:
|
|
697
717
|
|
|
698
718
|
- `STK`: For Stocks (e.g., `GOOGL`)
|
|
@@ -702,16 +722,27 @@ class Account(object):
|
|
|
702
722
|
- `COMD`: For Commodities (e.g., `CRUDOIL`, `GOLD`)
|
|
703
723
|
- `FUT` : For Futures (e.g., `USTNote_U4`)
|
|
704
724
|
- `CRYPTO`: For Cryptocurrencies (e.g., `BTC`, `ETH`)
|
|
725
|
+
- `BOND`: For Bonds (e.g., `USTN10YR`)
|
|
705
726
|
|
|
706
727
|
Returns `unknown` if the type cannot be determined.
|
|
707
728
|
"""
|
|
708
729
|
|
|
709
730
|
patterns = _SYMBOLS_TYPE_
|
|
710
731
|
info = self.get_symbol_info(symbol)
|
|
732
|
+
indices = self.get_symbols(symbol_type="IDX")
|
|
733
|
+
commodity = self.get_symbols(symbol_type="COMD")
|
|
711
734
|
if info is not None:
|
|
712
735
|
for symbol_type, pattern in patterns.items():
|
|
713
|
-
|
|
714
|
-
|
|
736
|
+
if (
|
|
737
|
+
symbol_type in ["IDX", "COMD"]
|
|
738
|
+
and self.broker == PepperstoneGroupLimited()
|
|
739
|
+
and info.name.endswith("-F")
|
|
740
|
+
and info.name in indices + commodity
|
|
741
|
+
):
|
|
742
|
+
symbol_type = "FUT"
|
|
743
|
+
pattern = r"\b(Forwards?)\b"
|
|
744
|
+
search = re.compile(pattern)
|
|
745
|
+
if re.search(search, info.path):
|
|
715
746
|
return symbol_type
|
|
716
747
|
return "unknown"
|
|
717
748
|
|
|
@@ -733,14 +764,15 @@ class Account(object):
|
|
|
733
764
|
return symbol_list
|
|
734
765
|
|
|
735
766
|
def get_fx_symbols(
|
|
736
|
-
self,
|
|
767
|
+
self,
|
|
768
|
+
category: Literal["majors", "minors", "exotics", "crosses", "ndfs"] = "majors",
|
|
737
769
|
) -> List[str]:
|
|
738
770
|
"""
|
|
739
771
|
Retrieves a list of forex symbols belonging to a specific category.
|
|
740
772
|
|
|
741
773
|
Args:
|
|
742
774
|
category (str, optional): The category of forex symbols to retrieve.
|
|
743
|
-
Possible values are 'majors', 'minors', 'exotics'.
|
|
775
|
+
Possible values are 'majors', 'minors', 'exotics', 'crosses', 'ndfs'.
|
|
744
776
|
Defaults to 'majors'.
|
|
745
777
|
|
|
746
778
|
Returns:
|
|
@@ -750,20 +782,22 @@ class Account(object):
|
|
|
750
782
|
ValueError: If an unsupported category is provided.
|
|
751
783
|
|
|
752
784
|
Notes:
|
|
753
|
-
This mthods works primarly with Admirals Group AS products,
|
|
785
|
+
This mthods works primarly with Admirals Group AS products and Pepperstone Group Limited,
|
|
754
786
|
For other brokers use `get_symbols()` or this method will use it by default.
|
|
755
787
|
"""
|
|
756
|
-
if self.broker
|
|
788
|
+
if self.broker not in [AdmiralMarktsGroup(), PepperstoneGroupLimited()]:
|
|
757
789
|
return self.get_symbols(symbol_type="FX")
|
|
758
790
|
else:
|
|
759
791
|
fx_categories = {
|
|
760
792
|
"majors": r"\b(Majors?)\b",
|
|
761
793
|
"minors": r"\b(Minors?)\b",
|
|
762
794
|
"exotics": r"\b(Exotics?)\b",
|
|
795
|
+
"crosses": r"\b(Crosses?)\b",
|
|
796
|
+
"ndfs": r"\b(NDFs?)\b",
|
|
763
797
|
}
|
|
764
798
|
return self._get_symbols_by_category("FX", category, fx_categories)
|
|
765
799
|
|
|
766
|
-
def get_stocks_from_country(self, country_code: str = "USA", etf=
|
|
800
|
+
def get_stocks_from_country(self, country_code: str = "USA", etf=False) -> List[str]:
|
|
767
801
|
"""
|
|
768
802
|
Retrieves a list of stock symbols from a specific country.
|
|
769
803
|
|
|
@@ -782,6 +816,9 @@ class Account(object):
|
|
|
782
816
|
* **United Kingdom:** GBR
|
|
783
817
|
* **United States:** USA
|
|
784
818
|
* **Switzerland:** CHE
|
|
819
|
+
* **Hong Kong:** HKG
|
|
820
|
+
* **Ireland:** IRL
|
|
821
|
+
* **Austria:** AUT
|
|
785
822
|
|
|
786
823
|
Args:
|
|
787
824
|
country (str, optional): The country code of stocks to retrieve.
|
|
@@ -794,11 +831,11 @@ class Account(object):
|
|
|
794
831
|
ValueError: If an unsupported country is provided.
|
|
795
832
|
|
|
796
833
|
Notes:
|
|
797
|
-
This mthods works primarly with Admirals Group AS products,
|
|
834
|
+
This mthods works primarly with Admirals Group AS products and Pepperstone Group Limited,
|
|
798
835
|
For other brokers use `get_symbols()` or this method will use it by default.
|
|
799
836
|
"""
|
|
800
837
|
|
|
801
|
-
if self.broker
|
|
838
|
+
if self.broker not in [AdmiralMarktsGroup(), PepperstoneGroupLimited()]:
|
|
802
839
|
stocks = self.get_symbols(symbol_type="STK")
|
|
803
840
|
return stocks
|
|
804
841
|
else:
|
bbstrader/metatrader/risk.py
CHANGED
|
@@ -11,7 +11,6 @@ from bbstrader.metatrader.utils import TIMEFRAMES, TimeFrame
|
|
|
11
11
|
|
|
12
12
|
_COMMD_SUPPORTED_ = [
|
|
13
13
|
"GOLD",
|
|
14
|
-
"XAUEUR",
|
|
15
14
|
"SILVER",
|
|
16
15
|
"BRENT",
|
|
17
16
|
"CRUDOIL",
|
|
@@ -19,11 +18,17 @@ _COMMD_SUPPORTED_ = [
|
|
|
19
18
|
"UKOIL",
|
|
20
19
|
"XAGEUR",
|
|
21
20
|
"XAGUSD",
|
|
21
|
+
"XAGAUD",
|
|
22
|
+
"XAGGBP",
|
|
22
23
|
"XAUAUD",
|
|
23
24
|
"XAUEUR",
|
|
24
25
|
"XAUUSD",
|
|
25
26
|
"XAUGBP",
|
|
26
27
|
"USOIL",
|
|
28
|
+
"SpotCrude",
|
|
29
|
+
"SpotBrent",
|
|
30
|
+
"NatGas",
|
|
31
|
+
"Soybeans",
|
|
27
32
|
]
|
|
28
33
|
|
|
29
34
|
_ADMIRAL_MARKETS_FUTURES_ = [
|
|
@@ -54,6 +59,27 @@ _ADMIRAL_MARKETS_FUTURES_ = [
|
|
|
54
59
|
"_HSCEI50_",
|
|
55
60
|
]
|
|
56
61
|
|
|
62
|
+
__PEPPERSTONE_FUTURES__ = [
|
|
63
|
+
"AUS200-F",
|
|
64
|
+
"GER40-F",
|
|
65
|
+
"HK50-F",
|
|
66
|
+
"JPN225-F",
|
|
67
|
+
"UK100-F",
|
|
68
|
+
"US30-F",
|
|
69
|
+
"NAS100-F",
|
|
70
|
+
"US500-F",
|
|
71
|
+
"Crude-F",
|
|
72
|
+
"Brent-F",
|
|
73
|
+
"XAUUSD-F",
|
|
74
|
+
"XAGUSD-F",
|
|
75
|
+
"USDX-F",
|
|
76
|
+
"EUSTX50-F",
|
|
77
|
+
"FRA40-F",
|
|
78
|
+
"GERTEC30-F",
|
|
79
|
+
"SPA35-F",
|
|
80
|
+
"SWI20-F",
|
|
81
|
+
]
|
|
82
|
+
|
|
57
83
|
__all__ = ["RiskManagement"]
|
|
58
84
|
|
|
59
85
|
|
|
@@ -303,7 +329,9 @@ class RiskManagement(Account):
|
|
|
303
329
|
tf_int = self._convert_time_frame(self._tf)
|
|
304
330
|
interval = round((minutes / tf_int) * 252)
|
|
305
331
|
|
|
306
|
-
rate = Rates(
|
|
332
|
+
rate = Rates(
|
|
333
|
+
self.symbol, timeframe=self._tf, start_pos=0, count=interval, **self.kwargs
|
|
334
|
+
)
|
|
307
335
|
returns = rate.returns * 100
|
|
308
336
|
std = returns.std()
|
|
309
337
|
point = self.get_symbol_info(self.symbol).point
|
|
@@ -355,7 +383,9 @@ class RiskManagement(Account):
|
|
|
355
383
|
tf_int = self._convert_time_frame(tf)
|
|
356
384
|
interval = round((minutes / tf_int) * 252)
|
|
357
385
|
|
|
358
|
-
rate = Rates(
|
|
386
|
+
rate = Rates(
|
|
387
|
+
self.symbol, timeframe=tf, start_pos=0, count=interval, **self.kwargs
|
|
388
|
+
)
|
|
359
389
|
returns = rate.returns * 100
|
|
360
390
|
p = self.get_account_info().margin_free
|
|
361
391
|
mu = returns.mean()
|
|
@@ -461,7 +491,7 @@ class RiskManagement(Account):
|
|
|
461
491
|
symbol = self.symbol.split(".")[0]
|
|
462
492
|
elif self.symbol.endswith("xx"):
|
|
463
493
|
symbol = self.symbol[:-2]
|
|
464
|
-
elif self.symbol.endswith(
|
|
494
|
+
elif self.symbol.endswith("-"):
|
|
465
495
|
symbol = self.symbol[:-1]
|
|
466
496
|
else:
|
|
467
497
|
symbol = self.symbol
|
|
@@ -471,8 +501,12 @@ class RiskManagement(Account):
|
|
|
471
501
|
f"Supported commodity symbols are: {', '.join(supported)}"
|
|
472
502
|
)
|
|
473
503
|
if FUT:
|
|
474
|
-
|
|
475
|
-
|
|
504
|
+
if "_" in self.symbol:
|
|
505
|
+
symbol = self.symbol[:-2]
|
|
506
|
+
else:
|
|
507
|
+
symbol = self.symbol
|
|
508
|
+
supported = _ADMIRAL_MARKETS_FUTURES_ + __PEPPERSTONE_FUTURES__
|
|
509
|
+
if str(symbol) not in supported:
|
|
476
510
|
raise ValueError(
|
|
477
511
|
f"Currency risk calculation for '{self.symbol}' is not a currently supported. \n"
|
|
478
512
|
f"Supported future symbols are: {', '.join(supported)}"
|
bbstrader/metatrader/trade.py
CHANGED
|
@@ -129,7 +129,7 @@ class Trade(RiskManagement):
|
|
|
129
129
|
- sl
|
|
130
130
|
- tp
|
|
131
131
|
- be
|
|
132
|
-
See the RiskManagement class for more details on these parameters.
|
|
132
|
+
See the ``bbstrader.metatrader.risk.RiskManagement`` class for more details on these parameters.
|
|
133
133
|
See `bbstrader.metatrader.account.check_mt5_connection()` for more details on how to connect to MT5 terminal.
|
|
134
134
|
"""
|
|
135
135
|
# Call the parent class constructor first
|
|
@@ -604,6 +604,11 @@ class Trade(RiskManagement):
|
|
|
604
604
|
result = self.send_order(request)
|
|
605
605
|
if result.retcode == Mt5.TRADE_RETCODE_DONE:
|
|
606
606
|
break
|
|
607
|
+
elif result.retcode == Mt5.TRADE_RETCODE_INVALID_VOLUME: #10014
|
|
608
|
+
new_volume = int(request["volume"])
|
|
609
|
+
if new_volume >= 1:
|
|
610
|
+
request["volume"] = new_volume
|
|
611
|
+
result = self.send_order(request)
|
|
607
612
|
elif result.retcode not in self._retcodes:
|
|
608
613
|
self._retcodes.append(result.retcode)
|
|
609
614
|
msg = trade_retcode_message(result.retcode)
|
|
@@ -644,25 +649,26 @@ class Trade(RiskManagement):
|
|
|
644
649
|
if type == "BMKT" or type == "SMKT":
|
|
645
650
|
self.opened_positions.append(result.order)
|
|
646
651
|
positions = self.get_positions(symbol=self.symbol)
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if position.
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
652
|
+
if positions is not None:
|
|
653
|
+
for position in positions:
|
|
654
|
+
if position.ticket == result.order:
|
|
655
|
+
if position.type == 0:
|
|
656
|
+
order_type = "BUY"
|
|
657
|
+
self.buy_positions.append(position.ticket)
|
|
658
|
+
else:
|
|
659
|
+
order_type = "SELL"
|
|
660
|
+
self.sell_positions.append(position.ticket)
|
|
661
|
+
profit = round(self.get_account_info().profit, 5)
|
|
662
|
+
order_info = (
|
|
663
|
+
f"2. {order_type} Position Opened, Symbol: {self.symbol}, Price: @{round(position.price_open,5)}, "
|
|
664
|
+
f"Sl: @{position.sl} Tp: @{position.tp}"
|
|
665
|
+
)
|
|
666
|
+
self.logger.info(order_info)
|
|
667
|
+
pos_info = (
|
|
668
|
+
f"3. [OPEN POSITIONS ON {self.symbol} = {len(positions)}, ACCOUNT OPEN PnL = {profit} "
|
|
669
|
+
f"{self.get_account_info().currency}]\n"
|
|
670
|
+
)
|
|
671
|
+
self.logger.info(pos_info)
|
|
666
672
|
else:
|
|
667
673
|
msg = trade_retcode_message(result.retcode)
|
|
668
674
|
self.logger.error(
|
|
@@ -1049,7 +1055,10 @@ class Trade(RiskManagement):
|
|
|
1049
1055
|
spread = self.get_symbol_info(self.symbol).spread
|
|
1050
1056
|
fees = self.get_stats()[0]["average_fee"] * -1
|
|
1051
1057
|
risk = self.currency_risk()["trade_profit"]
|
|
1052
|
-
|
|
1058
|
+
try:
|
|
1059
|
+
fees_points = round((fees / risk), 3)
|
|
1060
|
+
except ZeroDivisionError:
|
|
1061
|
+
fees_points = 0
|
|
1053
1062
|
# If Buy
|
|
1054
1063
|
if position.type == 0 and position.price_current > position.price_open:
|
|
1055
1064
|
# Calculate the break-even level and price
|
|
@@ -1156,7 +1165,10 @@ class Trade(RiskManagement):
|
|
|
1156
1165
|
point = self.get_symbol_info(self.symbol).point
|
|
1157
1166
|
fees = self.get_stats()[0]["average_fee"] * -1
|
|
1158
1167
|
risk = self.currency_risk()["trade_profit"]
|
|
1159
|
-
|
|
1168
|
+
try:
|
|
1169
|
+
min_be = round((fees / risk)) + 2
|
|
1170
|
+
except ZeroDivisionError:
|
|
1171
|
+
min_be = self.symbol_info(self.symbol).spread
|
|
1160
1172
|
be = self.get_break_even()
|
|
1161
1173
|
if th is not None:
|
|
1162
1174
|
win_be = th
|
|
@@ -1184,7 +1196,7 @@ class Trade(RiskManagement):
|
|
|
1184
1196
|
# The first one is the opening order
|
|
1185
1197
|
# The second is the closing order
|
|
1186
1198
|
history = self.get_trades_history(position=position, to_df=False)
|
|
1187
|
-
if len(history) == 2:
|
|
1199
|
+
if history is not None and len(history) == 2:
|
|
1188
1200
|
profit += history[1].profit
|
|
1189
1201
|
commission += history[0].commission
|
|
1190
1202
|
swap += history[0].swap
|
|
@@ -1455,7 +1467,7 @@ class Trade(RiskManagement):
|
|
|
1455
1467
|
for position in self.opened_positions:
|
|
1456
1468
|
time.sleep(0.1)
|
|
1457
1469
|
history = self.get_trades_history(position=position, to_df=False)
|
|
1458
|
-
if len(history) == 2:
|
|
1470
|
+
if history is not None and len(history) == 2:
|
|
1459
1471
|
result = history[1].profit
|
|
1460
1472
|
comm = history[0].commission
|
|
1461
1473
|
swap = history[0].swap
|
bbstrader/trading/execution.py
CHANGED
|
@@ -110,12 +110,13 @@ def _mt5_execution(
|
|
|
110
110
|
notify = kwargs.get("notify", False)
|
|
111
111
|
signal_tickers = kwargs.get("signal_tickers", symbols)
|
|
112
112
|
debug_mode = kwargs.get("debug_mode", False)
|
|
113
|
+
delay = kwargs.get("delay", 0)
|
|
113
114
|
if notify:
|
|
114
115
|
telegram = kwargs.get("telegram", False)
|
|
115
116
|
bot_token = kwargs.get("bot_token")
|
|
116
117
|
chat_id = kwargs.get("chat_id")
|
|
117
118
|
|
|
118
|
-
expert_ids = kwargs.get("
|
|
119
|
+
expert_ids = kwargs.get("expert_ids")
|
|
119
120
|
if expert_ids is None:
|
|
120
121
|
expert_ids = list(
|
|
121
122
|
set([trade.expert_id for trade in trades_instances.values()])
|
|
@@ -192,8 +193,9 @@ def _mt5_execution(
|
|
|
192
193
|
symbol_type = account.get_symbol_type(symbol)
|
|
193
194
|
desc = account.get_symbol_info(symbol).description
|
|
194
195
|
sigmsg = (
|
|
195
|
-
f"SIGNAL = {signal},
|
|
196
|
-
f"
|
|
196
|
+
f"SIGNAL = {signal}, \nSYMBOL={symbol}, \nTYPE={symbol_type}, \nDESCRIPTION={desc}, "
|
|
197
|
+
f"\nPRICE={price}, \nSTOPLIMIT={stoplimit}, \nSTRATEGY={STRATEGY}, \nTIMEFRAME={time_frame}"
|
|
198
|
+
f"\nBROKER={account.broker.name}, \nTIMESTAMP={datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
197
199
|
)
|
|
198
200
|
msg = f"Sending {signal} Order ... SYMBOL={symbol}, STRATEGY={STRATEGY}"
|
|
199
201
|
tfmsg = f"Time Frame Not completed !!! SYMBOL={symbol}, STRATEGY={STRATEGY}"
|
|
@@ -399,10 +401,11 @@ def _mt5_execution(
|
|
|
399
401
|
elif trade_time % iter_time == 0:
|
|
400
402
|
time_intervals += iter_time
|
|
401
403
|
else:
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
if use_trade_time:
|
|
405
|
+
raise ValueError(
|
|
406
|
+
f"iter_time must be a multiple of the {time_frame} !!!"
|
|
407
|
+
f"(e.g., if time_frame is 15m, iter_time must be 1.5, 3, 5, 15 etc)"
|
|
408
|
+
)
|
|
406
409
|
try:
|
|
407
410
|
FRIDAY = "friday"
|
|
408
411
|
check_mt5_connection(**kwargs)
|
|
@@ -449,15 +452,15 @@ def _mt5_execution(
|
|
|
449
452
|
period_end_action == "sleep" and today != FRIDAY or not closing
|
|
450
453
|
):
|
|
451
454
|
sleep_time = trades_instances[symbols[-1]].sleep_time()
|
|
452
|
-
sleepmsg(sleep_time)
|
|
453
|
-
time.sleep(60 * sleep_time)
|
|
455
|
+
sleepmsg(sleep_time + delay)
|
|
456
|
+
time.sleep(60 * sleep_time + delay)
|
|
454
457
|
logger.info(sessionmsg)
|
|
455
458
|
elif period_end_action == "sleep" and today == FRIDAY:
|
|
456
459
|
sleep_time = trades_instances[symbols[-1]].sleep_time(
|
|
457
460
|
weekend=True
|
|
458
461
|
)
|
|
459
|
-
sleepmsg(sleep_time)
|
|
460
|
-
time.sleep(60 * sleep_time)
|
|
462
|
+
sleepmsg(sleep_time + delay)
|
|
463
|
+
time.sleep(60 * sleep_time + delay)
|
|
461
464
|
logger.info(sessionmsg)
|
|
462
465
|
|
|
463
466
|
elif period.lower() == "week":
|
|
@@ -476,8 +479,8 @@ def _mt5_execution(
|
|
|
476
479
|
|
|
477
480
|
if day_end and today != FRIDAY:
|
|
478
481
|
sleep_time = trades_instances[symbols[-1]].sleep_time()
|
|
479
|
-
sleepmsg(sleep_time)
|
|
480
|
-
time.sleep(60 * sleep_time)
|
|
482
|
+
sleepmsg(sleep_time + delay)
|
|
483
|
+
time.sleep(60 * sleep_time + delay)
|
|
481
484
|
logger.info(sessionmsg)
|
|
482
485
|
elif day_end and today == FRIDAY:
|
|
483
486
|
strategy.perform_period_end_checks()
|
|
@@ -487,8 +490,8 @@ def _mt5_execution(
|
|
|
487
490
|
sleep_time = trades_instances[symbols[-1]].sleep_time(
|
|
488
491
|
weekend=True
|
|
489
492
|
)
|
|
490
|
-
sleepmsg(sleep_time)
|
|
491
|
-
time.sleep(60 * sleep_time)
|
|
493
|
+
sleepmsg(sleep_time + delay)
|
|
494
|
+
time.sleep(60 * sleep_time + delay)
|
|
492
495
|
logger.info(sessionmsg)
|
|
493
496
|
|
|
494
497
|
elif period.lower() == "month":
|
|
@@ -501,7 +504,7 @@ def _mt5_execution(
|
|
|
501
504
|
elif (
|
|
502
505
|
trade.days_end()
|
|
503
506
|
and today == FRIDAY
|
|
504
|
-
and num_days
|
|
507
|
+
and num_days >= 20
|
|
505
508
|
) and closing:
|
|
506
509
|
for id in expert_ids:
|
|
507
510
|
trade.close_positions(
|
|
@@ -511,17 +514,17 @@ def _mt5_execution(
|
|
|
511
514
|
trade.statistics(save=True)
|
|
512
515
|
if day_end and today != FRIDAY:
|
|
513
516
|
sleep_time = trades_instances[symbols[-1]].sleep_time()
|
|
514
|
-
sleepmsg(sleep_time)
|
|
515
|
-
time.sleep(60 * sleep_time)
|
|
517
|
+
sleepmsg(sleep_time + delay)
|
|
518
|
+
time.sleep(60 * sleep_time + delay)
|
|
516
519
|
logger.info(sessionmsg)
|
|
517
520
|
num_days += 1
|
|
518
521
|
elif day_end and today == FRIDAY:
|
|
519
522
|
sleep_time = trades_instances[symbols[-1]].sleep_time(weekend=True)
|
|
520
|
-
sleepmsg(sleep_time)
|
|
521
|
-
time.sleep(60 * sleep_time)
|
|
523
|
+
sleepmsg(sleep_time + delay)
|
|
524
|
+
time.sleep(60 * sleep_time + delay)
|
|
522
525
|
logger.info(sessionmsg)
|
|
523
526
|
num_days += 1
|
|
524
|
-
elif day_end and today == FRIDAY and num_days
|
|
527
|
+
elif day_end and today == FRIDAY and num_days >= 20:
|
|
525
528
|
strategy.perform_period_end_checks()
|
|
526
529
|
break
|
|
527
530
|
except Exception:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: bbstrader
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.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/
|
|
@@ -24,7 +24,7 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: pandas
|
|
26
26
|
Requires-Dist: pandas_ta
|
|
27
|
-
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: numpy<2.0.0
|
|
28
28
|
Requires-Dist: yfinance
|
|
29
29
|
Requires-Dist: scipy
|
|
30
30
|
Requires-Dist: hmmlearn
|
|
@@ -74,6 +74,13 @@ Dynamic: summary
|
|
|
74
74
|

|
|
75
75
|
|
|
76
76
|
[](https://bbstrader.readthedocs.io/en/latest/?badge=latest)
|
|
77
|
+
[](https://pypi.org/project/bbstrader/)
|
|
78
|
+
[](https://pypi.python.org/pypi/bbstrader)
|
|
79
|
+
[](https://pypi.org/project/bbstrader/)
|
|
80
|
+
[](https://pepy.tech/projects/bbstrader)
|
|
81
|
+
[](https://www.codefactor.io/repository/github/bbalouki/bbstrader)
|
|
82
|
+
[](https://www.linkedin.com/in/bertin-balouki-simyeli-15b17a1a6/)
|
|
83
|
+
[](https://paypal.me/bertinbalouki?country.x=SN&locale.x=en_US)
|
|
77
84
|
|
|
78
85
|
[Dcoumentation](https://bbstrader.readthedocs.io/en/latest/index.html)
|
|
79
86
|
|
|
@@ -86,7 +93,7 @@ BBSTrader is a trading system suite developed for MetaTrader 5 (MT5) and IBKR pl
|
|
|
86
93
|
- **Backtesting Module (btengine)** : Enables traders to rigorously test their trading strategies using historical data to evaluate performance before live deployment.
|
|
87
94
|
- **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.
|
|
88
95
|
- **MetaTrader5 Module (metatrader)**: Facilitates the direct execution of trading strategies on the MetaTrader 5 platform, supporting real-time trading across multiple financial instruments.
|
|
89
|
-
- **
|
|
96
|
+
- **Models Module**: Serves as a framework for implementing various types of financial models (risk managment models, Machine learing models etc).
|
|
90
97
|
- **Time serie Module (tseries)** designed for conducting advanced time series analysis in financial markets.
|
|
91
98
|
It leverages statistical models and algorithms to perform tasks such as cointegration testing, volatility modeling, and filter-based estimation to assist in trading strategy development, market analysis, and financial data exploration.
|
|
92
99
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
bbstrader/__ini__.py,sha256=
|
|
2
|
-
bbstrader/config.py,sha256=
|
|
1
|
+
bbstrader/__ini__.py,sha256=x9sw2BKGnPi3QF4MwBqJpCpyUgui1StbvBWGNISVAhQ,548
|
|
2
|
+
bbstrader/config.py,sha256=USwdS5qaGbc1Wp5rF0ckP3R1HEZJ8tY0tkZX4CkgNoc,4204
|
|
3
3
|
bbstrader/tseries.py,sha256=GYNDo03dYEnYHwcQYKpclNDGCwvZ_qAPyou0vhQndS0,69851
|
|
4
4
|
bbstrader/btengine/__init__.py,sha256=FL0kC0NcsnlTH-yuTv4lu6AexY1wZKN1AQ9rv9MZagQ,3009
|
|
5
5
|
bbstrader/btengine/backtest.py,sha256=ZzGhoN-_g0cF-OCyk173imze2OXEhykHTUiJ9MowDO8,14582
|
|
6
6
|
bbstrader/btengine/data.py,sha256=qVDk-cIgtWAeBM5eW08j9MFXBzu4yeWChoFIL9WINYI,26899
|
|
7
7
|
bbstrader/btengine/event.py,sha256=38mhZH9d53C4x7bZER2B0O6M18txzS4u7zveKyxeP5Y,8603
|
|
8
|
-
bbstrader/btengine/execution.py,sha256=
|
|
8
|
+
bbstrader/btengine/execution.py,sha256=6YfErbqJx2DTy6r4cfZLU8F1YsJG-p8jEhNepdb9Sxc,10376
|
|
9
9
|
bbstrader/btengine/performance.py,sha256=0meGbMFYzzI9n_09qf4RFpdyqQmCa6C_iu6PvE2POIE,10787
|
|
10
10
|
bbstrader/btengine/portfolio.py,sha256=M97FONcdZRoSKFFF66LRsO8_KLXr128AI5-LULhMiKk,16167
|
|
11
|
-
bbstrader/btengine/strategy.py,sha256=
|
|
11
|
+
bbstrader/btengine/strategy.py,sha256=EiE1P_V0wlFIsA-2U5POQ7qtRMkr1OTH1pQia4jBcGY,32937
|
|
12
12
|
bbstrader/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
bbstrader/core/data.py,sha256=3_odj9jxokyKU15j1aHTlgLQDjW75tKqGpCUfkpYq2Q,452
|
|
14
14
|
bbstrader/core/utils.py,sha256=oB4OC0tQDJ1FIaJCuNWUlTzOccHoACSJsP_f7ELrqXQ,1448
|
|
15
15
|
bbstrader/ibkr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
bbstrader/ibkr/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
bbstrader/metatrader/__init__.py,sha256=rrL_EtecsOCD2Cwhmpgta5CjSGT0K6vzSBiQoyCLe3M,283
|
|
18
|
-
bbstrader/metatrader/account.py,sha256=
|
|
18
|
+
bbstrader/metatrader/account.py,sha256=Le46-lFqYm4VGQn3nPl1DUVCuiuxNi3EVf56kF8gA5Y,58221
|
|
19
19
|
bbstrader/metatrader/rates.py,sha256=12hNcjRSKcOSjBAhDvpakqAiLaI0ndU6oBhCTIWDzr0,21089
|
|
20
|
-
bbstrader/metatrader/risk.py,sha256=
|
|
21
|
-
bbstrader/metatrader/trade.py,sha256=
|
|
20
|
+
bbstrader/metatrader/risk.py,sha256=H8Iz93g3EVnb9bOdi4RSFF9JN1CY-IutYHs9-epBTpQ,27579
|
|
21
|
+
bbstrader/metatrader/trade.py,sha256=k6IeKaC83sH_th9uNxeEIlNBzVoSzK1J-0jKIDIA7rc,73639
|
|
22
22
|
bbstrader/metatrader/utils.py,sha256=UnwWmmfgY-Cw1V0vL14ehuWr9AhjcJMtVZK8k19b0i4,17672
|
|
23
23
|
bbstrader/models/__init__.py,sha256=SnGBMQ-zcUIpms3oNeqg7EVDFpg-7OPjNAD8kvi_Q84,508
|
|
24
24
|
bbstrader/models/factors.py,sha256=dWuXh83hLkwxUp3XwjgUl-r3_cjVcV_s0aFRlSLIfo8,13332
|
|
@@ -27,11 +27,11 @@ bbstrader/models/optimization.py,sha256=gp0n9a9vwbUldaNiZUYry_4RP2NW0VFZ2k5NoOkz
|
|
|
27
27
|
bbstrader/models/portfolio.py,sha256=-Zq9cmzyOZUlGq9RWfAxClpX0KJZqYZYpc5EGNTcPGI,8302
|
|
28
28
|
bbstrader/models/risk.py,sha256=IFQoHXxpBwJiifANRgwyAUOp7EgTWBAhfJFCO1sGR3g,15405
|
|
29
29
|
bbstrader/trading/__init__.py,sha256=2VoxbzfP1XBLVuxJtjRhjEBCtnv9HqwQzfMV4B5mM7M,468
|
|
30
|
-
bbstrader/trading/execution.py,sha256=
|
|
30
|
+
bbstrader/trading/execution.py,sha256=fFywdEJXtVSSxKNGTpRnFHiprTI1jmDNd94Ox1F1aU4,30999
|
|
31
31
|
bbstrader/trading/scripts.py,sha256=pNwHr-3mW87G5fyIMd93wS43NkzOZn4npt4fLNnSUyk,1922
|
|
32
32
|
bbstrader/trading/strategies.py,sha256=rMvLIhX_8MQg7_Lbo127UqdTRxBUof2m3jgRQTm55p0,37019
|
|
33
|
-
bbstrader-0.2.
|
|
34
|
-
bbstrader-0.2.
|
|
35
|
-
bbstrader-0.2.
|
|
36
|
-
bbstrader-0.2.
|
|
37
|
-
bbstrader-0.2.
|
|
33
|
+
bbstrader-0.2.7.dist-info/LICENSE,sha256=P3PBO9RuYPzl6-PkjysTNnwmwMB64ph36Bz9DBj8MS4,1115
|
|
34
|
+
bbstrader-0.2.7.dist-info/METADATA,sha256=Ce_IeZ9fc-yb5dGN3pmq60woLrcos_WBi3T_Tras30k,11271
|
|
35
|
+
bbstrader-0.2.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
36
|
+
bbstrader-0.2.7.dist-info/top_level.txt,sha256=Wwj322jZmxGZ6gD_TdaPiPLjED5ReObm5omerwlmZIg,10
|
|
37
|
+
bbstrader-0.2.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|