investing-algorithm-framework 1.3.1__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 (282) hide show
  1. investing_algorithm_framework/__init__.py +195 -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 +31 -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 +2233 -264
  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/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 +6 -3
  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 +2 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
  41. investing_algorithm_framework/app/strategy.py +873 -52
  42. investing_algorithm_framework/app/task.py +5 -3
  43. investing_algorithm_framework/app/web/__init__.py +2 -1
  44. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  45. investing_algorithm_framework/app/web/controllers/orders.py +4 -3
  46. investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
  47. investing_algorithm_framework/app/web/controllers/positions.py +3 -3
  48. investing_algorithm_framework/app/web/create_app.py +4 -2
  49. investing_algorithm_framework/app/web/error_handler.py +1 -1
  50. investing_algorithm_framework/app/web/schemas/order.py +2 -2
  51. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  52. investing_algorithm_framework/cli/__init__.py +0 -0
  53. investing_algorithm_framework/cli/cli.py +231 -0
  54. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  55. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  56. investing_algorithm_framework/cli/initialize_app.py +603 -0
  57. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  58. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  59. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  60. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  61. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  62. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  63. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  64. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  65. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  66. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  67. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  68. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  69. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  70. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  71. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  72. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  73. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  74. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  75. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  76. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  77. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  78. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  79. investing_algorithm_framework/create_app.py +43 -9
  80. investing_algorithm_framework/dependency_container.py +121 -33
  81. investing_algorithm_framework/domain/__init__.py +109 -22
  82. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  83. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  84. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  87. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  88. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  92. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  93. investing_algorithm_framework/domain/config.py +60 -138
  94. investing_algorithm_framework/domain/constants.py +23 -34
  95. investing_algorithm_framework/domain/data_provider.py +334 -0
  96. investing_algorithm_framework/domain/data_structures.py +42 -0
  97. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  98. investing_algorithm_framework/domain/exceptions.py +51 -1
  99. investing_algorithm_framework/domain/models/__init__.py +29 -14
  100. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  101. investing_algorithm_framework/domain/models/base_model.py +3 -1
  102. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  104. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  105. investing_algorithm_framework/domain/models/event.py +35 -0
  106. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  107. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  108. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  109. investing_algorithm_framework/domain/models/order/order.py +243 -86
  110. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  111. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  112. investing_algorithm_framework/domain/models/portfolio/__init__.py +7 -2
  113. investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
  114. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
  115. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  116. investing_algorithm_framework/domain/models/position/__init__.py +3 -2
  117. investing_algorithm_framework/domain/models/position/position.py +29 -0
  118. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  119. investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
  120. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  121. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  122. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  123. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  124. investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
  125. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  126. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +111 -2
  128. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  129. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  130. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  132. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  133. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  134. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  135. investing_algorithm_framework/domain/order_executor.py +112 -0
  136. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  137. investing_algorithm_framework/domain/services/__init__.py +11 -0
  138. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  139. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  140. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  141. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  142. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  143. investing_algorithm_framework/domain/strategy.py +1 -29
  144. investing_algorithm_framework/domain/utils/__init__.py +16 -4
  145. investing_algorithm_framework/domain/utils/csv.py +22 -0
  146. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  147. investing_algorithm_framework/domain/utils/dates.py +57 -0
  148. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  149. investing_algorithm_framework/domain/utils/polars.py +53 -0
  150. investing_algorithm_framework/domain/utils/random.py +29 -0
  151. investing_algorithm_framework/download_data.py +244 -0
  152. investing_algorithm_framework/infrastructure/__init__.py +39 -11
  153. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  154. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  155. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  156. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  157. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  158. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +87 -13
  159. investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
  160. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  161. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  162. investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
  163. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  164. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  165. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +3 -2
  166. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  167. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
  168. investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
  169. investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
  170. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  171. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  172. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  173. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  174. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  175. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  176. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  177. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  178. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  179. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  180. investing_algorithm_framework/infrastructure/repositories/__init__.py +13 -5
  181. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  182. investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
  183. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  184. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  185. investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
  186. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  187. investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
  188. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  189. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  190. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  191. investing_algorithm_framework/infrastructure/services/__init__.py +9 -2
  192. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  193. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  194. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  195. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  196. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  197. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  198. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  199. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  200. investing_algorithm_framework/services/__init__.py +127 -10
  201. investing_algorithm_framework/services/configuration_service.py +95 -0
  202. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  203. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  204. investing_algorithm_framework/services/market_credential_service.py +40 -0
  205. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  206. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  207. investing_algorithm_framework/services/metrics/beta.py +0 -0
  208. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  209. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  210. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  211. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  212. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  213. investing_algorithm_framework/services/metrics/generate.py +358 -0
  214. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  215. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  216. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  217. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  218. investing_algorithm_framework/services/metrics/returns.py +452 -0
  219. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  220. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  221. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  222. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  223. investing_algorithm_framework/services/metrics/trades.py +473 -0
  224. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  225. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  226. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  227. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  228. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  229. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  230. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  231. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  232. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  233. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  234. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  235. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  236. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  237. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  238. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  239. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  240. investing_algorithm_framework/services/positions/__init__.py +7 -0
  241. investing_algorithm_framework/services/positions/position_service.py +210 -0
  242. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  243. investing_algorithm_framework/services/repository_service.py +8 -2
  244. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  245. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  246. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  247. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  248. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  249. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  250. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  251. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  252. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  253. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  254. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  255. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  256. investing_algorithm_framework/app/algorithm.py +0 -410
  257. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  258. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  259. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
  260. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  261. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  262. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  263. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  264. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
  265. investing_algorithm_framework/domain/singleton.py +0 -9
  266. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  267. investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
  268. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  269. investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
  270. investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
  271. investing_algorithm_framework/services/market_data_service.py +0 -75
  272. investing_algorithm_framework/services/order_service.py +0 -464
  273. investing_algorithm_framework/services/portfolio_service.py +0 -105
  274. investing_algorithm_framework/services/position_cost_service.py +0 -5
  275. investing_algorithm_framework/services/position_service.py +0 -50
  276. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
  277. investing_algorithm_framework/setup_logging.py +0 -40
  278. investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
  279. investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
  280. investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
  281. investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
  282. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -0,0 +1,199 @@
