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,93 @@
1
+ from dateutil.parser import parse
2
+
3
+ from investing_algorithm_framework.domain.exceptions import \
4
+ ImproperlyConfigured
5
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
6
+
7
+
8
+ class PortfolioConfiguration(BaseModel):
9
+ """
10
+ This class represents a portfolio configuration. It is used to
11
+ configure the portfolio that the user wants to create.
12
+
13
+ The portfolio configuration will have the following attributes:
14
+ - market: The market where the portfolio will be created
15
+ - trading_symbol: The trading symbol of the portfolio
16
+ - track_from: The date from which the portfolio will be tracked
17
+ - identifier: The identifier of the portfolio
18
+ - initial_balance: The initial balance of the portfolio
19
+
20
+ For backtesting, a portfolio configuration is used to create a
21
+ portfolio that will be used to simulate the trading of the algorithm. if
22
+ the user does not provide an initial balance, the portfolio will be created
23
+ with a balance of according to the initial balanace of
24
+ the PortfolioConfiguration class.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ market,
30
+ trading_symbol,
31
+ track_from=None,
32
+ identifier=None,
33
+ initial_balance=None,
34
+ ):
35
+ self._market = market
36
+
37
+ if self._market is not None:
38
+ self._market = self._market.upper()
39
+
40
+ self._track_from = None
41
+ self._trading_symbol = trading_symbol.upper()
42
+ self._identifier = identifier
43
+ self._initial_balance = initial_balance
44
+
45
+ if self.identifier is None:
46
+ self._identifier = market.upper()
47
+ else:
48
+ self._identifier = identifier.upper()
49
+
50
+ if track_from:
51
+ self._track_from = parse(track_from)
52
+
53
+ if self.trading_symbol is None:
54
+ raise ImproperlyConfigured(
55
+ "Portfolio configuration requires a trading symbol"
56
+ )
57
+
58
+ @property
59
+ def market(self):
60
+
61
+ if hasattr(self._market, "upper"):
62
+ return self._market.upper()
63
+
64
+ return self._market
65
+
66
+ @property
67
+ def track_from(self):
68
+ return self._track_from
69
+
70
+ @property
71
+ def identifier(self):
72
+ return self._identifier
73
+
74
+ @property
75
+ def trading_symbol(self):
76
+ return self._trading_symbol
77
+
78
+ @property
79
+ def initial_balance(self):
80
+ return self._initial_balance
81
+
82
+ @property
83
+ def has_initial_balance(self):
84
+ return self._initial_balance is not None
85
+
86
+ def __repr__(self):
87
+ return self.repr(
88
+ market=self.market,
89
+ trading_symbol=self.trading_symbol,
90
+ identifier=self.identifier,
91
+ track_from=self.track_from,
92
+ initial_balance=self.initial_balance
93
+ )
@@ -0,0 +1,208 @@
1
+ from datetime import timezone
2
+
3
+ from dateutil import parser
4
+
5
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
6
+
7
+
8
+ class PortfolioSnapshot(BaseModel):
9
+
10
+ def __init__(
11
+ self,
12
+ portfolio_id=None,
13
+ trading_symbol=None,
14
+ pending_value=None,
15
+ unallocated=None,
16
+ net_size=None,
17
+ total_net_gain=None,
18
+ total_revenue=None,
19
+ total_cost=None,
20
+ total_value=None,
21
+ cash_flow=None,
22
+ created_at=None,
23
+ position_snapshots=None,
24
+ metadata=None,
25
+ ):
26
+ self.portfolio_id = portfolio_id
27
+ self.trading_symbol = trading_symbol
28
+ self.pending_value = pending_value
29
+ self.unallocated = unallocated
30
+ self.total_net_gain = total_net_gain
31
+ self.total_revenue = total_revenue
32
+ self.total_value = total_value if total_value is not None else 0.0
33
+ self.net_size = net_size
34
+ self.total_cost = total_cost
35
+ self.cash_flow = cash_flow
36
+ self.metadata = metadata if metadata is not None else {}
37
+
38
+ if created_at is not None and isinstance(created_at, str):
39
+ self.created_at = parser.parse(created_at)
40
+ else:
41
+ self.created_at = created_at
42
+
43
+ # Make sure that created_at is a timezone aware datetime object
44
+ self.created_at = self.created_at.replace(tzinfo=timezone.utc)
45
+
46
+ if position_snapshots is None:
47
+ position_snapshots = []
48
+
49
+ self.position_snapshots = position_snapshots
50
+
51
+ def get_portfolio_id(self):
52
+ return self.portfolio_id
53
+
54
+ def set_portfolio_id(self, portfolio_id):
55
+ self.portfolio_id = portfolio_id
56
+
57
+ def get_trading_symbol(self):
58
+ return self.trading_symbol
59
+
60
+ def set_trading_symbol(self, trading_symbol):
61
+ self.trading_symbol = trading_symbol.upper()
62
+
63
+ def get_pending_value(self):
64
+ return self.pending_value
65
+
66
+ def set_pending_value(self, pending_value):
67
+ self.pending_value = pending_value
68
+
69
+ def get_unallocated(self):
70
+ return self.unallocated
71
+
72
+ def set_unallocated(self, unallocated):
73
+ self.unallocated = unallocated
74
+
75
+ def get_total_net_gain(self):
76
+ return self.total_net_gain
77
+
78
+ def set_total_net_gain(self, total_net_gain):
79
+ self.total_net_gain = total_net_gain
80
+
81
+ def get_total_revenue(self):
82
+ return self.total_revenue
83
+
84
+ def set_total_revenue(self, total_revenue):
85
+ self.total_revenue = total_revenue
86
+
87
+ def get_total_value(self):
88
+ return self.total_value
89
+
90
+ def set_total_value(self, total_value):
91
+ self.total_value = total_value
92
+
93
+ def get_total_cost(self):
94
+ return self.total_cost
95
+
96
+ def set_total_cost(self, total_cost):
97
+ self.total_cost = total_cost
98
+
99
+ def get_cash_flow(self):
100
+ return self.cash_flow
101
+
102
+ def set_cash_flow(self, cash_flow):
103
+ self.cash_flow = cash_flow
104
+
105
+ def get_created_at(self):
106
+ return self.created_at
107
+
108
+ def set_created_at(self, created_at):
109
+ self.created_at = created_at
110
+
111
+ def get_portfolio_snapshot_id(self):
112
+ return self.portfolio_snapshot_id
113
+
114
+ def set_position_snapshots(self, position_snapshots):
115
+ self.position_snapshots = position_snapshots
116
+
117
+ def get_position_snapshots(self):
118
+ return self.position_snapshots
119
+
120
+ def set_portfolio_snapshot_id(self, portfolio_snapshot_id):
121
+ self.portfolio_snapshot_id = portfolio_snapshot_id
122
+
123
+ def __repr__(self):
124
+ return self.repr(
125
+ portfolio_id=self.portfolio_id,
126
+ created_at=self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
127
+ trading_symbol=self.trading_symbol,
128
+ net_size=self.net_size,
129
+ unallocated=self.unallocated,
130
+ pending_value=self.pending_value,
131
+ total_net_gain=self.total_net_gain,
132
+ total_revenue=self.total_revenue,
133
+ total_cost=self.total_cost,
134
+ cash_flow=self.cash_flow,
135
+ metadata=self.metadata,
136
+ )
137
+
138
+ def to_dict(self, datetime_format=None):
139
+ """
140
+ Convert the portfolio snapshot object to a dictionary
141
+
142
+ Args:
143
+ datetime_format (str): The format to use for the datetime fields.
144
+ If None, the datetime fields will be returned as is.
145
+ Defaults to None.
146
+
147
+ Returns:
148
+ dict: A dictionary representation of the portfolio snapshot object.
149
+ """
150
+ def ensure_iso(value):
151
+ if hasattr(value, "isoformat"):
152
+ if value.tzinfo is None:
153
+ value = value.replace(tzinfo=timezone.utc)
154
+ return value.isoformat()
155
+ return value
156
+
157
+ created_at = ensure_iso(self.created_at) if self.created_at else None
158
+
159
+ return {
160
+ "metadata": self.metadata if self.metadata else {},
161
+ "portfolio_id": self.portfolio_id if self.portfolio_id else "",
162
+ "trading_symbol": self.trading_symbol
163
+ if self.trading_symbol else "",
164
+ "pending_value": self.pending_value if self.pending_value else 0.0,
165
+ "unallocated": self.unallocated if self.unallocated else 0.0,
166
+ "total_net_gain": self.total_net_gain
167
+ if self.total_net_gain else 0.0,
168
+ "total_revenue": self.total_revenue if self.total_revenue else 0.0,
169
+ "total_cost": self.total_cost if self.total_cost else 0.0,
170
+ "cash_flow": self.cash_flow if self.cash_flow else 0.0,
171
+ "net_size": self.net_size if self.net_size else 0.0,
172
+ "created_at": created_at if created_at else "",
173
+ "total_value": self.total_value if self.total_value else 0.0,
174
+ }
175
+
176
+ @staticmethod
177
+ def from_dict(data):
178
+ """
179
+ Create a PortfolioSnapshot object from a dictionary.
180
+
181
+ Args:
182
+ data (dict): A dictionary containing the portfolio snapshot data.
183
+
184
+ Returns:
185
+ PortfolioSnapshot: An instance of PortfolioSnapshot.
186
+ """
187
+ created_at_str = data.get("created_at")
188
+ created_at = parser.parse(created_at_str)
189
+
190
+ # Ensure created_at is timezone aware
191
+ created_at = created_at.replace(tzinfo=timezone.utc)
192
+
193
+ return PortfolioSnapshot(
194
+ net_size=data.get("net_size", 0.0),
195
+ created_at=created_at,
196
+ total_value=data.get("total_value", 0.0),
197
+ trading_symbol=data.get(
198
+ "trading_symbol", None
199
+ ),
200
+ portfolio_id=data.get("portfolio_id", None),
201
+ pending_value=data.get("pending_value", 0.0),
202
+ unallocated=data.get("unallocated", 0.0),
203
+ total_net_gain=data.get("total_net_gain", 0.0),
204
+ total_revenue=data.get("total_revenue", 0.0),
205
+ total_cost=data.get("total_cost", 0.0),
206
+ cash_flow=data.get("cash_flow", 0.0),
207
+ metadata=data.get("metadata", {})
208
+ )
@@ -0,0 +1,4 @@
1
+ from .position import Position
2
+ from .position_snapshot import PositionSnapshot
3
+
4
+ __all__ = ["Position", "PositionSnapshot"]
@@ -0,0 +1,68 @@
1
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
+
3
+
4
+ class Position(BaseModel):
5
+ """
6
+ This class represents a position in a portfolio.
7
+ """
8
+
9
+ def __init__(
10
+ self,
11
+ symbol=None,
12
+ amount=0,
13
+ cost=0,
14
+ portfolio_id=None
15
+ ):
16
+ self.symbol = symbol
17
+ self.amount = amount
18
+ self.cost = cost
19
+ self.portfolio_id = portfolio_id
20
+
21
+ def get_symbol(self):
22
+ return self.symbol
23
+
24
+ def set_symbol(self, symbol):
25
+ self.symbol = symbol.upper()
26
+
27
+ def get_amount(self):
28
+ return self.amount
29
+
30
+ def get_cost(self):
31
+ return self.cost
32
+
33
+ def set_cost(self, cost):
34
+ self.cost = cost
35
+
36
+ def set_amount(self, amount):
37
+ self.amount = amount
38
+
39
+ def get_portfolio_id(self):
40
+ return self.portfolio_id
41
+
42
+ def set_portfolio_id(self, portfolio_id):
43
+ self.portfolio_id = portfolio_id
44
+
45
+ def to_dict(self):
46
+ return {
47
+ "symbol": self.symbol,
48
+ "amount": self.amount,
49
+ "cost": self.cost,
50
+ "portfolio_id": self.portfolio_id,
51
+ }
52
+
53
+ @staticmethod
54
+ def from_dict(data: dict):
55
+ return Position(
56
+ symbol=data.get("symbol"),
57
+ amount=data.get("amount", 0),
58
+ cost=data.get("cost", 0),
59
+ portfolio_id=data.get("portfolio_id"),
60
+ )
61
+
62
+ def __repr__(self):
63
+ return self.repr(
64
+ symbol=self.symbol,
65
+ amount=self.amount,
66
+ cost=self.cost,
67
+ portfolio_id=self.portfolio_id,
68
+ )
@@ -0,0 +1,47 @@
1
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
+
3
+
4
+ class PositionSnapshot(BaseModel):
5
+
6
+ def __init__(
7
+ self,
8
+ symbol=None,
9
+ amount=0,
10
+ cost=0,
11
+ portfolio_snapshot_id=None
12
+ ):
13
+ self.symbol = symbol
14
+ self.amount = amount
15
+ self.cost = cost
16
+ self.portfolio_snapshot_id = portfolio_snapshot_id
17
+
18
+ def get_symbol(self):
19
+ return self.symbol
20
+
21
+ def set_symbol(self, symbol):
22
+ self.symbol = symbol.upper()
23
+
24
+ def get_amount(self):
25
+ return self.amount
26
+
27
+ def get_cost(self):
28
+ return self.cost
29
+
30
+ def set_cost(self, cost):
31
+ self.cost = cost
32
+
33
+ def set_amount(self, amount):
34
+ self.amount = amount
35
+
36
+ def get_portfolio_snapshot_id(self):
37
+ return self.portfolio_snapshot_id
38
+
39
+ def set_portfolio_snapshot_id(self, portfolio_snapshot_id):
40
+ self.portfolio_snapshot_id = portfolio_snapshot_id
41
+
42
+ def __repr__(self):
43
+ return self.repr(
44
+ symbol=self.symbol,
45
+ amount=self.amount,
46
+ portfolio_snapshot_id=self.portfolio_snapshot_id,
47
+ )
@@ -0,0 +1,45 @@
1
+ from enum import Enum
2
+
3
+
4
+ class SnapshotInterval(Enum):
5
+ STRATEGY_ITERATION = "STRATEGY_ITERATION"
6
+ DAILY = "DAILY"
7
+
8
+ @staticmethod
9
+ def from_string(value: str):
10
+
11
+ if isinstance(value, str):
12
+
13
+ for entry in SnapshotInterval:
14
+
15
+ if value.upper() == entry.value:
16
+ return entry
17
+
18
+ raise ValueError(
19
+ f"Could not convert {value} to SnapshotInterval"
20
+ )
21
+ return None
22
+
23
+ @staticmethod
24
+ def from_value(value):
25
+
26
+ if isinstance(value, str):
27
+ return SnapshotInterval.from_string(value)
28
+
29
+ if isinstance(value, SnapshotInterval):
30
+
31
+ for entry in SnapshotInterval:
32
+
33
+ if value == entry:
34
+ return entry
35
+
36
+ raise ValueError(
37
+ f"Could not convert {value} to SnapshotInterval"
38
+ )
39
+
40
+ def equals(self, other):
41
+
42
+ if isinstance(other, Enum):
43
+ return self.value == other.value
44
+ else:
45
+ return SnapshotInterval.from_string(other) == self
@@ -0,0 +1,33 @@
1
+ from dataclasses import dataclass
2
+
3
+ from .time_unit import TimeUnit
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class StrategyProfile:
8
+ """
9
+ StrategyProfile class that represents the profile of a trading strategy.
10
+ """
11
+ strategy_id: str = None
12
+ interval: int = None
13
+ time_unit: TimeUnit = None
14
+ trading_time_frame: str = None
15
+ trading_time_frame_start_date: str = None
16
+ backtest_start_date_data: str = None
17
+ backtest_data_index_date: str = None
18
+ symbols: list = None
19
+ market: str = None
20
+ trading_data_type: str = None
21
+ trading_data_types: list = None
22
+ data_sources: list = None
23
+
24
+ def get_runs_per_day(self):
25
+
26
+ if self.time_unit is None:
27
+ return 0
28
+ elif TimeUnit.SECOND.equals(self.time_unit):
29
+ return 86400 / self.interval
30
+ elif TimeUnit.MINUTE.equals(self.time_unit):
31
+ return 1440 / self.interval
32
+ else:
33
+ return 24 / self.interval
@@ -0,0 +1,153 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TimeFrame(Enum):
5
+ CURRENT = "CURRENT"
6
+ ONE_MINUTE = "1m"
7
+ TWO_MINUTE = "2m"
8
+ THREE_MINUTE = "3m"
9
+ FOUR_MINUTE = "4m"
10
+ FIVE_MINUTE = "5m"
11
+ TEN_MINUTE = "10m"
12
+ FIFTEEN_MINUTE = "15m"
13
+ THIRTY_MINUTE = "30m"
14
+ ONE_HOUR = "1h"
15
+ TWO_HOUR = "2h"
16
+ FOUR_HOUR = "4h"
17
+ TWELVE_HOUR = "12h"
18
+ ONE_DAY = "1d"
19
+ ONE_WEEK = "1W"
20
+ ONE_MONTH = "1M"
21
+ ONE_YEAR = "1Y"
22
+
23
+ @staticmethod
24
+ def from_string(value: str):
25
+
26
+ if isinstance(value, str):
27
+
28
+ for entry in TimeFrame:
29
+
30
+ # For hour timeframes compare with and without H
31
+ if "H" in entry.value:
32
+
33
+ if value == entry.value:
34
+ return entry
35
+
36
+ if value == entry.value.replace("H", "h"):
37
+ return entry
38
+
39
+ # For hour timeframes compare with and without H
40
+ if "d" in entry.value:
41
+
42
+ if value == entry.value:
43
+ return entry
44
+
45
+ if value == entry.value.replace("d", "D"):
46
+ return entry
47
+
48
+ if value == entry.value:
49
+ return entry
50
+
51
+ raise ValueError(
52
+ f"Could not convert {value} to TimeFrame"
53
+ )
54
+
55
+ @staticmethod
56
+ def from_value(value):
57
+
58
+ if isinstance(value, str):
59
+ return TimeFrame.from_string(value)
60
+
61
+ if isinstance(value, TimeFrame):
62
+
63
+ for entry in TimeFrame:
64
+
65
+ if value == entry:
66
+ return entry
67
+
68
+ raise ValueError(
69
+ f"Could not convert {value} to TimeFrame"
70
+ )
71
+
72
+ def equals(self, other):
73
+
74
+ if isinstance(other, Enum):
75
+ return self.value == other.value
76
+ else:
77
+ return TimeFrame.from_string(other) == self
78
+
79
+ @property
80
+ def amount_of_minutes(self):
81
+
82
+ if self.equals(TimeFrame.ONE_MINUTE):
83
+ return 1
84
+
85
+ if self.equals(TimeFrame.TWO_MINUTE):
86
+ return 2
87
+
88
+ if self.equals(TimeFrame.THREE_MINUTE):
89
+ return 3
90
+
91
+ if self.equals(TimeFrame.FOUR_MINUTE):
92
+ return 4
93
+
94
+ if self.equals(TimeFrame.FIVE_MINUTE):
95
+ return 5
96
+
97
+ if self.equals(TimeFrame.TEN_MINUTE):
98
+ return 10
99
+
100
+ if self.equals(TimeFrame.FIFTEEN_MINUTE):
101
+ return 15
102
+
103
+ if self.equals(TimeFrame.THIRTY_MINUTE):
104
+ return 30
105
+
106
+ if self.equals(TimeFrame.ONE_HOUR):
107
+ return 60
108
+
109
+ if self.equals(TimeFrame.TWO_HOUR):
110
+ return 120
111
+
112
+ if self.equals(TimeFrame.FOUR_HOUR):
113
+ return 240
114
+
115
+ if self.equals(TimeFrame.TWELVE_HOUR):
116
+ return 720
117
+
118
+ if self.equals(TimeFrame.ONE_DAY):
119
+ return 1440
120
+
121
+ if self.equals(TimeFrame.ONE_WEEK):
122
+ return 10080
123
+
124
+ if self.equals(TimeFrame.ONE_MONTH):
125
+ return 40320
126
+
127
+ if self.equals(TimeFrame.ONE_YEAR):
128
+ return 525600
129
+
130
+ raise ValueError(
131
+ f"Could not determine amount of minutes for {self.value}"
132
+ )
133
+
134
+ # Add comparison methods for ordering
135
+ def __lt__(self, other):
136
+ if isinstance(other, TimeFrame):
137
+ return self.amount_of_minutes < other.amount_of_minutes
138
+ raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
139
+
140
+ def __le__(self, other):
141
+ if isinstance(other, TimeFrame):
142
+ return self.amount_of_minutes <= other.amount_of_minutes
143
+ raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
144
+
145
+ def __gt__(self, other):
146
+ if isinstance(other, TimeFrame):
147
+ return self.amount_of_minutes > other.amount_of_minutes
148
+ raise TypeError(f"Cannot compare TimeFrame with {type(other)}")
149
+
150
+ def __ge__(self, other):
151
+ if isinstance(other, TimeFrame):
152
+ return self.amount_of_minutes >= other.amount_of_minutes
153
+ raise TypeError(f"Cannot compare TimeFrame with {type(other)}")