bbstrader 0.3.1__py3-none-any.whl → 0.3.2__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/__init__.py +1 -1
- bbstrader/__main__.py +2 -0
- bbstrader/btengine/backtest.py +7 -8
- bbstrader/btengine/execution.py +2 -2
- bbstrader/btengine/strategy.py +68 -17
- bbstrader/config.py +2 -2
- bbstrader/metatrader/account.py +77 -6
- bbstrader/metatrader/copier.py +530 -207
- bbstrader/metatrader/risk.py +1 -0
- bbstrader/metatrader/scripts.py +35 -9
- bbstrader/metatrader/trade.py +58 -41
- bbstrader/metatrader/utils.py +2 -0
- bbstrader/models/__init__.py +0 -1
- bbstrader/models/ml.py +55 -26
- bbstrader/models/nlp.py +145 -80
- bbstrader/models/optimization.py +1 -1
- bbstrader/models/risk.py +16 -386
- bbstrader/trading/execution.py +20 -12
- bbstrader/trading/strategies.py +9 -592
- bbstrader/tseries.py +39 -709
- {bbstrader-0.3.1.dist-info → bbstrader-0.3.2.dist-info}/METADATA +35 -40
- bbstrader-0.3.2.dist-info/RECORD +47 -0
- bbstrader-0.3.1.dist-info/RECORD +0 -47
- {bbstrader-0.3.1.dist-info → bbstrader-0.3.2.dist-info}/WHEEL +0 -0
- {bbstrader-0.3.1.dist-info → bbstrader-0.3.2.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.3.1.dist-info → bbstrader-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {bbstrader-0.3.1.dist-info → bbstrader-0.3.2.dist-info}/top_level.txt +0 -0
bbstrader/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ __author__ = "Bertin Balouki SIMYELI"
|
|
|
7
7
|
__copyright__ = "2023-2025 Bertin Balouki SIMYELI"
|
|
8
8
|
__email__ = "bertin@bbstrader.com"
|
|
9
9
|
__license__ = "MIT"
|
|
10
|
-
__version__ = "0.3.
|
|
10
|
+
__version__ = "0.3.2"
|
|
11
11
|
|
|
12
12
|
from bbstrader import compat # noqa: F401
|
|
13
13
|
from bbstrader import core # noqa: F401
|
bbstrader/__main__.py
CHANGED
bbstrader/btengine/backtest.py
CHANGED
|
@@ -248,14 +248,13 @@ def run_backtest(
|
|
|
248
248
|
|
|
249
249
|
start_date (datetime): Start date of the backtest.
|
|
250
250
|
|
|
251
|
-
data_handler (DataHandler):
|
|
251
|
+
data_handler (DataHandler): A subclass of the `DataHandler` class, responsible for managing
|
|
252
252
|
and processing market data. Available options include `CSVDataHandler`,
|
|
253
|
-
`MT5DataHandler`, and `YFDataHandler`.
|
|
254
|
-
instance is initialized before passing it to the function.
|
|
253
|
+
`MT5DataHandler`, and `YFDataHandler`.
|
|
255
254
|
|
|
256
255
|
strategy (Strategy): The trading strategy to be employed during the backtest.
|
|
257
|
-
The strategy must be
|
|
258
|
-
- `bars` (DataHandler): The `DataHandler`
|
|
256
|
+
The strategy must be a subclass of `Strategy` and should include the following attributes:
|
|
257
|
+
- `bars` (DataHandler): The `DataHandler` class for the strategy.
|
|
259
258
|
- `events` (Queue): Queue instance for managing events.
|
|
260
259
|
- `symbol_list` (List[str]): List of symbols to trade.
|
|
261
260
|
- `mode` (str): 'live' or 'backtest'.
|
|
@@ -307,9 +306,9 @@ def run_backtest(
|
|
|
307
306
|
>>> run_backtest(
|
|
308
307
|
... symbol_list=symbol_list,
|
|
309
308
|
... start_date=start,
|
|
310
|
-
... data_handler=MT5DataHandler
|
|
311
|
-
... strategy=StockIndexSTBOTrading
|
|
312
|
-
... exc_handler=MT5ExecutionHandler
|
|
309
|
+
... data_handler=MT5DataHandler,
|
|
310
|
+
... strategy=StockIndexSTBOTrading,
|
|
311
|
+
... exc_handler=MT5ExecutionHandler,
|
|
313
312
|
... initial_capital=100000.0,
|
|
314
313
|
... heartbeat=0.0,
|
|
315
314
|
... **kwargs
|
bbstrader/btengine/execution.py
CHANGED
|
@@ -98,7 +98,7 @@ class SimExecutionHandler(ExecutionHandler):
|
|
|
98
98
|
self.events.put(fill_event)
|
|
99
99
|
self.logger.info(
|
|
100
100
|
f"{event.direction} ORDER FILLED: SYMBOL={event.symbol}, "
|
|
101
|
-
f"QUANTITY={event.quantity}, PRICE @{event.price} EXCHANGE={fill_event.exchange}",
|
|
101
|
+
f"QUANTITY={event.quantity}, PRICE @{round(event.price, 5)} EXCHANGE={fill_event.exchange}",
|
|
102
102
|
custom_time=fill_event.timeindex,
|
|
103
103
|
)
|
|
104
104
|
|
|
@@ -264,7 +264,7 @@ class MT5ExecutionHandler(ExecutionHandler):
|
|
|
264
264
|
self.events.put(fill_event)
|
|
265
265
|
self.logger.info(
|
|
266
266
|
f"{direction} ORDER FILLED: SYMBOL={symbol}, QUANTITY={quantity}, "
|
|
267
|
-
f"PRICE @{price} EXCHANGE={fill_event.exchange}",
|
|
267
|
+
f"PRICE @{round(event.price, 5)} EXCHANGE={fill_event.exchange}",
|
|
268
268
|
custom_time=fill_event.timeindex,
|
|
269
269
|
)
|
|
270
270
|
|
bbstrader/btengine/strategy.py
CHANGED
|
@@ -12,17 +12,18 @@ from loguru import logger
|
|
|
12
12
|
from bbstrader.btengine.data import DataHandler
|
|
13
13
|
from bbstrader.btengine.event import Events, FillEvent, SignalEvent
|
|
14
14
|
from bbstrader.config import BBSTRADER_DIR
|
|
15
|
-
from bbstrader.metatrader
|
|
15
|
+
from bbstrader.metatrader import (
|
|
16
16
|
Account,
|
|
17
17
|
AdmiralMarktsGroup,
|
|
18
18
|
PepperstoneGroupLimited,
|
|
19
|
+
TradeOrder,
|
|
20
|
+
Rates,
|
|
21
|
+
TradeSignal,
|
|
22
|
+
TradingMode,
|
|
23
|
+
SymbolType
|
|
19
24
|
)
|
|
20
|
-
from bbstrader.metatrader.utils import SymbolType
|
|
21
|
-
from bbstrader.metatrader.rates import Rates
|
|
22
|
-
from bbstrader.metatrader.trade import TradeSignal, TradingMode
|
|
23
25
|
from bbstrader.models.optimization import optimized_weights
|
|
24
26
|
|
|
25
|
-
|
|
26
27
|
__all__ = ["Strategy", "MT5Strategy"]
|
|
27
28
|
|
|
28
29
|
logger.add(
|
|
@@ -71,8 +72,13 @@ class MT5Strategy(Strategy):
|
|
|
71
72
|
calculate signals for the MetaTrader 5 trading platform. The signals
|
|
72
73
|
are generated by the `MT5Strategy` object and sent to the the `Mt5ExecutionEngine`
|
|
73
74
|
for live trading and `MT5BacktestEngine` objects for backtesting.
|
|
74
|
-
"""
|
|
75
75
|
|
|
76
|
+
# NOTE
|
|
77
|
+
It is recommanded that every strategy specfic method to be a private method
|
|
78
|
+
in order to avoid naming collusion.
|
|
79
|
+
"""
|
|
80
|
+
tf: str
|
|
81
|
+
max_trades: Dict[str, int]
|
|
76
82
|
def __init__(
|
|
77
83
|
self,
|
|
78
84
|
events: Queue = None,
|
|
@@ -107,9 +113,11 @@ class MT5Strategy(Strategy):
|
|
|
107
113
|
self._initialize_portfolio()
|
|
108
114
|
self.kwargs = kwargs
|
|
109
115
|
self.periodes = 0
|
|
110
|
-
|
|
111
|
-
@property
|
|
116
|
+
|
|
117
|
+
@property
|
|
112
118
|
def account(self):
|
|
119
|
+
if self.mode != TradingMode.LIVE:
|
|
120
|
+
raise ValueError("account attribute is only allowed in Live mode")
|
|
113
121
|
return Account(**self.kwargs)
|
|
114
122
|
|
|
115
123
|
@property
|
|
@@ -127,7 +135,7 @@ class MT5Strategy(Strategy):
|
|
|
127
135
|
@property
|
|
128
136
|
def orders(self):
|
|
129
137
|
if self.mode == TradingMode.LIVE:
|
|
130
|
-
return self.account.get_orders()
|
|
138
|
+
return self.account.get_orders() or []
|
|
131
139
|
return self._orders
|
|
132
140
|
|
|
133
141
|
@property
|
|
@@ -139,7 +147,7 @@ class MT5Strategy(Strategy):
|
|
|
139
147
|
@property
|
|
140
148
|
def positions(self):
|
|
141
149
|
if self.mode == TradingMode.LIVE:
|
|
142
|
-
return self.account.get_positions()
|
|
150
|
+
return self.account.get_positions() or []
|
|
143
151
|
return self._positions
|
|
144
152
|
|
|
145
153
|
@property
|
|
@@ -345,7 +353,7 @@ class MT5Strategy(Strategy):
|
|
|
345
353
|
log = True
|
|
346
354
|
if log:
|
|
347
355
|
self.logger.info(
|
|
348
|
-
f"{signal} ORDER EXECUTED: SYMBOL={symbol}, QUANTITY={quantity}, PRICE @{price}",
|
|
356
|
+
f"{signal} ORDER EXECUTED: SYMBOL={symbol}, QUANTITY={quantity}, PRICE @{round(price, 5)}",
|
|
349
357
|
custom_time=dtime,
|
|
350
358
|
)
|
|
351
359
|
|
|
@@ -526,7 +534,7 @@ class MT5Strategy(Strategy):
|
|
|
526
534
|
def logmsg(order, type, symbol, dtime):
|
|
527
535
|
return self.logger.info(
|
|
528
536
|
f"{type} ORDER EXECUTED: SYMBOL={symbol}, QUANTITY={order.quantity}, "
|
|
529
|
-
f"PRICE @ {order.price}",
|
|
537
|
+
f"PRICE @ {round(order.price, 5)}",
|
|
530
538
|
custom_time=dtime,
|
|
531
539
|
)
|
|
532
540
|
|
|
@@ -614,7 +622,7 @@ class MT5Strategy(Strategy):
|
|
|
614
622
|
)
|
|
615
623
|
|
|
616
624
|
@staticmethod
|
|
617
|
-
def calculate_pct_change(current_price, lh_price):
|
|
625
|
+
def calculate_pct_change(current_price, lh_price) -> float:
|
|
618
626
|
return ((current_price - lh_price) / lh_price) * 100
|
|
619
627
|
|
|
620
628
|
def get_asset_values(
|
|
@@ -649,8 +657,8 @@ class MT5Strategy(Strategy):
|
|
|
649
657
|
In Live mode, the `bbstrader.metatrader.rates.Rates` class is used to get the historical data
|
|
650
658
|
so the value_type must be 'returns', 'open', 'high', 'low', 'close', 'adjclose', 'volume'.
|
|
651
659
|
"""
|
|
652
|
-
if mode not in [
|
|
653
|
-
raise ValueError("Mode must be
|
|
660
|
+
if mode not in [TradingMode.BACKTEST, TradingMode.LIVE]:
|
|
661
|
+
raise ValueError("Mode must be an instance of TradingMode")
|
|
654
662
|
asset_values = {}
|
|
655
663
|
if mode == TradingMode.BACKTEST:
|
|
656
664
|
if bars is None:
|
|
@@ -696,7 +704,7 @@ class MT5Strategy(Strategy):
|
|
|
696
704
|
if period_count == 0 or period_count is None:
|
|
697
705
|
return True
|
|
698
706
|
return period_count % signal_inverval == 0
|
|
699
|
-
|
|
707
|
+
|
|
700
708
|
@staticmethod
|
|
701
709
|
def stop_time(time_zone: str, stop_time: str) -> bool:
|
|
702
710
|
now = datetime.now(pytz.timezone(time_zone)).time()
|
|
@@ -760,6 +768,47 @@ class MT5Strategy(Strategy):
|
|
|
760
768
|
)
|
|
761
769
|
return prices
|
|
762
770
|
return np.array([])
|
|
771
|
+
|
|
772
|
+
def get_active_orders(self, symbol: str, strategy_id: int, order_type: int = None) -> List[TradeOrder]:
|
|
773
|
+
"""
|
|
774
|
+
Get the active orders for a given symbol and strategy.
|
|
775
|
+
|
|
776
|
+
Args:
|
|
777
|
+
symbol : The symbol for the trade.
|
|
778
|
+
strategy_id : The unique identifier for the strategy.
|
|
779
|
+
order_type : The type of order to filter by (optional):
|
|
780
|
+
"BUY_LIMIT": 2
|
|
781
|
+
"SELL_LIMIT": 3
|
|
782
|
+
"BUY_STOP": 4
|
|
783
|
+
"SELL_STOP": 5
|
|
784
|
+
"BUY_STOP_LIMIT": 6
|
|
785
|
+
"SELL_STOP_LIMIT": 7
|
|
786
|
+
|
|
787
|
+
Returns:
|
|
788
|
+
List[TradeOrder] : A list of active orders for the given symbol and strategy.
|
|
789
|
+
"""
|
|
790
|
+
orders = [o for o in self.orders if o.symbol == symbol and o.magic == strategy_id]
|
|
791
|
+
if order_type is not None and len(orders) > 0:
|
|
792
|
+
orders = [o for o in orders if o.type == order_type]
|
|
793
|
+
return orders
|
|
794
|
+
|
|
795
|
+
def exit_positions(self, position, prices, asset, th: float = 0.01):
|
|
796
|
+
if len(prices) == 0:
|
|
797
|
+
return False
|
|
798
|
+
tick_info = self.account.get_tick_info(asset)
|
|
799
|
+
bid, ask = tick_info.bid, tick_info.ask
|
|
800
|
+
if len(prices) == 1:
|
|
801
|
+
price = prices[0]
|
|
802
|
+
elif len(prices) in range(2, self.max_trades[asset] + 1):
|
|
803
|
+
price = np.mean(prices)
|
|
804
|
+
if (
|
|
805
|
+
position == 0
|
|
806
|
+
and self.calculate_pct_change(ask, price) >= th
|
|
807
|
+
or position == 1
|
|
808
|
+
and abs(self.calculate_pct_change(bid, price)) >= th
|
|
809
|
+
):
|
|
810
|
+
return True
|
|
811
|
+
return False
|
|
763
812
|
|
|
764
813
|
@staticmethod
|
|
765
814
|
def get_current_dt(time_zone: str = "US/Eastern") -> datetime:
|
|
@@ -796,7 +845,9 @@ class MT5Strategy(Strategy):
|
|
|
796
845
|
return dt_to
|
|
797
846
|
|
|
798
847
|
@staticmethod
|
|
799
|
-
def get_mt5_equivalent(
|
|
848
|
+
def get_mt5_equivalent(
|
|
849
|
+
symbols, symbol_type: str | SymbolType = SymbolType.STOCKS, **kwargs
|
|
850
|
+
) -> List[str]:
|
|
800
851
|
"""
|
|
801
852
|
Get the MetaTrader 5 equivalent symbols for the symbols in the list.
|
|
802
853
|
This method is used to get the symbols that are available on the MetaTrader 5 platform.
|
bbstrader/config.py
CHANGED
|
@@ -3,8 +3,8 @@ from pathlib import Path
|
|
|
3
3
|
from typing import List
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
TERMINAL = "
|
|
7
|
-
BASE_FOLDER = "C
|
|
6
|
+
TERMINAL = "/terminal64.exe"
|
|
7
|
+
BASE_FOLDER = "C:/Program Files/"
|
|
8
8
|
|
|
9
9
|
AMG_PATH = BASE_FOLDER + "Admirals Group MT5 Terminal" + TERMINAL
|
|
10
10
|
PGL_PATH = BASE_FOLDER + "Pepperstone MetaTrader 5" + TERMINAL
|
bbstrader/metatrader/account.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
3
|
import urllib.request
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
5
|
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
|
6
6
|
|
|
7
7
|
import pandas as pd
|
|
@@ -54,9 +54,7 @@ BROKERS_TIMEZONES = {
|
|
|
54
54
|
"PGL": "Europe/Helsinki",
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
_ADMIRAL_MARKETS_URL_ =
|
|
58
|
-
"https://cabinet.a-partnership.com/visit/?bta=35537&brand=admiralmarkets"
|
|
59
|
-
)
|
|
57
|
+
_ADMIRAL_MARKETS_URL_ = "https://one.justmarkets.link/a/tufvj0xugm/registration/trader"
|
|
60
58
|
_JUST_MARKETS_URL_ = "https://one.justmarkets.link/a/tufvj0xugm/registration/trader"
|
|
61
59
|
_FTMO_URL_ = "https://trader.ftmo.com/?affiliates=JGmeuQqepAZLMcdOEQRp"
|
|
62
60
|
_ADMIRAL_MARKETS_PRODUCTS_ = [
|
|
@@ -138,13 +136,13 @@ AMG_EXCHANGES = {
|
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
|
|
141
|
-
def check_mt5_connection(**kwargs):
|
|
139
|
+
def check_mt5_connection(**kwargs) -> bool:
|
|
142
140
|
"""
|
|
143
141
|
Initialize the connection to the MetaTrader 5 terminal.
|
|
144
142
|
|
|
145
143
|
Args:
|
|
146
144
|
path (str, optional): The path to the MetaTrader 5 terminal executable file.
|
|
147
|
-
Defaults to None (e.g., "C
|
|
145
|
+
Defaults to None (e.g., "C:/Program Files/MetaTrader 5/terminal64.exe").
|
|
148
146
|
login (int, optional): The login ID of the trading account. Defaults to None.
|
|
149
147
|
password (str, optional): The password of the trading account. Defaults to None.
|
|
150
148
|
server (str, optional): The name of the trade server to which the client terminal is connected.
|
|
@@ -165,6 +163,7 @@ def check_mt5_connection(**kwargs):
|
|
|
165
163
|
timeout = kwargs.get("timeout", 60_000)
|
|
166
164
|
portable = kwargs.get("portable", False)
|
|
167
165
|
|
|
166
|
+
init = False
|
|
168
167
|
if path is None and (login or password or server):
|
|
169
168
|
raise ValueError(
|
|
170
169
|
"You must provide a path to the terminal executable file"
|
|
@@ -189,6 +188,7 @@ def check_mt5_connection(**kwargs):
|
|
|
189
188
|
raise_mt5_error(INIT_MSG)
|
|
190
189
|
except Exception:
|
|
191
190
|
raise_mt5_error(INIT_MSG)
|
|
191
|
+
return init
|
|
192
192
|
|
|
193
193
|
|
|
194
194
|
def shutdown_mt5():
|
|
@@ -391,6 +391,7 @@ class Account(object):
|
|
|
391
391
|
password: Optional[str] = None,
|
|
392
392
|
server: Optional[str] = None,
|
|
393
393
|
timeout: Optional[int] = 60_000,
|
|
394
|
+
path: Optional[str] = None,
|
|
394
395
|
) -> Union[AccountInfo, None]:
|
|
395
396
|
"""
|
|
396
397
|
Get info on the current trading account or a specific account .
|
|
@@ -408,6 +409,8 @@ class Account(object):
|
|
|
408
409
|
If not specified, the value of 60 000 (60 seconds) is applied.
|
|
409
410
|
If the connection is not established within the specified time,
|
|
410
411
|
the call is forcibly terminated and the exception is generated.
|
|
412
|
+
path (str, optional): The path to the MetaTrader 5 terminal executable file.
|
|
413
|
+
Defaults to None (e.g., "C:/Program Files/MetaTrader 5/terminal64.exe").
|
|
411
414
|
|
|
412
415
|
Returns:
|
|
413
416
|
- AccountInfo in the form of a Namedtuple structure.
|
|
@@ -419,6 +422,15 @@ class Account(object):
|
|
|
419
422
|
# connect to the trade account specifying a password and a server
|
|
420
423
|
if account is not None and password is not None and server is not None:
|
|
421
424
|
try:
|
|
425
|
+
# If a path is provided, initialize the MT5 terminal with it
|
|
426
|
+
if path is not None:
|
|
427
|
+
check_mt5_connection(
|
|
428
|
+
path=path,
|
|
429
|
+
login=account,
|
|
430
|
+
password=password,
|
|
431
|
+
server=server,
|
|
432
|
+
timeout=timeout,
|
|
433
|
+
)
|
|
422
434
|
authorized = mt5.login(
|
|
423
435
|
account, password=password, server=server, timeout=timeout
|
|
424
436
|
)
|
|
@@ -1079,6 +1091,39 @@ class Account(object):
|
|
|
1079
1091
|
except Exception as e:
|
|
1080
1092
|
raise_mt5_error(e)
|
|
1081
1093
|
|
|
1094
|
+
def calculate_profit(
|
|
1095
|
+
self,
|
|
1096
|
+
action: Literal["buy", "sell"],
|
|
1097
|
+
symbol: str,
|
|
1098
|
+
lot: float,
|
|
1099
|
+
price_open: float,
|
|
1100
|
+
price_close: float,
|
|
1101
|
+
) -> float:
|
|
1102
|
+
"""
|
|
1103
|
+
Calculate profit in the account currency for a specified trading operation.
|
|
1104
|
+
|
|
1105
|
+
Args:
|
|
1106
|
+
action (str): The trading action, either 'buy' or 'sell'.
|
|
1107
|
+
symbol (str): The symbol of the financial instrument.
|
|
1108
|
+
lot (float): The lot size of the order.
|
|
1109
|
+
price_open (float): The price at which to position was opened.
|
|
1110
|
+
price_close (float): The price at which to position was closed.
|
|
1111
|
+
|
|
1112
|
+
Returns:
|
|
1113
|
+
float: The profit value
|
|
1114
|
+
|
|
1115
|
+
Raises:
|
|
1116
|
+
MT5TerminalError: A specific exception based on the error code.
|
|
1117
|
+
|
|
1118
|
+
"""
|
|
1119
|
+
actions = {"buy": mt5.ORDER_TYPE_BUY, "sell": mt5.ORDER_TYPE_SELL}
|
|
1120
|
+
try:
|
|
1121
|
+
return mt5.order_calc_profit(
|
|
1122
|
+
actions[action], symbol, lot, price_open, price_close
|
|
1123
|
+
)
|
|
1124
|
+
except Exception as e:
|
|
1125
|
+
raise_mt5_error(e)
|
|
1126
|
+
|
|
1082
1127
|
def check_order(self, request: Dict[str, Any]) -> OrderCheckResult:
|
|
1083
1128
|
"""
|
|
1084
1129
|
Check funds sufficiency for performing a required trading operation.
|
|
@@ -1527,3 +1572,29 @@ class Account(object):
|
|
|
1527
1572
|
else:
|
|
1528
1573
|
history_orders = [TradeOrder(**td._asdict()) for td in history_orders]
|
|
1529
1574
|
return tuple(history_orders)
|
|
1575
|
+
|
|
1576
|
+
def get_today_deals(self, id, group=None) -> List[TradeDeal]:
|
|
1577
|
+
"""
|
|
1578
|
+
Get all today deals for a specific symbol or group of symbols
|
|
1579
|
+
|
|
1580
|
+
Args:
|
|
1581
|
+
id (int): strategy or expert id
|
|
1582
|
+
group (str): Symbol or group or symbol
|
|
1583
|
+
Returns:
|
|
1584
|
+
List[TradeDeal]: List of today deals
|
|
1585
|
+
"""
|
|
1586
|
+
date_from = datetime.now() - timedelta(days=2)
|
|
1587
|
+
history = (
|
|
1588
|
+
self.get_trades_history(date_from=date_from, group=group, to_df=False) or []
|
|
1589
|
+
)
|
|
1590
|
+
positions_ids = set([deal.position_id for deal in history if deal.magic == id])
|
|
1591
|
+
today_deals = []
|
|
1592
|
+
for position in positions_ids:
|
|
1593
|
+
deal = self.get_trades_history(
|
|
1594
|
+
date_from=date_from, position=position, to_df=False
|
|
1595
|
+
)
|
|
1596
|
+
if deal is not None and len(deal) == 2:
|
|
1597
|
+
deal_time = datetime.fromtimestamp(deal[1].time)
|
|
1598
|
+
if deal_time.date() == datetime.now().date():
|
|
1599
|
+
today_deals.append(deal[1])
|
|
1600
|
+
return today_deals
|