1
+ import ccxt
2
+ from logging import getLogger
3
+ from typing import Union
4
+
5
+ from investing_algorithm_framework.domain import PortfolioProvider, \
6
+ OperationalException, Order, Position, MarketCredential
7
+
8
+
9
+ logger = getLogger("investing_algorithm_framework")
10
+
11
+
12
+ class CCXTPortfolioProvider(PortfolioProvider):
13
+ """
14
+ Implementation of Portfolio Provider for CCXT.
15
+ """
16
+
17
+ def get_order(
18
+ self, portfolio, order, market_credential
19
+ ) -> Union[Order, None]:
20
+ """
21
+ Method to check if there are any pending orders for the portfolio.
22
+ This method will retrieve the open orders from the exchange and
23
+ check if there are any pending orders for the portfolio.
24
+
25
+ !IMPORTANT: This function should return None if the order is
26
+ not found or if the order is not available on the
27
+ exchange or broker. Please do not throw an exception if the
28
+ order is not found.
29
+
30
+ Args:
31
+ portfolio: Portfolio object
32
+ order: Order object from the database
33
+ market_credential: Market credential object
34
+
35
+ Returns:
36
+ None
37
+ """
38
+ exchange = self.initialize_exchange(
39
+ portfolio.market, market_credential
40
+ )
41
+
42
+ if not exchange.has['fetchOrder']:
43
+ raise OperationalException(
44
+ f"Market service {portfolio.market} does not support "
45
+ f"functionality get_order"
46
+ )
47
+
48
+ symbol = order.get_symbol()
49
+
50
+ try:
51
+ external_order = exchange.fetchOrder(order.external_id, symbol)
52
+ external_order = Order.from_ccxt_order(external_order)
53
+ external_order.id = order.id
54
+ return external_order
55
+ except Exception as e:
56
+ logger.exception(e)
57
+ raise OperationalException("Could not retrieve order")
58
+
59
+ def get_position(
60
+ self, portfolio, symbol, market_credential
61
+ ) -> Union[Position, None]:
62
+ """
63
+ Function to get the position for a given symbol in the portfolio.
64
+ The returned position should be an object that reflects the current
65
+ state of the position on the exchange or broker.
66
+
67
+ !IMPORTANT: This function should return None if the position is
68
+ not found or if the position is not available on the
69
+ exchange or broker. Please do not throw an exception if the
70
+ position is not found.
71
+
72
+ Args:
73
+ portfolio (Portfolio): Portfolio object
74
+ symbol (str): Symbol object
75
+ market_credential (MarketCredential): MarketCredential object
76
+
77
+ Returns:
78
+ Position: Position for the given symbol in the portfolio
79
+ """
80
+
81
+ exchange = self.initialize_exchange(
82
+ portfolio.market, market_credential
83
+ )
84
+
85
+ if not exchange.has['fetchBalance']:
86
+ raise OperationalException(
87
+ f"Market service {portfolio.market} does not support "
88
+ f"functionality get_balance"
89
+ )
90
+
91
+ try:
92
+ amount = exchange.fetchBalance()["free"]
93
+
94
+ if symbol not in amount:
95
+ return None
96
+
97
+ return Position(
98
+ symbol=symbol,
99
+ amount=amount[symbol],
100
+ cost=0,
101
+ portfolio_id=portfolio.id
102
+ )
103
+ except Exception as e:
104
+ logger.exception(e)
105
+ raise OperationalException(
106
+ f"Please make sure you have "
107
+ f"registered a valid market credential "
108
+ f"object to the app: {str(e)}"
109
+ )
110
+
111
+ @staticmethod
112
+ def initialize_exchange(market, market_credential):
113
+ """
114
+ Function to initialize the exchange for the market.
115
+
116
+ Args:
117
+ market (str): The market to initialize the exchange for
118
+ market_credential (MarketCredential): The market credential to use
119
+ for the exchange
120
+
121
+ Returns:
122
+
123
+ """
124
+ market = market.lower()
125
+
126
+ if not hasattr(ccxt, market):
127
+ raise OperationalException(
128
+ f"No ccxt exchange for market id {market}"
129
+ )
130
+
131
+ exchange_class = getattr(ccxt, market)
132
+
133
+ if exchange_class is None:
134
+ raise OperationalException(
135
+ f"No market service found for market id {market}"
136
+ )
137
+
138
+ # Check the credentials for the exchange
139
+ (CCXTPortfolioProvider
140
+ .check_credentials(exchange_class, market_credential))
141
+ exchange = exchange_class({
142
+ 'apiKey': market_credential.api_key,
143
+ 'secret': market_credential.secret_key,
144
+ })
145
+ return exchange
146
+
147
+ @staticmethod
148
+ def check_credentials(
149
+ exchange_class, market_credential: MarketCredential
150
+ ):
151
+ """
152
+ Function to check if the credentials are valid for the exchange.
153
+
154
+ Args:
155
+ exchange_class: The exchange class to check the credentials for
156
+ market_credential: The market credential to use for the exchange
157
+
158
+ Raises:
159
+ OperationalException: If the credentials are not valid
160
+
161
+ Returns:
162
+ None
163
+ """
164
+ exchange = exchange_class()
165
+ credentials_info = exchange.requiredCredentials
166
+ market = market_credential.get_market()
167
+
168
+ if ('apiKey' in credentials_info
169
+ and credentials_info["apiKey"]
170
+ and market_credential.get_api_key() is None):
171
+ raise OperationalException(
172
+ f"Market credential for market {market}"
173
+ " requires an api key, either"
174
+ " as an argument or as an environment variable"
175
+ f" named as {market.upper()}_API_KEY"
176
+ )
177
+
178
+ if ('secret' in credentials_info
179
+ and credentials_info["secret"]
180
+ and market_credential.get_secret_key() is None):
181
+ raise OperationalException(
182
+ f"Market credential for market {market}"
183
+ " requires a secret key, either"
184
+ " as an argument or as an environment variable"
185
+ f" named as {market.upper()}_SECRET_KEY"
186
+ )
187
+
188
+ def supports_market(self, market):
189
+ """
190
+ Function to check if the market is supported by the portfolio
191
+ provider.
192
+
193
+ Args:
194
+ market: Market object
195
+
196
+ Returns:
197
+ bool: True if the market is supported, False otherwise
198
+ """
199
+ return hasattr(ccxt, market.lower())
@@ -1,13 +1,21 @@
1
1
  from .order_repository import SQLOrderRepository
