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,471 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from datetime import datetime
|
|
3
|
-
from time import sleep
|
|
4
|
-
from typing import Dict
|
|
5
|
-
|
|
6
|
-
import ccxt
|
|
7
|
-
import polars as pl
|
|
8
|
-
from dateutil import parser
|
|
9
|
-
|
|
10
|
-
from investing_algorithm_framework.domain import OperationalException, Order, \
|
|
11
|
-
MarketService, DATETIME_FORMAT
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class CCXTMarketService(MarketService):
|
|
17
|
-
"""
|
|
18
|
-
Market service implementation using the CCXT library
|
|
19
|
-
"""
|
|
20
|
-
msec = 1000
|
|
21
|
-
minute = 60 * msec
|
|
22
|
-
|
|
23
|
-
def __init__(self, market_credential_service):
|
|
24
|
-
super(CCXTMarketService, self).__init__(
|
|
25
|
-
market_credential_service=market_credential_service,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def config(self):
|
|
30
|
-
return self._config
|
|
31
|
-
|
|
32
|
-
@config.setter
|
|
33
|
-
def config(self, config):
|
|
34
|
-
self._config = config
|
|
35
|
-
|
|
36
|
-
def initialize_exchange(self, market, market_credential):
|
|
37
|
-
market = market.lower()
|
|
38
|
-
if not hasattr(ccxt, market):
|
|
39
|
-
raise OperationalException(
|
|
40
|
-
f"No market service found for market id {market}"
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
exchange_class = getattr(ccxt, market)
|
|
44
|
-
|
|
45
|
-
if exchange_class is None:
|
|
46
|
-
raise OperationalException(
|
|
47
|
-
f"No market service found for market id {market}"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
if market_credential is not None:
|
|
51
|
-
exchange = exchange_class({
|
|
52
|
-
'apiKey': market_credential.api_key,
|
|
53
|
-
'secret': market_credential.secret_key,
|
|
54
|
-
})
|
|
55
|
-
else:
|
|
56
|
-
exchange = exchange_class({})
|
|
57
|
-
|
|
58
|
-
return exchange
|
|
59
|
-
|
|
60
|
-
def pair_exists(self, target_symbol: str, trading_symbol: str, market):
|
|
61
|
-
market_credential = self.get_market_credential(market)
|
|
62
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
63
|
-
|
|
64
|
-
if not exchange.has['fetchTicker']:
|
|
65
|
-
raise OperationalException(
|
|
66
|
-
f"Market service {market} does not support "
|
|
67
|
-
f"functionality pair_exists"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
71
|
-
try:
|
|
72
|
-
data = exchange.fetchTicker(symbol)
|
|
73
|
-
return "symbol" in data
|
|
74
|
-
except OperationalException:
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
|
-
def get_ticker(self, symbol, market):
|
|
78
|
-
market_credential = self.get_market_credential(market)
|
|
79
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
80
|
-
|
|
81
|
-
if not exchange.has['fetchTicker']:
|
|
82
|
-
raise OperationalException(
|
|
83
|
-
f"Market service {market} does not support "
|
|
84
|
-
f"functionality get_ticker"
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
try:
|
|
88
|
-
return exchange.fetchTicker(symbol)
|
|
89
|
-
except Exception as e:
|
|
90
|
-
logger.exception(e)
|
|
91
|
-
raise OperationalException(
|
|
92
|
-
f"Could not retrieve ticker for symbol {symbol}"
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
def get_tickers(self, symbols, market):
|
|
96
|
-
market_credential = self.get_market_credential(market)
|
|
97
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
98
|
-
|
|
99
|
-
if not exchange.has['fetchTickers']:
|
|
100
|
-
raise OperationalException(
|
|
101
|
-
f"Market service {market} does not support "
|
|
102
|
-
f"functionality get_tickers"
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
try:
|
|
106
|
-
return exchange.fetchTickers(symbols)
|
|
107
|
-
except Exception as e:
|
|
108
|
-
logger.exception(e)
|
|
109
|
-
raise OperationalException(
|
|
110
|
-
"Could not retrieve selection of tickers"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
def get_order_book(self, symbol, market):
|
|
114
|
-
market_credential = self.get_market_credential(market)
|
|
115
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
116
|
-
|
|
117
|
-
if not exchange.has['fetchOrderBook']:
|
|
118
|
-
raise OperationalException(
|
|
119
|
-
f"Market service {market} does not support "
|
|
120
|
-
f"functionality get_order_book"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
try:
|
|
124
|
-
return exchange.fetchOrderBook(symbol)
|
|
125
|
-
except Exception as e:
|
|
126
|
-
logger.exception(e)
|
|
127
|
-
raise OperationalException("Could not retrieve order book")
|
|
128
|
-
|
|
129
|
-
def get_order_books(self, symbols, market):
|
|
130
|
-
data = {}
|
|
131
|
-
|
|
132
|
-
for symbol in symbols:
|
|
133
|
-
try:
|
|
134
|
-
entry = self.get_order_book(symbol, market)
|
|
135
|
-
del entry['symbol']
|
|
136
|
-
data[symbol] = entry
|
|
137
|
-
except Exception as e:
|
|
138
|
-
logger.exception(e)
|
|
139
|
-
|
|
140
|
-
return data
|
|
141
|
-
|
|
142
|
-
def get_order(self, order, market):
|
|
143
|
-
market_credential = self.get_market_credential(market)
|
|
144
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
145
|
-
symbol = f"{order.target_symbol.upper()}/" \
|
|
146
|
-
f"{order.trading_symbol.upper()}"
|
|
147
|
-
|
|
148
|
-
if not exchange.has['fetchOrder']:
|
|
149
|
-
raise OperationalException(
|
|
150
|
-
f"Market service {market} does not support "
|
|
151
|
-
f"functionality get_order"
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
try:
|
|
155
|
-
order = exchange.fetchOrder(order.external_id, symbol)
|
|
156
|
-
return Order.from_ccxt_order(order)
|
|
157
|
-
except Exception as e:
|
|
158
|
-
logger.exception(e)
|
|
159
|
-
raise OperationalException("Could not retrieve order")
|
|
160
|
-
|
|
161
|
-
def get_orders(self, symbol, market, since: datetime = None):
|
|
162
|
-
market_credential = self.get_market_credential(market)
|
|
163
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
164
|
-
|
|
165
|
-
if self.config is not None and "DATETIME_FORMAT" in self.config:
|
|
166
|
-
datetime_format = self.config["DATETIME_FORMAT"]
|
|
167
|
-
else:
|
|
168
|
-
datetime_format = DATETIME_FORMAT
|
|
169
|
-
|
|
170
|
-
if not exchange.has['fetchOrders']:
|
|
171
|
-
raise OperationalException(
|
|
172
|
-
f"Market service {market} does not support "
|
|
173
|
-
f"functionality get_orders"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
if since is not None:
|
|
177
|
-
since = exchange.parse8601(datetime_format)
|
|
178
|
-
|
|
179
|
-
try:
|
|
180
|
-
ccxt_orders = exchange.fetchOrders(symbol, since=since)
|
|
181
|
-
return [Order.from_ccxt_order(order) for order in ccxt_orders]
|
|
182
|
-
except Exception as e:
|
|
183
|
-
logger.exception(e)
|
|
184
|
-
raise OperationalException("Could not retrieve orders")
|
|
185
|
-
else:
|
|
186
|
-
try:
|
|
187
|
-
ccxt_orders = exchange.fetchOrders(symbol)
|
|
188
|
-
return [Order.from_ccxt_order(order) for order in ccxt_orders]
|
|
189
|
-
except Exception as e:
|
|
190
|
-
logger.exception(e)
|
|
191
|
-
raise OperationalException("Could not retrieve orders")
|
|
192
|
-
|
|
193
|
-
def get_balance(self, market) -> Dict[str, float]:
|
|
194
|
-
market_credential = self.get_market_credential(market)
|
|
195
|
-
|
|
196
|
-
if market_credential is None:
|
|
197
|
-
raise OperationalException(
|
|
198
|
-
f"You don't have a market credential for market {market}"
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
202
|
-
|
|
203
|
-
if not exchange.has['fetchBalance']:
|
|
204
|
-
raise OperationalException(
|
|
205
|
-
f"Market service {market} does not support "
|
|
206
|
-
f"functionality get_balance"
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
try:
|
|
210
|
-
return exchange.fetchBalance()["free"]
|
|
211
|
-
except Exception as e:
|
|
212
|
-
logger.exception(e)
|
|
213
|
-
raise OperationalException(
|
|
214
|
-
f"Please make sure you have "
|
|
215
|
-
f"registered a valid market credential "
|
|
216
|
-
f"object to the app: {str(e)}"
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
def create_limit_buy_order(
|
|
220
|
-
self,
|
|
221
|
-
target_symbol: str,
|
|
222
|
-
trading_symbol: str,
|
|
223
|
-
amount: float,
|
|
224
|
-
price: float,
|
|
225
|
-
market
|
|
226
|
-
):
|
|
227
|
-
market_credential = self.get_market_credential(market)
|
|
228
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
229
|
-
|
|
230
|
-
if not exchange.has['createLimitBuyOrder']:
|
|
231
|
-
raise OperationalException(
|
|
232
|
-
f"Market service {market} does not support "
|
|
233
|
-
f"functionality create_limit_buy_order"
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
237
|
-
|
|
238
|
-
try:
|
|
239
|
-
order = exchange.createLimitBuyOrder(
|
|
240
|
-
symbol, amount, price
|
|
241
|
-
)
|
|
242
|
-
return Order.from_ccxt_order(order)
|
|
243
|
-
except Exception as e:
|
|
244
|
-
logger.exception(e)
|
|
245
|
-
raise OperationalException("Could not create limit buy order")
|
|
246
|
-
|
|
247
|
-
def create_limit_sell_order(
|
|
248
|
-
self,
|
|
249
|
-
target_symbol: str,
|
|
250
|
-
trading_symbol: str,
|
|
251
|
-
amount: float,
|
|
252
|
-
price: float,
|
|
253
|
-
market
|
|
254
|
-
):
|
|
255
|
-
market_credential = self.get_market_credential(market)
|
|
256
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
257
|
-
|
|
258
|
-
if not exchange.has['createLimitSellOrder']:
|
|
259
|
-
raise OperationalException(
|
|
260
|
-
f"Market service {market} does not support "
|
|
261
|
-
f"functionality create_limit_sell_order"
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
265
|
-
|
|
266
|
-
try:
|
|
267
|
-
order = exchange.createLimitSellOrder(
|
|
268
|
-
symbol, amount, price
|
|
269
|
-
)
|
|
270
|
-
return Order.from_ccxt_order(order)
|
|
271
|
-
except Exception as e:
|
|
272
|
-
logger.exception(e)
|
|
273
|
-
raise OperationalException("Could not create limit sell order")
|
|
274
|
-
|
|
275
|
-
def create_market_sell_order(
|
|
276
|
-
self,
|
|
277
|
-
target_symbol: str,
|
|
278
|
-
trading_symbol: str,
|
|
279
|
-
amount: float,
|
|
280
|
-
market
|
|
281
|
-
):
|
|
282
|
-
market_credential = self.get_market_credential(market)
|
|
283
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
284
|
-
|
|
285
|
-
if not exchange.has['createMarketSellOrder']:
|
|
286
|
-
raise OperationalException(
|
|
287
|
-
f"Market service {market} does not support "
|
|
288
|
-
f"functionality create_market_sell_order"
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
292
|
-
|
|
293
|
-
try:
|
|
294
|
-
order = exchange.createMarketSellOrder(symbol, amount)
|
|
295
|
-
return Order.from_ccxt_order(order)
|
|
296
|
-
except Exception as e:
|
|
297
|
-
logger.exception(e)
|
|
298
|
-
raise OperationalException("Could not create market sell order")
|
|
299
|
-
|
|
300
|
-
def cancel_order(self, order, market):
|
|
301
|
-
market_credential = self.get_market_credential(market)
|
|
302
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
303
|
-
|
|
304
|
-
if not exchange.has['cancelOrder']:
|
|
305
|
-
raise OperationalException(
|
|
306
|
-
f"Market service {market} does not support "
|
|
307
|
-
f"functionality cancel_order"
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
exchange.cancelOrder(
|
|
311
|
-
order.get_order_reference(),
|
|
312
|
-
f"{order.get_target_symbol()}/{order.get_trading_symbol()}")
|
|
313
|
-
|
|
314
|
-
def get_open_orders(
|
|
315
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
316
|
-
):
|
|
317
|
-
market_credential = self.get_market_credential(market)
|
|
318
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
319
|
-
|
|
320
|
-
if not exchange.has['fetchOpenOrders']:
|
|
321
|
-
raise OperationalException(
|
|
322
|
-
f"Market service {market} does not support "
|
|
323
|
-
f"functionality get_open_orders"
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
try:
|
|
327
|
-
if target_symbol is None or trading_symbol is None:
|
|
328
|
-
return exchange.fetchOpenOrders()
|
|
329
|
-
|
|
330
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
331
|
-
ccxt_orders = exchange.fetchOpenOrders(symbol)
|
|
332
|
-
return [Order.from_ccxt_order(order) for order in ccxt_orders]
|
|
333
|
-
except Exception as e:
|
|
334
|
-
logger.exception(e)
|
|
335
|
-
raise OperationalException("Could not retrieve open orders")
|
|
336
|
-
|
|
337
|
-
def get_closed_orders(
|
|
338
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
339
|
-
):
|
|
340
|
-
market_credential = self.get_market_credential(market)
|
|
341
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
342
|
-
|
|
343
|
-
if not exchange.has['fetchClosedOrders']:
|
|
344
|
-
raise OperationalException(
|
|
345
|
-
f"Market service {market} does not support "
|
|
346
|
-
f"functionality get_closed_orders"
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
if target_symbol is None or trading_symbol is None:
|
|
351
|
-
return exchange.fetchClosedOrders()
|
|
352
|
-
|
|
353
|
-
symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
|
|
354
|
-
ccxt_orders = exchange.fetchClosedOrders(symbol)
|
|
355
|
-
return [Order.from_ccxt_order(order) for order in ccxt_orders]
|
|
356
|
-
except Exception as e:
|
|
357
|
-
logger.exception(e)
|
|
358
|
-
raise OperationalException("Could not retrieve closed orders")
|
|
359
|
-
|
|
360
|
-
def get_ohlcv(
|
|
361
|
-
self, symbol, time_frame, from_timestamp, market, to_timestamp=None
|
|
362
|
-
) -> pl.DataFrame:
|
|
363
|
-
"""
|
|
364
|
-
Function to retrieve ohlcv data for a symbol, time frame and market
|
|
365
|
-
|
|
366
|
-
Args:
|
|
367
|
-
symbol (str): The symbol to retrieve ohlcv data for
|
|
368
|
-
time_frame: The time frame to retrieve ohlcv data for
|
|
369
|
-
from_timestamp: The start date to retrieve ohlcv data from
|
|
370
|
-
market: The market to retrieve ohlcv data from
|
|
371
|
-
to_timestamp: The end date to retrieve ohlcv data to
|
|
372
|
-
|
|
373
|
-
Returns:
|
|
374
|
-
DataFrame: The ohlcv data for the symbol, time frame and market
|
|
375
|
-
in polars DataFrame format
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
if self.config is not None and "DATETIME_FORMAT" in self.config:
|
|
379
|
-
datetime_format = self.config["DATETIME_FORMAT"]
|
|
380
|
-
else:
|
|
381
|
-
datetime_format = DATETIME_FORMAT
|
|
382
|
-
|
|
383
|
-
market_credential = self.get_market_credential(market)
|
|
384
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
385
|
-
|
|
386
|
-
if not exchange.has['fetchOHLCV']:
|
|
387
|
-
raise OperationalException(
|
|
388
|
-
f"Market service {market} does not support "
|
|
389
|
-
f"functionality get_ohclvs"
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
from_time_stamp = exchange.parse8601(
|
|
393
|
-
from_timestamp.strftime(datetime_format)
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
if to_timestamp is None:
|
|
397
|
-
to_timestamp = exchange.milliseconds()
|
|
398
|
-
else:
|
|
399
|
-
to_timestamp = exchange.parse8601(
|
|
400
|
-
to_timestamp.strftime(datetime_format)
|
|
401
|
-
)
|
|
402
|
-
data = []
|
|
403
|
-
|
|
404
|
-
while from_time_stamp < to_timestamp:
|
|
405
|
-
ohlcv = exchange.fetch_ohlcv(symbol, time_frame, from_time_stamp)
|
|
406
|
-
|
|
407
|
-
if len(ohlcv) > 0:
|
|
408
|
-
from_time_stamp = \
|
|
409
|
-
ohlcv[-1][0] + exchange.parse_timeframe(time_frame) * 1000
|
|
410
|
-
else:
|
|
411
|
-
from_time_stamp = to_timestamp
|
|
412
|
-
|
|
413
|
-
for candle in ohlcv:
|
|
414
|
-
datetime_stamp = parser.parse(exchange.iso8601(candle[0]))
|
|
415
|
-
|
|
416
|
-
to_timestamp_datetime = parser.parse(
|
|
417
|
-
exchange.iso8601(to_timestamp),
|
|
418
|
-
)
|
|
419
|
-
|
|
420
|
-
if datetime_stamp <= to_timestamp_datetime:
|
|
421
|
-
datetime_stamp = datetime_stamp\
|
|
422
|
-
.strftime(datetime_format)
|
|
423
|
-
|
|
424
|
-
data.append(
|
|
425
|
-
[datetime_stamp] +
|
|
426
|
-
[float(value) for value in candle[1:]]
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
sleep(exchange.rateLimit / 1000)
|
|
430
|
-
|
|
431
|
-
# Predefined column names
|
|
432
|
-
col_names = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
|
|
433
|
-
|
|
434
|
-
# Combine the Series into a DataFrame with given column names
|
|
435
|
-
df = pl.DataFrame(data, schema=col_names, orient="row")
|
|
436
|
-
return df
|
|
437
|
-
|
|
438
|
-
def get_ohlcvs(
|
|
439
|
-
self,
|
|
440
|
-
symbols,
|
|
441
|
-
time_frame,
|
|
442
|
-
from_timestamp,
|
|
443
|
-
market,
|
|
444
|
-
to_timestamp=None
|
|
445
|
-
) -> Dict[str, pl.DataFrame]:
|
|
446
|
-
ohlcvs = {}
|
|
447
|
-
|
|
448
|
-
for symbol in symbols:
|
|
449
|
-
|
|
450
|
-
try:
|
|
451
|
-
ohlcvs[symbol] = self.get_ohlcv(
|
|
452
|
-
symbol=symbol,
|
|
453
|
-
time_frame=time_frame,
|
|
454
|
-
from_timestamp=from_timestamp,
|
|
455
|
-
to_timestamp=to_timestamp,
|
|
456
|
-
market=market
|
|
457
|
-
)
|
|
458
|
-
except Exception as e:
|
|
459
|
-
logger.exception(e)
|
|
460
|
-
logger.error(f"Could not retrieve ohlcv data for {symbol}")
|
|
461
|
-
|
|
462
|
-
return ohlcvs
|
|
463
|
-
|
|
464
|
-
def get_symbols(self, market):
|
|
465
|
-
"""
|
|
466
|
-
Get all available symbols for a given market
|
|
467
|
-
"""
|
|
468
|
-
market_credential = self.get_market_credential(market)
|
|
469
|
-
exchange = self.initialize_exchange(market, market_credential)
|
|
470
|
-
market_information = exchange.load_markets()
|
|
471
|
-
return list(market_information.keys())
|