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,384 @@
1
+ import logging
2
+ from datetime import datetime, timezone
3
+
4
+ from dateutil.parser import parse
5
+
6
+ from investing_algorithm_framework.domain.exceptions import \
7
+ OperationalException
8
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
9
+ from investing_algorithm_framework.domain.models.order.order_side import \
10
+ OrderSide
11
+ from investing_algorithm_framework.domain.models.order.order_status import \
12
+ OrderStatus
13
+ from investing_algorithm_framework.domain.models.order.order_type import \
14
+ OrderType
15
+
16
+ logger = logging.getLogger("investing_algorithm_framework")
17
+
18
+
19
+ class Order(BaseModel):
20
+ """
21
+ Order model class to represent an order of the trading bot
22
+ """
23
+ def __init__(
24
+ self,
25
+ order_type,
26
+ order_side,
27
+ amount,
28
+ status: OrderStatus | None | str = OrderStatus.CREATED.value,
29
+ target_symbol=None,
30
+ trading_symbol=None,
31
+ price=None,
32
+ created_at=None,
33
+ updated_at=None,
34
+ external_id=None,
35
+ cost=None,
36
+ filled=None,
37
+ remaining=None,
38
+ fee=None,
39
+ position_id=None,
40
+ order_fee=None,
41
+ order_fee_currency=None,
42
+ order_fee_rate=None,
43
+ id=None,
44
+ metadata=None,
45
+ ):
46
+ if target_symbol is None:
47
+ raise OperationalException("Target symbol is not specified")
48
+
49
+ if trading_symbol is None:
50
+ raise OperationalException("Trading symbol is not specified")
51
+
52
+ self.target_symbol = target_symbol.upper()
53
+ self.trading_symbol = trading_symbol.upper()
54
+
55
+ if order_side is None:
56
+ raise OperationalException("Order side is not set")
57
+
58
+ if order_type is None:
59
+ raise OperationalException("Order type is not set")
60
+
61
+ if status is None:
62
+ raise OperationalException("Status is not set")
63
+
64
+ self.created_at = created_at
65
+ self.updated_at = updated_at
66
+
67
+ if self.created_at is None:
68
+ self.created_at = datetime.now(tz=timezone.utc)
69
+
70
+ if self.updated_at is None:
71
+ self.updated_at = datetime.now(tz=timezone.utc)
72
+
73
+ self.external_id = external_id
74
+ self.price = price
75
+ self.order_side = OrderSide.from_value(order_side).value
76
+ self.order_type = OrderType.from_value(order_type).value
77
+ self.status = OrderStatus.from_value(status).value
78
+ self.position_id = position_id
79
+ self.amount = amount
80
+ self.filled = filled
81
+ self.remaining = remaining
82
+ self.fee = fee
83
+ self._available_amount = self.filled
84
+ self.order_fee = order_fee
85
+ self.order_fee_currency = order_fee_currency
86
+ self.order_fee_rate = order_fee_rate
87
+ self.id = id
88
+ self.cost = cost
89
+ self.metadata = metadata if metadata is not None else {}
90
+
91
+ def get_id(self):
92
+ return self.id
93
+
94
+ def get_external_id(self):
95
+ return self.external_id
96
+
97
+ def get_target_symbol(self):
98
+ return self.target_symbol
99
+
100
+ def get_trading_symbol(self):
101
+ return self.trading_symbol
102
+
103
+ def get_price(self):
104
+ if self.price is not None:
105
+ return self.price
106
+
107
+ return 0
108
+
109
+ def set_price(self, price):
110
+ self.price = price
111
+
112
+ def get_order_fee_currency(self):
113
+ return self.order_fee_currency
114
+
115
+ def set_order_fee_currency(self, order_fee_currency):
116
+ self.order_fee_currency = order_fee_currency
117
+
118
+ def get_order_fee_rate(self):
119
+ return self.order_fee_rate
120
+
121
+ def set_order_fee_rate(self, order_fee_rate):
122
+ self.order_fee_rate = order_fee_rate
123
+
124
+ def get_order_fee(self):
125
+ return self.order_fee
126
+
127
+ def set_order_fee(self, order_fee):
128
+ self.order_fee = order_fee
129
+
130
+ def get_order_size(self):
131
+ return self.order_side
132
+
133
+ def get_status(self) -> OrderStatus:
134
+ return self.status
135
+
136
+ def set_status(self, status):
137
+ self.status = OrderStatus.from_value(status).value
138
+
139
+ def get_order_type(self):
140
+ return self.order_type
141
+
142
+ def get_order_side(self):
143
+ return self.order_side
144
+
145
+ def get_amount(self):
146
+ return self.amount
147
+
148
+ def set_amount(self, amount):
149
+ self.amount = amount
150
+
151
+ def set_external_id(self, external_id):
152
+ self.external_id = external_id
153
+
154
+ def get_created_at(self):
155
+ return self.created_at
156
+
157
+ def set_created_at(self, created_at):
158
+ self.created_at = created_at
159
+
160
+ def get_updated_at(self):
161
+ return self.updated_at
162
+
163
+ def set_updated_at(self, updated_at):
164
+ self.updated_at = updated_at
165
+
166
+ def get_filled(self):
167
+
168
+ if self.filled is None:
169
+ return 0
170
+
171
+ return self.filled
172
+
173
+ def set_filled(self, filled):
174
+ self.filled = filled
175
+
176
+ def get_remaining(self):
177
+
178
+ if self.remaining is None:
179
+ return self.get_amount() - self.get_filled()
180
+
181
+ return self.remaining
182
+
183
+ def set_remaining(self, remaining):
184
+ self.remaining = remaining
185
+
186
+ def get_fee(self):
187
+ return self.fee
188
+
189
+ def set_fee(self, order_fee):
190
+ self.fee = order_fee
191
+
192
+ def get_symbol(self):
193
+ return (self.get_target_symbol().upper() + "/"
194
+ + self.get_trading_symbol().upper())
195
+
196
+ @property
197
+ def symbol(self):
198
+ return (self.get_target_symbol().upper() + "/"
199
+ + self.get_trading_symbol().upper())
200
+
201
+ def get_available_amount(self):
202
+
203
+ if self._available_amount is None:
204
+ return self.get_filled()
205
+
206
+ return self._available_amount
207
+
208
+ @property
209
+ def available_amount(self):
210
+ return self.get_available_amount()
211
+
212
+ def set_available_amount(self, available_amount):
213
+ self._available_amount = available_amount
214
+
215
+ @available_amount.setter
216
+ def available_amount(self, available_amount):
217
+ self.set_available_amount(available_amount)
218
+
219
+ def to_dict(self, datetime_format=None):
220
+ """
221
+ Convert the Order object to a dictionary
222
+
223
+ Args:
224
+ datetime_format (str): The format to use for the datetime fields.
225
+ If None, the datetime fields will be returned as is.
226
+ Defaults to None.
227
+
228
+ Returns:
229
+ dict: A dictionary representation of the Order object.
230
+ """
231
+
232
+ def ensure_iso(value):
233
+ if hasattr(value, "isoformat"):
234
+ if value.tzinfo is None:
235
+ value = value.replace(tzinfo=timezone.utc)
236
+ return value.isoformat()
237
+ return value
238
+
239
+ created_at = ensure_iso(self.created_at) if self.created_at else None
240
+ updated_at = ensure_iso(self.updated_at) if self.updated_at else None
241
+
242
+ # Ensure status is a string
243
+ self.status = OrderStatus.from_value(self.status).value
244
+
245
+ return {
246
+ "id": self.id,
247
+ "external_id": self.external_id,
248
+ "target_symbol": self.target_symbol,
249
+ "trading_symbol": self.trading_symbol,
250
+ "order_side": self.order_side,
251
+ "order_type": self.order_type,
252
+ "status": self.status,
253
+ "price": self.price,
254
+ "amount": self.amount,
255
+ "created_at": created_at,
256
+ "updated_at": updated_at,
257
+ "cost": self.cost,
258
+ "filled": self.filled,
259
+ "remaining": self.remaining,
260
+ "order_fee_currency": self.order_fee_currency,
261
+ "order_fee_rate": self.order_fee_rate,
262
+ "order_fee": self.order_fee,
263
+ "metadata": self.metadata if hasattr(self, 'metadata') else {},
264
+ }
265
+
266
+ @staticmethod
267
+ def from_dict(data: dict):
268
+ created_at = data.get("created_at", None)
269
+ updated_at = data.get("updated_at", None)
270
+ symbol = data.get("symbol", None)
271
+
272
+ if symbol is not None:
273
+ target_symbol = symbol.split("/")[0]
274
+ trading_symbol = symbol.split("/")[1]
275
+ else:
276
+ target_symbol = data.get("target_symbol", None)
277
+ trading_symbol = data.get("trading_symbol", None)
278
+
279
+ if created_at is not None:
280
+ created_at = parse(created_at)
281
+
282
+ if updated_at is not None:
283
+ updated_at = parse(updated_at)
284
+
285
+ order = Order(
286
+ id=data.get("id", None),
287
+ external_id=data.get("external_id", None),
288
+ target_symbol=target_symbol,
289
+ trading_symbol=trading_symbol,
290
+ price=data.get("price", None),
291
+ amount=data.get("amount", None),
292
+ status=data.get("status", None),
293
+ order_type=data.get("order_type", None),
294
+ order_side=data.get("order_side", None),
295
+ filled=data.get("filled", None),
296
+ remaining=data.get("remaining", None),
297
+ fee=data.get("fee", None),
298
+ cost=data.get("cost", None),
299
+ created_at=created_at,
300
+ updated_at=updated_at,
301
+ order_fee=data.get("order_fee", None),
302
+ order_fee_currency=data.get("order_fee_currency", None),
303
+ order_fee_rate=data.get("order_fee_rate", None),
304
+ metadata=data.get("metadata", {}),
305
+ )
306
+ return order
307
+
308
+ @staticmethod
309
+ def from_ccxt_order(ccxt_order):
310
+ """
311
+ Create an Order object from a ccxt order object
312
+ :param ccxt_order: ccxt order object
313
+ :return: Order object
314
+ """
315
+ status = OrderStatus.from_value(ccxt_order["status"])
316
+ target_symbol = ccxt_order.get("symbol").split("/")[0]
317
+ trading_symbol = ccxt_order.get("symbol").split("/")[1]
318
+ ccxt_fee = ccxt_order.get("fee", None)
319
+ order_fee = None
320
+ order_fee_currency = None
321
+ order_fee_rate = None
322
+
323
+ if ccxt_fee is not None:
324
+ order_fee = ccxt_fee.get("cost", None)
325
+ order_fee_currency = ccxt_fee.get("currency", None)
326
+ order_fee_rate = ccxt_fee.get("rate", None)
327
+
328
+ created_at = ccxt_order.get("datetime", None)
329
+ created_at = parse(created_at) if created_at else None
330
+
331
+ # Make sure that the created_at is a utc datetime object
332
+ if created_at and created_at.tzinfo is None:
333
+ created_at = created_at.replace(tzinfo=timezone.utc)
334
+
335
+ return Order(
336
+ external_id=ccxt_order.get("id", None),
337
+ target_symbol=target_symbol,
338
+ trading_symbol=trading_symbol,
339
+ price=ccxt_order.get("price", None),
340
+ amount=ccxt_order.get("amount", None),
341
+ status=status,
342
+ cost=ccxt_order.get("cost", None),
343
+ order_type=ccxt_order.get("type", None),
344
+ order_side=ccxt_order.get("side", None),
345
+ filled=ccxt_order.get("filled", None),
346
+ remaining=ccxt_order.get("remaining", None),
347
+ order_fee=order_fee,
348
+ order_fee_currency=order_fee_currency,
349
+ order_fee_rate=order_fee_rate,
350
+ created_at=created_at
351
+ )
352
+
353
+ def __repr__(self):
354
+
355
+ if not hasattr(self, "id"):
356
+ id_value = "ccxt external order"
357
+ else:
358
+ id_value = self.id
359
+
360
+ return self.repr(
361
+ id=id_value,
362
+ price=self.get_price(),
363
+ amount=self.get_amount(),
364
+ external_id=self.external_id,
365
+ status=self.status,
366
+ target_symbol=self.target_symbol,
367
+ trading_symbol=self.trading_symbol,
368
+ order_side=self.order_side,
369
+ order_type=self.order_type,
370
+ filled=self.get_filled(),
371
+ remaining=self.get_remaining(),
372
+ created_at=self.get_created_at(),
373
+ updated_at=self.get_updated_at(),
374
+ )
375
+
376
+ def get_size(self):
377
+ """
378
+ Get the size of the order
379
+
380
+ Returns:
381
+ float: The size of the order
382
+ """
383
+ return self.get_amount() * self.get_price() \
384
+ if self.get_price() is not None else 0
@@ -0,0 +1,36 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OrderSide(Enum):
5
+ SELL = 'SELL'
6
+ BUY = 'BUY'
7
+
8
+ @staticmethod
9
+ def from_string(value: str):
10
+
11
+ if isinstance(value, str):
12
+ for order_type in OrderSide:
13
+
14
+ if value.upper() == order_type.value:
15
+ return order_type
16
+
17
+ raise ValueError(f"Could not convert value {value} to OrderSide")
18
+
19
+ @staticmethod
20
+ def from_value(value):
21
+
22
+ if isinstance(value, OrderSide):
23
+ for order_side in OrderSide:
24
+
25
+ if value == order_side:
26
+ return order_side
27
+
28
+ return OrderSide.from_string(value)
29
+
30
+ def equals(self, other):
31
+
32
+ if isinstance(other, Enum):
33
+ return self.value == other.value
34
+
35
+ else:
36
+ return OrderSide.from_string(other) == self
@@ -0,0 +1,37 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OrderStatus(Enum):
5
+ CREATED = 'CREATED'
6
+ OPEN = "OPEN"
7
+ CLOSED = "CLOSED"
8
+ CANCELED = "CANCELED"
9
+ EXPIRED = "EXPIRED"
10
+ REJECTED = "REJECTED"
11
+
12
+ @staticmethod
13
+ def from_string(value: str):
14
+
15
+ if isinstance(value, str):
16
+ for order_type in OrderStatus:
17
+
18
+ if value.upper() == order_type.value:
19
+ return order_type
20
+
21
+ raise ValueError(f"Could not convert value {value} to OrderStatus")
22
+
23
+ @staticmethod
24
+ def from_value(value):
25
+
26
+ if isinstance(value, OrderStatus):
27
+ for order_status in OrderStatus:
28
+
29
+ if value == order_status:
30
+ return order_status
31
+ elif isinstance(value, str):
32
+ return OrderStatus.from_string(value)
33
+
34
+ raise ValueError(f"Could not convert value: {value} to OrderStatus")
35
+
36
+ def equals(self, other):
37
+ return OrderStatus.from_value(other) == self
@@ -0,0 +1,30 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OrderType(Enum):
5
+ LIMIT = 'LIMIT'
6
+
7
+ @staticmethod
8
+ def from_string(value: str):
9
+
10
+ if isinstance(value, str):
11
+ for order_type in OrderType:
12
+
13
+ if value.upper() == order_type.value:
14
+ return order_type
15
+
16
+ raise ValueError(f"Could not convert value: {value} to OrderType")
17
+
18
+ @staticmethod
19
+ def from_value(value):
20
+
21
+ if isinstance(value, OrderType):
22
+ for order_type in OrderType:
23
+
24
+ if value == order_type:
25
+ return order_type
26
+
27
+ return OrderType.from_string(value)
28
+
29
+ def equals(self, other):
30
+ return OrderType.from_value(other) == self
@@ -0,0 +1,9 @@
1
+ from .portfolio import Portfolio
2
+ from .portfolio_configuration import PortfolioConfiguration
3
+ from .portfolio_snapshot import PortfolioSnapshot
4
+
5
+ __all__ = [
6
+ "PortfolioConfiguration",
7
+ "Portfolio",
8
+ "PortfolioSnapshot",
9
+ ]
@@ -0,0 +1,169 @@
1
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
+
3
+
4
+ class Portfolio(BaseModel):
5
+ """
6
+ Portfolio base class.
7
+
8
+ A portfolio is a collection of positions that are managed by an algorithm.
9
+
10
+ Attributes:
11
+ * identifier: str, unique identifier of the portfolio
12
+ * trading_symbol: str, trading symbol of the portfolio
13
+ * unallocated: float, the size of the trading symbol that is not
14
+ allocated. For example, if the trading symbol is USDT and the unallocated
15
+ is 1000, it means that the portfolio has 1000 USDT that is not
16
+ allocated to any position.
17
+ * net_size: float, net size of the portfolio is the initial balance of the
18
+ portfolio plus the all the net gains of the trades. The
19
+ * realized: float, the realized gain of the portfolio is the sum of all the
20
+ realized gains of the trades.
21
+ * total_revenue: float, the total revenue of the portfolio is the sum
22
+ of all the orders (price * size)
23
+ * total_cost: float, the total cost of the portfolio is the sum of all the
24
+ costs of the trades (price * size (for buy orders)
25
+ or -price * size (for sell orders))
26
+ * total_net_gain: float, the total net gain of the portfolio is the sum of
27
+ all the net gains of the trades
28
+ * total_trade_volume: float, the total trade volume of the
29
+ portfolio is the sum of all the sizes of the trades
30
+ * market: str, the market of the portfolio (e.g. BITVAVO, BINANCE)
31
+ * created_at: datetime, the datetime when the portfolio was created
32
+ * updated_at: datetime, the datetime when the portfolio was last updated
33
+ * initialized: bool, whether the portfolio is initialized or not
34
+ * initial_balance: float, the initial balance of the portfolio
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ identifier,
40
+ trading_symbol,
41
+ net_size,
42
+ unallocated,
43
+ initial_balance,
44
+ market,
45
+ realized=0,
46
+ total_revenue=0,
47
+ total_cost=0,
48
+ total_net_gain=0,
49
+ total_trade_volume=0,
50
+ created_at=None,
51
+ updated_at=None,
52
+ initialized=False,
53
+ ):
54
+ self.identifier = identifier
55
+ self.updated_at = None
56
+ self.trading_symbol = trading_symbol.upper()
57
+ self.net_size = net_size
58
+ self.unallocated = unallocated
59
+ self.initial_balance = initial_balance
60
+ self.realized = realized
61
+ self.total_revenue = total_revenue
62
+ self.total_cost = total_cost
63
+ self.total_net_gain = total_net_gain
64
+ self.total_trade_volume = total_trade_volume
65
+ self.market = market.upper()
66
+ self.created_at = created_at
67
+ self.updated_at = updated_at
68
+ self.initialized = initialized
69
+ self._allocated = None
70
+
71
+ def __repr__(self):
72
+ return self.repr(
73
+ identifier=self.identifier,
74
+ trading_symbol=self.trading_symbol,
75
+ net_size=self.net_size,
76
+ unallocated=self.unallocated,
77
+ realized=self.realized,
78
+ total_revenue=self.total_revenue,
79
+ total_cost=self.total_cost,
80
+ market=self.market,
81
+ initial_balance=self.initial_balance
82
+ )
83
+
84
+ def get_identifier(self):
85
+ return self.identifier
86
+
87
+ def get_unallocated(self):
88
+ return self.unallocated
89
+
90
+ def get_net_size(self):
91
+ return self.net_size
92
+
93
+ def get_realized(self):
94
+ return self.realized
95
+
96
+ def get_total_revenue(self):
97
+ return self.total_revenue
98
+
99
+ def get_total_cost(self):
100
+ return self.total_cost
101
+
102
+ def get_total_net_gain(self):
103
+ return self.total_net_gain
104
+
105
+ def get_total_trade_volume(self):
106
+ return self.total_trade_volume
107
+
108
+ def get_created_at(self):
109
+ return self.created_at
110
+
111
+ def get_updated_at(self):
112
+ return self.updated_at
113
+
114
+ def get_trading_symbol(self):
115
+ return self.trading_symbol
116
+
117
+ def get_market(self):
118
+ return self.market
119
+
120
+ def get_initial_balance(self):
121
+ return self.initial_balance
122
+
123
+ @property
124
+ def allocated(self):
125
+
126
+ if self._allocated is None:
127
+ return 0.0
128
+
129
+ return self._allocated
130
+
131
+ @allocated.setter
132
+ def allocated(self, value):
133
+ self._allocated = value
134
+
135
+ @staticmethod
136
+ def from_portfolio_configuration(portfolio_configuration):
137
+ """
138
+ Function to create a portfolio from a portfolio configuration
139
+
140
+ We assume that a portfolio that is created from a configuration
141
+ is always un initialized.
142
+
143
+ Args:
144
+ portfolio_configuration: PortfolioConfiguration
145
+
146
+ Returns:
147
+ Portfolio
148
+ """
149
+ return Portfolio(
150
+ identifier=portfolio_configuration.identifier,
151
+ trading_symbol=portfolio_configuration.trading_symbol,
152
+ unallocated=portfolio_configuration.initial_balance,
153
+ net_size=portfolio_configuration.initial_balance,
154
+ market=portfolio_configuration.market,
155
+ initial_balance=portfolio_configuration.initial_balance,
156
+ initialized=False
157
+ )
158
+
159
+ def to_dict(self):
160
+ return {
161
+ "trading_symbol": self.trading_symbol,
162
+ "market": self.market,
163
+ "unallocated": self.unallocated,
164
+ "identifier": self.identifier,
165
+ "created_at": self.created_at,
166
+ "updated_at": self.updated_at,
167
+ "initialized": self.initialized,
168
+ "initial_balance": self.initial_balance,
169
+ }