2
- from .order_fee_repository import SQLOrderFeeRepository
3
- from .position_repository import SQLPositionRepository
2
+ from .order_metadata_repository import SQLOrderMetadataRepository
4
3
  from .portfolio_repository import SQLPortfolioRepository
5
- from .position_cost_repository import SQLPositionCostRepository
4
+ from .portfolio_snapshot_repository import SQLPortfolioSnapshotRepository
5
+ from .position_repository import SQLPositionRepository
6
+ from .position_snapshot_repository import SQLPositionSnapshotRepository
7
+ from .trade_repository import SQLTradeRepository
8
+ from .trade_stop_loss_repository import SQLTradeStopLossRepository
9
+ from .trade_take_profit_repository import SQLTradeTakeProfitRepository
6
10
 
7
11
  __all__ = [
8
- "SQLOrderFeeRepository",
9
12
  "SQLOrderRepository",
10
13
  "SQLPositionRepository",
14
+ "SQLPositionSnapshotRepository",
11
15
  "SQLPortfolioRepository",
12
- "SQLPositionCostRepository"
16
+ "SQLPortfolioSnapshotRepository",
17
+ "SQLTradeRepository",
18
+ "SQLTradeTakeProfitRepository",
19
+ "SQLTradeStopLossRepository",
20
+ "SQLOrderMetadataRepository"
13
21
  ]
