investing-algorithm-framework 7.19.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of investing-algorithm-framework might be problematic. Click here for more details.

Files changed (260) hide show
  1. investing_algorithm_framework/__init__.py +197 -0
  2. investing_algorithm_framework/app/__init__.py +47 -0
  3. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  4. investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
  5. investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
  6. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  7. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  8. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  9. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  10. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  11. investing_algorithm_framework/app/app.py +2204 -0
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1667 -0
  14. investing_algorithm_framework/app/eventloop.py +590 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  31. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  32. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  33. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  34. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  35. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  36. investing_algorithm_framework/app/stateless/__init__.py +35 -0
  37. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
  38. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
  39. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
  40. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
  41. investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
  42. investing_algorithm_framework/app/strategy.py +675 -0
  43. investing_algorithm_framework/app/task.py +41 -0
  44. investing_algorithm_framework/app/web/__init__.py +5 -0
  45. investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
  46. investing_algorithm_framework/app/web/controllers/orders.py +20 -0
  47. investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
  48. investing_algorithm_framework/app/web/controllers/positions.py +18 -0
  49. investing_algorithm_framework/app/web/create_app.py +20 -0
  50. investing_algorithm_framework/app/web/error_handler.py +59 -0
  51. investing_algorithm_framework/app/web/responses.py +20 -0
  52. investing_algorithm_framework/app/web/run_strategies.py +4 -0
  53. investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
  54. investing_algorithm_framework/app/web/schemas/order.py +12 -0
  55. investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
  56. investing_algorithm_framework/app/web/schemas/position.py +15 -0
  57. investing_algorithm_framework/app/web/setup_cors.py +6 -0
  58. investing_algorithm_framework/cli/__init__.py +0 -0
  59. investing_algorithm_framework/cli/cli.py +207 -0
  60. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
  61. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  62. investing_algorithm_framework/cli/initialize_app.py +603 -0
  63. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  64. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  65. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  66. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  67. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  68. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  69. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  70. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  71. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  72. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  73. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  74. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  75. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  76. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  77. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  78. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  79. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  80. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  81. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  82. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  83. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  84. investing_algorithm_framework/create_app.py +54 -0
  85. investing_algorithm_framework/dependency_container.py +155 -0
  86. investing_algorithm_framework/domain/__init__.py +148 -0
  87. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  88. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  92. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  93. investing_algorithm_framework/domain/backtesting/backtest_run.py +435 -0
  94. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  95. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  96. investing_algorithm_framework/domain/config.py +111 -0
  97. investing_algorithm_framework/domain/constants.py +83 -0
  98. investing_algorithm_framework/domain/data_provider.py +334 -0
  99. investing_algorithm_framework/domain/data_structures.py +42 -0
  100. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  101. investing_algorithm_framework/domain/exceptions.py +112 -0
  102. investing_algorithm_framework/domain/models/__init__.py +43 -0
  103. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  104. investing_algorithm_framework/domain/models/base_model.py +25 -0
  105. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  106. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  107. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  108. investing_algorithm_framework/domain/models/event.py +35 -0
  109. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  110. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  111. investing_algorithm_framework/domain/models/order/__init__.py +6 -0
  112. investing_algorithm_framework/domain/models/order/order.py +384 -0
  113. investing_algorithm_framework/domain/models/order/order_side.py +36 -0
  114. investing_algorithm_framework/domain/models/order/order_status.py +37 -0
  115. investing_algorithm_framework/domain/models/order/order_type.py +30 -0
  116. investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
  117. investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
  118. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
  119. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  120. investing_algorithm_framework/domain/models/position/__init__.py +4 -0
  121. investing_algorithm_framework/domain/models/position/position.py +68 -0
  122. investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -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 +153 -0
  126. investing_algorithm_framework/domain/models/time_interval.py +124 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +149 -0
  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 +13 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +388 -0
  132. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
  133. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  134. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
  135. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
  136. investing_algorithm_framework/domain/order_executor.py +112 -0
  137. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  138. investing_algorithm_framework/domain/positions/__init__.py +4 -0
  139. investing_algorithm_framework/domain/positions/position_size.py +41 -0
  140. investing_algorithm_framework/domain/services/__init__.py +11 -0
  141. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  142. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  143. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  144. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  145. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  146. investing_algorithm_framework/domain/stateless_actions.py +7 -0
  147. investing_algorithm_framework/domain/strategy.py +44 -0
  148. investing_algorithm_framework/domain/utils/__init__.py +27 -0
  149. investing_algorithm_framework/domain/utils/csv.py +104 -0
  150. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  151. investing_algorithm_framework/domain/utils/dates.py +57 -0
  152. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  153. investing_algorithm_framework/domain/utils/polars.py +53 -0
  154. investing_algorithm_framework/domain/utils/random.py +41 -0
  155. investing_algorithm_framework/domain/utils/signatures.py +17 -0
  156. investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
  157. investing_algorithm_framework/domain/utils/synchronized.py +12 -0
  158. investing_algorithm_framework/download_data.py +108 -0
  159. investing_algorithm_framework/infrastructure/__init__.py +50 -0
  160. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  161. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  162. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  163. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  164. investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
  165. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
  166. investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
  167. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  168. investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
  169. investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
  170. investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
  171. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  172. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  173. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
  174. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  175. investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
  176. investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
  177. investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
  178. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  179. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  180. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  181. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
  182. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
  183. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  184. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  185. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  186. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  187. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  188. investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
  189. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  190. investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
  191. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
  192. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  193. investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
  194. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  195. investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
  196. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  197. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
  198. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
  199. investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
  200. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  201. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  202. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  203. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  204. investing_algorithm_framework/services/__init__.py +132 -0
  205. investing_algorithm_framework/services/backtesting/__init__.py +5 -0
  206. investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
  207. investing_algorithm_framework/services/configuration_service.py +96 -0
  208. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  209. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  210. investing_algorithm_framework/services/market_credential_service.py +40 -0
  211. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  212. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  213. investing_algorithm_framework/services/metrics/beta.py +0 -0
  214. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  215. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  216. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  217. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  218. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  219. investing_algorithm_framework/services/metrics/generate.py +358 -0
  220. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  221. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  222. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  223. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  224. investing_algorithm_framework/services/metrics/returns.py +452 -0
  225. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  226. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  227. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  228. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  229. investing_algorithm_framework/services/metrics/trades.py +500 -0
  230. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  231. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  232. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  233. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  234. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  235. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  237. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  238. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  239. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  240. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  241. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
  242. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  243. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  244. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  245. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  246. investing_algorithm_framework/services/positions/__init__.py +7 -0
  247. investing_algorithm_framework/services/positions/position_service.py +210 -0
  248. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  249. investing_algorithm_framework/services/repository_service.py +40 -0
  250. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  251. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
  252. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
  253. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
  254. investing_algorithm_framework/services/trade_service/__init__.py +3 -0
  255. investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
  256. investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
  257. investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
  258. investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
  259. investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
  260. investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -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,4 @@
