investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__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.
Files changed (276) hide show
  1. investing_algorithm_framework/__init__.py +192 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +29 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2220 -379
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -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/time_metrics_table.py +80 -0
  31. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  32. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  33. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  34. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  35. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  36. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  37. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  39. investing_algorithm_framework/app/strategy.py +867 -60
  40. investing_algorithm_framework/app/task.py +5 -3
  41. investing_algorithm_framework/app/web/__init__.py +2 -1
  42. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  43. investing_algorithm_framework/app/web/controllers/orders.py +3 -2
  44. investing_algorithm_framework/app/web/controllers/positions.py +2 -2
  45. investing_algorithm_framework/app/web/create_app.py +4 -2
  46. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  47. investing_algorithm_framework/cli/__init__.py +0 -0
  48. investing_algorithm_framework/cli/cli.py +231 -0
  49. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  50. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  51. investing_algorithm_framework/cli/initialize_app.py +603 -0
  52. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  53. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  55. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  56. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  58. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  59. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  60. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  61. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  62. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  63. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  64. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  65. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  66. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  67. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  68. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  69. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  70. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  71. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  72. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  73. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  74. investing_algorithm_framework/create_app.py +40 -7
  75. investing_algorithm_framework/dependency_container.py +100 -47
  76. investing_algorithm_framework/domain/__init__.py +97 -30
  77. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  78. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  79. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  81. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  82. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  83. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  84. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  87. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  88. investing_algorithm_framework/domain/config.py +59 -136
  89. investing_algorithm_framework/domain/constants.py +18 -37
  90. investing_algorithm_framework/domain/data_provider.py +334 -0
  91. investing_algorithm_framework/domain/data_structures.py +42 -0
  92. investing_algorithm_framework/domain/exceptions.py +51 -1
  93. investing_algorithm_framework/domain/models/__init__.py +26 -19
  94. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  95. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  96. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  97. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  98. investing_algorithm_framework/domain/models/event.py +35 -0
  99. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  100. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  101. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  102. investing_algorithm_framework/domain/models/order/order.py +198 -65
  103. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  104. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  105. investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
  106. investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
  107. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
  108. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  109. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  110. investing_algorithm_framework/domain/models/position/position.py +20 -0
  111. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  112. investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
  113. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  114. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  115. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  116. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  117. investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
  118. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  119. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  120. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  121. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  122. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  123. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  124. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  125. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  126. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  127. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  128. investing_algorithm_framework/domain/order_executor.py +112 -0
  129. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  130. investing_algorithm_framework/domain/services/__init__.py +11 -0
  131. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  132. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  133. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  134. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  135. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  136. investing_algorithm_framework/domain/strategy.py +1 -29
  137. investing_algorithm_framework/domain/utils/__init__.py +15 -5
  138. investing_algorithm_framework/domain/utils/csv.py +22 -0
  139. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  140. investing_algorithm_framework/domain/utils/dates.py +57 -0
  141. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  142. investing_algorithm_framework/domain/utils/polars.py +53 -0
  143. investing_algorithm_framework/domain/utils/random.py +29 -0
  144. investing_algorithm_framework/download_data.py +244 -0
  145. investing_algorithm_framework/infrastructure/__init__.py +37 -11
  146. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  147. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  148. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  149. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  150. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  151. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  152. investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
  153. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  154. investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
  155. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  156. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  157. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  158. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
  159. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
  160. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
  161. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  162. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  163. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  164. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  165. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  166. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  167. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  168. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  169. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  170. investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
  171. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  172. investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
  173. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  174. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  175. investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
  176. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  177. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  178. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  179. investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
  180. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  181. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  182. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  183. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  184. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  185. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  186. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  187. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  188. investing_algorithm_framework/services/__init__.py +123 -15
  189. investing_algorithm_framework/services/configuration_service.py +77 -11
  190. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  191. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  192. investing_algorithm_framework/services/market_credential_service.py +40 -0
  193. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  194. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  195. investing_algorithm_framework/services/metrics/beta.py +0 -0
  196. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  197. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  198. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  199. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  200. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  201. investing_algorithm_framework/services/metrics/generate.py +358 -0
  202. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  203. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  204. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  205. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  206. investing_algorithm_framework/services/metrics/returns.py +452 -0
  207. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  208. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  209. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  210. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  211. investing_algorithm_framework/services/metrics/trades.py +473 -0
  212. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  213. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  214. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  215. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  216. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  217. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  218. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  219. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  220. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  221. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  222. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  223. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  224. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  225. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  226. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  227. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  228. investing_algorithm_framework/services/positions/__init__.py +7 -0
  229. investing_algorithm_framework/services/positions/position_service.py +210 -0
  230. investing_algorithm_framework/services/repository_service.py +8 -2
  231. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  232. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  233. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  234. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  235. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  237. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  238. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  239. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  240. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  241. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  242. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  243. investing_algorithm_framework/app/algorithm.py +0 -630
  244. investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
  245. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  246. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  247. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
  248. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  249. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  250. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  251. investing_algorithm_framework/domain/models/trade.py +0 -78
  252. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  253. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  254. investing_algorithm_framework/domain/singleton.py +0 -9
  255. investing_algorithm_framework/domain/utils/backtesting.py +0 -82
  256. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  257. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  258. investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
  259. investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
  260. investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
  261. investing_algorithm_framework/services/backtest_service.py +0 -268
  262. investing_algorithm_framework/services/market_data_service.py +0 -77
  263. investing_algorithm_framework/services/order_backtest_service.py +0 -122
  264. investing_algorithm_framework/services/order_service.py +0 -752
  265. investing_algorithm_framework/services/portfolio_service.py +0 -164
  266. investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
  267. investing_algorithm_framework/services/position_cost_service.py +0 -5
  268. investing_algorithm_framework/services/position_service.py +0 -63
  269. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
  270. investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
  271. investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
  272. investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
  273. investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
  274. /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
  275. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  276. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -1,164 +0,0 @@