@@ -0,0 +1,17 @@
1
+ from investing_algorithm_framework.infrastructure.models import \
2
+ SQLOrderMetadata
3
+ from .repository import Repository
4
+
5
+
6
+ class SQLOrderMetadataRepository(Repository):
7
+ base_class = SQLOrderMetadata
8
+ DEFAULT_NOT_FOUND_MESSAGE = "The requested order metadata was not found"
9
+
10
+ def _apply_query_params(self, db, query, query_params):
11
+
12
+ if "order_id" in query_params:
13
+ query = query.filter(
14
+ SQLOrderMetadata.order_id == query_params["order_id"]
15
+ )
16
+
17
+ return query
@@ -1,22 +1,24 @@
1
- from .repository import Repository
2
- from investing_algorithm_framework.infrastructure.models import SQLOrder, \
3
- SQLPosition, SQLPortfolio
4
1
  from investing_algorithm_framework.domain import OrderStatus, OrderType, \
5
2
  OrderSide
3
+ from investing_algorithm_framework.infrastructure.models import SQLOrder, \
4
+ SQLPosition, SQLPortfolio
5
+ from .repository import Repository
6
6
 
7
7
 
8
8
  class SQLOrderRepository(Repository):
9
9
  base_class = SQLOrder
10
+ DEFAULT_NOT_FOUND_MESSAGE = "The requested order was not found"
10
11
 
11
12
  def _apply_query_params(self, db, query, query_params):
13
+ id_query_param = self.get_query_param("id", query_params)
12
14
  external_id_query_param = self.get_query_param(
13
15
  "external_id", query_params
14
16
  )
15
17
  portfolio_query_param = self.get_query_param(
16
18
  "portfolio_id", query_params
17
19
  )
18
- side_query_param = self.get_query_param("side", query_params)
19
- type_query_param = self.get_query_param("type", query_params)
20
+ side_query_param = self.get_query_param("order_side", query_params)
21
+ type_query_param = self.get_query_param("order_type", query_params)
20
22
  status_query_param = self.get_query_param("status", query_params)
21
23
  price_query_param = self.get_query_param("price", query_params)
22
24
  amount_query_param = self.get_query_param("amount", query_params)
@@ -24,35 +26,42 @@ class SQLOrderRepository(Repository):
24
26
  "position", query_params, many=True
25
27
  )
26
28
  target_symbol_query_param = self.get_query_param(
27
- "symbol", query_params
29
+ "target_symbol", query_params
28
30
  )
29
31
  trading_symbol_query_param = self.get_query_param(
30
32
  "trading_symbol", query_params
31
33
  )
34
+ order_by_created_at_asc = self.get_query_param(
35
+ "order_by_created_at_asc", query_params
36
+ )
32
37
 
33
- if portfolio_query_param:
38
+ if id_query_param:
39
+ query = query.filter_by(id=id_query_param)
40
+
41
+ if portfolio_query_param is not None:
34
42
  portfolio = db.query(SQLPortfolio).filter_by(
35
- identifier=portfolio_query_param
43
+ id=portfolio_query_param
36
44
  ).first()
