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.

Files changed (256) hide show
  1. investing_algorithm_framework/__init__.py +168 -45
  2. investing_algorithm_framework/app/__init__.py +32 -1
  3. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  4. investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
  5. investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
  6. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  7. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  8. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  9. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  10. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  11. investing_algorithm_framework/app/app.py +1933 -589
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1725 -0
  14. investing_algorithm_framework/app/eventloop.py +590 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  31. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  32. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  33. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  34. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  35. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  36. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +4 -2
  37. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +1 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/strategy.py +664 -84
  41. investing_algorithm_framework/app/task.py +5 -3
  42. investing_algorithm_framework/app/web/__init__.py +2 -1
  43. investing_algorithm_framework/app/web/create_app.py +4 -2
  44. investing_algorithm_framework/cli/__init__.py +0 -0
  45. investing_algorithm_framework/cli/cli.py +226 -0
  46. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  47. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  48. investing_algorithm_framework/cli/initialize_app.py +603 -0
  49. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  50. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  51. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  52. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  53. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  55. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  56. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  58. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  59. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  60. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  61. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  62. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  63. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  64. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  65. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  66. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  67. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  68. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  69. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  70. investing_algorithm_framework/create_app.py +40 -6
  71. investing_algorithm_framework/dependency_container.py +72 -56
  72. investing_algorithm_framework/domain/__init__.py +71 -47
  73. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  74. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  75. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  76. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  77. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  78. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  79. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  81. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  82. investing_algorithm_framework/domain/config.py +59 -91
  83. investing_algorithm_framework/domain/constants.py +13 -38
  84. investing_algorithm_framework/domain/data_provider.py +334 -0
  85. investing_algorithm_framework/domain/data_structures.py +3 -2
  86. investing_algorithm_framework/domain/exceptions.py +51 -1
  87. investing_algorithm_framework/domain/models/__init__.py +17 -12
  88. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  89. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  90. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  91. investing_algorithm_framework/domain/models/event.py +35 -0
  92. investing_algorithm_framework/domain/models/market/market_credential.py +55 -1
  93. investing_algorithm_framework/domain/models/order/order.py +77 -83
  94. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  95. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  96. investing_algorithm_framework/domain/models/portfolio/portfolio.py +81 -3
  97. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
  98. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  99. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  100. investing_algorithm_framework/domain/models/position/position.py +12 -0
  101. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  102. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  104. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  105. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  106. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  107. investing_algorithm_framework/domain/models/time_frame.py +37 -0
  108. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  109. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  110. investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
  111. investing_algorithm_framework/domain/models/trade/trade.py +295 -171
  112. investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
  113. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  114. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  115. investing_algorithm_framework/domain/order_executor.py +112 -0
  116. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  117. investing_algorithm_framework/domain/services/__init__.py +2 -9
  118. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
  119. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  120. investing_algorithm_framework/domain/strategy.py +1 -29
  121. investing_algorithm_framework/domain/utils/__init__.py +12 -7
  122. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  123. investing_algorithm_framework/domain/utils/dates.py +57 -0
  124. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  125. investing_algorithm_framework/domain/utils/polars.py +53 -0
  126. investing_algorithm_framework/domain/utils/random.py +29 -0
  127. investing_algorithm_framework/download_data.py +108 -0
  128. investing_algorithm_framework/infrastructure/__init__.py +31 -18
  129. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  130. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  131. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  132. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  133. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  134. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  135. investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
  136. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
  137. investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
  138. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  139. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  140. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  141. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
  142. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
  143. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  144. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  145. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  146. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  147. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  148. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  149. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  150. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  151. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  152. investing_algorithm_framework/infrastructure/repositories/__init__.py +8 -0
  153. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  154. investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
  155. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
  156. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  157. investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
  158. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  159. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  160. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  161. investing_algorithm_framework/infrastructure/services/__init__.py +4 -4
  162. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  163. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  164. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  165. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  166. investing_algorithm_framework/services/__init__.py +113 -16
  167. investing_algorithm_framework/services/backtesting/__init__.py +0 -7
  168. investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
  169. investing_algorithm_framework/services/configuration_service.py +77 -11
  170. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  171. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  172. investing_algorithm_framework/services/market_credential_service.py +16 -1
  173. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  174. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  175. investing_algorithm_framework/services/metrics/beta.py +0 -0
  176. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  177. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  178. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  179. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  180. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  181. investing_algorithm_framework/services/metrics/generate.py +358 -0
  182. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  183. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  184. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  185. investing_algorithm_framework/services/metrics/returns.py +452 -0
  186. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  187. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  188. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  189. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  190. investing_algorithm_framework/services/metrics/trades.py +500 -0
  191. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  192. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  193. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  194. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  195. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  196. investing_algorithm_framework/services/order_service/__init__.py +3 -1
  197. investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
  198. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  199. investing_algorithm_framework/services/order_service/order_service.py +407 -326
  200. investing_algorithm_framework/services/portfolios/__init__.py +3 -1
  201. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
  202. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
  203. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  204. investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
  205. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
  206. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
  207. investing_algorithm_framework/services/positions/__init__.py +7 -0
  208. investing_algorithm_framework/services/positions/position_service.py +210 -0
  209. investing_algorithm_framework/services/repository_service.py +8 -2
  210. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  211. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  212. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  213. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  214. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  215. investing_algorithm_framework/services/trade_service/trade_service.py +1013 -315
  216. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  217. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  218. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  219. investing_algorithm_framework-7.19.15.dist-info/RECORD +263 -0
  220. investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
  221. investing_algorithm_framework/app/algorithm.py +0 -1105
  222. investing_algorithm_framework/domain/graphs.py +0 -382
  223. investing_algorithm_framework/domain/metrics/__init__.py +0 -6
  224. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
  225. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
  226. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  227. investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
  228. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
  229. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  230. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  231. investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
  232. investing_algorithm_framework/domain/services/market_service.py +0 -153
  233. investing_algorithm_framework/domain/singleton.py +0 -9
  234. investing_algorithm_framework/domain/utils/backtesting.py +0 -472
  235. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
  236. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
  237. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
  238. investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
  239. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  240. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
  241. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  242. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  243. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -350
  244. investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
  245. investing_algorithm_framework/services/backtesting/graphs.py +0 -61
  246. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
  247. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
  248. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
  249. investing_algorithm_framework/services/position_service.py +0 -31
  250. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
  251. investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
  252. investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
  253. /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
  254. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  255. {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  256. {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, RESERVED_BALANCES, APP_MODE, SYMBOLS, \
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
- self,
18
- trade_service: TradeService,
19
- configuration_service,
20
- order_repository,
21
- position_repository,
22
- portfolio_repository,
23
- portfolio_configuration_service,
24
- market_credential_service,
25
- market_service
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.order_repository = order_repository
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 algorithm is running stateless it will update the unallocated
44
- balance of the portfolio to the available balance on the exchange.
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
- If the algorithm is running stateful, the unallocated balance of the
47
- portfolio will only check if the amount on the exchange is less
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
- If in the config the RESERVED_BALANCES key is set, the reserved amount
55
- will be subtracted from the unallocated amount. This is to prevent
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
- # Get the unallocated balance of the portfolio from the exchange
72
- balances = self.market_service.get_balance(market=portfolio.market)
73
-
74
- if portfolio.trading_symbol.upper() not in balances:
75
- unallocated = 0
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
- if portfolio.unallocated is not None and \
94
- unallocated != portfolio.unallocated:
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 unallocated < portfolio.unallocated:
96
+ if portfolio.initial_balance > available:
97
97
  raise OperationalException(
98
- "There seems to be a mismatch between "
99
- "the portfolio configuration and the balances on"
100
- " the exchange. "
101
- f"Please make sure that the available "
102
- f"{portfolio.trading_symbol} "
103
- f"on your exchange {portfolio.market} "
104
- "matches the portfolio configuration amount of: "
105
- f"{portfolio.unallocated} "
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
- return portfolio
139
-
140
- def sync_positions(self, portfolio):
141
- """
142
- Method to sync the portfolio balances with the balances
143
- on the exchange.
144
- This method will retrieve the balances from the exchange and update
145
- the portfolio balances accordingly.
146
-
147
- If the unallocated balance of the portfolio is less than the available
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
- if key in reserved_balances:
176
- logger.info(
177
- f"{key} has reserved balance of {reserved_balances[key]}"
178
- )
179
- reserved = float(reserved_balances[key])
180
- amount = amount - reserved
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
- if self.position_repository.exists({"symbol": key}):
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 function will retrieve all orders from the exchange and
202
- update the portfolio balances and positions accordingly.
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
- If the order exists, we will check if the filled amount of the order
213
- has changed. If the filled amount has changed, we will update the
214
- order in the database and update the portfolio and position balances
169
+ Args:
170
+ portfolio: Portfolio object
215
171
 
216
- if the status of an existing order has changed, we will update the
217
- order in the database and update the portfolio and position balances
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
- portfolio_configuration = self.portfolio_configuration_service \
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
- if not isinstance(symbols, list):
355
- raise OperationalException(
356
- "The symbols configuration should be a list of strings"
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
- return available_symbols
182
+ self.order_service.check_pending_orders(portfolio)
@@ -0,0 +1,7 @@
1
+ from .position_service import PositionService
2
+ from .position_snapshot_service import PositionSnapshotService
3
+
4
+ __all__ = [
5
+ "PositionService",
6
+ "PositionSnapshotService"
7
+ ]