bbstrader 0.3.4__py3-none-any.whl → 0.3.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/__init__.py +10 -1
- bbstrader/__main__.py +5 -0
- bbstrader/apps/_copier.py +3 -3
- bbstrader/btengine/strategy.py +113 -38
- bbstrader/compat.py +18 -10
- bbstrader/config.py +0 -16
- bbstrader/core/scripts.py +4 -3
- bbstrader/metatrader/account.py +51 -26
- bbstrader/metatrader/analysis.py +30 -16
- bbstrader/metatrader/copier.py +136 -58
- bbstrader/metatrader/trade.py +39 -45
- bbstrader/metatrader/utils.py +5 -4
- bbstrader/models/factors.py +17 -13
- bbstrader/models/ml.py +96 -49
- bbstrader/models/nlp.py +83 -66
- bbstrader/trading/execution.py +39 -22
- bbstrader/tseries.py +103 -127
- {bbstrader-0.3.4.dist-info → bbstrader-0.3.6.dist-info}/METADATA +29 -46
- bbstrader-0.3.6.dist-info/RECORD +62 -0
- bbstrader-0.3.6.dist-info/top_level.txt +3 -0
- docs/conf.py +56 -0
- tests/__init__.py +0 -0
- tests/engine/__init__.py +1 -0
- tests/engine/test_backtest.py +58 -0
- tests/engine/test_data.py +536 -0
- tests/engine/test_events.py +300 -0
- tests/engine/test_execution.py +219 -0
- tests/engine/test_portfolio.py +307 -0
- tests/metatrader/__init__.py +0 -0
- tests/metatrader/test_account.py +1769 -0
- tests/metatrader/test_rates.py +292 -0
- tests/metatrader/test_risk_management.py +700 -0
- tests/metatrader/test_trade.py +439 -0
- bbstrader-0.3.4.dist-info/RECORD +0 -49
- bbstrader-0.3.4.dist-info/top_level.txt +0 -1
- {bbstrader-0.3.4.dist-info → bbstrader-0.3.6.dist-info}/WHEEL +0 -0
- {bbstrader-0.3.4.dist-info → bbstrader-0.3.6.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.3.4.dist-info → bbstrader-0.3.6.dist-info}/licenses/LICENSE +0 -0
bbstrader/models/ml.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import warnings
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
3
4
|
from itertools import product
|
|
4
5
|
from time import time
|
|
5
6
|
|
|
@@ -7,7 +8,6 @@ import lightgbm as lgb
|
|
|
7
8
|
import matplotlib.pyplot as plt
|
|
8
9
|
import numpy as np
|
|
9
10
|
import pandas as pd
|
|
10
|
-
import pandas_ta as ta
|
|
11
11
|
import seaborn as sns
|
|
12
12
|
import yfinance as yf
|
|
13
13
|
from alphalens import performance as perf
|
|
@@ -22,15 +22,30 @@ from loguru import logger as log
|
|
|
22
22
|
from scipy.stats import spearmanr
|
|
23
23
|
from sklearn.preprocessing import LabelEncoder, StandardScaler
|
|
24
24
|
|
|
25
|
+
try:
|
|
26
|
+
# This is to fix posix import error in pandas_ta
|
|
27
|
+
# On windows systems
|
|
28
|
+
import posix # noqa: F401
|
|
29
|
+
except (ImportError, Exception):
|
|
30
|
+
import bbstrader.compat # noqa: F401
|
|
31
|
+
|
|
32
|
+
import pandas_ta as ta
|
|
33
|
+
|
|
25
34
|
warnings.filterwarnings("ignore")
|
|
26
35
|
|
|
27
36
|
__all__ = ["OneStepTimeSeriesSplit", "MultipleTimeSeriesCV", "LightGBModel"]
|
|
28
37
|
|
|
29
38
|
|
|
30
39
|
class OneStepTimeSeriesSplit:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Assumes the index contains a level labeled 'date'
|
|
40
|
+
"""
|
|
41
|
+
Generates tuples of train_idx, test_idx pairs
|
|
42
|
+
Assumes the index contains a level labeled 'date'
|
|
43
|
+
|
|
44
|
+
References
|
|
45
|
+
----------
|
|
46
|
+
Stefan Jansen (2020). Machine Learning for Algorithmic Trading - Second Edition.
|
|
47
|
+
Chapter 12, Boosting Your Trading Strategy.
|
|
48
|
+
"""
|
|
34
49
|
|
|
35
50
|
def __init__(self, n_splits=3, test_period_length=1, shuffle=False):
|
|
36
51
|
self.n_splits = n_splits
|
|
@@ -62,11 +77,15 @@ class OneStepTimeSeriesSplit:
|
|
|
62
77
|
|
|
63
78
|
|
|
64
79
|
class MultipleTimeSeriesCV:
|
|
65
|
-
__author__ = "Stefan Jansen"
|
|
66
80
|
"""
|
|
67
81
|
Generates tuples of train_idx, test_idx pairs
|
|
68
82
|
Assumes the MultiIndex contains levels 'symbol' and 'date'
|
|
69
83
|
purges overlapping outcomes
|
|
84
|
+
|
|
85
|
+
References
|
|
86
|
+
----------
|
|
87
|
+
Stefan Jansen (2020). Machine Learning for Algorithmic Trading - Second Edition.
|
|
88
|
+
Chapter 12, Boosting Your Trading Strategy.
|
|
70
89
|
"""
|
|
71
90
|
|
|
72
91
|
def __init__(
|
|
@@ -187,7 +206,7 @@ class LightGBModel(object):
|
|
|
187
206
|
# Compute Bollinger Bands using pandas_ta
|
|
188
207
|
bb = ta.bbands(close, length=20)
|
|
189
208
|
return pd.DataFrame(
|
|
190
|
-
{"bb_high": bb["BBU_20_2.0"], "bb_low": bb["BBL_20_2.0"]}, index=close.index
|
|
209
|
+
{"bb_high": bb["BBU_20_2.0_2.0"], "bb_low": bb["BBL_20_2.0_2.0"]}, index=close.index
|
|
191
210
|
)
|
|
192
211
|
|
|
193
212
|
def _compute_atr(self, stock_data):
|
|
@@ -235,26 +254,29 @@ class LightGBModel(object):
|
|
|
235
254
|
return prices
|
|
236
255
|
|
|
237
256
|
def download_boosting_data(self, tickers, start, end=None):
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
try:
|
|
258
|
+
data = yf.download(
|
|
259
|
+
tickers,
|
|
260
|
+
start=start,
|
|
261
|
+
end=end,
|
|
262
|
+
progress=False,
|
|
263
|
+
auto_adjust=True,
|
|
264
|
+
threads=True,
|
|
265
|
+
)
|
|
266
|
+
if data.empty:
|
|
267
|
+
return pd.DataFrame()
|
|
268
|
+
|
|
269
|
+
data = (
|
|
270
|
+
data.stack(level=1).rename_axis(["Date", "symbol"]).reset_index(level=1)
|
|
271
|
+
)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
self.logger.error(f"An error occurred during data download: {e}")
|
|
274
|
+
return pd.DataFrame()
|
|
275
|
+
|
|
276
|
+
# The rest of the data processing is the same as the original function
|
|
256
277
|
if "Adj Close" in data.columns:
|
|
257
278
|
data = data.drop(columns=["Adj Close"])
|
|
279
|
+
|
|
258
280
|
data = (
|
|
259
281
|
data.rename(columns={s: s.lower().replace(" ", "_") for s in data.columns})
|
|
260
282
|
.set_index("symbol", append=True)
|
|
@@ -265,17 +287,11 @@ class LightGBModel(object):
|
|
|
265
287
|
return data
|
|
266
288
|
|
|
267
289
|
def download_metadata(self, tickers):
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
# use regex=False for literal string replacements
|
|
272
|
-
.str.replace("-", "", regex=False)
|
|
273
|
-
.str.replace("&", "and", regex=False)
|
|
274
|
-
.str.replace(" ", "_", regex=False)
|
|
275
|
-
.str.replace("__", "_", regex=False)
|
|
276
|
-
)
|
|
290
|
+
"""
|
|
291
|
+
Downloads metadata for multiple tickers concurrently using a thread pool.
|
|
292
|
+
"""
|
|
277
293
|
|
|
278
|
-
|
|
294
|
+
METADATA_KEYS = [
|
|
279
295
|
"industry",
|
|
280
296
|
"sector",
|
|
281
297
|
"exchange",
|
|
@@ -296,7 +312,7 @@ class LightGBModel(object):
|
|
|
296
312
|
"marketCap",
|
|
297
313
|
]
|
|
298
314
|
|
|
299
|
-
|
|
315
|
+
COLUMN_MAP = {
|
|
300
316
|
"industry": "industry",
|
|
301
317
|
"sector": "sector",
|
|
302
318
|
"exchange": "exchange",
|
|
@@ -316,22 +332,53 @@ class LightGBModel(object):
|
|
|
316
332
|
"askSize": "asksize",
|
|
317
333
|
"marketCap": "marketcap",
|
|
318
334
|
}
|
|
319
|
-
|
|
320
|
-
|
|
335
|
+
|
|
336
|
+
def _clean_text_column(series: pd.Series) -> pd.Series:
|
|
337
|
+
"""Helper function to clean text columns."""
|
|
338
|
+
return (
|
|
339
|
+
series.str.lower()
|
|
340
|
+
.str.replace("-", "", regex=False)
|
|
341
|
+
.str.replace("&", "and", regex=False)
|
|
342
|
+
.str.replace(" ", "_", regex=False)
|
|
343
|
+
.str.replace("__", "_", regex=False)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def _fetch_single_ticker_info(symbol):
|
|
347
|
+
"""Worker function to fetch and process info for one ticker."""
|
|
321
348
|
try:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
349
|
+
ticker_info = yf.Ticker(symbol).info
|
|
350
|
+
return {key: ticker_info.get(key) for key in METADATA_KEYS}
|
|
351
|
+
except Exception:
|
|
352
|
+
return None
|
|
353
|
+
|
|
354
|
+
data = []
|
|
355
|
+
with ThreadPoolExecutor(max_workers=20) as executor:
|
|
356
|
+
future_to_ticker = {
|
|
357
|
+
executor.submit(_fetch_single_ticker_info, ticker): ticker
|
|
358
|
+
for ticker in tickers
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
for future in as_completed(future_to_ticker):
|
|
362
|
+
result = future.result()
|
|
363
|
+
if result:
|
|
364
|
+
data.append(result)
|
|
365
|
+
|
|
366
|
+
if not data:
|
|
367
|
+
return pd.DataFrame()
|
|
368
|
+
|
|
329
369
|
metadata = pd.DataFrame(data)
|
|
330
|
-
metadata = metadata.rename(columns=
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
370
|
+
metadata = metadata.rename(columns=COLUMN_MAP)
|
|
371
|
+
|
|
372
|
+
if "dyield" in metadata.columns:
|
|
373
|
+
metadata.dyield = metadata.dyield.fillna(0)
|
|
374
|
+
if "sector" in metadata.columns:
|
|
375
|
+
metadata.sector = _clean_text_column(metadata.sector)
|
|
376
|
+
if "industry" in metadata.columns:
|
|
377
|
+
metadata.industry = _clean_text_column(metadata.industry)
|
|
378
|
+
|
|
379
|
+
if "symbol" in metadata.columns:
|
|
380
|
+
metadata = metadata.set_index("symbol")
|
|
381
|
+
|
|
335
382
|
return metadata
|
|
336
383
|
|
|
337
384
|
def _select_nlargest_liquidity_stocks(
|
bbstrader/models/nlp.py
CHANGED
|
@@ -577,44 +577,55 @@ class SentimentAnalyzer(object):
|
|
|
577
577
|
**kwargs,
|
|
578
578
|
) -> Dict[str, float]:
|
|
579
579
|
"""
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
Process
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
580
|
+
Compute sentiment scores for a list of financial tickers based on news and social media data.
|
|
581
|
+
|
|
582
|
+
Process
|
|
583
|
+
-------
|
|
584
|
+
1. Collect news articles and posts related to each ticker from various sources:
|
|
585
|
+
* Yahoo Finance News
|
|
586
|
+
* Google Finance News
|
|
587
|
+
* Reddit posts
|
|
588
|
+
* Financial Modeling Prep (FMP) news
|
|
589
|
+
2. Analyze sentiment from each source:
|
|
590
|
+
* Uses VADER for Yahoo and Google Finance news.
|
|
591
|
+
* Uses TextBlob for Reddit and FMP news.
|
|
592
|
+
3. Compute an overall sentiment score using a weighted average approach.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
tickers : list of str or list of tuple
|
|
597
|
+
A list of asset tickers to analyze.
|
|
598
|
+
* If using tuples, the first element is the ticker and the second is the asset type.
|
|
599
|
+
* If using a single string, the asset type must be specified or defaults to "stock".
|
|
600
|
+
lexicon : dict, optional
|
|
601
|
+
A custom sentiment lexicon to update VADER's default lexicon. Default is None.
|
|
602
|
+
asset_type : str, optional
|
|
603
|
+
The type of asset. Default is "stock".
|
|
604
|
+
Supported types include:
|
|
605
|
+
* "stock": Stock symbols (e.g., AAPL, MSFT)
|
|
606
|
+
* "etf": Exchange-traded funds (e.g., SPY, QQQ)
|
|
607
|
+
* "future": Futures contracts (e.g., CL=F for crude oil)
|
|
608
|
+
* "forex": Forex pairs (e.g., EURUSD=X, USDJPY=X)
|
|
609
|
+
* "crypto": Cryptocurrency pairs (e.g., BTC-USD, ETH-USD)
|
|
610
|
+
* "index": Stock market indices (e.g., ^GSPC for S&P 500)
|
|
611
|
+
top_news : int, optional
|
|
612
|
+
Number of news articles/posts to fetch per source. Default is 10.
|
|
613
|
+
**kwargs : dict
|
|
614
|
+
Additional parameters for API authentication and data retrieval. Must include:
|
|
615
|
+
* fmp_api (str): API key for Financial Modeling Prep.
|
|
616
|
+
* client_id, client_secret, user_agent (str): Credentials for Reddit API.
|
|
617
|
+
|
|
618
|
+
Returns
|
|
619
|
+
-------
|
|
620
|
+
dict of str to float
|
|
621
|
+
A dictionary mapping each ticker to its overall sentiment score.
|
|
622
|
+
* Positive values indicate positive sentiment.
|
|
623
|
+
* Negative values indicate negative sentiment.
|
|
624
|
+
* Zero indicates neutral sentiment.
|
|
625
|
+
|
|
626
|
+
Notes
|
|
627
|
+
-----
|
|
628
|
+
Ticker names must follow Yahoo Finance conventions.
|
|
618
629
|
"""
|
|
619
630
|
|
|
620
631
|
sentiment_results = {}
|
|
@@ -761,30 +772,36 @@ class SentimentAnalyzer(object):
|
|
|
761
772
|
The dashboard visualizes sentiment scores for given tickers using interactive
|
|
762
773
|
bar and scatter plots. It fetches new sentiment data at specified intervals.
|
|
763
774
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
775
|
+
Parameters
|
|
776
|
+
----------
|
|
777
|
+
tickers : list of str or list of tuple
|
|
778
|
+
A list of financial asset tickers to analyze.
|
|
779
|
+
* If using tuples, the first element is the ticker and the second is the asset type.
|
|
780
|
+
* If using a single string, the asset type must be specified or defaults to "stock".
|
|
781
|
+
asset_type : str, optional
|
|
782
|
+
The type of financial asset ("stock", "forex", "crypto"). Default is "stock".
|
|
783
|
+
lexicon : dict, optional
|
|
784
|
+
A custom sentiment lexicon. Default is None.
|
|
785
|
+
interval : int, optional
|
|
786
|
+
The refresh interval (in milliseconds) for sentiment data updates. Default is 100000.
|
|
787
|
+
top_n : int, optional
|
|
788
|
+
The number of top and bottom assets to display in the sentiment bar chart. Default is 20.
|
|
789
|
+
**kwargs : dict
|
|
790
|
+
Additional arguments required for fetching sentiment data. Must include:
|
|
791
|
+
* client_id (str): Reddit API client ID.
|
|
792
|
+
* client_secret (str): Reddit API client secret.
|
|
793
|
+
* user_agent (str): User agent for Reddit API.
|
|
794
|
+
* fmp_api (str): Financial Modeling Prep (FMP) API key.
|
|
795
|
+
|
|
796
|
+
Returns
|
|
797
|
+
-------
|
|
798
|
+
None
|
|
799
|
+
Starts a real-time interactive dashboard. Does not return any value.
|
|
800
|
+
|
|
801
|
+
Example
|
|
802
|
+
-------
|
|
803
|
+
.. code-block:: python
|
|
786
804
|
|
|
787
|
-
Example Usage:
|
|
788
805
|
sa = SentimentAnalyzer()
|
|
789
806
|
sa.display_sentiment_dashboard(
|
|
790
807
|
tickers=["AAPL", "TSLA", "GOOGL"],
|
|
@@ -799,12 +816,12 @@ class SentimentAnalyzer(object):
|
|
|
799
816
|
fmp_api="your_fmp_api_key",
|
|
800
817
|
)
|
|
801
818
|
|
|
802
|
-
Notes
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
819
|
+
Notes
|
|
820
|
+
-----
|
|
821
|
+
* Sentiment analysis is performed using financial news and social media discussions.
|
|
822
|
+
* The dashboard updates in real-time at the specified interval.
|
|
823
|
+
* The dashboard will keep running unless manually stopped (Ctrl+C).
|
|
806
824
|
"""
|
|
807
|
-
|
|
808
825
|
app = dash.Dash(__name__)
|
|
809
826
|
|
|
810
827
|
sentiment_history = {ticker: [] for ticker in tickers}
|
bbstrader/trading/execution.py
CHANGED
|
@@ -45,6 +45,7 @@ MT5_ENGINE_TIMEFRAMES = list(_TF_MAPPING.keys())
|
|
|
45
45
|
TradingDays = ["monday", "tuesday", "wednesday", "thursday", "friday"]
|
|
46
46
|
WEEK_DAYS = TradingDays + ["saturday", "sunday"]
|
|
47
47
|
FRIDAY = "friday"
|
|
48
|
+
WEEK_ENDS = ["friday", "saturday", "sunday"]
|
|
48
49
|
|
|
49
50
|
BUYS = ["BMKT", "BLMT", "BSTP", "BSTPLMT"]
|
|
50
51
|
SELLS = ["SMKT", "SLMT", "SSTP", "SSTPLMT"]
|
|
@@ -513,17 +514,12 @@ class Mt5ExecutionEngine:
|
|
|
513
514
|
logger.info(sessionmsg)
|
|
514
515
|
|
|
515
516
|
def _check_is_day_ends(self, trade: Trade, symbol, period_type, today, closing):
|
|
516
|
-
if trade.days_end():
|
|
517
|
-
self._logmsgif("Day", symbol) if today
|
|
517
|
+
if trade.days_end() or (today in WEEK_ENDS and today != FRIDAY):
|
|
518
|
+
self._logmsgif("Day", symbol) if today not in WEEK_ENDS else self._logmsgif(
|
|
518
519
|
"Week", symbol
|
|
519
520
|
)
|
|
520
521
|
if (
|
|
521
|
-
(
|
|
522
|
-
period_type == "month"
|
|
523
|
-
and today == FRIDAY
|
|
524
|
-
and self._is_month_ends()
|
|
525
|
-
and closing
|
|
526
|
-
)
|
|
522
|
+
(period_type == "month" and self._is_month_ends() and closing)
|
|
527
523
|
or (period_type == "week" and today == FRIDAY and closing)
|
|
528
524
|
or (period_type == "day" and closing)
|
|
529
525
|
or (period_type == "24/7" and closing)
|
|
@@ -546,15 +542,17 @@ class Mt5ExecutionEngine:
|
|
|
546
542
|
self.strategy.perform_period_end_checks()
|
|
547
543
|
if self.period_end_action == "break" and closing:
|
|
548
544
|
sys.exit(0)
|
|
549
|
-
elif
|
|
545
|
+
elif (
|
|
546
|
+
self.period_end_action == "sleep" and today not in WEEK_ENDS or not closing
|
|
547
|
+
):
|
|
550
548
|
self._sleep_over_night(sessionmsg)
|
|
551
|
-
elif self.period_end_action == "sleep" and today
|
|
549
|
+
elif self.period_end_action == "sleep" and today in WEEK_ENDS:
|
|
552
550
|
self._sleep_over_weekend(sessionmsg)
|
|
553
551
|
|
|
554
552
|
def _weekly_end_checks(self, today, closing, sessionmsg):
|
|
555
|
-
if today
|
|
553
|
+
if today not in WEEK_ENDS:
|
|
556
554
|
self._sleep_over_night(sessionmsg)
|
|
557
|
-
|
|
555
|
+
else:
|
|
558
556
|
self.strategy.perform_period_end_checks()
|
|
559
557
|
if self.period_end_action == "break" and closing:
|
|
560
558
|
sys.exit(0)
|
|
@@ -562,9 +560,9 @@ class Mt5ExecutionEngine:
|
|
|
562
560
|
self._sleep_over_weekend(sessionmsg)
|
|
563
561
|
|
|
564
562
|
def _monthly_end_cheks(self, today, closing, sessionmsg):
|
|
565
|
-
if today
|
|
563
|
+
if today not in WEEK_ENDS and not self._is_month_ends():
|
|
566
564
|
self._sleep_over_night(sessionmsg)
|
|
567
|
-
elif
|
|
565
|
+
elif self._is_month_ends() and closing:
|
|
568
566
|
self.strategy.perform_period_end_checks()
|
|
569
567
|
sys.exit(0)
|
|
570
568
|
else:
|
|
@@ -956,7 +954,11 @@ class Mt5ExecutionEngine:
|
|
|
956
954
|
def _handle_period_end_actions(self, today):
|
|
957
955
|
try:
|
|
958
956
|
check_mt5_connection(**self.kwargs)
|
|
959
|
-
day_end =
|
|
957
|
+
day_end = (
|
|
958
|
+
all(trade.days_end() for trade in self.trades_instances.values())
|
|
959
|
+
or (today in WEEK_ENDS and today != FRIDAY)
|
|
960
|
+
and self.period != "24/7"
|
|
961
|
+
)
|
|
960
962
|
closing = self._is_closing()
|
|
961
963
|
sessionmsg = f"{self.ACCOUNT} STARTING NEW TRADING SESSION ...\n"
|
|
962
964
|
self._perform_period_end_actions(
|
|
@@ -1012,13 +1014,28 @@ class Mt5ExecutionEngine:
|
|
|
1012
1014
|
|
|
1013
1015
|
|
|
1014
1016
|
def RunMt5Engine(account_id: str, **kwargs):
|
|
1015
|
-
"""
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1017
|
+
"""
|
|
1018
|
+
Start an MT5 execution engine for a given account.
|
|
1019
|
+
|
|
1020
|
+
Parameters
|
|
1021
|
+
----------
|
|
1022
|
+
account_id : str
|
|
1023
|
+
Account ID to run the execution engine on.
|
|
1024
|
+
|
|
1025
|
+
**kwargs : dict
|
|
1026
|
+
Additional keyword arguments. Possible keys include:
|
|
1027
|
+
|
|
1028
|
+
* symbol_list : list
|
|
1029
|
+
List of symbols to trade.
|
|
1030
|
+
* trades_instances : dict
|
|
1031
|
+
Dictionary of Trade instances.
|
|
1032
|
+
* strategy_cls : class
|
|
1033
|
+
Strategy class to use for trading.
|
|
1034
|
+
|
|
1035
|
+
Returns
|
|
1036
|
+
-------
|
|
1037
|
+
None
|
|
1038
|
+
Initializes and runs the MT5 execution engine.
|
|
1022
1039
|
"""
|
|
1023
1040
|
log.info(f"Starting execution engine for {account_id}")
|
|
1024
1041
|
|