1
- import logging
2
- from datetime import datetime
3
-
4
- from investing_algorithm_framework.domain import OrderSide, OrderStatus
5
- from investing_algorithm_framework.services.repository_service \
6
- import RepositoryService
7
-
8
- logger = logging.getLogger("investing_algorithm_framework")
9
-
10
-
11
- class PortfolioService(RepositoryService):
12
-
13
- def __init__(
14
- self,
15
- market_service,
16
- position_repository,
17
- order_service,
18
- portfolio_repository,
19
- portfolio_configuration_service,
20
- portfolio_snapshot_service
21
- ):
22
- self.market_service = market_service
23
- self.position_repository = position_repository
24
- self.portfolio_configuration_service = portfolio_configuration_service
25
- self.order_service = order_service
26
- self.portfolio_snapshot_service = portfolio_snapshot_service
27
- super(PortfolioService, self).__init__(portfolio_repository)
28
-
29
- def find(self, query_params):
30
- portfolio = self.repository.find(query_params)
31
- portfolio_configuration = self.portfolio_configuration_service\
32
- .get(portfolio.identifier)
33
- portfolio.configuration = portfolio_configuration
34
- return portfolio
35
-
36
- def create(self, data):
37
- unallocated = data.get("unallocated", 0)
38
- portfolio = super(PortfolioService, self).create(data)
39
- self.position_repository.create(
40
- {
41
- "symbol": portfolio.get_trading_symbol(),
42
- "amount": unallocated,
43
- "portfolio_id": portfolio.id,
44
- "cost": unallocated
45
- }
46
- )
47
- self.create_snapshot(portfolio.id, created_at=portfolio.created_at)
48
- return portfolio
49
-
50
- def sync_portfolios(self):
51
-
52
- for portfolio in self.get_all():
53
- portfolio_configuration = self.portfolio_configuration_service\
54
- .get(portfolio.identifier)
55
-
56
- self.market_service.initialize(portfolio_configuration)
57
- balances = self.market_service.get_balance()
58
-
59
- for symbol in balances:
60
- logger.info(f"Syncing {symbol} balance")
61
- logger.info(f"Balance: {balances[symbol]}")
62
-
63
- if "free" in balances[symbol]:
64
-
65
- if self.position_repository.exists(
66
- {"portfolio_id": portfolio.id, "symbol": symbol}
67
- ):
68
- position = self.position_repository.find(
69
- {
70
- "portfolio_id": portfolio.id,
71
- "symbol": symbol
72
- }
73
- )
74
- self.position_repository.update(
75
- position.id,
76
- {"amount": balances[symbol]["free"]}
77
- )
78
- else:
79
- self.position_repository.create(
80
- {
81
- "symbol": symbol,
82
- "amount": balances[symbol]["free"],
83
- "portfolio_id": portfolio.id
84
- }
85
- )
86
-
87
- if symbol == portfolio.trading_symbol:
88
- if portfolio.unallocated != balances[symbol]["free"]:
89
- logger.info(
90
- "Updating unallocated balance "
91
- f"from {portfolio.unallocated} "
92
- f"to {balances[symbol]['free']}"
93
- )
94
- difference = balances[symbol]["free"] \
95
- - portfolio.get_unallocated()
96
- self.update(
97
- portfolio.id,
98
- {
99
- "unallocated": portfolio.get_unallocated() + difference,
100
- "net_size": portfolio.get_net_size() + difference
101
- }
102
- )
103
-
104
-
105
- for position in self.position_repository.get_all(
106
- {"portfolio_id": portfolio.id}
107
- ):
108
- if position.symbol == portfolio.trading_symbol:
109
- continue
110
-
111
- logger.info(f"Syncing {position.symbol} orders")
112
-
113
- external_orders = self.market_service\
114
- .get_orders(
115
- f"{position.symbol}/{portfolio.trading_symbol}",
116
- since=portfolio_configuration.track_from
117
- )
118
-
119
- logger.info(
120
- f"Found {len(external_orders)} external orders "
121
- f"for position {position.symbol}"
122
- )
123
-
124
- for external_order in external_orders:
125
-
126
- if self.order_service.exists(
127
- {"external_id": external_order.external_id}
128
- ):
129
- logger.info("Updating existing order")
130
- order = self.order_service.find(
131
- {"external_id": external_order.external_id}
132
- )
133
- self.order_service.update(
134
- order.id, external_order.to_dict()
135
- )
136
- else:
137
- logger.info(
138
- "Creating new order based on external order"
139
- )
140
- data = external_order.to_dict()
141
- data["position_id"] = position.id
142
- data["portfolio_id"] = portfolio.id
143
- self.order_service.create(
144
- data, execute=False, validate=False, sync=False
145
- )
146
-
147
- def create_snapshot(self, portfolio_id, created_at=None):
148
-
149
- if created_at is None:
150
- created_at = datetime.utcnow()
151
-
152
- portfolio = self.get(portfolio_id)
153
- pending_orders = self.order_service.get_all(
154
- {
155
- "order_side": OrderSide.BUY.value,
156
- "status": OrderStatus.OPEN.value,
157
- "portfolio_id": portfolio.id
158
- }
159
- )
160
- return self.portfolio_snapshot_service.create_snapshot(
161
- portfolio,
162
- pending_orders=pending_orders,
163
- created_at=created_at
164
- )
@@ -1,68 +0,0 @@
1
- from datetime import datetime
2
-
3
- from investing_algorithm_framework.services.repository_service import \
4
- RepositoryService
5
- from investing_algorithm_framework.domain import parse_decimal_to_string
6
-
7
-
8
- class PortfolioSnapshotService(RepositoryService):
9
-
10
- def __init__(
11
- self,
12
- repository,
13
- position_repository,
14
- position_snapshot_service
15
- ):
16
- self.position_snapshot_service = position_snapshot_service
17
- self.position_repository = position_repository
18
- super(PortfolioSnapshotService, self).__init__(repository)
19
-
20
- def create_snapshot(
21
- self,
22
- portfolio,
23
- pending_orders=None,
24
- created_orders=None,
25
- created_at=None,
26
- cash_flow=0
27
- ):
28
- pending_value = 0
29
-
30
- if created_orders is not None:
31
- for order in created_orders:
32
- pending_value += order.get_price() * order.get_amount()
33
-
34
- if pending_orders is not None:
35
- for order in pending_orders:
36
- pending_value += order.get_price() * order.get_remaining()
37
-
38
- if created_at is None:
39
- created_at = datetime.now()
40
-
41
- data = {
42
- "portfolio_id": portfolio.id,
43
- "trading_symbol": portfolio.trading_symbol,
44
- "pending_value": pending_value,
45
- "unallocated": portfolio.unallocated,
46
- "total_net_gain": portfolio.total_net_gain,
47
- "total_revenue": portfolio.total_revenue,
48
- "total_cost": portfolio.total_cost,
49
- "cash_flow": cash_flow,
50
- "created_at": created_at,
51
- }
52
- snapshot = self.create(data)
53
- positions = self.position_repository.get_all(
54
- {"portfolio": portfolio.id}
55
- )
56
-
57
- for position in positions:
58
- self.position_snapshot_service.create_snapshot(
59
- snapshot.id, position
60
- )
61
-
62
- return snapshot
63
-
64
- def get_latest_snapshot(self, portfolio_id):
65
- pass
66
-
67
- def get_snapshots(self, portfolio_id, start_date=None, end_date=None):
68
- pass
@@ -1,5 +0,0 @@
1
- from .portfolio_service import RepositoryService
2
-
3
-
4
- class PositionCostService(RepositoryService):
5
- pass
@@ -1,63 +0,0 @@
1
- from investing_algorithm_framework.services.repository_service import \
2
- RepositoryService
3
-
4
-
5
- class PositionService(RepositoryService):
6
-
7
- def __init__(self, repository, order_repository, market_service):
8
- super().__init__(repository)
9
- self.market_service = market_service
10
- self.order_repository = order_repository
11
-
12
- def sync_positions(self, portfolio, market_service):
13
- self.market_service.initialize(portfolio.portfolio_configuration)
14
- balances = self.market_service.balances()
15
-
16
- # Get trading symbol position
17
- trading_symbol_position = balances[portfolio.get_trading_symbol()]
18
-
19
- if portfolio.portfolio_configuration.has_unallocated_limit \
20
- and trading_symbol_position["free"] > \
21
- portfolio.portfolio_configuration.max_unallocated:
22
- self._sync_position(
23
- portfolio.portfolio_configuration.max_unallocated,
24
- portfolio.id,
25
- portfolio.get_trading_symbol(),
26
- )
27
- else:
28
- self._sync_position(
29
- trading_symbol_position["free"],
30
- portfolio.id,
31
- portfolio.get_trading_symbol(),
32
- )
33
-
34
- for balance_entry in balances:
35
- symbol = balance_entry
36
-
37
- if symbol == portfolio.get_trading_symbol():
38
- continue
39
-
40
- if "free" in balances[symbol]:
41
- self._sync_position(
42
- balances[symbol]["free"],
43
- portfolio.id,
44
- symbol,
45
- )
46
-
47
- def _sync_position(self, synced_amount, portfolio_id, symbol):
48
- position = self.find({"symbol": symbol, "portfolio": portfolio_id})
49
-
50
- if position.amount != synced_amount:
51
- self.update(position.id, {"amount": synced_amount})
52
-
53
- def close_position(self, position_id, portfolio):
54
- self.market_service.initialize(portfolio.portfolio_configuration)
55
- position = self.get(position_id)
56
-
57
- if position.amount > 0:
58
- self.market_service.create_market_sell_order(
59
- position.symbol,
60
- portfolio.get_trading_symbol(),
61
- position.amount,
62
- )
63
-
@@ -1,225 +0,0 @@
1
- import logging
2
- import schedule
3
- from datetime import datetime
4
- from investing_algorithm_framework.domain import StoppableThread, TimeUnit, \
5
- OperationalException
6
-
7
- logger = logging.getLogger("investing_algorithm_framework")
8
-
9
-
10
- class StrategyOrchestratorService:
11
-
12
- def __init__(self, market_data_service):
13
- self.history = {}
14
- self._strategies = []
15
- self._tasks = []
16
- self.threads = []
17
- self.iterations = 0
18
- self.max_iterations = -1
19
- self.clear()
20
- self.market_data_service = market_data_service
21
-
22
- def cleanup_threads(self):
23
-
24
- for stoppable in self.threads:
25
- if not stoppable.is_alive():
26
- # get results from thread
27
- stoppable.done = True
28
- self.threads = [t for t in self.threads if not t.done]
29
-
30
- def run_strategy(self, strategy, algorithm, sync=False):
31
- self.cleanup_threads()
32
-
33
- matching_thread = next(
34
- (t for t in self.threads if t.name == strategy.worker_id),
35
- None
36
- )
37
-
38
- # Don't run a strategy that is already running
39
- if matching_thread:
40
- return
41
-
42
- market_data = self.market_data_service.get_data_for_strategy(strategy)
43
-
44
- logger.info(f"Running strategy {strategy.worker_id}")
45
-
46
- if sync:
47
- strategy.run_strategy(
48
- market_data=market_data, algorithm=algorithm
49
- )
50
- else:
51
- self.iterations += 1
52
- thread = StoppableThread(
53
- target=strategy.run_strategy,
54
- kwargs={
55
- "market_data": market_data,
56
- "algorithm": algorithm
57
- }
58
- )
59
- thread.name = strategy.worker_id
60
- thread.start()
61
- self.threads.append(thread)
62
-
63
- self.history[strategy.worker_id] = {"last_run": datetime.utcnow()}
64
-
65
- def run_task(self, task, algorithm, sync=False):
66
- self.cleanup_threads()
67
-
68
- matching_thread = next(
69
- (t for t in self.threads if t.name == task.worker_id),
70
- None
71
- )
72
-
73
- # Don't run a strategy that is already running
74
- if matching_thread:
75
- return
76
-
77
- logger.info(f"Running task {task.worker_id}")
78
-
79
- if sync:
80
- task.run(algorithm=algorithm)
81
- else:
82
- self.iterations += 1
83
- thread = StoppableThread(
84
- target=task.run,
85
- kwargs={"algorithm": algorithm}
86
- )
87
- thread.name = task.worker_id
88
- thread.start()
89
- self.threads.append(thread)
90
-
91
- self.history[task.worker_id] = {"last_run": datetime.utcnow()}
92
-
93
- def start(self, algorithm, number_of_iterations=None):
94
- self.max_iterations = number_of_iterations
95
-
96
- for strategy in self.strategies:
97
-
98
- if TimeUnit.SECOND.equals(strategy.time_unit):
99
- schedule.every(strategy.interval)\
100
- .seconds.do(self.run_strategy, strategy, algorithm)
101
- elif TimeUnit.MINUTE.equals(strategy.time_unit):
102
- schedule.every(strategy.interval)\
103
- .minutes.do(self.run_strategy, strategy, algorithm)
104
- elif TimeUnit.HOUR.equals(strategy.time_unit):
105
- schedule.every(strategy.interval)\
106
- .hours.do(self.run_strategy, strategy, algorithm)
107
-
108
- for task in self.tasks:
109
-
110
- if TimeUnit.SECOND.equals(task.time_unit):
111
- schedule.every(task.interval)\
112
- .seconds.do(self.run_task, task, algorithm)
113
- elif TimeUnit.MINUTE.equals(task.time_unit):
114
- schedule.every(task.interval)\
115
- .minutes.do(self.run_task, task, algorithm)
116
- elif TimeUnit.HOUR.equals(task.time_unit):
117
- schedule.every(task.interval)\
118
- .hours.do(self.run_task, task, algorithm)
119
-
120
- def stop(self):
121
- for thread in self.threads:
122
- thread.stop()
123
-
124
- schedule.clear()
125
-
126
- def clear(self):
127
- self.threads = []
128
- schedule.clear()
129
-
130
- def get_strategies(self, identifiers=None):
131
- if identifiers is None:
132
- return self.strategies
133
-
134
- strategies = []
135
- for strategy in self.strategies:
136
- if strategy.worker_id in identifiers:
137
- strategies.append(strategy)
138
-
139
- return strategies
140
-
141
- def get_tasks(self):
142
- return self._tasks
143
-
144
- def get_jobs(self):
145
- return schedule.jobs
146
-
147
- def run_pending_jobs(self):
148
-
149
- if self.max_iterations is not None and \
150
- self.max_iterations != -1 and \
151
- self.iterations >= self.max_iterations:
152
- self.clear()
153
- else:
154
- schedule.run_pending()
155
-
156
- def add_strategy(self, strategy):
157
-
158
- if strategy.worker_id not in self._strategies:
159
- self._strategies.append(strategy)
160
- else:
161
- raise OperationalException(
162
- "Strategy already exists with the same name"
163
- )
164
-
165
- def add_strategies(self, strategies):
166
- has_duplicates = False
167
-
168
- for i in range(len(strategies)):
169
- for j in range(i + 1, len(strategies)):
170
- if strategies[i].worker_id == strategies[j].worker_id:
171
- has_duplicates = True
172
- break
173
-
174
- if has_duplicates:
175
- raise OperationalException(
176
- "There are duplicate strategies with the same name"
177
- )
178
-
179
- self.strategies = strategies
180
-
181
- def add_tasks(self, tasks):
182
- has_duplicates = False
183
-
184
- for i in range(len(tasks)):
185
- for j in range(i + 1, len(tasks)):
186
- if tasks[i].worker_id == tasks[j].worker_id:
187
- has_duplicates = True
188
- break
189
-
190
- if has_duplicates:
191
- raise OperationalException(
192
- "There are duplicate tasks with the same name"
193
- )
194
-
195
- self.tasks = tasks
196
-
197
- @property
198
- def strategies(self):
199
- return self._strategies
200
-
201
- @strategies.setter
202
- def strategies(self, strategies):
203
- self._strategies = strategies
204
-
205
- @property
206
- def tasks(self):
207
- return self._tasks
208
-
209
- @tasks.setter
210
- def tasks(self, tasks):
211
- self._tasks = tasks
212
-
213
- @property
214
- def running(self):
215
- if len(self.strategies) == 0 and len(self.tasks) == 0:
216
- return False
217
-
218
- if self.max_iterations == -1:
219
- return True
220
-
221
- return self.max_iterations is None \
222
- or self.iterations < self.max_iterations
223
-
224
- def has_run(self, worker_id):
225
- return worker_id in self.history
@@ -1,8 +0,0 @@
1
- # Marc van Duyn | Mod and Contributor
2
- <a href="https://linkedin.com/in/marc-van-duyn">
3
- <img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" width="200px" />
4
- </a>
5
- <br/>
6
- <a href="https://github.com/MDUYN">
7
- <img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white" width="200px" />
8
- </a>