bbstrader 0.2.92__py3-none-any.whl → 0.2.94__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.

Files changed (36) hide show
  1. bbstrader/__ini__.py +20 -20
  2. bbstrader/__main__.py +50 -50
  3. bbstrader/btengine/__init__.py +54 -54
  4. bbstrader/btengine/data.py +11 -9
  5. bbstrader/btengine/scripts.py +157 -157
  6. bbstrader/compat.py +19 -19
  7. bbstrader/config.py +137 -137
  8. bbstrader/core/data.py +22 -22
  9. bbstrader/core/utils.py +146 -146
  10. bbstrader/metatrader/__init__.py +6 -6
  11. bbstrader/metatrader/account.py +1516 -1516
  12. bbstrader/metatrader/copier.py +750 -735
  13. bbstrader/metatrader/rates.py +584 -584
  14. bbstrader/metatrader/risk.py +749 -748
  15. bbstrader/metatrader/scripts.py +81 -81
  16. bbstrader/metatrader/trade.py +1836 -1826
  17. bbstrader/metatrader/utils.py +645 -645
  18. bbstrader/models/__init__.py +10 -10
  19. bbstrader/models/factors.py +312 -312
  20. bbstrader/models/ml.py +1272 -1265
  21. bbstrader/models/optimization.py +182 -182
  22. bbstrader/models/portfolio.py +223 -223
  23. bbstrader/models/risk.py +398 -398
  24. bbstrader/trading/__init__.py +11 -11
  25. bbstrader/trading/execution.py +846 -842
  26. bbstrader/trading/script.py +155 -155
  27. bbstrader/trading/scripts.py +69 -69
  28. bbstrader/trading/strategies.py +860 -860
  29. bbstrader/tseries.py +1842 -1842
  30. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/LICENSE +21 -21
  31. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/METADATA +188 -187
  32. bbstrader-0.2.94.dist-info/RECORD +44 -0
  33. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/WHEEL +1 -1
  34. bbstrader-0.2.92.dist-info/RECORD +0 -44
  35. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/entry_points.txt +0 -0
  36. {bbstrader-0.2.92.dist-info → bbstrader-0.2.94.dist-info}/top_level.txt +0 -0
