investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__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 investing-algorithm-framework might be problematic. Click here for more details.
- investing_algorithm_framework/__init__.py +147 -44
- investing_algorithm_framework/app/__init__.py +23 -6
- investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +1322 -707
- investing_algorithm_framework/app/context.py +196 -88
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +16 -5
- investing_algorithm_framework/app/reporting/ascii.py +57 -202
- investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
- investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
- investing_algorithm_framework/app/reporting/generate.py +100 -114
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
- investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
- investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
- investing_algorithm_framework/app/strategy.py +315 -175
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/cli/cli.py +30 -12
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
- investing_algorithm_framework/cli/initialize_app.py +20 -1
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
- investing_algorithm_framework/create_app.py +3 -5
- investing_algorithm_framework/dependency_container.py +25 -39
- investing_algorithm_framework/domain/__init__.py +45 -38
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +27 -0
- investing_algorithm_framework/domain/constants.py +6 -34
- investing_algorithm_framework/domain/data_provider.py +200 -56
- investing_algorithm_framework/domain/exceptions.py +34 -1
- investing_algorithm_framework/domain/models/__init__.py +10 -19
- investing_algorithm_framework/domain/models/base_model.py +0 -6
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -0
- investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
- investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +34 -13
- investing_algorithm_framework/domain/models/order/order_status.py +1 -1
- investing_algorithm_framework/domain/models/order/order_type.py +1 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +9 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +7 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +63 -1
- investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
- investing_algorithm_framework/domain/models/trade/trade.py +56 -32
- investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
- investing_algorithm_framework/domain/order_executor.py +19 -0
- investing_algorithm_framework/domain/portfolio_provider.py +20 -1
- investing_algorithm_framework/domain/services/__init__.py +0 -13
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +5 -1
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +17 -14
- investing_algorithm_framework/download_data.py +40 -10
- investing_algorithm_framework/infrastructure/__init__.py +13 -25
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
- investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
- investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
- investing_algorithm_framework/services/__init__.py +105 -8
- investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
- investing_algorithm_framework/services/configuration_service.py +14 -4
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
- investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
- investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
- investing_algorithm_framework/services/order_service/order_service.py +9 -71
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
- investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
- investing_algorithm_framework/services/repository_service.py +5 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
- investing_algorithm_framework/app/reporting/evaluation.py +0 -243
- investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
- investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
- investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
- investing_algorithm_framework/domain/models/data_source.py +0 -21
- investing_algorithm_framework/domain/models/date_range.py +0 -64
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/services/observable.py +0 -51
- investing_algorithm_framework/domain/services/observer.py +0 -19
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
- investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
- investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
- investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
- /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
|
@@ -1,543 +0,0 @@
|
|
|
1
|
-
import csv
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
from abc import abstractmethod, ABC
|
|
5
|
-
from datetime import datetime, timedelta
|
|
6
|
-
|
|
7
|
-
import polars
|
|
8
|
-
|
|
9
|
-
from investing_algorithm_framework.domain import TimeFrame, \
|
|
10
|
-
OperationalException
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class BacktestMarketDataSource(ABC):
|
|
16
|
-
column_names = []
|
|
17
|
-
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
identifier,
|
|
21
|
-
market,
|
|
22
|
-
symbol,
|
|
23
|
-
backtest_data_start_date=None,
|
|
24
|
-
backtest_data_index_date=None,
|
|
25
|
-
):
|
|
26
|
-
self._identifier = identifier
|
|
27
|
-
self._market = market
|
|
28
|
-
self._symbol = symbol
|
|
29
|
-
self._backtest_data_start_date = backtest_data_start_date
|
|
30
|
-
self._backtest_data_index_date = backtest_data_index_date
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def config(self):
|
|
34
|
-
return self._config
|
|
35
|
-
|
|
36
|
-
@config.setter
|
|
37
|
-
def config(self, value):
|
|
38
|
-
self._config = value
|
|
39
|
-
|
|
40
|
-
def _data_source_exists(self, file_path):
|
|
41
|
-
"""
|
|
42
|
-
Function to check if the data source exists.
|
|
43
|
-
This function will check if the file exists and if the column names
|
|
44
|
-
are correct.
|
|
45
|
-
|
|
46
|
-
This function will return True if the file exists and the column names
|
|
47
|
-
are correct. If the file does not exist or the column names are not
|
|
48
|
-
correct, this function will return False.
|
|
49
|
-
|
|
50
|
-
This function prevents the backtest datasource to download the data
|
|
51
|
-
every time the backtest is run.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
file_path: str - the file path of the data storage file
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
bool - True if the file exists and the column names are correct,
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
if os.path.isfile(file_path):
|
|
62
|
-
df = polars.read_csv(file_path)
|
|
63
|
-
|
|
64
|
-
if df.columns != self.column_names:
|
|
65
|
-
raise OperationalException(
|
|
66
|
-
f"Wrong column names on {file_path}, required "
|
|
67
|
-
f"column names are {self.column_names}"
|
|
68
|
-
)
|
|
69
|
-
else:
|
|
70
|
-
return False
|
|
71
|
-
|
|
72
|
-
return True
|
|
73
|
-
except Exception:
|
|
74
|
-
return False
|
|
75
|
-
|
|
76
|
-
def write_data_to_file_path(self, data_file, data):
|
|
77
|
-
"""
|
|
78
|
-
Function to write data to a csv file.
|
|
79
|
-
This function will write the column names and all the data to the
|
|
80
|
-
file.
|
|
81
|
-
"""
|
|
82
|
-
with open(data_file, "w") as file:
|
|
83
|
-
column_headers = self.column_names
|
|
84
|
-
writer = csv.writer(file)
|
|
85
|
-
writer.writerow(column_headers)
|
|
86
|
-
rows = data
|
|
87
|
-
writer.writerows(rows)
|
|
88
|
-
|
|
89
|
-
@abstractmethod
|
|
90
|
-
def prepare_data(
|
|
91
|
-
self,
|
|
92
|
-
config,
|
|
93
|
-
backtest_start_date,
|
|
94
|
-
backtest_end_date,
|
|
95
|
-
):
|
|
96
|
-
"""
|
|
97
|
-
Function to prepare the data for the backtest.
|
|
98
|
-
This function needs to be implemented by the child class.
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
config: dict - the configuration of the application
|
|
102
|
-
backtest_start_date: datetime - the start date of the backtest
|
|
103
|
-
backtest_end_date: datetime - the end date of the backtest
|
|
104
|
-
|
|
105
|
-
Returns:
|
|
106
|
-
None
|
|
107
|
-
"""
|
|
108
|
-
pass
|
|
109
|
-
|
|
110
|
-
@abstractmethod
|
|
111
|
-
def get_data(self, date, config):
|
|
112
|
-
"""
|
|
113
|
-
Function to get the data for the backtest. This function needs to be
|
|
114
|
-
implemented by the child class.
|
|
115
|
-
|
|
116
|
-
Args:
|
|
117
|
-
date: datetime - the date for which the data is required
|
|
118
|
-
config: dict - the configuration of the application
|
|
119
|
-
"""
|
|
120
|
-
pass
|
|
121
|
-
|
|
122
|
-
@property
|
|
123
|
-
def identifier(self):
|
|
124
|
-
return self._identifier
|
|
125
|
-
|
|
126
|
-
def get_identifier(self):
|
|
127
|
-
return self.identifier
|
|
128
|
-
|
|
129
|
-
@property
|
|
130
|
-
def market(self):
|
|
131
|
-
return self._market
|
|
132
|
-
|
|
133
|
-
def get_market(self):
|
|
134
|
-
return self.market
|
|
135
|
-
|
|
136
|
-
@property
|
|
137
|
-
def symbol(self):
|
|
138
|
-
return self._symbol
|
|
139
|
-
|
|
140
|
-
def get_symbol(self):
|
|
141
|
-
return self.symbol
|
|
142
|
-
|
|
143
|
-
@abstractmethod
|
|
144
|
-
def empty(self):
|
|
145
|
-
pass
|
|
146
|
-
|
|
147
|
-
@property
|
|
148
|
-
def market_credential_service(self):
|
|
149
|
-
return self._market_credential_service
|
|
150
|
-
|
|
151
|
-
@market_credential_service.setter
|
|
152
|
-
def market_credential_service(self, value):
|
|
153
|
-
self._market_credential_service = value
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def backtest_data_start_date(self):
|
|
157
|
-
return self._backtest_data_start_date
|
|
158
|
-
|
|
159
|
-
@backtest_data_start_date.setter
|
|
160
|
-
def backtest_data_start_date(self, value):
|
|
161
|
-
self._backtest_data_start_date = value
|
|
162
|
-
|
|
163
|
-
@property
|
|
164
|
-
def backtest_data_index_date(self):
|
|
165
|
-
return self._backtest_data_index_date
|
|
166
|
-
|
|
167
|
-
@backtest_data_index_date.setter
|
|
168
|
-
def backtest_data_index_date(self, value):
|
|
169
|
-
self._backtest_data_index_date = value
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
class MarketDataSource(ABC):
|
|
173
|
-
|
|
174
|
-
def __init__(
|
|
175
|
-
self,
|
|
176
|
-
market,
|
|
177
|
-
symbol,
|
|
178
|
-
identifier=None,
|
|
179
|
-
storage_path=None
|
|
180
|
-
):
|
|
181
|
-
self._identifier = identifier
|
|
182
|
-
self._market = market
|
|
183
|
-
self._symbol = symbol
|
|
184
|
-
self._market_credential_service = None
|
|
185
|
-
self._config = None
|
|
186
|
-
self._storage_path = storage_path
|
|
187
|
-
|
|
188
|
-
if self._identifier is None:
|
|
189
|
-
self._identifier = f"{self.market}_{self.symbol}"
|
|
190
|
-
|
|
191
|
-
@property
|
|
192
|
-
def config(self):
|
|
193
|
-
return self._config
|
|
194
|
-
|
|
195
|
-
@config.setter
|
|
196
|
-
def config(self, value):
|
|
197
|
-
self._config = value
|
|
198
|
-
|
|
199
|
-
def initialize(self, config):
|
|
200
|
-
pass
|
|
201
|
-
|
|
202
|
-
@property
|
|
203
|
-
def identifier(self):
|
|
204
|
-
return self._identifier
|
|
205
|
-
|
|
206
|
-
@identifier.setter
|
|
207
|
-
def identifier(self, value):
|
|
208
|
-
self._identifier = value
|
|
209
|
-
|
|
210
|
-
def get_identifier(self):
|
|
211
|
-
return self.identifier
|
|
212
|
-
|
|
213
|
-
@property
|
|
214
|
-
def market(self):
|
|
215
|
-
return self._market
|
|
216
|
-
|
|
217
|
-
def get_market(self):
|
|
218
|
-
return self.market
|
|
219
|
-
|
|
220
|
-
@property
|
|
221
|
-
def symbol(self):
|
|
222
|
-
return self._symbol
|
|
223
|
-
|
|
224
|
-
def get_symbol(self):
|
|
225
|
-
return self.symbol
|
|
226
|
-
|
|
227
|
-
@property
|
|
228
|
-
def storage_path(self):
|
|
229
|
-
return self._storage_path
|
|
230
|
-
|
|
231
|
-
def get_storage_path(self):
|
|
232
|
-
return self.storage_path
|
|
233
|
-
|
|
234
|
-
@abstractmethod
|
|
235
|
-
def get_data(
|
|
236
|
-
self,
|
|
237
|
-
start_date: datetime = None,
|
|
238
|
-
end_date: datetime = None,
|
|
239
|
-
config=None,
|
|
240
|
-
):
|
|
241
|
-
"""
|
|
242
|
-
Get data from the market data source.
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
config (dict): the configuration of the application
|
|
246
|
-
start_date (optional) (datetime): the start date of the data
|
|
247
|
-
end_date (optional) (datetime): the end date of the data
|
|
248
|
-
|
|
249
|
-
Returns:
|
|
250
|
-
DataFrame: the data from the market data source
|
|
251
|
-
"""
|
|
252
|
-
pass
|
|
253
|
-
|
|
254
|
-
@abstractmethod
|
|
255
|
-
def to_backtest_market_data_source(self) -> BacktestMarketDataSource:
|
|
256
|
-
pass
|
|
257
|
-
|
|
258
|
-
@property
|
|
259
|
-
def market_credential_service(self):
|
|
260
|
-
return self._market_credential_service
|
|
261
|
-
|
|
262
|
-
@market_credential_service.setter
|
|
263
|
-
def market_credential_service(self, value):
|
|
264
|
-
self._market_credential_service = value
|
|
265
|
-
|
|
266
|
-
@staticmethod
|
|
267
|
-
def get_file_name_symbol(file_path):
|
|
268
|
-
"""
|
|
269
|
-
Static function that extracts the symbol from a give data filepath,
|
|
270
|
-
given that the data file path is in the format
|
|
271
|
-
{DATA_TYPE}_{TARGET_SYMBOL}_{TRADING_SYMBOL}_{MARKET}_
|
|
272
|
-
{time_frame}_{START_DATETIME}_{END_DATETIME}.csv
|
|
273
|
-
|
|
274
|
-
Parameters:
|
|
275
|
-
file_path: str - the given file path of
|
|
276
|
-
the data storage file
|
|
277
|
-
|
|
278
|
-
Returns:
|
|
279
|
-
string representing the symbol
|
|
280
|
-
"""
|
|
281
|
-
parts = file_path.split("_")
|
|
282
|
-
|
|
283
|
-
if len(parts) < 6:
|
|
284
|
-
return None
|
|
285
|
-
|
|
286
|
-
return "".join([parts[1], '/', parts[2]])
|
|
287
|
-
|
|
288
|
-
@staticmethod
|
|
289
|
-
def get_file_name_time_frame(file_path):
|
|
290
|
-
"""
|
|
291
|
-
Static function that extracts the time_frame from a give data filepath,
|
|
292
|
-
given that the data file path is in the format
|
|
293
|
-
{DATA_TYPE}_{TARGET_SYMBOL}_{TRADING_SYMBOL}_{MARKET}_
|
|
294
|
-
{time_frame}_{START_DATETIME}_{END_DATETIME}.csv
|
|
295
|
-
|
|
296
|
-
Parameters:
|
|
297
|
-
file_path: str - the given file path of the data storage file
|
|
298
|
-
|
|
299
|
-
Returns:
|
|
300
|
-
string representing the time_frame
|
|
301
|
-
"""
|
|
302
|
-
parts = file_path.split("_")
|
|
303
|
-
|
|
304
|
-
if len(parts) < 6:
|
|
305
|
-
return None
|
|
306
|
-
|
|
307
|
-
return TimeFrame.from_string(parts[4])
|
|
308
|
-
|
|
309
|
-
@staticmethod
|
|
310
|
-
def get_file_name_market(file_path):
|
|
311
|
-
"""
|
|
312
|
-
Static function that extracts the time_frame from a give data filepath,
|
|
313
|
-
given that the data file path is in the format
|
|
314
|
-
{DATA_TYPE}_{TARGET_SYMBOL}_{TRADING_SYMBOL}_{MARKET}
|
|
315
|
-
_{time_frame}_{START_DATETIME}_{END_DATETIME}.csv
|
|
316
|
-
|
|
317
|
-
Parameters:
|
|
318
|
-
file_path: str - the given file path of the data storage file
|
|
319
|
-
|
|
320
|
-
Returns:
|
|
321
|
-
string representing the market
|
|
322
|
-
"""
|
|
323
|
-
parts = file_path.split("_")
|
|
324
|
-
|
|
325
|
-
if len(parts) < 6:
|
|
326
|
-
return None
|
|
327
|
-
|
|
328
|
-
return TimeFrame.from_string(parts[3])
|
|
329
|
-
|
|
330
|
-
@staticmethod
|
|
331
|
-
def get_file_name_start_datetime(file_path):
|
|
332
|
-
"""
|
|
333
|
-
Static function that extracts the time_frame from a give data filepath,
|
|
334
|
-
given that the data file path is in the format
|
|
335
|
-
{DATA_TYPE}_{TARGET_SYMBOL}_{TRADING_SYMBOL}_{MARKET}_
|
|
336
|
-
{time_frame}_{START_DATETIME}_{END_DATETIME}.csv
|
|
337
|
-
|
|
338
|
-
Parameters:
|
|
339
|
-
file_path: str - the given file path of the data storage file
|
|
340
|
-
|
|
341
|
-
Returns:
|
|
342
|
-
string representing the start datetime
|
|
343
|
-
"""
|
|
344
|
-
parts = file_path.split("_")
|
|
345
|
-
|
|
346
|
-
if len(parts) < 6:
|
|
347
|
-
return None
|
|
348
|
-
|
|
349
|
-
return TimeFrame.from_string(parts[5])
|
|
350
|
-
|
|
351
|
-
@staticmethod
|
|
352
|
-
def get_file_name_end_datetime(file_path):
|
|
353
|
-
"""
|
|
354
|
-
Static function that extracts the time_frame
|
|
355
|
-
from a give data filepath, given that the data file
|
|
356
|
-
path is in the format
|
|
357
|
-
{DATA_TYPE}_{TARGET_SYMBOL}_{TRADING_SYMBOL}_{MARKET}_
|
|
358
|
-
{time_frame}_{START_DATETIME}_{END_DATETIME}.csv
|
|
359
|
-
|
|
360
|
-
Parameters:
|
|
361
|
-
file_path: str - the given file path of the data storage file
|
|
362
|
-
|
|
363
|
-
Returns:
|
|
364
|
-
string representing the end datetime
|
|
365
|
-
"""
|
|
366
|
-
parts = file_path.split("_")
|
|
367
|
-
|
|
368
|
-
if len(parts) < 6:
|
|
369
|
-
return None
|
|
370
|
-
|
|
371
|
-
return TimeFrame.from_string(parts[6])
|
|
372
|
-
|
|
373
|
-
@staticmethod
|
|
374
|
-
def create_storage_file_path(
|
|
375
|
-
storage_path,
|
|
376
|
-
data_type,
|
|
377
|
-
symbol,
|
|
378
|
-
market,
|
|
379
|
-
time_frame,
|
|
380
|
-
start_datetime,
|
|
381
|
-
end_datetime,
|
|
382
|
-
):
|
|
383
|
-
"""
|
|
384
|
-
Static function that creates a storage file path given the parameters
|
|
385
|
-
|
|
386
|
-
Parameters:
|
|
387
|
-
storage_path: str - the storage path of the data storage file
|
|
388
|
-
data_type: str - the type of data
|
|
389
|
-
symbol: str - the asset symbol
|
|
390
|
-
market: str - the market
|
|
391
|
-
time_frame: str - the time_frame
|
|
392
|
-
start_datetime: datetime - the start datetime
|
|
393
|
-
end_datetime: datetime - the end datetime
|
|
394
|
-
|
|
395
|
-
Returns:
|
|
396
|
-
string representing the storage file path
|
|
397
|
-
"""
|
|
398
|
-
|
|
399
|
-
target_symbol, trading_symbol = symbol.split('/')
|
|
400
|
-
path = os.path.join(
|
|
401
|
-
storage_path,
|
|
402
|
-
f"{data_type}_{target_symbol}_{trading_symbol}_{market}_" +
|
|
403
|
-
f"{time_frame}_{start_datetime}_{end_datetime}.csv"
|
|
404
|
-
)
|
|
405
|
-
return path
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
class OHLCVMarketDataSource(MarketDataSource, ABC):
|
|
409
|
-
"""
|
|
410
|
-
Abstract class for ohlcv market data sources.
|
|
411
|
-
"""
|
|
412
|
-
def __init__(
|
|
413
|
-
self,
|
|
414
|
-
market,
|
|
415
|
-
symbol,
|
|
416
|
-
time_frame,
|
|
417
|
-
identifier=None,
|
|
418
|
-
window_size=None,
|
|
419
|
-
storage_path=None,
|
|
420
|
-
):
|
|
421
|
-
super().__init__(
|
|
422
|
-
identifier=identifier,
|
|
423
|
-
market=market,
|
|
424
|
-
symbol=symbol,
|
|
425
|
-
storage_path=storage_path
|
|
426
|
-
)
|
|
427
|
-
self._window_size = window_size
|
|
428
|
-
self._time_frame = time_frame
|
|
429
|
-
|
|
430
|
-
@property
|
|
431
|
-
def time_frame(self):
|
|
432
|
-
return self._time_frame
|
|
433
|
-
|
|
434
|
-
def get_time_frame(self):
|
|
435
|
-
return self.time_frame
|
|
436
|
-
|
|
437
|
-
def create_start_date(self, end_date, time_frame, window_size):
|
|
438
|
-
minutes = TimeFrame.from_value(time_frame).amount_of_minutes
|
|
439
|
-
return end_date - timedelta(minutes=window_size * minutes)
|
|
440
|
-
|
|
441
|
-
def create_end_date(self, start_date, time_frame, window_size):
|
|
442
|
-
minutes = TimeFrame.from_value(time_frame).amount_of_minutes
|
|
443
|
-
return start_date + timedelta(minutes=window_size * minutes)
|
|
444
|
-
|
|
445
|
-
@property
|
|
446
|
-
def window_size(self):
|
|
447
|
-
return self._window_size
|
|
448
|
-
|
|
449
|
-
@window_size.setter
|
|
450
|
-
def window_size(self, value):
|
|
451
|
-
|
|
452
|
-
if not isinstance(value, int):
|
|
453
|
-
raise OperationalException(
|
|
454
|
-
"Window size must be an integer"
|
|
455
|
-
)
|
|
456
|
-
|
|
457
|
-
self._window_size = value
|
|
458
|
-
|
|
459
|
-
def get_date_ranges(
|
|
460
|
-
self,
|
|
461
|
-
start_date: datetime,
|
|
462
|
-
end_date: datetime,
|
|
463
|
-
window_size: int,
|
|
464
|
-
time_frame
|
|
465
|
-
):
|
|
466
|
-
"""
|
|
467
|
-
Function to get the date ranges of the market data source based
|
|
468
|
-
on the window size and the time_frame. The date ranges
|
|
469
|
-
will be calculated based on the start date and the end date.
|
|
470
|
-
|
|
471
|
-
Args:
|
|
472
|
-
start_date: datetime - The start date
|
|
473
|
-
end_date: datetime - The end date
|
|
474
|
-
window_size: int - The window size
|
|
475
|
-
time_frame: str - The time frame
|
|
476
|
-
|
|
477
|
-
Returns:
|
|
478
|
-
list - A list of tuples with the date ranges
|
|
479
|
-
"""
|
|
480
|
-
|
|
481
|
-
if start_date > end_date:
|
|
482
|
-
raise OperationalException(
|
|
483
|
-
"Start date must be before end date"
|
|
484
|
-
)
|
|
485
|
-
|
|
486
|
-
time_frame = TimeFrame.from_value(time_frame)
|
|
487
|
-
new_end_date = start_date + timedelta(
|
|
488
|
-
minutes=window_size * time_frame.amount_of_minutes
|
|
489
|
-
)
|
|
490
|
-
ranges = [(start_date, new_end_date)]
|
|
491
|
-
start_date = new_end_date
|
|
492
|
-
|
|
493
|
-
if new_end_date > end_date:
|
|
494
|
-
return [(start_date, end_date)]
|
|
495
|
-
|
|
496
|
-
while start_date < end_date:
|
|
497
|
-
new_end_date = start_date + timedelta(
|
|
498
|
-
minutes=self.window_size * time_frame.amount_of_minutes
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
if new_end_date > end_date:
|
|
502
|
-
new_end_date = end_date
|
|
503
|
-
|
|
504
|
-
ranges.append((start_date, new_end_date))
|
|
505
|
-
start_date = new_end_date
|
|
506
|
-
|
|
507
|
-
return ranges
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
class TickerMarketDataSource(MarketDataSource, ABC):
|
|
511
|
-
"""
|
|
512
|
-
Abstract class for ticker market data sources.
|
|
513
|
-
"""
|
|
514
|
-
|
|
515
|
-
def __init__(
|
|
516
|
-
self,
|
|
517
|
-
identifier,
|
|
518
|
-
market=None,
|
|
519
|
-
symbol=None,
|
|
520
|
-
):
|
|
521
|
-
super().__init__(
|
|
522
|
-
identifier=identifier,
|
|
523
|
-
market=market,
|
|
524
|
-
symbol=symbol,
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
class OrderBookMarketDataSource(MarketDataSource, ABC):
|
|
529
|
-
"""
|
|
530
|
-
Abstract class for order book market data sources.
|
|
531
|
-
"""
|
|
532
|
-
|
|
533
|
-
def __init__(
|
|
534
|
-
self,
|
|
535
|
-
identifier,
|
|
536
|
-
market,
|
|
537
|
-
symbol,
|
|
538
|
-
):
|
|
539
|
-
super().__init__(
|
|
540
|
-
identifier=identifier,
|
|
541
|
-
market=market,
|
|
542
|
-
symbol=symbol,
|
|
543
|
-
)
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from typing import Dict
|
|
5
|
-
|
|
6
|
-
from polars import DataFrame
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger("investing_algorithm_framework")
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MarketService(ABC):
|
|
12
|
-
|
|
13
|
-
def __init__(self, market_credential_service):
|
|
14
|
-
self._market_credential_service = market_credential_service
|
|
15
|
-
self._config = None
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def config(self):
|
|
19
|
-
return self._config
|
|
20
|
-
|
|
21
|
-
@config.setter
|
|
22
|
-
def config(self, value):
|
|
23
|
-
self._config = value
|
|
24
|
-
|
|
25
|
-
@abstractmethod
|
|
26
|
-
def pair_exists(
|
|
27
|
-
self,
|
|
28
|
-
market,
|
|
29
|
-
target_symbol: str,
|
|
30
|
-
trading_symbol: str,
|
|
31
|
-
):
|
|
32
|
-
raise NotImplementedError()
|
|
33
|
-
|
|
34
|
-
@abstractmethod
|
|
35
|
-
def get_ticker(self, symbol, market):
|
|
36
|
-
raise NotImplementedError()
|
|
37
|
-
|
|
38
|
-
@abstractmethod
|
|
39
|
-
def get_tickers(self, symbols, market):
|
|
40
|
-
raise NotImplementedError()
|
|
41
|
-
|
|
42
|
-
@abstractmethod
|
|
43
|
-
def get_order_book(self, symbol, market):
|
|
44
|
-
raise NotImplementedError()
|
|
45
|
-
|
|
46
|
-
@abstractmethod
|
|
47
|
-
def get_order_books(self, symbols, market):
|
|
48
|
-
raise NotImplementedError()
|
|
49
|
-
|
|
50
|
-
@abstractmethod
|
|
51
|
-
def get_order(self, order, market):
|
|
52
|
-
raise NotImplementedError()
|
|
53
|
-
|
|
54
|
-
@abstractmethod
|
|
55
|
-
def get_orders(self, symbol, market, since: datetime = None):
|
|
56
|
-
raise NotImplementedError()
|
|
57
|
-
|
|
58
|
-
@abstractmethod
|
|
59
|
-
def get_balance(self, market) -> Dict[str, float]:
|
|
60
|
-
raise NotImplementedError()
|
|
61
|
-
|
|
62
|
-
@abstractmethod
|
|
63
|
-
def create_limit_buy_order(
|
|
64
|
-
self,
|
|
65
|
-
target_symbol: str,
|
|
66
|
-
trading_symbol: str,
|
|
67
|
-
amount: float,
|
|
68
|
-
price: float,
|
|
69
|
-
market
|
|
70
|
-
):
|
|
71
|
-
raise NotImplementedError()
|
|
72
|
-
|
|
73
|
-
@abstractmethod
|
|
74
|
-
def create_limit_sell_order(
|
|
75
|
-
self,
|
|
76
|
-
target_symbol: str,
|
|
77
|
-
trading_symbol: str,
|
|
78
|
-
amount: float,
|
|
79
|
-
price: float,
|
|
80
|
-
market
|
|
81
|
-
):
|
|
82
|
-
raise NotImplementedError()
|
|
83
|
-
|
|
84
|
-
@abstractmethod
|
|
85
|
-
def create_market_sell_order(
|
|
86
|
-
self,
|
|
87
|
-
target_symbol: str,
|
|
88
|
-
trading_symbol: str,
|
|
89
|
-
amount: float,
|
|
90
|
-
market
|
|
91
|
-
):
|
|
92
|
-
raise NotImplementedError()
|
|
93
|
-
|
|
94
|
-
@abstractmethod
|
|
95
|
-
def cancel_order(self, order, market):
|
|
96
|
-
raise NotImplementedError()
|
|
97
|
-
|
|
98
|
-
@abstractmethod
|
|
99
|
-
def get_open_orders(
|
|
100
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
101
|
-
):
|
|
102
|
-
raise NotImplementedError()
|
|
103
|
-
|
|
104
|
-
@abstractmethod
|
|
105
|
-
def get_closed_orders(
|
|
106
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
107
|
-
):
|
|
108
|
-
raise NotImplementedError()
|
|
109
|
-
|
|
110
|
-
@abstractmethod
|
|
111
|
-
def get_ohlcv(
|
|
112
|
-
self, symbol, time_frame, from_timestamp, market, to_timestamp=None
|
|
113
|
-
) -> DataFrame:
|
|
114
|
-
raise NotImplementedError()
|
|
115
|
-
|
|
116
|
-
@abstractmethod
|
|
117
|
-
def get_ohlcvs(
|
|
118
|
-
self, symbols, time_frame, from_timestamp, market, to_timestamp=None
|
|
119
|
-
) -> Dict[str, DataFrame]:
|
|
120
|
-
raise NotImplementedError()
|
|
121
|
-
|
|
122
|
-
@property
|
|
123
|
-
def market_credentials(self):
|
|
124
|
-
|
|
125
|
-
if self._market_credential_service is None:
|
|
126
|
-
return []
|
|
127
|
-
|
|
128
|
-
return self._market_credential_service.get_all()
|
|
129
|
-
|
|
130
|
-
def get_market_credentials(self):
|
|
131
|
-
return self._market_credential_service.get_all()
|
|
132
|
-
|
|
133
|
-
def get_market_credential(self, market):
|
|
134
|
-
|
|
135
|
-
if self.market_credentials is None:
|
|
136
|
-
return None
|
|
137
|
-
|
|
138
|
-
if market is None:
|
|
139
|
-
return None
|
|
140
|
-
|
|
141
|
-
for market_data_credentials in self.market_credentials:
|
|
142
|
-
|
|
143
|
-
if market_data_credentials.market.upper() == market.upper():
|
|
144
|
-
return market_data_credentials
|
|
145
|
-
|
|
146
|
-
return None
|
|
147
|
-
|
|
148
|
-
@abstractmethod
|
|
149
|
-
def get_symbols(self, market):
|
|
150
|
-
"""
|
|
151
|
-
Get all available symbols for a market
|
|
152
|
-
"""
|
|
153
|
-
raise NotImplementedError()
|