37
45
 
38
- if portfolio is None:
39
- return query.filter_by(id=-1)
46
+ if portfolio:
47
+ positions = db.query(SQLPosition).filter_by(
48
+ portfolio_id=portfolio.id
49
+ ).all()
50
+ position_ids = [p.id for p in positions]
51
+ query = query.filter(SQLOrder.position_id.in_(position_ids))
52
+ else:
53
+ query = query.filter_by(id=None)
40
54
 
41
- positions = db.query(SQLPosition).filter_by(
42
- portfolio_id=portfolio.id
43
- ).all()
44
- position_ids = [p.id for p in positions]
45
- query = query.filter(SQLOrder.position_id.in_(position_ids))
46
55
  if external_id_query_param:
47
56
  query = query.filter_by(external_id=external_id_query_param)
48
57
 
49
58
  if side_query_param:
50
- side = OrderSide.from_value(side_query_param)
51
- query = query.filter_by(side=side.value)
59
+ order_side = OrderSide.from_value(side_query_param)
60
+ query = query.filter_by(order_side=order_side.value)
52
61
 
53
62
  if type_query_param:
54
63
  order_type = OrderType.from_value(type_query_param)
55
- query = query.filter_by(type=order_type.value)
64
+ query = query.filter_by(order_type=order_type.value)
56
65
 
57
66
  if status_query_param:
58
67
  status = OrderStatus.from_value(status_query_param)
@@ -79,5 +88,9 @@ class SQLOrderRepository(Repository):
79
88
  SQLOrder.trading_symbol == trading_symbol_query_param
80
89
  )
81
90
 
82
- query = query.order_by(SQLOrder.created_at.desc())
91
+ if order_by_created_at_asc:
92
+ query = query.order_by(SQLOrder.created_at.asc())
93
+ else:
94
+ query = query.order_by(SQLOrder.created_at.desc())
95
+
83
96
  return query
@@ -17,10 +17,10 @@ class SQLPortfolioRepository(Repository):
17
17
  query = query.filter_by(id=id_query_param)
18
18
 
19
19
  if market_query_param:
20
- query = query.filter_by(market=market_query_param.lower())
20
+ query = query.filter_by(market=market_query_param.upper())
21
21
 
22
22
  if identifier_query_param:
23
- query = query.filter_by(identifier=identifier_query_param.lower())
23
+ query = query.filter_by(identifier=identifier_query_param.upper())
24
24
 
25
25
  if position_query_param:
26
26
  position = db.query(SQLPosition)\
@@ -0,0 +1,56 @@
1
+ from investing_algorithm_framework.infrastructure.models import \
2
+ SQLPortfolioSnapshot
3
+ from .repository import Repository
4
+
5
+
6
+ class SQLPortfolioSnapshotRepository(Repository):
7
+ base_class = SQLPortfolioSnapshot
8
+ DEFAULT_NOT_FOUND_MESSAGE = "Portfolio snapshot not found"
9
+
10
+ def _apply_query_params(self, db, query, query_params):
11
+ portfolio_id_query_param = self.get_query_param(
12
+ "portfolio_id", query_params
13
+ )
14
+ created_at_query_param = self.get_query_param(
15
+ "created_at", query_params
16
+ )
17
+ created_at_gt_query_param = self.get_query_param(
18
+ "created_at_gt", query_params
19
+ )
20
+ created_at_gte_query_param = self.get_query_param(
21
+ "created_at_gte", query_params
22
+ )
23
+ created_at_lt_query_param = self.get_query_param(
24
+ "created_at_lt", query_params
25
+ )
26
+ created_at_lte_query_param = self.get_query_param(
27
+ "created_at_lte", query_params
28
+ )
29
+
30
+ if portfolio_id_query_param is not None:
31
+ query = query.filter_by(portfolio_id=portfolio_id_query_param)
32
+
33
+ if created_at_query_param is not None:
34
+ query = query.filter_by(created_at=created_at_query_param)
35
+
36
+ if created_at_gt_query_param is not None:
37
+ query = query.filter(
38
+ SQLPortfolioSnapshot.created_at > created_at_gt_query_param
39
+ )
40
+
41
+ if created_at_gte_query_param is not None:
42
+ query = query.filter(
43
+ SQLPortfolioSnapshot.created_at >= created_at_gt_query_param
44
+ )
45
+
46
+ if created_at_lt_query_param is not None:
47
+ query = query.filter(
48
+ SQLPortfolioSnapshot.created_at < created_at_lt_query_param
49
+ )
50
+
51
+ if created_at_lte_query_param is not None:
52
+ query = query.filter(
53
+ SQLPortfolioSnapshot.created_at <= created_at_lte_query_param
54
+ )
55
+
56
+ return query
@@ -1,6 +1,7 @@
1
+ from sqlalchemy import cast, Numeric, Float
2
+
3
+ from investing_algorithm_framework.infrastructure.models import SQLPosition
1
4
  from .repository import Repository
