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
@@ -0,0 +1,112 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from investing_algorithm_framework.domain import Order
4
+
5
+
6
+ class OrderExecutor(ABC):
7
+ """
8
+ Abstract base class for order executors. The OrderExecutor class is
9
+ responsible for executing orders in a trading algorithm.
10
+
11
+ Attributes:
12
+ _priority (int): The priority of the order executor compared to
13
+ other order executors. The lower the number, the higher the
14
+ priority. The framework will use this priority when searching
15
+ for an order executor for a specific market.
16
+ _config (dict): Reference to the application configuration.
17
+ """
18
+
19
+ def __init__(self, priority=1):
20
+ self._priority = priority
21
+ self._config = None
22
+
23
+ @property
24
+ def priority(self):
25
+ """
26
+ Returns the priority of the order executor.
27
+ """
28
+ return self._priority
29
+
30
+ @property
31
+ def config(self):
32
+ """
33
+ Returns the configuration of the order executor.
34
+ This can be used to store any configuration that is needed
35
+ for the order executor.
36
+ """
37
+ return self._config
38
+
39
+ @config.setter
40
+ def config(self, config):
41
+ """
42
+ Setter for the app config. This will be used to set a reference
43
+ to the config of the Application when the app is started.
44
+ """
45
+ self._config = config
46
+
47
+ @abstractmethod
48
+ def execute_order(self, portfolio, order, market_credential) -> Order:
49
+ """
50
+ Executes an order for a given portfolio. The order executor should
51
+ create an order on the exchange or broker and return an order object
52
+ that reflects the order on the exchange or broker. This should be
53
+ done by setting the external_id of the order to the id of the order
54
+ on the exchange or broker.
55
+
56
+ !Important: This function should not throw an exception if the order
57
+ is not successfully executed. Instead, it should return an order
58
+ instance with the status set to OrderStatus.FAILED.
59
+
60
+ Args:
61
+ order: The order to be executed
62
+ portfolio: The portfolio in which the order will be executed
63
+ market_credential: The market credential to use for the order
64
+
65
+ Returns:
66
+ Order: Instance of the executed order. The order instance
67
+ should copy the id of the order that has been provided as a
68
+ """
69
+ raise NotImplementedError(
70
+ "Subclasses must implement this method."
71
+ )
72
+
73
+ @abstractmethod
74
+ def cancel_order(self, portfolio, order, market_credential) -> Order:
75
+ """
76
+ Cancels an order for a given portfolio. The order executor should
77
+ cancel the order on the exchange or broker and return an order
78
+ object that reflects the order on the exchange or broker.
79
+
80
+ Args:
81
+ order: The order to be canceled
82
+ portfolio: The portfolio in which the order was executed
83
+ market_credential: The market credential to use for the order
84
+
85
+ Returns:
86
+ Order: Instance of the canceled order.
87
+ """
88
+ raise NotImplementedError(
89
+ "Subclasses must implement this method."
90
+ )
91
+
92
+ @abstractmethod
93
+ def supports_market(self, market):
94
+ """
95
+ Checks if the order executor supports the given market.
96
+
97
+ Args:
98
+ market: The market to check
99
+
100
+ Returns:
101
+ bool: True if the order executor supports the market, False
102
+ otherwise.
103
+ """
104
+ raise NotImplementedError(
105
+ "Subclasses must implement this method."
106
+ )
107
+
108
+ def __repr__(self):
109
+ """
110
+ Returns a string representation of the order executor.
111
+ """
112
+ return f"{self.__class__.__name__}(priority={self._priority})"
@@ -0,0 +1,118 @@
1
+ from typing import Union
2
+ from abc import ABC, abstractmethod
3
+
4
+ from investing_algorithm_framework.domain import Order, Position
5
+
6
+
7
+ class PortfolioProvider(ABC):
8
+ """
9
+ Abstract base class for portfolio providers. The PortfolioProvider class
10
+ is responsible for managing and providing access to trading portfolios.
11
+
12
+ Attributes:
13
+ _priority (int): The priority of the portfolio provider compared to
14
+ other portfolio providers. The lower the number, the higher the
15
+ priority. The framework will use this priority when searching
16
+ for a portfolio provider for a specific symbol or market.
17
+ _config (dict): Reference to the application configuration.
18
+ """
19
+
20
+ def __init__(self, priority=1):
21
+ self._priority = priority
22
+ self._config = None
23
+
24
+ @property
25
+ def priority(self):
26
+ """
27
+ Returns the priority of the portfolio provider.
28
+ """
29
+ return self._priority
30
+
31
+ @priority.setter
32
+ def priority(self, value: int):
33
+ """
34
+ Sets the priority of the portfolio provider.
35
+ """
36
+ self._priority = value
37
+
38
+ @property
39
+ def config(self):
40
+ """
41
+ Returns the configuration of the order executor.
42
+ This can be used to store any configuration that is needed
43
+ for the order executor.
44
+ """
45
+ return self._config
46
+
47
+ @config.setter
48
+ def config(self, config):
49
+ """
50
+ Setter for the app config. This will be used to set a reference
51
+ to the config of the Application when the app is started.
52
+ """
53
+ self._config = config
54
+
55
+ @abstractmethod
56
+ def get_order(
57
+ self, portfolio, order, market_credential
58
+ ) -> Union[Order, None]:
59
+ """
60
+ Function to get an order from the exchange or broker. The returned
61
+ should be an order object that reflects the current state of the
62
+ order on the exchange or broker.
63
+
64
+ !IMPORTANT: This function should return None if the order is
65
+ not found or if the order is not available on the
66
+ exchange or broker. Please do not throw an exception if the
67
+ order is not found.
68
+
69
+ Args:
70
+ portfolio: Portfolio object
71
+ order: Order object from the database
72
+ market_credential: Market credential object
73
+
74
+ Returns:
75
+ Order: Order object reflecting the order on the exchange or broker
76
+ """
77
+ raise NotImplementedError("Subclasses must implement this method.")
78
+
79
+ @abstractmethod
80
+ def get_position(
81
+ self, portfolio, symbol, market_credential
82
+ ) -> Union[Position, None]:
83
+ """
84
+ Function to get the position for a given symbol in the portfolio.
85
+ The returned position should be an object that reflects the current
86
+ state of the position on the exchange or broker.
87
+
88
+ !IMPORTANT: This function should return None if the position is
89
+ not found or if the position is not available on the
90
+ exchange or broker. Please do not throw an exception if the
91
+ position is not found.
92
+
93
+ Args:
94
+ portfolio: Portfolio object
95
+ symbol: Symbol object
96
+ market_credential: MarketCredential object
97
+
98
+ Returns:
99
+ float: Position for the given symbol in the portfolio
100
+ """
101
+ raise NotImplementedError("Subclasses must implement this method.")
102
+
103
+ @abstractmethod
104
+ def supports_market(self, market) -> bool:
105
+ """
106
+ Function to check if the market is supported by the portfolio
107
+ provider.
108
+
109
+ Args:
110
+ market: Market object
111
+
112
+ Returns:
113
+ bool: True if the market is supported, False otherwise
114
+ """
115
+ raise NotImplementedError("Subclasses must implement this method.")
116
+
117
+ def __repr__(self):
118
+ return f"{self.__class__.__name__}(priority={self.priority})"
@@ -0,0 +1,11 @@
1
+ from .market_credential_service import MarketCredentialService
2
+ from .portfolios import AbstractPortfolioSyncService
3
+ from .rounding_service import RoundingService
4
+ from .state_handler import StateHandler
5
+
6
+ __all__ = [
7
+ "MarketCredentialService",
8
+ "AbstractPortfolioSyncService",
9
+ "RoundingService",
10
+ "StateHandler",
11
+ ]
@@ -0,0 +1,37 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List, Optional
3
+
4
+ from investing_algorithm_framework.domain import MarketCredential
5
+
6
+
7
+ class MarketCredentialService(ABC):
8
+ """
9
+ This class is responsible for managing the market credentials of the app.
10
+ """
11
+ @abstractmethod
12
+ def add(self, market_data_credential: MarketCredential):
13
+ """
14
+ Add a market credential in the repository
15
+ """
16
+ raise NotImplementedError()
17
+
18
+ @abstractmethod
19
+ def add_all(self, market_data_credentials: List[MarketCredential]):
20
+ """
21
+ Add a list of market credentials in the repository
22
+ """
23
+ raise NotImplementedError()
24
+
25
+ @abstractmethod
26
+ def get(self, market) -> Optional[MarketCredential]:
27
+ """
28
+ Get a market credential from the repository
29
+ """
30
+ raise NotImplementedError()
31
+
32
+ @abstractmethod
33
+ def get_all(self) -> List[MarketCredential]:
34
+ """
35
+ Get all market credentials from the repository
36
+ """
37
+ raise NotImplementedError()
@@ -0,0 +1,5 @@
1
+ from .portfolio_sync_service import AbstractPortfolioSyncService
2
+
3
+ __all__ = [
4
+ "AbstractPortfolioSyncService"
5
+ ]
@@ -0,0 +1,9 @@
1
+ class AbstractPortfolioSyncService:
2
+ """
3
+ Service to sync the portfolio with the exchange.
4
+ """
5
+ def sync_unallocated(self, portfolio):
6
+ pass
7
+
8
+ def sync_orders(self, portfolio):
9
+ pass
@@ -0,0 +1,27 @@
1
+ import decimal
2
+
3
+
4
+ class RoundingService:
5
+ """
6
+ Service to round numbers to a certain amount of decimals.
7
+ It will always round down.
8
+ """
9
+
10
+ @staticmethod
11
+ def round_down(value, amount_of_decimals):
12
+
13
+ if RoundingService.count_decimals(value) <= amount_of_decimals:
14
+ return value
15
+
16
+ with decimal.localcontext() as ctx:
17
+ d = decimal.Decimal(value)
18
+ ctx.rounding = decimal.ROUND_DOWN
19
+ return float(round(d, amount_of_decimals))
20
+
21
+ @staticmethod
22
+ def count_decimals(number):
23
+ decimal_str = str(number)
24
+ if '.' in decimal_str:
25
+ return len(decimal_str.split('.')[1])
26
+ else:
27
+ return 0
@@ -0,0 +1,38 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class StateHandler(ABC):
5
+ """
6
+ Abstract base class for state handlers.
7
+
8
+ This class defines the
9
+ interface for state handlers, which are responsible for
10
+ saving and loading state information.
11
+ """
12
+
13
+ @abstractmethod
14
+ def initialize(self):
15
+ """
16
+ Initialize the state handler.
17
+ """
18
+ pass
19
+
20
+ @abstractmethod
21
+ def save(self, target_directory: str):
22
+ """
23
+ Save the state to the specified directory.
24
+
25
+ Args:
26
+ target_directory (str): Directory to save the state
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ def load(self, target_directory: str):
32
+ """
33
+ Load the state from the specified directory.
34
+
35
+ Args:
36
+ target_directory (str): Directory to load the state
37
+ """
38
+ pass
@@ -1,4 +1,4 @@
1
- from .models import TradingTimeFrame, TradingDataType, TimeUnit
1
+ from .models import TimeUnit
2
2
 
3
3
 
4
4
  class Strategy:
@@ -11,9 +11,6 @@ class Strategy:
11
11
  worker_id=None,
12
12
  symbols=None,
13
13
  limit=None,
14
- trading_data_type=None,
15
- trading_data_types=None,
16
- trading_time_frame=None,
17
14
  ):
18
15
  self._market = market
19
16
  self._time_unit = TimeUnit.from_value(time_unit).value
@@ -22,19 +19,6 @@ class Strategy:
22
19
  self._limit = limit
23
20
  self._worker_id = worker_id
24
21
 
25
- if trading_data_type is not None:
26
- self._trading_data_type = TradingDataType \
27
- .from_value(trading_data_type)
28
-
29
- if trading_data_types is not None:
30
- trading_data_types = [TradingDataType.from_value(trading_data_type)
31
- for trading_data_type in trading_data_types]
32
- self._trading_data_types = trading_data_types
33
-
34
- if trading_time_frame is not None:
35
- self._trading_time_frame = TradingTimeFrame \
36
- .from_value(trading_time_frame)
37
-
38
22
  @property
39
23
  def market(self):
40
24
  return self._market
@@ -55,18 +39,6 @@ class Strategy:
55
39
  def limit(self):
56
40
  return self._limit
57
41
 
58
- @property
59
- def trading_data_type(self):
60
- return self._trading_data_type
61
-
62
- @property
63
- def trading_data_types(self):
64
- return self._trading_data_types
65
-
66
- @property
67
- def trading_time_frame(self):
68
- return self._trading_time_frame
69
-
70
42
  @property
71
43
  def worker_id(self):
72
44
  return self._worker_id
@@ -1,17 +1,27 @@
1
- from .random import random_string
1
+ from .csv import get_total_amount_of_rows, append_dict_as_row_to_csv, \
2
+ add_column_headers_to_csv, csv_to_list, load_csv_into_dict
3
+ from .random import random_string, random_number
2
4
  from .stoppable_thread import StoppableThread
3
5
  from .synchronized import synchronized
4
- from .csv import get_total_amount_of_rows, append_dict_as_row_to_csv, \
5
- add_column_headers_to_csv, csv_to_list
6
- from .backtesting import pretty_print_backtest
6
+ from .polars import convert_polars_to_pandas
7
+ from .dates import is_timezone_aware, sync_timezones, get_timezone
8
+ from .jupyter_notebook_detection import is_jupyter_notebook
9
+ from .custom_tqdm import tqdm
7
10
 
8
11
  __all__ = [
9
12
  'synchronized',
10
13
  'StoppableThread',
11
14
  'random_string',
15
+ 'random_number',
12
16
  'get_total_amount_of_rows',
13
17
  'append_dict_as_row_to_csv',
14
18
  'add_column_headers_to_csv',
15
19
  'csv_to_list',
16
- 'pretty_print_backtest'
20
+ 'load_csv_into_dict',
21
+ 'convert_polars_to_pandas',
22
+ 'is_timezone_aware',
23
+ 'sync_timezones',
24
+ 'get_timezone',
25
+ 'is_jupyter_notebook',
26
+ 'tqdm'
17
27
  ]
@@ -80,3 +80,25 @@ def remove_row(csv_path: str, row_index: int) -> None:
80
80
  index += 1
81
81
 
82
82
  shutil.move(temp_file, csv_path)
83
+
84
+
85
+ def load_csv_into_dict(file_path) -> Dict:
86
+ """
87
+ Load a CSV file into a dictionary.
88
+
89
+ Args:
90
+ file_path (str): Path to the CSV file.
91
+
92
+ Returns:
93
+ dict: Dictionary containing the data loaded from the CSV file.
94
+ """
95
+ data_dict = {}
96
+
97
+ with open(file_path, 'r', newline='', encoding='utf-8') as csvfile:
98
+ csv_reader = csv.DictReader(csvfile)
99
+
100
+ for row in csv_reader:
101
+ for key, value in row.items():
102
+ data_dict[key] = value
103
+
104
+ return data_dict
@@ -0,0 +1,22 @@
1
+ from tqdm.notebook import tqdm as tqdm_notebook
2
+ from tqdm import tqdm as tqdm_terminal
3
+
4
+ from .jupyter_notebook_detection import is_jupyter_notebook
5
+
6
+
7
+ def tqdm(*args, **kwargs):
8
+ """
9
+ Returns a tqdm progress bar that adapts to the
10
+ environment (Jupyter Notebook or terminal).
11
+
12
+ Args:
13
+ *args: Positional arguments for tqdm.
14
+ **kwargs: Keyword arguments for tqdm.
15
+
16
+ Returns:
17
+ tqdm object: A tqdm progress bar.
18
+ """
19
+ if is_jupyter_notebook():
20
+ return tqdm_notebook(*args, **kwargs)
21
+ else:
22
+ return tqdm_terminal(*args, **kwargs)
@@ -0,0 +1,57 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+ import pytz
4
+
5
+
6
+ def is_timezone_aware(dt: datetime) -> bool:
7
+ """
8
+ Check if a datetime object is timezone-aware.
9
+
10
+ Args:
11
+ dt (datetime): The datetime object to check.
12
+
13
+ Returns:
14
+ bool: True if the datetime is timezone-aware, False otherwise.
15
+ """
16
+ return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
17
+
18
+
19
+ def get_timezone(dt: datetime) -> Optional[pytz.tzinfo.BaseTzInfo]:
20
+ """
21
+ Returns the timezone info from a datetime object.
22
+
23
+ Args:
24
+ dt (datetime): The datetime object to check.
25
+
26
+ Returns:
27
+ pytz timezone info if available, otherwise None.
28
+ """
29
+ if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
30
+ return dt.tzinfo
31
+ return None
32
+
33
+
34
+ def sync_timezones(reference_dt: datetime, naive_dt: datetime) -> datetime:
35
+ """
36
+ Synchronize a naive datetime with the timezone of a reference datetime.
37
+
38
+ Args:
39
+ reference_dt (datetime): A timezone-aware datetime to
40
+ use as a reference.
41
+ naive_dt (datetime): A naive datetime to be synchronized.
42
+
43
+ Returns:
44
+ Datetime: A timezone-aware datetime that matches the
45
+ timezone of the reference.
46
+ """
47
+ tz = get_timezone(reference_dt)
48
+
49
+ if tz is None:
50
+ return naive_dt
51
+
52
+ # Check if tz has a localize method (pytz)
53
+ if hasattr(tz, 'localize'):
54
+ return tz.localize(naive_dt)
55
+ else:
56
+ # fallback (e.g., zoneinfo or other)
57
+ return naive_dt.replace(tzinfo=tz)
@@ -0,0 +1,19 @@
1
+ def is_jupyter_notebook():
2
+ """
3
+ Check if the code is running in a Jupyter Notebook environment.
4
+
5
+ Returns:
6
+ bool: True if running in a Jupyter Notebook, False otherwise.
7
+ """
8
+ try:
9
+ # Check for the presence of the 'IPython' module
10
+ from IPython import get_ipython
11
+ return 'IPKernelApp' in get_ipython().config
12
+ except ImportError:
13
+ return False
14
+ except AttributeError:
15
+ # If get_ipython() does not have 'config', it is not a Jupyter Notebook
16
+ return False
17
+ except Exception:
18
+ # Catch any other exceptions and return False
19
+ return False
@@ -0,0 +1,53 @@
1
+ import pandas as pd
2
+ from polars import DataFrame as PolarsDataFrame
3
+
4
+
5
+ def convert_polars_to_pandas(
6
+ data: PolarsDataFrame,
7
+ remove_duplicates=True,
8
+ add_index=True,
9
+ add_datetime_column=True,
10
+ datetime_column_name="Datetime"
11
+ ):
12
+ """
13
+ Function to convert polars dataframe to pandas dataframe.
14
+
15
+ The function will set the index to the datetime column. The reason
16
+ for this is that You can filter with clean, readable code in a faster way
17
+ then with filtering on a column that is not the index.
18
+
19
+ Args:
20
+ data:Polars Dataframe - The original polars dataframe
21
+ remove_duplicates: Boolean - If set to true, all duplicate
22
+ dates will be removed from the dataframe
23
+ add_index: Boolean - If set to true, an index will
24
+ be added to the dataframe
25
+ add_datetime_column: Boolean - If set to true, a datetime
26
+ column will be added to the dataframe
27
+ datetime_column_name: String - the column name that has the
28
+ datetime object. By default this is set to column name Datetime
29
+ This is only used if add_index is set to True
30
+
31
+ Returns:
32
+ DataFrame: Pandas DataFrame that has been converted
33
+ from a Polars DataFrame
34
+ """
35
+
36
+ if not isinstance(data, PolarsDataFrame):
37
+ raise ValueError("Data must be a Polars DataFrame")
38
+
39
+ df = data.to_pandas().copy()
40
+
41
+ if add_datetime_column and datetime_column_name not in df.columns:
42
+ df[datetime_column_name] = pd.to_datetime(df.index)
43
+
44
+ # Ensure datetime column is datetime type
45
+ df[datetime_column_name] = pd.to_datetime(df[datetime_column_name])
46
+
47
+ if remove_duplicates:
48
+ df = df.drop_duplicates(subset=datetime_column_name, keep="first")
49
+
50
+ if add_index:
51
+ df.set_index(datetime_column_name, inplace=True)
52
+
53
+ return df
@@ -3,6 +3,16 @@ import string
3
3
 
4
4
 
5
5
  def random_string(n, spaces: bool = False):
6
+ """
7
+ Function to generate a random string of n characters.
8
+
9
+ Args:
10
+ n: number of characters
11
+ spaces: if True, include spaces in the string
12
+
13
+ Returns:
14
+ str: Random string of n characters
15
+ """
6
16
 
7
17
  if spaces:
8
18
  return ''.join(
@@ -10,3 +20,22 @@ def random_string(n, spaces: bool = False):
10
20
  )
11
21
 
12
22
  return ''.join(random.choice(string.ascii_lowercase) for _ in range(n))
23
+
24
+
25
+ def random_number(n, variable_size: bool = False):
26
+ """
27
+ Function to generate a random number of n digits.
28
+
29
+ Args:
30
+ n: number of digits
31
+ variable_size: if True, the number of digits will be variable
32
+ between 1 and n
33
+
34
+ Returns:
35
+ int: Random number of n digits
36
+ """
37
+
38
+ if variable_size:
39
+ n = random.randint(1, n)
40
+
41
+ return int(''.join(random.choice(string.digits) for _ in range(n)))