1
+ from .position_size import PositionSize
2
+
3
+
4
+ __all__ = ["PositionSize"]
@@ -0,0 +1,41 @@
1
+ from typing import Optional
2
+ from investing_algorithm_framework.domain.exceptions import \
3
+ OperationalException
4
+
5
+
6
+ class PositionSize:
7
+ """
8
+ Defines how much capital to allocate to a specific symbol.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ symbol: str,
14
+ percentage_of_portfolio: Optional[float] = None,
15
+ fixed_amount: Optional[float] = None
16
+ ):
17
+ self.symbol = symbol
18
+ self.percentage_of_portfolio = percentage_of_portfolio
19
+ self.fixed_amount = fixed_amount
20
+
21
+ def get_size(self, portfolio, asset_price) -> float:
22
+ """
23
+ Calculate size in currency/units.
24
+ """
25
+ if self.fixed_amount is not None:
26
+ return self.fixed_amount
27
+ elif self.percentage_of_portfolio is not None:
28
+ total_value = portfolio.get_unallocated() + portfolio.allocated
29
+ return total_value * (self.percentage_of_portfolio / 100)
30
+ else:
31
+ raise OperationalException(
32
+ "A position size object must have either a fixed amount or a "
33
+ "percentage of the portfolio defined."
34
+ )
35
+
36
+ def __repr__(self) -> str:
37
+ return (
38
+ f"PositionSize(symbol={self.symbol}, "
39
+ f"percentage_of_portfolio={self.percentage_of_portfolio}, "
40
+ f"fixed_amount={self.fixed_amount})"
41
+ )
@@ -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
@@ -0,0 +1,7 @@
1
+ from enum import Enum
2
+
3
+
4
+ class StatelessActions(Enum):
5
+ RUN_STRATEGY = 'RUN_STRATEGY'
6
+ CHECK_PENDING_ORDERS = 'CHECK_PENDING_ORDERS'
7
+ PING = 'PING'
@@ -0,0 +1,44 @@
1
+ from .models import TimeUnit
2
+
3
+
4
+ class Strategy:
5
+
6
+ def __init__(
7
+ self,
8
+ market=None,
9
+ time_unit=None,
10
+ interval=None,
11
+ worker_id=None,
12
+ symbols=None,
13
+ limit=None,
14
+ ):
15
+ self._market = market
16
+ self._time_unit = TimeUnit.from_value(time_unit).value
17
+ self._interval = interval
18
+ self._symbols = symbols
19
+ self._limit = limit
20
+ self._worker_id = worker_id
21
+
22
+ @property
23
+ def market(self):
24
+ return self._market
25
+
26
+ @property
27
+ def time_unit(self):
28
+ return self._time_unit
29
+
30
+ @property
31
+ def interval(self):
32
+ return self._interval
33
+
34
+ @property
35
+ def symbols(self):
36
+ return self._symbols
37
+
38
+ @property
39
+ def limit(self):
40
+ return self._limit
41
+
42
+ @property
43
+ def worker_id(self):
44
+ return self._worker_id
@@ -0,0 +1,27 @@
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
4
+ from .stoppable_thread import StoppableThread
5
+ from .synchronized import synchronized
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
10
+
11
+ __all__ = [
12
+ 'synchronized',
13
+ 'StoppableThread',
14
+ 'random_string',
15
+ 'random_number',
16
+ 'get_total_amount_of_rows',
17
+ 'append_dict_as_row_to_csv',
18
+ 'add_column_headers_to_csv',
19
+ 'csv_to_list',
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'
27
+ ]
@@ -0,0 +1,104 @@
1
+ import csv
2
+ import shutil
3
+ import tempfile
4
+ from typing import Dict, List
5
+
6
+ from ..exceptions import OperationalException
7
+
8
+
9
+ # Function to add column headers to csv
10
+ def add_column_headers_to_csv(file_name: str, column_names: List[str]) -> None:
11
+
12
+ # Open file in append mode
13
+ with open(file_name, 'a+', newline='') as write_obj:
14
+
15
+ # Create a writer object from csv module
16
+ csv_writer = csv.writer(write_obj)
17
+
18
+ # Add the columns
19
+ csv_writer.writerow(column_names)
20
+
21
+
22
+ # Function to append dict as a row to csv
23
+ def append_dict_as_row_to_csv(
24
+ file_name: str, dict_data: Dict, field_names: List[str]
25
+ ) -> None:
26
+ result_dict = {field: dict_data[field] for field in field_names}
27
+
28
+ # Open file in append mode
29
+ with open(file_name, 'a', newline='') as write_obj:
30
+ # Create a writer object from csv module
31
+ dict_writer = csv.DictWriter(write_obj, fieldnames=field_names)
32
+ # Add dictionary as wor in the csv
33
+ dict_writer.writerow(result_dict)
34
+
35
+
36
+ def get_total_amount_of_rows(file_path: str):
37
+ file = open(file_path)
38
+ reader = csv.reader(file)
39
+ rows_count = len(list(reader))
40
+ file.close()
41
+ return rows_count
42
+
43
+
44
+ # Function to convert a csv to a list of data_provider
45
+ def csv_to_list(csv_path: str, strip_column_headers: bool = False) -> List:
46
+ data = []
47
+ with open(csv_path, "r") as csv_f:
48
+ reader = csv.reader(csv_f, delimiter=',')
49
+
50
+ for row in reader:
51
+
52
+ if row: # avoid blank lines
53
+ data.append(row)
54
+
55
+ # Delete columns
56
+ if strip_column_headers:
57
+ del data[0]
58
+
59
+ return data
60
+
61
+
62
+ # Function to remove a row of a csv corresponding to a particular index
63
+ def remove_row(csv_path: str, row_index: int) -> None:
64
+
65
+ if row_index < 0:
66
+ raise OperationalException('Negative index number')
67
+
68
+ _, temp_file = tempfile.mkstemp(suffix='.csv')
69
+
70
+ with open(csv_path, 'r') as inf, open(temp_file, 'w') as out_f:
71
+ writer = csv.writer(out_f)
72
+ index = 0
73
+
74
+ for row in csv.reader(inf):
75
+
76
+ # Copy every row except the index row
77
+ if index != row_index:
78
+ writer.writerow(row)
79
+
80
+ index += 1
81
+
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)