investing-algorithm-framework 7.19.14__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 +197 -0
- investing_algorithm_framework/app/__init__.py +47 -0
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
- 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 +2204 -0
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1667 -0
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- 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 +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/__init__.py +35 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
- investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
- investing_algorithm_framework/app/strategy.py +675 -0
- investing_algorithm_framework/app/task.py +41 -0
- investing_algorithm_framework/app/web/__init__.py +5 -0
- investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
- investing_algorithm_framework/app/web/controllers/orders.py +20 -0
- investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
- investing_algorithm_framework/app/web/controllers/positions.py +18 -0
- investing_algorithm_framework/app/web/create_app.py +20 -0
- investing_algorithm_framework/app/web/error_handler.py +59 -0
- investing_algorithm_framework/app/web/responses.py +20 -0
- investing_algorithm_framework/app/web/run_strategies.py +4 -0
- investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
- investing_algorithm_framework/app/web/schemas/order.py +12 -0
- investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
- investing_algorithm_framework/app/web/schemas/position.py +15 -0
- investing_algorithm_framework/app/web/setup_cors.py +6 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +207 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- 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_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/create_app.py +54 -0
- investing_algorithm_framework/dependency_container.py +155 -0
- investing_algorithm_framework/domain/__init__.py +148 -0
- 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 +435 -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 +111 -0
- investing_algorithm_framework/domain/constants.py +83 -0
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +112 -0
- investing_algorithm_framework/domain/models/__init__.py +43 -0
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +25 -0
- 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/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +384 -0
- investing_algorithm_framework/domain/models/order/order_side.py +36 -0
- investing_algorithm_framework/domain/models/order/order_status.py +37 -0
- investing_algorithm_framework/domain/models/order/order_type.py +30 -0
- investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +4 -0
- investing_algorithm_framework/domain/models/position/position.py +68 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +153 -0
- investing_algorithm_framework/domain/models/time_interval.py +124 -0
- investing_algorithm_framework/domain/models/time_unit.py +149 -0
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +13 -0
- investing_algorithm_framework/domain/models/trade/trade.py +388 -0
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/positions/__init__.py +4 -0
- investing_algorithm_framework/domain/positions/position_size.py +41 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/stateless_actions.py +7 -0
- investing_algorithm_framework/domain/strategy.py +44 -0
- investing_algorithm_framework/domain/utils/__init__.py +27 -0
- investing_algorithm_framework/domain/utils/csv.py +104 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +41 -0
- investing_algorithm_framework/domain/utils/signatures.py +17 -0
- investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
- investing_algorithm_framework/domain/utils/synchronized.py +12 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +50 -0
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
- investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/services/__init__.py +132 -0
- investing_algorithm_framework/services/backtesting/__init__.py +5 -0
- investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
- investing_algorithm_framework/services/configuration_service.py +96 -0
- 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/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +114 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +181 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +40 -0
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
- investing_algorithm_framework/services/trade_service/__init__.py +3 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
- investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
- investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
- investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
- investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
- investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, DateTime, Float
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import Trade, TradeStatus
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
from investing_algorithm_framework.infrastructure.models\
|
|
9
|
+
.order_trade_association import order_trade_association
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SQLTrade(Trade, SQLBaseModel, SQLAlchemyModelExtension):
|
|
13
|
+
"""
|
|
14
|
+
SQL Trade model
|
|
15
|
+
|
|
16
|
+
A trade is a combination of a buy and sell order that has been opened or
|
|
17
|
+
closed.
|
|
18
|
+
|
|
19
|
+
A trade is considered opened when a buy order is executed and there is
|
|
20
|
+
no corresponding sell order. A trade is considered closed when a sell
|
|
21
|
+
order is executed and the amount of the sell order is equal or larger
|
|
22
|
+
to the amount of the buy order.
|
|
23
|
+
|
|
24
|
+
A single sell order can close multiple buy orders. Also, a single
|
|
25
|
+
buy order can be closed by multiple sell orders.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
orders: str, the id of the buy order
|
|
29
|
+
target_symbol: str, the target symbol of the trade
|
|
30
|
+
trading_symbol: str, the trading symbol of the trade
|
|
31
|
+
closed_at: datetime, the datetime when the trade was closed
|
|
32
|
+
amount: float, the amount of the trade
|
|
33
|
+
available_amount: float, the available amount of the trade
|
|
34
|
+
remaining: float, the remaining amount that is not filled by the
|
|
35
|
+
buy order that opened the trade.
|
|
36
|
+
filled_amount: float, the filled amount of the trade by the buy
|
|
37
|
+
order that opened the trade.
|
|
38
|
+
net_gain: float, the net gain of the trade
|
|
39
|
+
last_reported_price: float, the last reported price of the trade
|
|
40
|
+
last_reported_price_datetime: datetime, the datetime when the last
|
|
41
|
+
reported price was reported
|
|
42
|
+
created_at: datetime, the datetime when the trade was created
|
|
43
|
+
updated_at: datetime, the datetime when the trade was last updated
|
|
44
|
+
status: str, the status of the trade
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
__tablename__ = "trades"
|
|
48
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
49
|
+
orders = relationship(
|
|
50
|
+
'SQLOrder',
|
|
51
|
+
secondary=order_trade_association,
|
|
52
|
+
back_populates='trades',
|
|
53
|
+
lazy='joined'
|
|
54
|
+
)
|
|
55
|
+
target_symbol = Column(String)
|
|
56
|
+
trading_symbol = Column(String)
|
|
57
|
+
closed_at = Column(DateTime, default=None)
|
|
58
|
+
opened_at = Column(DateTime, default=None)
|
|
59
|
+
open_price = Column(Float, default=None)
|
|
60
|
+
amount = Column(Float, default=None)
|
|
61
|
+
available_amount = Column(Float, default=None)
|
|
62
|
+
filled_amount = Column(Float, default=None)
|
|
63
|
+
remaining = Column(Float, default=None)
|
|
64
|
+
net_gain = Column(Float, default=0)
|
|
65
|
+
cost = Column(Float, default=0)
|
|
66
|
+
last_reported_price = Column(Float, default=None)
|
|
67
|
+
last_reported_price_datetime = Column(DateTime, default=None)
|
|
68
|
+
high_water_mark = Column(Float, default=None)
|
|
69
|
+
high_water_mark_datetime = Column(DateTime, default=None)
|
|
70
|
+
updated_at = Column(DateTime, default=None)
|
|
71
|
+
status = Column(String, default=TradeStatus.CREATED.value)
|
|
72
|
+
# Stop losses should be actively loaded
|
|
73
|
+
stop_losses = relationship(
|
|
74
|
+
'SQLTradeStopLoss',
|
|
75
|
+
back_populates='trade',
|
|
76
|
+
lazy='joined'
|
|
77
|
+
)
|
|
78
|
+
# Take profits should be actively loaded
|
|
79
|
+
take_profits = relationship(
|
|
80
|
+
'SQLTradeTakeProfit',
|
|
81
|
+
back_populates='trade',
|
|
82
|
+
lazy='joined'
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
buy_order,
|
|
88
|
+
target_symbol,
|
|
89
|
+
trading_symbol,
|
|
90
|
+
opened_at,
|
|
91
|
+
amount,
|
|
92
|
+
available_amount,
|
|
93
|
+
filled_amount,
|
|
94
|
+
remaining,
|
|
95
|
+
status=TradeStatus.CREATED.value,
|
|
96
|
+
closed_at=None,
|
|
97
|
+
updated_at=None,
|
|
98
|
+
net_gain=0,
|
|
99
|
+
cost=0,
|
|
100
|
+
last_reported_price=None,
|
|
101
|
+
last_reported_price_datetime=None,
|
|
102
|
+
high_water_mark=None,
|
|
103
|
+
high_water_mark_datetime=None,
|
|
104
|
+
sell_orders=[],
|
|
105
|
+
stop_losses=[],
|
|
106
|
+
take_profits=[],
|
|
107
|
+
):
|
|
108
|
+
self.orders = [buy_order]
|
|
109
|
+
self.open_price = buy_order.price
|
|
110
|
+
self.target_symbol = target_symbol
|
|
111
|
+
self.trading_symbol = trading_symbol
|
|
112
|
+
self.closed_at = closed_at
|
|
113
|
+
self.amount = amount
|
|
114
|
+
self.available_amount = available_amount
|
|
115
|
+
self.filled_amount = filled_amount
|
|
116
|
+
self.remaining = remaining
|
|
117
|
+
self.net_gain = net_gain
|
|
118
|
+
self.cost = cost
|
|
119
|
+
self.last_reported_price = last_reported_price
|
|
120
|
+
self.last_reported_price_datetime = last_reported_price_datetime
|
|
121
|
+
self.high_water_mark = high_water_mark
|
|
122
|
+
self.high_water_mark_datetime = high_water_mark_datetime
|
|
123
|
+
self.opened_at = opened_at
|
|
124
|
+
self.updated_at = updated_at
|
|
125
|
+
self.status = status
|
|
126
|
+
self.stop_losses = stop_losses
|
|
127
|
+
self.take_profits = take_profits
|
|
128
|
+
|
|
129
|
+
if sell_orders is not None:
|
|
130
|
+
self.orders.extend(sell_orders)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import TradeStopLoss
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SQLTradeStopLoss(TradeStopLoss, SQLBaseModel, SQLAlchemyModelExtension):
|
|
11
|
+
"""
|
|
12
|
+
SQLTradeStopLoss model
|
|
13
|
+
|
|
14
|
+
A trade stop loss is a stop loss strategy for a trade.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
* trade: Trade - the trade that the take profit is for
|
|
18
|
+
* take_profit: float - the take profit percentage
|
|
19
|
+
* trade_risk_type: TradeRiskType - the type of trade risk, either
|
|
20
|
+
trailing or fixed
|
|
21
|
+
* sell_percentage: float - the percentage of the trade to sell when the
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__tablename__ = "trade_stop_losses"
|
|
26
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
27
|
+
trade_id = Column(Integer, ForeignKey('trades.id'))
|
|
28
|
+
trade = relationship('SQLTrade', back_populates='stop_losses')
|
|
29
|
+
trade_risk_type = Column(String)
|
|
30
|
+
percentage = Column(Float)
|
|
31
|
+
sell_percentage = Column(Float)
|
|
32
|
+
open_price = Column(Float)
|
|
33
|
+
high_water_mark = Column(Float)
|
|
34
|
+
high_water_mark_date = Column(String)
|
|
35
|
+
stop_loss_price = Column(Float)
|
|
36
|
+
sell_prices = Column(String)
|
|
37
|
+
sell_dates = Column(String)
|
|
38
|
+
sell_amount = Column(Float)
|
|
39
|
+
sold_amount = Column(Float)
|
|
40
|
+
active = Column(Boolean)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import TradeTakeProfit
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SQLTradeTakeProfit(
|
|
11
|
+
TradeTakeProfit, SQLBaseModel, SQLAlchemyModelExtension
|
|
12
|
+
):
|
|
13
|
+
"""
|
|
14
|
+
SQLTradeTakeProfit model
|
|
15
|
+
|
|
16
|
+
A trade take profit is a take profit strategy for a trade.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
* trade: Trade - the trade that the take profit is for
|
|
20
|
+
* take_profit: float - the take profit percentage
|
|
21
|
+
* trade_risk_type: TradeRiskType - the type of trade risk, either
|
|
22
|
+
trailing or fixed
|
|
23
|
+
* sell_percentage: float - the percentage of the trade to sell when the
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__tablename__ = "trade_take_profits"
|
|
27
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
28
|
+
trade_id = Column(Integer, ForeignKey('trades.id'))
|
|
29
|
+
trade = relationship('SQLTrade', back_populates='take_profits')
|
|
30
|
+
trade_risk_type = Column(String)
|
|
31
|
+
percentage = Column(Float)
|
|
32
|
+
sell_percentage = Column(Float)
|
|
33
|
+
open_price = Column(Float)
|
|
34
|
+
high_water_mark = Column(Float)
|
|
35
|
+
high_water_mark_date = Column(String)
|
|
36
|
+
sell_prices = Column(String)
|
|
37
|
+
take_profit_price = Column(Float)
|
|
38
|
+
sell_amount = Column(Float)
|
|
39
|
+
sell_dates = Column(String)
|
|
40
|
+
sold_amount = Column(Float)
|
|
41
|
+
active = Column(Boolean)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .ccxt_order_executor import CCXTOrderExecutor
|
|
2
|
+
from .backtest_oder_executor import BacktestOrderExecutor
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_default_order_executors():
|
|
6
|
+
"""
|
|
7
|
+
Function to get the default order executors.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
list: List of default order executors.
|
|
11
|
+
"""
|
|
12
|
+
return [
|
|
13
|
+
CCXTOrderExecutor(),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'CCXTOrderExecutor',
|
|
19
|
+
'BacktestOrderExecutor',
|
|
20
|
+
'get_default_order_executors',
|
|
21
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from investing_algorithm_framework.domain import OrderExecutor, OrderStatus, \
|
|
2
|
+
INDEX_DATETIME, Order
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BacktestOrderExecutor(OrderExecutor):
|
|
6
|
+
"""
|
|
7
|
+
Backtest implementation of order executor. This executor is used to
|
|
8
|
+
simulate order execution in a backtesting environment.
|
|
9
|
+
|
|
10
|
+
!Important: This executor does not actually execute orders on any market.
|
|
11
|
+
It should be used only for backtesting purposes.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def execute_order(self, portfolio, order, market_credential) -> Order:
|
|
15
|
+
order.status = OrderStatus.OPEN.value
|
|
16
|
+
order.remaining = order.get_amount()
|
|
17
|
+
order.filled = 0
|
|
18
|
+
order.updated_at = self.config[INDEX_DATETIME]
|
|
19
|
+
return order
|
|
20
|
+
|
|
21
|
+
def cancel_order(self, portfolio, order, market_credential) -> Order:
|
|
22
|
+
order.status = OrderStatus.CANCELED.value
|
|
23
|
+
order.remaining = 0
|
|
24
|
+
order.updated_at = self.config[INDEX_DATETIME]
|
|
25
|
+
return order
|
|
26
|
+
|
|
27
|
+
def supports_market(self, market):
|
|
28
|
+
return True
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
|
|
3
|
+
import ccxt
|
|
4
|
+
|
|
5
|
+
from investing_algorithm_framework.domain import OrderExecutor, \
|
|
6
|
+
OperationalException, Order, OrderStatus, OrderSide, OrderType, \
|
|
7
|
+
MarketCredential
|
|
8
|
+
|
|
9
|
+
logger = getLogger("investing_algorithm_framework")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CCXTOrderExecutor(OrderExecutor):
|
|
13
|
+
"""
|
|
14
|
+
CCXTOrderExecutor is a class that implements the OrderExecutor
|
|
15
|
+
interface for executing orders using the CCXT library.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def execute_order(self, portfolio, order, market_credential) -> Order:
|
|
19
|
+
"""
|
|
20
|
+
Executes an order for a given portfolio on a CCXT exchange.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
order: The order to be executed
|
|
24
|
+
portfolio: The portfolio in which the order will be executed
|
|
25
|
+
market_credential: The market credential to use for the order
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Order: Instance of the executed order. The order instance
|
|
29
|
+
should copy the id of the order that has been provided as a
|
|
30
|
+
"""
|
|
31
|
+
market = portfolio.market
|
|
32
|
+
exchange = self.initialize_exchange(market, market_credential)
|
|
33
|
+
symbol = order.get_symbol()
|
|
34
|
+
amount = order.get_amount()
|
|
35
|
+
price = order.get_price()
|
|
36
|
+
order_type = order.get_order_type()
|
|
37
|
+
order_side = order.get_order_side()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
if OrderType.LIMIT.equals(order_type):
|
|
41
|
+
if OrderSide.BUY.equals(order_side):
|
|
42
|
+
|
|
43
|
+
# Check if the exchange supports the
|
|
44
|
+
# createLimitBuyOrder method
|
|
45
|
+
if not hasattr(exchange, "createLimitBuyOrder"):
|
|
46
|
+
raise OperationalException(
|
|
47
|
+
f"Exchange {market} does not support "
|
|
48
|
+
f"functionality createLimitBuyOrder"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Create a limit buy order
|
|
52
|
+
external_order = exchange.createLimitBuyOrder(
|
|
53
|
+
symbol, amount, price,
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
# Check if the exchange supports
|
|
57
|
+
# the createLimitSellOrder method
|
|
58
|
+
if not hasattr(exchange, "createLimitSellOrder"):
|
|
59
|
+
raise OperationalException(
|
|
60
|
+
f"Exchange {market} does not support "
|
|
61
|
+
f"functionality createLimitSellOrder"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Create a limit sell order
|
|
65
|
+
external_order = exchange.createLimitSellOrder(
|
|
66
|
+
symbol, amount, price,
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
raise OperationalException(
|
|
70
|
+
f"Order type {order_type} not supported "
|
|
71
|
+
f"by CCXT OrderExecutor"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
external_order = Order.from_ccxt_order(external_order)
|
|
75
|
+
external_order.id = order.id
|
|
76
|
+
return external_order
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.exception(e)
|
|
79
|
+
raise OperationalException("Could not create limit buy order")
|
|
80
|
+
|
|
81
|
+
def cancel_order(self, portfolio, order, market_credential) -> Order:
|
|
82
|
+
"""
|
|
83
|
+
Cancels an order for a given portfolio on a CCXT exchange.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
order: The order to be canceled
|
|
87
|
+
portfolio: The portfolio in which the order was executed
|
|
88
|
+
market_credential: The market credential to use for the order
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Order: Instance of the canceled order.
|
|
92
|
+
"""
|
|
93
|
+
market = portfolio.market
|
|
94
|
+
exchange = self.initialize_exchange(market, market_credential)
|
|
95
|
+
|
|
96
|
+
if not exchange.has['cancelOrder']:
|
|
97
|
+
raise OperationalException(
|
|
98
|
+
f"Exchange {market} does not support "
|
|
99
|
+
f"functionality cancelOrder"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
exchange.cancelOrder(
|
|
104
|
+
order.get_external_id(),
|
|
105
|
+
f"{order.get_target_symbol()}/{order.get_trading_symbol()}"
|
|
106
|
+
)
|
|
107
|
+
order.status = OrderStatus.CANCELED.value
|
|
108
|
+
return order
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.exception(e)
|
|
111
|
+
raise OperationalException("Could not cancel order")
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def initialize_exchange(market, market_credential):
|
|
115
|
+
"""
|
|
116
|
+
Function to initialize the exchange for the market.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
market (str): The market to initialize the exchange for
|
|
120
|
+
market_credential (MarketCredential): The market credential to use
|
|
121
|
+
for the exchange
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
market = market.lower()
|
|
127
|
+
|
|
128
|
+
if not hasattr(ccxt, market):
|
|
129
|
+
raise OperationalException(
|
|
130
|
+
f"No ccxt exchange for market id {market}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
exchange_class = getattr(ccxt, market)
|
|
134
|
+
|
|
135
|
+
if exchange_class is None:
|
|
136
|
+
raise OperationalException(
|
|
137
|
+
f"No market service found for market id {market}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Check the credentials for the exchange
|
|
141
|
+
CCXTOrderExecutor.check_credentials(exchange_class, market_credential)
|
|
142
|
+
exchange = exchange_class({
|
|
143
|
+
'apiKey': market_credential.api_key,
|
|
144
|
+
'secret': market_credential.secret_key,
|
|
145
|
+
})
|
|
146
|
+
return exchange
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def check_credentials(
|
|
150
|
+
exchange_class, market_credential: MarketCredential
|
|
151
|
+
):
|
|
152
|
+
"""
|
|
153
|
+
Function to check if the credentials are valid for the exchange.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
exchange_class: The exchange class to check the credentials for
|
|
157
|
+
market_credential: The market credential to use for the exchange
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
OperationalException: If the credentials are not valid
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
None
|
|
164
|
+
"""
|
|
165
|
+
exchange = exchange_class()
|
|
166
|
+
credentials_info = exchange.requiredCredentials
|
|
167
|
+
market = market_credential.get_market()
|
|
168
|
+
|
|
169
|
+
if ('apiKey' in credentials_info
|
|
170
|
+
and credentials_info["apiKey"]
|
|
171
|
+
and market_credential.get_api_key() is None):
|
|
172
|
+
raise OperationalException(
|
|
173
|
+
f"Market credential for market {market}"
|
|
174
|
+
" requires an api key, either"
|
|
175
|
+
" as an argument or as an environment variable"
|
|
176
|
+
f" named as {market.upper()}_API_KEY"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if ('secret' in credentials_info
|
|
180
|
+
and credentials_info["secret"]
|
|
181
|
+
and market_credential.get_secret_key() is None):
|
|
182
|
+
raise OperationalException(
|
|
183
|
+
f"Market credential for market {market}"
|
|
184
|
+
" requires a secret key, either"
|
|
185
|
+
" as an argument or as an environment variable"
|
|
186
|
+
f" named as {market.upper()}_SECRET_KEY"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def supports_market(self, market):
|
|
190
|
+
"""
|
|
191
|
+
Function to check if the market is supported by the portfolio
|
|
192
|
+
provider.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
market: Market object
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
bool: True if the market is supported, False otherwise
|
|
199
|
+
"""
|
|
200
|
+
return hasattr(ccxt, market.lower())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .ccxt_portfolio_provider import CCXTPortfolioProvider
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_default_portfolio_providers():
|
|
5
|
+
"""
|
|
6
|
+
Function to get the default portfolio providers.
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
list: List of default portfolio providers.
|
|
10
|
+
"""
|
|
11
|
+
return [
|
|
12
|
+
CCXTPortfolioProvider(),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"CCXTPortfolioProvider",
|
|
18
|
+
"get_default_portfolio_providers",
|
|
19
|
+
]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import ccxt
|
|
2
|
+
from logging import getLogger
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
from investing_algorithm_framework.domain import PortfolioProvider, \
|
|
6
|
+
OperationalException, Order, Position, MarketCredential
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = getLogger("investing_algorithm_framework")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CCXTPortfolioProvider(PortfolioProvider):
|
|
13
|
+
"""
|
|
14
|
+
Implementation of Portfolio Provider for CCXT.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def get_order(
|
|
18
|
+
self, portfolio, order, market_credential
|
|
19
|
+
) -> Union[Order, None]:
|
|
20
|
+
"""
|
|
21
|
+
Method to check if there are any pending orders for the portfolio.
|
|
22
|
+
This method will retrieve the open orders from the exchange and
|
|
23
|
+
check if there are any pending orders for the portfolio.
|
|
24
|
+
|
|
25
|
+
!IMPORTANT: This function should return None if the order is
|
|
26
|
+
not found or if the order is not available on the
|
|
27
|
+
exchange or broker. Please do not throw an exception if the
|
|
28
|
+
order is not found.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
portfolio: Portfolio object
|
|
32
|
+
order: Order object from the database
|
|
33
|
+
market_credential: Market credential object
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
None
|
|
37
|
+
"""
|
|
38
|
+
exchange = self.initialize_exchange(
|
|
39
|
+
portfolio.market, market_credential
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if not exchange.has['fetchOrder']:
|
|
43
|
+
raise OperationalException(
|
|
44
|
+
f"Market service {portfolio.market} does not support "
|
|
45
|
+
f"functionality get_order"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
symbol = order.get_symbol()
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
external_order = exchange.fetchOrder(order.external_id, symbol)
|
|
52
|
+
external_order = Order.from_ccxt_order(external_order)
|
|
53
|
+
external_order.id = order.id
|
|
54
|
+
return external_order
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.exception(e)
|
|
57
|
+
raise OperationalException("Could not retrieve order")
|
|
58
|
+
|
|
59
|
+
def get_position(
|
|
60
|
+
self, portfolio, symbol, market_credential
|
|
61
|
+
) -> Union[Position, None]:
|
|
62
|
+
"""
|
|
63
|
+
Function to get the position for a given symbol in the portfolio.
|
|
64
|
+
The returned position should be an object that reflects the current
|
|
65
|
+
state of the position on the exchange or broker.
|
|
66
|
+
|
|
67
|
+
!IMPORTANT: This function should return None if the position is
|
|
68
|
+
not found or if the position is not available on the
|
|
69
|
+
exchange or broker. Please do not throw an exception if the
|
|
70
|
+
position is not found.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
portfolio (Portfolio): Portfolio object
|
|
74
|
+
symbol (str): Symbol object
|
|
75
|
+
market_credential (MarketCredential): MarketCredential object
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Position: Position for the given symbol in the portfolio
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
exchange = self.initialize_exchange(
|
|
82
|
+
portfolio.market, market_credential
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if not exchange.has['fetchBalance']:
|
|
86
|
+
raise OperationalException(
|
|
87
|
+
f"Market service {portfolio.market} does not support "
|
|
88
|
+
f"functionality get_balance"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
amount = exchange.fetchBalance()["free"]
|
|
93
|
+
|
|
94
|
+
if symbol not in amount:
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
return Position(
|
|
98
|
+
symbol=symbol,
|
|
99
|
+
amount=amount[symbol],
|
|
100
|
+
cost=0,
|
|
101
|
+
portfolio_id=portfolio.id
|
|
102
|
+
)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.exception(e)
|
|
105
|
+
raise OperationalException(
|
|
106
|
+
f"Please make sure you have "
|
|
107
|
+
f"registered a valid market credential "
|
|
108
|
+
f"object to the app: {str(e)}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def initialize_exchange(market, market_credential):
|
|
113
|
+
"""
|
|
114
|
+
Function to initialize the exchange for the market.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
market (str): The market to initialize the exchange for
|
|
118
|
+
market_credential (MarketCredential): The market credential to use
|
|
119
|
+
for the exchange
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
market = market.lower()
|
|
125
|
+
|
|
126
|
+
if not hasattr(ccxt, market):
|
|
127
|
+
raise OperationalException(
|
|
128
|
+
f"No ccxt exchange for market id {market}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
exchange_class = getattr(ccxt, market)
|
|
132
|
+
|
|
133
|
+
if exchange_class is None:
|
|
134
|
+
raise OperationalException(
|
|
135
|
+
f"No market service found for market id {market}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Check the credentials for the exchange
|
|
139
|
+
(CCXTPortfolioProvider
|
|
140
|
+
.check_credentials(exchange_class, market_credential))
|
|
141
|
+
exchange = exchange_class({
|
|
142
|
+
'apiKey': market_credential.api_key,
|
|
143
|
+
'secret': market_credential.secret_key,
|
|
144
|
+
})
|
|
145
|
+
return exchange
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def check_credentials(
|
|
149
|
+
exchange_class, market_credential: MarketCredential
|
|
150
|
+
):
|
|
151
|
+
"""
|
|
152
|
+
Function to check if the credentials are valid for the exchange.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
exchange_class: The exchange class to check the credentials for
|
|
156
|
+
market_credential: The market credential to use for the exchange
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
OperationalException: If the credentials are not valid
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
None
|
|
163
|
+
"""
|
|
164
|
+
exchange = exchange_class()
|
|
165
|
+
credentials_info = exchange.requiredCredentials
|
|
166
|
+
market = market_credential.get_market()
|
|
167
|
+
|
|
168
|
+
if ('apiKey' in credentials_info
|
|
169
|
+
and credentials_info["apiKey"]
|
|
170
|
+
and market_credential.get_api_key() is None):
|
|
171
|
+
raise OperationalException(
|
|
172
|
+
f"Market credential for market {market}"
|
|
173
|
+
" requires an api key, either"
|
|
174
|
+
" as an argument or as an environment variable"
|
|
175
|
+
f" named as {market.upper()}_API_KEY"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if ('secret' in credentials_info
|
|
179
|
+
and credentials_info["secret"]
|
|
180
|
+
and market_credential.get_secret_key() is None):
|
|
181
|
+
raise OperationalException(
|
|
182
|
+
f"Market credential for market {market}"
|
|
183
|
+
" requires a secret key, either"
|
|
184
|
+
" as an argument or as an environment variable"
|
|
185
|
+
f" named as {market.upper()}_SECRET_KEY"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def supports_market(self, market):
|
|
189
|
+
"""
|
|
190
|
+
Function to check if the market is supported by the portfolio
|
|
191
|
+
provider.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
market: Market object
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
bool: True if the market is supported, False otherwise
|
|
198
|
+
"""
|
|
199
|
+
return hasattr(ccxt, market.lower())
|