investing-algorithm-framework 3.7.0__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 +168 -45
- investing_algorithm_framework/app/__init__.py +32 -1
- 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 +1933 -589
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1725 -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/action_handlers/__init__.py +4 -2
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/strategy.py +664 -84
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +226 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -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 +40 -6
- investing_algorithm_framework/dependency_container.py +72 -56
- investing_algorithm_framework/domain/__init__.py +71 -47
- 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 +59 -91
- investing_algorithm_framework/domain/constants.py +13 -38
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +3 -2
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +17 -12
- 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/market_credential.py +55 -1
- investing_algorithm_framework/domain/models/order/order.py +77 -83
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +81 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +12 -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 +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +37 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +66 -2
- investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
- investing_algorithm_framework/domain/models/trade/trade.py +295 -171
- investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +2 -9
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +12 -7
- 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 +29 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +31 -18
- 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 +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
- investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
- investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
- 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 +1 -1
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
- 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 +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -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 +8 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +4 -4
- 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 +113 -16
- investing_algorithm_framework/services/backtesting/__init__.py +0 -7
- investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
- investing_algorithm_framework/services/configuration_service.py +77 -11
- 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 +16 -1
- 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/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 +3 -1
- investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +407 -326
- investing_algorithm_framework/services/portfolios/__init__.py +3 -1
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/repository_service.py +8 -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 +1013 -315
- 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-7.19.15.dist-info/RECORD +263 -0
- investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -1105
- investing_algorithm_framework/domain/graphs.py +0 -382
- investing_algorithm_framework/domain/metrics/__init__.py +0 -6
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/domain/utils/backtesting.py +0 -472
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
- investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
- 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 -350
- investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
- investing_algorithm_framework/services/backtesting/graphs.py +0 -61
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
- investing_algorithm_framework/services/position_service.py +0 -31
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
- investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
- investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
- /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from investing_algorithm_framework.domain import OperationalException, \
|
|
4
|
-
AbstractPortfolioSyncService,
|
|
5
|
-
OrderSide, AppMode
|
|
4
|
+
AbstractPortfolioSyncService, ENVIRONMENT, Environment
|
|
6
5
|
from investing_algorithm_framework.services.trade_service import TradeService
|
|
7
6
|
|
|
8
7
|
logger = logging.getLogger(__name__)
|
|
@@ -11,27 +10,39 @@ logger = logging.getLogger(__name__)
|
|
|
11
10
|
class PortfolioSyncService(AbstractPortfolioSyncService):
|
|
12
11
|
"""
|
|
13
12
|
Service to sync the portfolio with the exchange.
|
|
13
|
+
|
|
14
|
+
This service will sync the portfolio with the exchange
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
trade_service: TradeService object
|
|
18
|
+
configuration_service: ConfigurationService object
|
|
19
|
+
order_service: OrderService object
|
|
20
|
+
position_repository: PositionRepository object
|
|
21
|
+
portfolio_repository: PortfolioRepository object
|
|
22
|
+
market_credential_service: MarketCredentialService object
|
|
23
|
+
portfolio_configuration_service: PortfolioConfigurationService object
|
|
24
|
+
portfolio_provider_lookup: PortfolioProviderLookup object
|
|
14
25
|
"""
|
|
15
26
|
|
|
16
27
|
def __init__(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
self,
|
|
29
|
+
trade_service: TradeService,
|
|
30
|
+
configuration_service,
|
|
31
|
+
order_service,
|
|
32
|
+
position_repository,
|
|
33
|
+
portfolio_repository,
|
|
34
|
+
portfolio_configuration_service,
|
|
35
|
+
market_credential_service,
|
|
36
|
+
portfolio_provider_lookup
|
|
26
37
|
):
|
|
27
38
|
self.trade_service = trade_service
|
|
28
39
|
self.configuration_service = configuration_service
|
|
29
|
-
self.
|
|
40
|
+
self.order_service = order_service
|
|
30
41
|
self.position_repository = position_repository
|
|
31
42
|
self.portfolio_repository = portfolio_repository
|
|
32
43
|
self.market_credential_service = market_credential_service
|
|
33
|
-
self.market_service = market_service
|
|
34
44
|
self.portfolio_configuration_service = portfolio_configuration_service
|
|
45
|
+
self.portfolio_provider_lookup = portfolio_provider_lookup
|
|
35
46
|
|
|
36
47
|
def sync_unallocated(self, portfolio):
|
|
37
48
|
"""
|
|
@@ -40,23 +51,26 @@ class PortfolioSyncService(AbstractPortfolioSyncService):
|
|
|
40
51
|
available balance of the portfolio from the exchange and update the
|
|
41
52
|
unallocated balance of the portfolio accordingly.
|
|
42
53
|
|
|
43
|
-
If the
|
|
44
|
-
|
|
54
|
+
If the portfolio already exists (exists in the database),
|
|
55
|
+
then a check is done if the exchange has the available
|
|
56
|
+
balance of the portfolio unallocated balance. If the exchange
|
|
57
|
+
does not have the available balance of the portfolio,
|
|
58
|
+
an OperationalException will be raised.
|
|
59
|
+
|
|
60
|
+
If the portfolio does not exist, the portfolio will be created with
|
|
61
|
+
the unallocated balance of the portfolio set to the available
|
|
62
|
+
balance on the exchange. If also a initial balance is set in
|
|
63
|
+
the portfolio configuration, the unallocated balance will be set
|
|
64
|
+
to the initial balance (given the balance is available on
|
|
65
|
+
the exchange). If the initial balance is not set, the
|
|
66
|
+
unallocated balance will be set to the available balance
|
|
67
|
+
on the exchange.
|
|
45
68
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
than the unallocated balance of the portfolio. If the amount on the
|
|
49
|
-
exchange is less than the unallocated balance of the portfolio, the
|
|
50
|
-
unallocated balance of the portfolio will be updated to the amount on
|
|
51
|
-
the exchange or an OperationalException will be raised if the
|
|
52
|
-
throw_exception_on_insufficient_balance is set to True.
|
|
69
|
+
Args:
|
|
70
|
+
portfolio: Portfolio object
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
the algorithm from using reserved balances for trading. The reserved
|
|
57
|
-
config is not used for the stateless mode, because this would mean
|
|
58
|
-
that the algorithm should be aware of how much it already used for
|
|
59
|
-
trading. This is not possible in stateless mode.
|
|
72
|
+
Returns:
|
|
73
|
+
Portfolio object
|
|
60
74
|
"""
|
|
61
75
|
market_credential = self.market_credential_service.get(
|
|
62
76
|
portfolio.market
|
|
@@ -68,312 +82,101 @@ class PortfolioSyncService(AbstractPortfolioSyncService):
|
|
|
68
82
|
f"{portfolio.market}. Cannot sync unallocated amount."
|
|
69
83
|
)
|
|
70
84
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
else:
|
|
77
|
-
unallocated = float(balances[portfolio.trading_symbol.upper()])
|
|
78
|
-
|
|
79
|
-
reserved_unallocated = 0
|
|
80
|
-
config = self.configuration_service.config
|
|
81
|
-
mode = config.get(APP_MODE)
|
|
82
|
-
|
|
83
|
-
if not AppMode.STATELESS.equals(mode):
|
|
84
|
-
if RESERVED_BALANCES in config:
|
|
85
|
-
reserved = config[RESERVED_BALANCES]
|
|
86
|
-
|
|
87
|
-
if portfolio.trading_symbol.upper() in reserved:
|
|
88
|
-
reserved_unallocated \
|
|
89
|
-
= reserved[portfolio.trading_symbol.upper()]
|
|
90
|
-
|
|
91
|
-
unallocated = unallocated - reserved_unallocated
|
|
85
|
+
portfolio_provider = self.portfolio_provider_lookup\
|
|
86
|
+
.get_portfolio_provider(portfolio.market)
|
|
87
|
+
position = portfolio_provider.get_position(
|
|
88
|
+
portfolio, portfolio.trading_symbol, market_credential
|
|
89
|
+
)
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
if not portfolio.initialized:
|
|
92
|
+
# Check if the portfolio has an initial balance set
|
|
93
|
+
if portfolio.initial_balance is not None:
|
|
94
|
+
available = position.amount
|
|
95
95
|
|
|
96
|
-
if
|
|
96
|
+
if portfolio.initial_balance > available:
|
|
97
97
|
raise OperationalException(
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
f"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
f"{portfolio.trading_symbol}.
|
|
107
|
-
f"You have currently {unallocated} "
|
|
108
|
-
f"{portfolio.trading_symbol} available on the "
|
|
109
|
-
f"exchange."
|
|
98
|
+
"The initial balance of the " +
|
|
99
|
+
"portfolio configuration " +
|
|
100
|
+
f"({portfolio.initial_balance} "
|
|
101
|
+
f"{portfolio.trading_symbol}) is more " +
|
|
102
|
+
"than the available balance on the exchange. " +
|
|
103
|
+
"Please make sure that the initial balance of " +
|
|
104
|
+
"the portfolio configuration is less " +
|
|
105
|
+
"than the available balance on the " +
|
|
106
|
+
f"exchange {available} {portfolio.trading_symbol}."
|
|
110
107
|
)
|
|
108
|
+
else:
|
|
109
|
+
unallocated = portfolio.initial_balance
|
|
110
|
+
else:
|
|
111
|
+
# If the portfolio does not have an initial balance
|
|
112
|
+
# set, get the available balance on the exchange
|
|
113
|
+
if position is None:
|
|
114
|
+
raise OperationalException(
|
|
115
|
+
f"There is no available balance on the exchange for "
|
|
116
|
+
f"{portfolio.trading_symbol.upper()} on market "
|
|
117
|
+
f"{portfolio.market}. Please make sure that you have "
|
|
118
|
+
f"an available balance on the exchange for "
|
|
119
|
+
f"{portfolio.trading_symbol.upper()} on market "
|
|
120
|
+
f"{portfolio.market}."
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
unallocated = position.amount
|
|
111
124
|
|
|
112
|
-
# If portfolio does not exist and initial balance is set,
|
|
113
|
-
# create the portfolio with the initial balance
|
|
114
|
-
if unallocated > portfolio.unallocated and \
|
|
115
|
-
not self.portfolio_repository.exists(
|
|
116
|
-
{"identifier": portfolio.identifier}
|
|
117
|
-
):
|
|
118
|
-
unallocated = portfolio.unallocated
|
|
119
|
-
|
|
120
|
-
if not self.portfolio_repository.exists(
|
|
121
|
-
{"identifier": portfolio.identifier}
|
|
122
|
-
):
|
|
123
|
-
create_data = {
|
|
124
|
-
"identifier": portfolio.get_identifier(),
|
|
125
|
-
"market": portfolio.get_market().upper(),
|
|
126
|
-
"trading_symbol": portfolio.get_trading_symbol(),
|
|
127
|
-
"unallocated": unallocated,
|
|
128
|
-
}
|
|
129
|
-
portfolio = self.portfolio_repository.create(create_data)
|
|
130
|
-
else:
|
|
131
125
|
update_data = {
|
|
132
126
|
"unallocated": unallocated,
|
|
127
|
+
"net_size": unallocated,
|
|
128
|
+
"initialized": True
|
|
133
129
|
}
|
|
134
130
|
portfolio = self.portfolio_repository.update(
|
|
135
131
|
portfolio.id, update_data
|
|
136
132
|
)
|
|
137
133
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
balance on the exchange, the unallocated balance of the portfolio will
|
|
149
|
-
be updated to match the available balance on the exchange.
|
|
150
|
-
|
|
151
|
-
If the unallocated balance of the portfolio is more than the available
|
|
152
|
-
balance on the exchange, an OperationalException will be raised.
|
|
153
|
-
"""
|
|
154
|
-
portfolio_configuration = self.portfolio_configuration_service \
|
|
155
|
-
.get(portfolio.identifier)
|
|
156
|
-
balances = self.market_service \
|
|
157
|
-
.get_balance(market=portfolio_configuration.market)
|
|
158
|
-
reserved_balances = self.configuration_service.config \
|
|
159
|
-
.get(RESERVED_BALANCES, {})
|
|
160
|
-
symbols = self._get_symbols(portfolio)
|
|
161
|
-
|
|
162
|
-
# If config symbols is set, add the symbols to the balances
|
|
163
|
-
if SYMBOLS in self.configuration_service.config \
|
|
164
|
-
and self.configuration_service.config[SYMBOLS] is not None:
|
|
165
|
-
for symbol in symbols:
|
|
166
|
-
target_symbol = symbol.split("/")[0]
|
|
167
|
-
|
|
168
|
-
if target_symbol not in balances:
|
|
169
|
-
balances[target_symbol] = 0
|
|
170
|
-
|
|
171
|
-
for key, value in balances.items():
|
|
172
|
-
logger.info(f"Syncing balance for {key}")
|
|
173
|
-
amount = float(value)
|
|
134
|
+
# Update also a trading symbol position
|
|
135
|
+
trading_symbol_position = self.position_repository.find(
|
|
136
|
+
{
|
|
137
|
+
"symbol": portfolio.trading_symbol,
|
|
138
|
+
"portfolio_id": portfolio.id
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
self.position_repository.update(
|
|
142
|
+
trading_symbol_position.id, {"amount": unallocated}
|
|
143
|
+
)
|
|
174
144
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
145
|
+
else:
|
|
146
|
+
# Check if the portfolio unallocated balance is
|
|
147
|
+
# available on the exchange
|
|
148
|
+
if portfolio.unallocated > 0:
|
|
149
|
+
if position is None or portfolio.unallocated > position.amount:
|
|
150
|
+
raise OperationalException(
|
|
151
|
+
f"Out of sync: the unallocated balance"
|
|
152
|
+
" of the exiting portfolio is more than the available"
|
|
153
|
+
" balance on the exchange. Please make sure"
|
|
154
|
+
" that you have at least "
|
|
155
|
+
f"{portfolio.unallocated}"
|
|
156
|
+
f" {portfolio.trading_symbol.upper()} available"
|
|
157
|
+
" on the exchange."
|
|
158
|
+
)
|
|
181
159
|
|
|
182
|
-
|
|
183
|
-
position = self.position_repository.find({"symbol": key})
|
|
184
|
-
data = {"amount": amount}
|
|
185
|
-
self.position_repository.update(position.id, data)
|
|
186
|
-
else:
|
|
187
|
-
portfolio = self.portfolio_repository.find(
|
|
188
|
-
{"identifier": portfolio.identifier}
|
|
189
|
-
)
|
|
190
|
-
self.position_repository.create(
|
|
191
|
-
{
|
|
192
|
-
"symbol": key,
|
|
193
|
-
"amount": amount,
|
|
194
|
-
"portfolio_id": portfolio.id
|
|
195
|
-
}
|
|
196
|
-
)
|
|
160
|
+
return portfolio
|
|
197
161
|
|
|
198
162
|
def sync_orders(self, portfolio):
|
|
199
163
|
"""
|
|
200
164
|
Function to sync all local orders with the orders on the exchange.
|
|
201
|
-
This
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
First all orders are retrieved from the exchange and updated in the
|
|
205
|
-
database. If the order does not exist in the database, it will be
|
|
206
|
-
created and treated as a new order.
|
|
207
|
-
|
|
208
|
-
When an order is closed on the exchange, the order will be updated
|
|
209
|
-
in the database to closed. We will also then update the portfolio
|
|
210
|
-
and position balances accordingly.
|
|
165
|
+
This method will go over all local open orders and check if they are
|
|
166
|
+
changed on the exchange. If they are, the local order will be
|
|
167
|
+
updated to match the status on the exchange.
|
|
211
168
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
order in the database and update the portfolio and position balances
|
|
169
|
+
Args:
|
|
170
|
+
portfolio: Portfolio object
|
|
215
171
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
During the syncing of the orders, new orders are not executed. They
|
|
220
|
-
are only created in the database. This is to prevent the algorithm
|
|
221
|
-
from executing orders that are already executed on the exchange.
|
|
172
|
+
Returns:
|
|
173
|
+
None
|
|
222
174
|
"""
|
|
223
175
|
|
|
224
|
-
|
|
225
|
-
.get(portfolio.identifier)
|
|
226
|
-
symbols = self._get_symbols(portfolio)
|
|
227
|
-
positions = self.position_repository.get_all(
|
|
228
|
-
{"portfolio_id": portfolio_configuration.identifier}
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
# Remove the portfolio trading symbol from the symbols
|
|
232
|
-
symbols = [
|
|
233
|
-
symbol for symbol in symbols if symbol != portfolio.trading_symbol
|
|
234
|
-
]
|
|
235
|
-
|
|
236
|
-
# Check if there are orders for the available symbols
|
|
237
|
-
for symbol in symbols:
|
|
238
|
-
symbol = f"{symbol.upper()}"
|
|
239
|
-
orders = self.market_service.get_orders(
|
|
240
|
-
symbol=symbol,
|
|
241
|
-
since=portfolio_configuration.track_from,
|
|
242
|
-
market=portfolio.market
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
if orders is not None and len(orders) > 0:
|
|
246
|
-
# Order the list of orders by created_at
|
|
247
|
-
ordered_external_order_list = sorted(
|
|
248
|
-
orders, key=lambda x: x.created_at
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
if portfolio_configuration.track_from is not None:
|
|
252
|
-
ordered_external_order_list = [
|
|
253
|
-
order for order in ordered_external_order_list
|
|
254
|
-
if order.created_at >= portfolio_configuration
|
|
255
|
-
.track_from
|
|
256
|
-
]
|
|
257
|
-
|
|
258
|
-
for external_order in ordered_external_order_list:
|
|
259
|
-
|
|
260
|
-
if self.order_repository.exists(
|
|
261
|
-
{"external_id": external_order.external_id}
|
|
262
|
-
):
|
|
263
|
-
logger.info("Updating existing order")
|
|
264
|
-
order = self.order_repository.find(
|
|
265
|
-
{"external_id": external_order.external_id}
|
|
266
|
-
)
|
|
267
|
-
self.order_repository.update(
|
|
268
|
-
order.id, external_order.to_dict()
|
|
269
|
-
)
|
|
270
|
-
else:
|
|
271
|
-
logger.info(
|
|
272
|
-
"Creating new order, based on external order"
|
|
273
|
-
)
|
|
274
|
-
data = external_order.to_dict()
|
|
275
|
-
data.pop("trade_closed_at", None)
|
|
276
|
-
data.pop("trade_closed_price", None)
|
|
277
|
-
data.pop("trade_closed_amount", None)
|
|
278
|
-
position_id = None
|
|
279
|
-
|
|
280
|
-
# Get position id
|
|
281
|
-
for position in positions:
|
|
282
|
-
if position.symbol == external_order.target_symbol:
|
|
283
|
-
position_id = position.id
|
|
284
|
-
break
|
|
285
|
-
|
|
286
|
-
# Create the new order
|
|
287
|
-
new_order_data = {
|
|
288
|
-
"target_symbol": external_order.target_symbol,
|
|
289
|
-
"trading_symbol": portfolio.trading_symbol,
|
|
290
|
-
"amount": external_order.amount,
|
|
291
|
-
"price": external_order.price,
|
|
292
|
-
"order_side": external_order.order_side,
|
|
293
|
-
"order_type": external_order.order_type,
|
|
294
|
-
"external_id": external_order.external_id,
|
|
295
|
-
"status": "open",
|
|
296
|
-
"position_id": position_id,
|
|
297
|
-
"created_at": external_order.created_at,
|
|
298
|
-
}
|
|
299
|
-
new_order = self.order_repository.create(
|
|
300
|
-
new_order_data,
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
# Update the order to its current status
|
|
304
|
-
# By default it should not sync the unallocated
|
|
305
|
-
# balance as this has already by done.
|
|
306
|
-
# Position amounts should be updated
|
|
307
|
-
update_data = {
|
|
308
|
-
"status": external_order.status,
|
|
309
|
-
"filled": external_order.filled,
|
|
310
|
-
"remaining": external_order.remaining,
|
|
311
|
-
"updated_at": external_order.created_at,
|
|
312
|
-
}
|
|
313
|
-
self.order_repository.update(
|
|
314
|
-
new_order.id, update_data
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
def sync_trades(self, portfolio):
|
|
318
|
-
orders = self.order_repository.get_all(
|
|
319
|
-
{
|
|
320
|
-
"portfolio": portfolio.identifier,
|
|
321
|
-
"order_by_created_at_asc": True
|
|
322
|
-
}
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
sell_orders = [
|
|
326
|
-
order for order in orders
|
|
327
|
-
if OrderSide.SELL.equals(order.order_side)
|
|
328
|
-
]
|
|
329
|
-
|
|
330
|
-
for sell_order in sell_orders:
|
|
331
|
-
self.trade_service.close_trades(
|
|
332
|
-
sell_order, sell_order.get_filled()
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
def _get_symbols(self, portfolio):
|
|
336
|
-
config = self.configuration_service.config
|
|
337
|
-
available_symbols = []
|
|
338
|
-
|
|
339
|
-
# Check if there already are positions
|
|
340
|
-
positions = self.position_repository.get_all(
|
|
341
|
-
{"identifier": portfolio.get_identifier()}
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
if len(positions) > 0:
|
|
345
|
-
available_symbols = [
|
|
346
|
-
f"{position.get_symbol()}/{portfolio.get_trading_symbol()}"
|
|
347
|
-
for position in positions
|
|
348
|
-
if position.get_symbol() != portfolio.trading_symbol
|
|
349
|
-
]
|
|
350
|
-
|
|
351
|
-
if SYMBOLS in config and config[SYMBOLS] is not None:
|
|
352
|
-
symbols = config[SYMBOLS]
|
|
176
|
+
config = self.configuration_service.get_config()
|
|
353
177
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
market_symbols = self.market_service.get_symbols(portfolio.market)
|
|
360
|
-
|
|
361
|
-
for symbol in symbols:
|
|
362
|
-
|
|
363
|
-
if symbol not in market_symbols:
|
|
364
|
-
raise OperationalException(
|
|
365
|
-
f"The symbol {symbol} in the configuration is not "
|
|
366
|
-
"available on the exchange. Please make sure that the "
|
|
367
|
-
"symbols in the configuration are available on the "
|
|
368
|
-
"exchange. The available symbols on the exchange are: "
|
|
369
|
-
f"{market_symbols}"
|
|
370
|
-
)
|
|
371
|
-
else:
|
|
372
|
-
|
|
373
|
-
if symbol not in available_symbols:
|
|
374
|
-
available_symbols.append(symbol)
|
|
375
|
-
else:
|
|
376
|
-
market_symbols = self.market_service.get_symbols(portfolio.market)
|
|
377
|
-
available_symbols = market_symbols
|
|
178
|
+
if ENVIRONMENT in config \
|
|
179
|
+
and Environment.BACKTEST.equals(config[ENVIRONMENT]):
|
|
180
|
+
return
|
|
378
181
|
|
|
379
|
-
|
|
182
|
+
self.order_service.check_pending_orders(portfolio)
|