2
- from investing_algorithm_framework.infrastructure.models import SQLPosition, \
3
- SQLPortfolio
4
5
 
5
6
 
6
7
  class SQLPositionRepository(Repository):
@@ -8,16 +9,58 @@ class SQLPositionRepository(Repository):
8
9
  DEFAULT_NOT_FOUND_MESSAGE = "Position not found"
9
10
 
10
11
  def _apply_query_params(self, db, query, query_params):
12
+ id_query_param = self.get_query_param("id", query_params)
11
13
  amount_query_param = self.get_query_param("amount", query_params)
12
14
  symbol_query_param = self.get_query_param("symbol", query_params)
13
15
  portfolio_query_param = self.get_query_param("portfolio", query_params)
16
+ amount_gt_query_param = self.get_query_param("amount_gt", query_params)
17
+ amount_gte_query_param = self.get_query_param(
18
+ "amount_gte", query_params
19
+ )
20
+ amount_lt_query_param = self.get_query_param("amount_lt", query_params)
21
+ amount_lte_query_param = self.get_query_param(
22
+ "amount_lte", query_params
23
+ )
24
+ order_id_query_param = self.get_query_param("order_id", query_params)
25
+
26
+ if id_query_param:
27
+ query = query.filter_by(id=id_query_param)
14
28
 
15
29
  if amount_query_param:
16
- query = query.filter(SQLPosition.amount == amount_query_param)
30
+ query = query.filter(
31
+ cast(SQLPosition.amount, Float) == amount_query_param
32
+ )
17
33
 
18
34
  if symbol_query_param:
19
35
  query = query.filter_by(symbol=symbol_query_param)
20
36
 
21
- if portfolio_query_param:
37
+ if portfolio_query_param is not None:
22
38
  query = query.filter_by(portfolio_id=portfolio_query_param)
39
+
40
+ if amount_gt_query_param is not None:
41
+ query = query.filter(
42
+ cast(SQLPosition.amount, Numeric) > amount_gt_query_param
43
+ )
44
+
45
+ if amount_gte_query_param is not None:
46
+ query = query.filter(
47
+ cast(SQLPosition.amount, Numeric) >= amount_gte_query_param
48
+ )
49
+
50
+ if amount_lt_query_param is not None:
51
+ query = query.filter(
52
+ cast(SQLPosition.amount, Numeric) < amount_lt_query_param
53
+ )
54
+
55
+ if amount_lte_query_param:
56
+ query = query.filter(
57
+ cast(SQLPosition.amount, Numeric) <= amount_lte_query_param
58
+ )
59
+ # Filter by order_id, orders is a one-to-many relationship
60
+ # with 3 position
61
+ if order_id_query_param:
62
+ query = query.filter(
63
+ SQLPosition.orders.any(id=order_id_query_param)
64
+ )
65
+
23
66
  return query
@@ -0,0 +1,21 @@
1
+ from investing_algorithm_framework.infrastructure.models import \
2
+ SQLPositionSnapshot
3
+ from .repository import Repository
4
+
5
+
6
+ class SQLPositionSnapshotRepository(Repository):
7
+ base_class = SQLPositionSnapshot
8
+ DEFAULT_NOT_FOUND_MESSAGE = "Position snapshot not found"
9
+
10
+ def _apply_query_params(self, db, query, query_params):
11
+ portfolio_snapshot_query_param = self.get_query_param(
12
+ "portfolio_snapshot", query_params
13
+ )
14
+
15
+ if portfolio_snapshot_query_param is not None:
16
+ query = query\
17
+ .filter_by(
18
+ portfolio_snapshot_id=portfolio_snapshot_query_param
19
+ )
20
+
21
+ return query