bbstrader/compat.py CHANGED
@@ -1,19 +1,19 @@
1
- import platform
2
- import sys
3
-
4
-
5
- def setup_mock_metatrader():
6
- """Mock MetaTrader5 on Linux to prevent import errors."""
7
- if platform.system() != "Windows":
8
- from unittest.mock import MagicMock
9
-
10
- class Mock(MagicMock):
11
- @classmethod
12
- def __getattr__(cls, name):
13
- return MagicMock()
14
-
15
- MOCK_MODULES = ["MetaTrader5"]
16
- sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
17
-
18
-
19
- setup_mock_metatrader()
1
+ import platform
2
+ import sys
3
+
4
+
5
+ def setup_mock_metatrader():
6
+ """Mock MetaTrader5 on Linux and MacOS to prevent import errors."""
7
+ if platform.system() != "Windows":
8
+ from unittest.mock import MagicMock
9
+
10
+ class Mock(MagicMock):
11
+ @classmethod
12
+ def __getattr__(cls, name):
13
+ return MagicMock()
14
+
15
+ MOCK_MODULES = ["MetaTrader5"]
16
+ sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
17
+
18
+
19
+ setup_mock_metatrader()
bbstrader/config.py CHANGED
@@ -1,137 +1,137 @@
1
- import logging
2
- from pathlib import Path
3
- from typing import List
4
-
5
-
6
- TERMINAL = "\\terminal64.exe"
7
- BASE_FOLDER = "C:\\Program Files\\"
8
-
9
- AMG_PATH = BASE_FOLDER + "Admirals Group MT5 Terminal" + TERMINAL
10
- PGL_PATH = BASE_FOLDER + "Pepperstone MetaTrader 5" + TERMINAL
11
- FTMO_PATH = BASE_FOLDER + "FTMO MetaTrader 5" + TERMINAL
12
- JGM_PATH = BASE_FOLDER + "JustMarkets MetaTrader 5" + TERMINAL
13
-
14
- BROKERS_PATHS = {
15
- "AMG": AMG_PATH,
16
- "FTMO": FTMO_PATH,
17
- "PGL": PGL_PATH,
18
- "JGM": JGM_PATH,
19
- }
20
-
21
-
22
- def get_config_dir(name: str = ".bbstrader") -> Path:
23
- """
24
- Get the path to the configuration directory.
25
-
26
- Args:
27
- name: The name of the configuration directory.
28
-
29
- Returns:
30
- The path to the configuration directory.
31
- """
32
- home_dir = Path.home() / name
33
- if not home_dir.exists():
34
- home_dir.mkdir()
35
- return home_dir
36
-
37
-
38
- BBSTRADER_DIR = get_config_dir()
39
-
40
-
41
- class LogLevelFilter(logging.Filter):
42
- def __init__(self, levels: List[int]):
43
- """
44
- Initializes the filter with specific logging levels.
45
-
46
- Args:
47
- levels: A list of logging level values (integers) to include.
48
- """
49
- super().__init__()
50
- self.levels = levels
51
-
52
- def filter(self, record: logging.LogRecord) -> bool:
53
- """
54
- Filters log records based on their level.
55
-
56
- Args:
57
- record: The log record to check.
58
-
59
- Returns:
60
- True if the record's level is in the allowed levels, False otherwise.
61
- """
62
- return record.levelno in self.levels
63
-
64
-
65
- class CustomFormatter(logging.Formatter):
66
- def formatTime(self, record, datefmt=None):
67
- if hasattr(record, "custom_time"):
68
- # Use the custom time if provided
69
- record.created = record.custom_time.timestamp()
70
- return super().formatTime(record, datefmt)
71
-
72
-
73
- class CustomLogger(logging.Logger):
74
- def __init__(self, name, level=logging.NOTSET):
75
- super().__init__(name, level)
76
-
77
- def _log(
78
- self,
79
- level,
80
- msg,
81
- args,
82
- exc_info=None,
83
- extra=None,
84
- stack_info=False,
85
- stacklevel=1,
86
- custom_time=None,
87
- ):
88
- if extra is None:
89
- extra = {}
90
- # Add custom_time to the extra dictionary if provided
91
- if custom_time:
92
- extra["custom_time"] = custom_time
93
- super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
94
-
95
- def info(self, msg, *args, custom_time=None, **kwargs):
96
- self._log(logging.INFO, msg, args, custom_time=custom_time, **kwargs)
97
-
98
- def debug(self, msg, *args, custom_time=None, **kwargs):
99
- self._log(logging.DEBUG, msg, args, custom_time=custom_time, **kwargs)
100
-
101
- def warning(self, msg, *args, custom_time=None, **kwargs):
102
- self._log(logging.WARNING, msg, args, custom_time=custom_time, **kwargs)
103
-
104
- def error(self, msg, *args, custom_time=None, **kwargs):
105
- self._log(logging.ERROR, msg, args, custom_time=custom_time, **kwargs)
106
-
107
- def critical(self, msg, *args, custom_time=None, **kwargs):
108
- self._log(logging.CRITICAL, msg, args, custom_time=custom_time, **kwargs)
109
-
110
-
111
- def config_logger(log_file: str, console_log=True):
112
- # Use the CustomLogger
113
- logging.setLoggerClass(CustomLogger)
114
- logger = logging.getLogger(__name__)
115
- logger.setLevel(logging.DEBUG)
116
-
117
- # File handler
118
- file_handler = logging.FileHandler(log_file)
119
- file_handler.setLevel(logging.INFO)
120
-
121
- # Custom formatter
122
- formatter = CustomFormatter(
123
- "%(asctime)s - %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
124
- )
125
- file_handler.setFormatter(formatter)
126
-
127
- # Add the handler to the logger
128
- logger.addHandler(file_handler)
129
-
130
- if console_log:
131
- # Handler for the console with a different level
132
- console_handler = logging.StreamHandler()
133
- console_handler.setLevel(logging.DEBUG)
134
- console_handler.setFormatter(formatter)
135
- logger.addHandler(console_handler)
136
-
137
- return logger
1
+ import logging
2
+ from pathlib import Path
3
+ from typing import List
4
+
5
+
6
+ TERMINAL = "\\terminal64.exe"
7
+ BASE_FOLDER = "C:\\Program Files\\"
8
+
9
+ AMG_PATH = BASE_FOLDER + "Admirals Group MT5 Terminal" + TERMINAL
10
+ PGL_PATH = BASE_FOLDER + "Pepperstone MetaTrader 5" + TERMINAL
11
+ FTMO_PATH = BASE_FOLDER + "FTMO MetaTrader 5" + TERMINAL
12
+ JGM_PATH = BASE_FOLDER + "JustMarkets MetaTrader 5" + TERMINAL
13
+
14
+ BROKERS_PATHS = {
15
+ "AMG": AMG_PATH,
16
+ "FTMO": FTMO_PATH,
17
+ "PGL": PGL_PATH,
18
+ "JGM": JGM_PATH,
19
+ }
20
+
21
+
22
+ def get_config_dir(name: str = ".bbstrader") -> Path:
23
+ """
24
+ Get the path to the configuration directory.
25
+
26
+ Args:
27
+ name: The name of the configuration directory.
28
+
29
+ Returns:
30
+ The path to the configuration directory.
31
+ """
32
+ home_dir = Path.home() / name
33
+ if not home_dir.exists():
34
+ home_dir.mkdir()
35
+ return home_dir
36
+
37
+
38
+ BBSTRADER_DIR = get_config_dir()
39
+
40
+
41
+ class LogLevelFilter(logging.Filter):
42
+ def __init__(self, levels: List[int]):
43
+ """
44
+ Initializes the filter with specific logging levels.
45
+
46
+ Args:
47
+ levels: A list of logging level values (integers) to include.
48
+ """
49
+ super().__init__()
50
+ self.levels = levels
51
+
52
+ def filter(self, record: logging.LogRecord) -> bool:
53
+ """
54
+ Filters log records based on their level.
55
+
56
+ Args:
57
+ record: The log record to check.
58
+
59
+ Returns:
60
+ True if the record's level is in the allowed levels, False otherwise.
61
+ """
62
+ return record.levelno in self.levels
63
+
64
+
65
+ class CustomFormatter(logging.Formatter):
66
+ def formatTime(self, record, datefmt=None):
67
+ if hasattr(record, "custom_time"):
68
+ # Use the custom time if provided
69
+ record.created = record.custom_time.timestamp()
70
+ return super().formatTime(record, datefmt)
71
+
72
+
73
+ class CustomLogger(logging.Logger):
74
+ def __init__(self, name, level=logging.NOTSET):
75
+ super().__init__(name, level)
76
+
77
+ def _log(
78
+ self,
79
+ level,
80
+ msg,
81
+ args,
82
+ exc_info=None,
83
+ extra=None,
84
+ stack_info=False,
85
+ stacklevel=1,
86
+ custom_time=None,
87
+ ):
88
+ if extra is None:
89
+ extra = {}
90
+ # Add custom_time to the extra dictionary if provided
91
+ if custom_time:
92
+ extra["custom_time"] = custom_time
93
+ super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
94
+
95
+ def info(self, msg, *args, custom_time=None, **kwargs):
96
+ self._log(logging.INFO, msg, args, custom_time=custom_time, **kwargs)
97
+
98
+ def debug(self, msg, *args, custom_time=None, **kwargs):
99
+ self._log(logging.DEBUG, msg, args, custom_time=custom_time, **kwargs)
100
+
101
+ def warning(self, msg, *args, custom_time=None, **kwargs):
102
+ self._log(logging.WARNING, msg, args, custom_time=custom_time, **kwargs)
103
+
104
+ def error(self, msg, *args, custom_time=None, **kwargs):
105
+ self._log(logging.ERROR, msg, args, custom_time=custom_time, **kwargs)
106
+
107
+ def critical(self, msg, *args, custom_time=None, **kwargs):
108
+ self._log(logging.CRITICAL, msg, args, custom_time=custom_time, **kwargs)
109
+
110
+
111
+ def config_logger(log_file: str, console_log=True):
112
+ # Use the CustomLogger
113
+ logging.setLoggerClass(CustomLogger)
114
+ logger = logging.getLogger(__name__)
115
+ logger.setLevel(logging.DEBUG)
116
+
117
+ # File handler
118
+ file_handler = logging.FileHandler(log_file)
119
+ file_handler.setLevel(logging.INFO)
120
+
121
+ # Custom formatter
122
+ formatter = CustomFormatter(
123
+ "%(asctime)s - %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
124
+ )
125
+ file_handler.setFormatter(formatter)
126
+
127
+ # Add the handler to the logger
128
+ logger.addHandler(file_handler)
129
+
130
+ if console_log:
131
+ # Handler for the console with a different level
132
+ console_handler = logging.StreamHandler()
133
+ console_handler.setLevel(logging.DEBUG)
134
+ console_handler.setFormatter(formatter)
135
+ logger.addHandler(console_handler)
136
+
137
+ return logger
bbstrader/core/data.py CHANGED
@@ -1,22 +1,22 @@
1
- from financetoolkit import Toolkit
2
-
3
- __all__ = [
4
- 'FMP',
5
- ]
6
-
7
-
8
- class FMP(Toolkit):
9
- """
10
- FMPData class for fetching data from Financial Modeling Prep API
11
- using the Toolkit class from financetoolkit package.
12
-
13
- See `financetoolkit` for more details.
14
-
15
- """
16
-
17
- def __init__(self, api_key: str = '', symbols: str | list = 'AAPL'):
18
- super().__init__(tickers=symbols, api_key=api_key)
19
-
20
-
21
- class DataBendo:
22
- ...
1
+ from financetoolkit import Toolkit
2
+
3
+ __all__ = [
4
+ 'FMP',
5
+ ]
6
+
7
+
8
+ class FMP(Toolkit):
9
+ """
10
+ FMPData class for fetching data from Financial Modeling Prep API
11
+ using the Toolkit class from financetoolkit package.
12
+
13
+ See `financetoolkit` for more details.
14
+
15
+ """
16
+
17
+ def __init__(self, api_key: str = '', symbols: str | list = 'AAPL'):
18
+ super().__init__(tickers=symbols, api_key=api_key)
19
+
20
+
21
+ class DataBendo:
22
+ ...
bbstrader/core/utils.py CHANGED
@@ -1,146 +1,146 @@
1
- import configparser
2
- import importlib
3
- import importlib.util
4
- import os
5
- from dataclasses import dataclass
6
- from enum import Enum
7
- from typing import Any, Dict, List
8
-
9
-
10
- def load_module(file_path):
11
- """Load a module from a file path.
12
-
13
- Args:
14
- file_path: Path to the file to load.
15
-
16
- Returns:
17
- The loaded module.
18
- """
19
- if not os.path.exists(file_path):
20
- raise FileNotFoundError(
21
- f"Strategy file {file_path} not found. Please create it."
22
- )
23
- spec = importlib.util.spec_from_file_location("strategies", file_path)
24
- module = importlib.util.module_from_spec(spec)
25
- spec.loader.exec_module(module)
26
- return module
27
-
28
-
29
- def load_class(module, class_name, base_class):
30
- """Load a class from a module.
31
-
32
- Args:
33
- module: The module to load the class from.
34
- class_name: The name of the class to load.
35
- base_class: The base class that the class must inherit from.
36
- """
37
- if not hasattr(module, class_name):
38
- raise AttributeError(f"{class_name} not found in {module}")
39
- class_ = getattr(module, class_name)
40
- if not issubclass(class_, base_class):
41
- raise TypeError(f"{class_name} must inherit from {base_class}.")
42
- return class_
43
-
44
-
45
- def auto_convert(value):
46
- """Convert string values to appropriate data types"""
47
- if value.lower() in {"true", "false"}: # Boolean
48
- return value.lower() == "true"
49
- elif value.lower() in {"none", "null"}: # None
50
- return None
51
- elif value.isdigit():
52
- return int(value)
53
- try:
54
- return float(value)
55
- except ValueError:
56
- return value
57
-
58
-
59
- def dict_from_ini(file_path, sections: str | List[str] = None) -> Dict[str, Any]:
60
- """Reads an INI file and converts it to a dictionary with proper data types.
61
-
62
- Args:
63
- file_path: Path to the INI file to read.
64
- sections: Optional list of sections to read from the INI file.
65
-
66
- Returns:
67
- A dictionary containing the INI file contents with proper data types.
68
- """
69
- config = configparser.ConfigParser()
70
- config.read(file_path)
71
-
72
- ini_dict = {}
73
- for section in config.sections():
74
- ini_dict[section] = {
75
- key: auto_convert(value) for key, value in config.items(section)
76
- }
77
-
78
- if isinstance(sections, str):
79
- try:
80
- return ini_dict[sections]
81
- except KeyError:
82
- raise KeyError(f"{sections} not found in the {file_path} file")
83
- if isinstance(sections, list):
84
- sect_dict = {}
85
- for section in sections:
86
- try:
87
- sect_dict[section] = ini_dict[section]
88
- except KeyError:
89
- raise KeyError(f"{section} not found in the {file_path} file")
90
- return ini_dict
91
-
92
-
93
- class TradeAction(Enum):
94
- """
95
- An enumeration class for trade actions.
96
- """
97
-
98
- BUY = "LONG"
99
- LONG = "LONG"
100
- SELL = "SHORT"
101
- EXIT = "EXIT"
102
- BMKT = "BMKT"
103
- SMKT = "SMKT"
104
- BLMT = "BLMT"
105
- SLMT = "SLMT"
106
- BSTP = "BSTP"
107
- SSTP = "SSTP"
108
- SHORT = "SHORT"
109
- BSTPLMT = "BSTPLMT"
110
- SSTPLMT = "SSTPLMT"
111
- EXIT_LONG = "EXIT_LONG"
112
- EXIT_SHORT = "EXIT_SHORT"
113
- EXIT_STOP = "EXIT_STOP"
114
- EXIT_LIMIT = "EXIT_LIMIT"
115
- EXIT_LONG_STOP = "EXIT_LONG_STOP"
116
- EXIT_LONG_LIMIT = "EXIT_LONG_LIMIT"
117
- EXIT_SHORT_STOP = "EXIT_SHORT_STOP"
118
- EXIT_SHORT_LIMIT = "EXIT_SHORT_LIMIT"
119
- EXIT_LONG_STOP_LIMIT = "EXIT_LONG_STOP_LIMIT"
120
- EXIT_SHORT_STOP_LIMIT = "EXIT_SHORT_STOP_LIMIT"
121
- EXIT_PROFITABLES = "EXIT_PROFITABLES"
122
- EXIT_LOSINGS = "EXIT_LOSINGS"
123
- EXIT_ALL_POSITIONS = "EXIT_ALL_POSITIONS"
124
- EXIT_ALL_ORDERS = "EXIT_ALL_ORDERS"
125
-
126
- def __str__(self):
127
- return self.value
128
-
129
-
130
- @dataclass()
131
- class TradeSignal:
132
- """
133
- A dataclass for storing trading signal.
134
- """
135
-
136
- id: int
137
- symbol: str
138
- action: TradeAction
139
- price: float = None
140
- stoplimit: float = None
141
-
142
- def __repr__(self):
143
- return (
144
- f"TradeSignal(id={self.id}, symbol='{self.symbol}', "
145
- f"action='{self.action.value}', price={self.price}, stoplimit={self.stoplimit})"
146
- )
1
+ import configparser
2
+ import importlib
3
+ import importlib.util
4
+ import os
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from typing import Any, Dict, List
8
+
9
+
10
+ def load_module(file_path):
11
+ """Load a module from a file path.
12
+
13
+ Args:
14
+ file_path: Path to the file to load.
15
+
16
+ Returns:
17
+ The loaded module.
18
+ """
19
+ if not os.path.exists(file_path):
20
+ raise FileNotFoundError(
21
+ f"Strategy file {file_path} not found. Please create it."
22
+ )
23
+ spec = importlib.util.spec_from_file_location("bbstrader.cli", file_path)
24
+ module = importlib.util.module_from_spec(spec)
25
+ spec.loader.exec_module(module)
26
+ return module
27
+
28
+
29
+ def load_class(module, class_name, base_class):
30
+ """Load a class from a module.
31
+
32
+ Args:
33
+ module: The module to load the class from.
34
+ class_name: The name of the class to load.
35
+ base_class: The base class that the class must inherit from.
36
+ """
37
+ if not hasattr(module, class_name):
38
+ raise AttributeError(f"{class_name} not found in {module}")
39
+ class_ = getattr(module, class_name)
40
+ if not issubclass(class_, base_class):
41
+ raise TypeError(f"{class_name} must inherit from {base_class}.")
42
+ return class_
43
+
44
+
45
+ def auto_convert(value):
46
+ """Convert string values to appropriate data types"""
47
+ if value.lower() in {"true", "false"}: # Boolean
48
+ return value.lower() == "true"
49
+ elif value.lower() in {"none", "null"}: # None
50
+ return None
51
+ elif value.isdigit():
52
+ return int(value)
53
+ try:
54
+ return float(value)
55
+ except ValueError:
56
+ return value
57
+
58
+
59
+ def dict_from_ini(file_path, sections: str | List[str] = None) -> Dict[str, Any]:
60
+ """Reads an INI file and converts it to a dictionary with proper data types.
61
+
62
+ Args:
63
+ file_path: Path to the INI file to read.
64
+ sections: Optional list of sections to read from the INI file.
65
+
66
+ Returns:
67
+ A dictionary containing the INI file contents with proper data types.
68
+ """
69
+ config = configparser.ConfigParser(interpolation=None)
70
+ config.read(file_path)
71
+
72
+ ini_dict = {}
73
+ for section in config.sections():
74
+ ini_dict[section] = {
75
+ key: auto_convert(value) for key, value in config.items(section)
76
+ }
77
+
78
+ if isinstance(sections, str):
79
+ try:
80
+ return ini_dict[sections]
81
+ except KeyError:
82
+ raise KeyError(f"{sections} not found in the {file_path} file")
83
+ if isinstance(sections, list):
84
+ sect_dict = {}
85
+ for section in sections:
86
+ try:
87
+ sect_dict[section] = ini_dict[section]
88
+ except KeyError:
89
+ raise KeyError(f"{section} not found in the {file_path} file")
90
+ return ini_dict
91
+
92
+
93
+ class TradeAction(Enum):
94
+ """
95
+ An enumeration class for trade actions.
96
+ """
97
+
98
+ BUY = "LONG"
99
+ LONG = "LONG"
100
+ SELL = "SHORT"
101
+ EXIT = "EXIT"
102
+ BMKT = "BMKT"
103
+ SMKT = "SMKT"
104
+ BLMT = "BLMT"
105
+ SLMT = "SLMT"
106
+ BSTP = "BSTP"
107
+ SSTP = "SSTP"
108
+ SHORT = "SHORT"
109
+ BSTPLMT = "BSTPLMT"
110
+ SSTPLMT = "SSTPLMT"
111
+ EXIT_LONG = "EXIT_LONG"
112
+ EXIT_SHORT = "EXIT_SHORT"
113
+ EXIT_STOP = "EXIT_STOP"
114
+ EXIT_LIMIT = "EXIT_LIMIT"
115
+ EXIT_LONG_STOP = "EXIT_LONG_STOP"
116
+ EXIT_LONG_LIMIT = "EXIT_LONG_LIMIT"
117
+ EXIT_SHORT_STOP = "EXIT_SHORT_STOP"
118
+ EXIT_SHORT_LIMIT = "EXIT_SHORT_LIMIT"
119
+ EXIT_LONG_STOP_LIMIT = "EXIT_LONG_STOP_LIMIT"
120
+ EXIT_SHORT_STOP_LIMIT = "EXIT_SHORT_STOP_LIMIT"
121
+ EXIT_PROFITABLES = "EXIT_PROFITABLES"
122
+ EXIT_LOSINGS = "EXIT_LOSINGS"
123
+ EXIT_ALL_POSITIONS = "EXIT_ALL_POSITIONS"
124
+ EXIT_ALL_ORDERS = "EXIT_ALL_ORDERS"
125
+
126
+ def __str__(self):
127
+ return self.value
128
+
129
+
130
+ @dataclass()
131
+ class TradeSignal:
132
+ """
133
+ A dataclass for storing trading signal.
134
+ """
135
+
136
+ id: int
137
+ symbol: str
138
+ action: TradeAction
139
+ price: float = None
140
+ stoplimit: float = None
141
+
142
+ def __repr__(self):
143
+ return (
144
+ f"TradeSignal(id={self.id}, symbol='{self.symbol}', "
145
+ f"action='{self.action.value}', price={self.price}, stoplimit={self.stoplimit})"
146
+ )
@@ -1,7 +1,7 @@
1
-
2
- from bbstrader.metatrader.account import * # noqa: F403
3
- from bbstrader.metatrader.rates import * # noqa: F403
4
- from bbstrader.metatrader.risk import * # noqa: F403
5
- from bbstrader.metatrader.trade import * # noqa: F403
6
- from bbstrader.metatrader.utils import * # noqa: F403*
1
+
2
+ from bbstrader.metatrader.account import * # noqa: F403
3
+ from bbstrader.metatrader.rates import * # noqa: F403
4
+ from bbstrader.metatrader.risk import * # noqa: F403
5
+ from bbstrader.metatrader.trade import * # noqa: F403
6
+ from bbstrader.metatrader.utils import * # noqa: F403*
7
7
  from bbstrader.metatrader.copier import * # noqa: F403