bbstrader 0.2.4__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 +18 -0
- bbstrader/btengine/__init__.py +54 -0
- bbstrader/btengine/backtest.py +360 -0
- bbstrader/btengine/data.py +712 -0
- bbstrader/btengine/event.py +221 -0
- bbstrader/btengine/execution.py +251 -0
- bbstrader/btengine/performance.py +347 -0
- bbstrader/btengine/portfolio.py +406 -0
- bbstrader/btengine/strategy.py +779 -0
- bbstrader/config.py +133 -0
- bbstrader/core/__init__.py +0 -0
- bbstrader/core/data.py +22 -0
- bbstrader/core/utils.py +57 -0
- bbstrader/ibkr/__init__.py +0 -0
- bbstrader/ibkr/utils.py +0 -0
- bbstrader/metatrader/__init__.py +6 -0
- bbstrader/metatrader/account.py +1488 -0
- bbstrader/metatrader/rates.py +579 -0
- bbstrader/metatrader/risk.py +702 -0
- bbstrader/metatrader/trade.py +1690 -0
- bbstrader/metatrader/utils.py +641 -0
- bbstrader/models/__init__.py +10 -0
- bbstrader/models/factors.py +312 -0
- bbstrader/models/ml.py +1264 -0
- bbstrader/models/optimization.py +182 -0
- bbstrader/models/portfolio.py +223 -0
- bbstrader/models/risk.py +398 -0
- bbstrader/trading/__init__.py +11 -0
- bbstrader/trading/execution.py +726 -0
- bbstrader/trading/scripts.py +67 -0
- bbstrader/trading/strategies.py +860 -0
- bbstrader/tseries.py +1816 -0
- bbstrader-0.2.4.dist-info/LICENSE +21 -0
- bbstrader-0.2.4.dist-info/METADATA +174 -0
- bbstrader-0.2.4.dist-info/RECORD +37 -0
- bbstrader-0.2.4.dist-info/WHEEL +5 -0
- bbstrader-0.2.4.dist-info/top_level.txt +1 -0
bbstrader/config.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
AMG_PATH = "C:\\Program Files\\Admirals Group MT5 Terminal\\terminal64.exe"
|
|
6
|
+
FTMO_PATH = "C:\\Program Files\\FTMO MetaTrader 5\\terminal64.exe"
|
|
7
|
+
XCB_PATH = "C:\\Program Files\\4xCube MT5 Terminal\\terminal64.exe"
|
|
8
|
+
TML_PATH = "C:\\Program Files\\Trinota Markets MetaTrader 5 Terminal\\terminal64.exe"
|
|
9
|
+
|
|
10
|
+
BROKERS_PATHS = {
|
|
11
|
+
"AMG": AMG_PATH,
|
|
12
|
+
"FTMO": FTMO_PATH,
|
|
13
|
+
"XCB": XCB_PATH,
|
|
14
|
+
"TML": TML_PATH,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_config_dir(name: str = ".bbstrader") -> Path:
|
|
19
|
+
"""
|
|
20
|
+
Get the path to the configuration directory.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
name: The name of the configuration directory.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
The path to the configuration directory.
|
|
27
|
+
"""
|
|
28
|
+
home_dir = Path.home() / name
|
|
29
|
+
if not home_dir.exists():
|
|
30
|
+
home_dir.mkdir()
|
|
31
|
+
return home_dir
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
BBSTRADER_DIR = get_config_dir()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LogLevelFilter(logging.Filter):
|
|
38
|
+
def __init__(self, levels: List[int]):
|
|
39
|
+
"""
|
|
40
|
+
Initializes the filter with specific logging levels.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
levels: A list of logging level values (integers) to include.
|
|
44
|
+
"""
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.levels = levels
|
|
47
|
+
|
|
48
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Filters log records based on their level.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
record: The log record to check.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if the record's level is in the allowed levels, False otherwise.
|
|
57
|
+
"""
|
|
58
|
+
return record.levelno in self.levels
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CustomFormatter(logging.Formatter):
|
|
62
|
+
def formatTime(self, record, datefmt=None):
|
|
63
|
+
if hasattr(record, "custom_time"):
|
|
64
|
+
# Use the custom time if provided
|
|
65
|
+
record.created = record.custom_time.timestamp()
|
|
66
|
+
return super().formatTime(record, datefmt)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CustomLogger(logging.Logger):
|
|
70
|
+
def __init__(self, name, level=logging.NOTSET):
|
|
71
|
+
super().__init__(name, level)
|
|
72
|
+
|
|
73
|
+
def _log(
|
|
74
|
+
self,
|
|
75
|
+
level,
|
|
76
|
+
msg,
|
|
77
|
+
args,
|
|
78
|
+
exc_info=None,
|
|
79
|
+
extra=None,
|
|
80
|
+
stack_info=False,
|
|
81
|
+
stacklevel=1,
|
|
82
|
+
custom_time=None,
|
|
83
|
+
):
|
|
84
|
+
if extra is None:
|
|
85
|
+
extra = {}
|
|
86
|
+
# Add custom_time to the extra dictionary if provided
|
|
87
|
+
if custom_time:
|
|
88
|
+
extra["custom_time"] = custom_time
|
|
89
|
+
super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
|
|
90
|
+
|
|
91
|
+
def info(self, msg, *args, custom_time=None, **kwargs):
|
|
92
|
+
self._log(logging.INFO, msg, args, custom_time=custom_time, **kwargs)
|
|
93
|
+
|
|
94
|
+
def debug(self, msg, *args, custom_time=None, **kwargs):
|
|
95
|
+
self._log(logging.DEBUG, msg, args, custom_time=custom_time, **kwargs)
|
|
96
|
+
|
|
97
|
+
def warning(self, msg, *args, custom_time=None, **kwargs):
|
|
98
|
+
self._log(logging.WARNING, msg, args, custom_time=custom_time, **kwargs)
|
|
99
|
+
|
|
100
|
+
def error(self, msg, *args, custom_time=None, **kwargs):
|
|
101
|
+
self._log(logging.ERROR, msg, args, custom_time=custom_time, **kwargs)
|
|
102
|
+
|
|
103
|
+
def critical(self, msg, *args, custom_time=None, **kwargs):
|
|
104
|
+
self._log(logging.CRITICAL, msg, args, custom_time=custom_time, **kwargs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def config_logger(log_file: str, console_log=True):
|
|
108
|
+
# Use the CustomLogger
|
|
109
|
+
logging.setLoggerClass(CustomLogger)
|
|
110
|
+
logger = logging.getLogger(__name__)
|
|
111
|
+
logger.setLevel(logging.DEBUG)
|
|
112
|
+
|
|
113
|
+
# File handler
|
|
114
|
+
file_handler = logging.FileHandler(log_file)
|
|
115
|
+
file_handler.setLevel(logging.INFO)
|
|
116
|
+
|
|
117
|
+
# Custom formatter
|
|
118
|
+
formatter = CustomFormatter(
|
|
119
|
+
"%(asctime)s - %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
120
|
+
)
|
|
121
|
+
file_handler.setFormatter(formatter)
|
|
122
|
+
|
|
123
|
+
# Add the handler to the logger
|
|
124
|
+
logger.addHandler(file_handler)
|
|
125
|
+
|
|
126
|
+
if console_log:
|
|
127
|
+
# Handler for the console with a different level
|
|
128
|
+
console_handler = logging.StreamHandler()
|
|
129
|
+
console_handler.setLevel(logging.DEBUG)
|
|
130
|
+
console_handler.setFormatter(formatter)
|
|
131
|
+
logger.addHandler(console_handler)
|
|
132
|
+
|
|
133
|
+
return logger
|
|
File without changes
|
bbstrader/core/data.py
ADDED
|
@@ -0,0 +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
|
+
...
|
bbstrader/core/utils.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TradeAction(Enum):
|
|
6
|
+
"""
|
|
7
|
+
An enumeration class for trade actions.
|
|
8
|
+
"""
|
|
9
|
+
BUY = "LONG"
|
|
10
|
+
LONG = "LONG"
|
|
11
|
+
SELL = "SHORT"
|
|
12
|
+
EXIT = "EXIT"
|
|
13
|
+
BMKT = "BMKT"
|
|
14
|
+
SMKT = "SMKT"
|
|
15
|
+
BLMT = "BLMT"
|
|
16
|
+
SLMT = "SLMT"
|
|
17
|
+
BSTP = "BSTP"
|
|
18
|
+
SSTP = "SSTP"
|
|
19
|
+
SHORT = "SHORT"
|
|
20
|
+
BSTPLMT = "BSTPLMT"
|
|
21
|
+
SSTPLMT = "SSTPLMT"
|
|
22
|
+
EXIT_LONG = "EXIT_LONG"
|
|
23
|
+
EXIT_SHORT = "EXIT_SHORT"
|
|
24
|
+
EXIT_STOP = "EXIT_STOP"
|
|
25
|
+
EXIT_LIMIT = "EXIT_LIMIT"
|
|
26
|
+
EXIT_LONG_STOP = "EXIT_LONG_STOP"
|
|
27
|
+
EXIT_LONG_LIMIT = "EXIT_LONG_LIMIT"
|
|
28
|
+
EXIT_SHORT_STOP = "EXIT_SHORT_STOP"
|
|
29
|
+
EXIT_SHORT_LIMIT = "EXIT_SHORT_LIMIT"
|
|
30
|
+
EXIT_LONG_STOP_LIMIT = "EXIT_LONG_STOP_LIMIT"
|
|
31
|
+
EXIT_SHORT_STOP_LIMIT = "EXIT_SHORT_STOP_LIMIT"
|
|
32
|
+
EXIT_PROFITABLES = "EXIT_PROFITABLES"
|
|
33
|
+
EXIT_LOSINGS = "EXIT_LOSINGS"
|
|
34
|
+
EXIT_ALL_POSITIONS = "EXIT_ALL_POSITIONS"
|
|
35
|
+
EXIT_ALL_ORDERS = "EXIT_ALL_ORDERS"
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
return self.value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass()
|
|
42
|
+
class TradeSignal:
|
|
43
|
+
"""
|
|
44
|
+
A dataclass for storing trading signal.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
id: int
|
|
48
|
+
symbol: str
|
|
49
|
+
action: TradeAction
|
|
50
|
+
price: float = None
|
|
51
|
+
stoplimit: float = None
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
return (
|
|
55
|
+
f"TradeSignal(id={self.id}, symbol='{self.symbol}', "
|
|
56
|
+
f"action='{self.action.value}', price={self.price}, stoplimit={self.stoplimit})"
|
|
57
|
+
)
|
|
File without changes
|
bbstrader/ibkr/utils.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
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
|