investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. investing_algorithm_framework/__init__.py +192 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +29 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2220 -379
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  31. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  32. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  33. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  34. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  35. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  36. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  37. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  39. investing_algorithm_framework/app/strategy.py +867 -60
  40. investing_algorithm_framework/app/task.py +5 -3
  41. investing_algorithm_framework/app/web/__init__.py +2 -1
  42. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  43. investing_algorithm_framework/app/web/controllers/orders.py +3 -2
  44. investing_algorithm_framework/app/web/controllers/positions.py +2 -2
  45. investing_algorithm_framework/app/web/create_app.py +4 -2
  46. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  47. investing_algorithm_framework/cli/__init__.py +0 -0
  48. investing_algorithm_framework/cli/cli.py +231 -0
  49. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  50. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  51. investing_algorithm_framework/cli/initialize_app.py +603 -0
  52. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  53. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  55. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  56. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  58. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  59. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  60. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  61. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  62. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  63. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  64. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  65. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  66. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  67. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  68. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  69. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  70. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  71. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  72. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  73. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  74. investing_algorithm_framework/create_app.py +40 -7
  75. investing_algorithm_framework/dependency_container.py +100 -47
  76. investing_algorithm_framework/domain/__init__.py +97 -30
  77. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  78. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  79. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  81. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  82. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  83. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  84. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  87. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  88. investing_algorithm_framework/domain/config.py +59 -136
  89. investing_algorithm_framework/domain/constants.py +18 -37
  90. investing_algorithm_framework/domain/data_provider.py +334 -0
  91. investing_algorithm_framework/domain/data_structures.py +42 -0
  92. investing_algorithm_framework/domain/exceptions.py +51 -1
  93. investing_algorithm_framework/domain/models/__init__.py +26 -19
  94. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  95. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  96. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  97. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  98. investing_algorithm_framework/domain/models/event.py +35 -0
  99. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  100. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  101. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  102. investing_algorithm_framework/domain/models/order/order.py +198 -65
  103. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  104. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  105. investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
  106. investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
  107. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
  108. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  109. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  110. investing_algorithm_framework/domain/models/position/position.py +20 -0
  111. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  112. investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
  113. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  114. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  115. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  116. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  117. investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
  118. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  119. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  120. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  121. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  122. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  123. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  124. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  125. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  126. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  127. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  128. investing_algorithm_framework/domain/order_executor.py +112 -0
  129. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  130. investing_algorithm_framework/domain/services/__init__.py +11 -0
  131. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  132. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  133. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  134. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  135. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  136. investing_algorithm_framework/domain/strategy.py +1 -29
  137. investing_algorithm_framework/domain/utils/__init__.py +15 -5
  138. investing_algorithm_framework/domain/utils/csv.py +22 -0
  139. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  140. investing_algorithm_framework/domain/utils/dates.py +57 -0
  141. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  142. investing_algorithm_framework/domain/utils/polars.py +53 -0
  143. investing_algorithm_framework/domain/utils/random.py +29 -0
  144. investing_algorithm_framework/download_data.py +244 -0
  145. investing_algorithm_framework/infrastructure/__init__.py +37 -11
  146. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  147. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  148. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  149. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  150. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  151. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  152. investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
  153. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  154. investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
  155. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  156. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  157. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  158. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
  159. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
  160. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
  161. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  162. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  163. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  164. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  165. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  166. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  167. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  168. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  169. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  170. investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
  171. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  172. investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
  173. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  174. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  175. investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
  176. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  177. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  178. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  179. investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
  180. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  181. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  182. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  183. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  184. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  185. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  186. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  187. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  188. investing_algorithm_framework/services/__init__.py +123 -15
  189. investing_algorithm_framework/services/configuration_service.py +77 -11
  190. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  191. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  192. investing_algorithm_framework/services/market_credential_service.py +40 -0
  193. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  194. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  195. investing_algorithm_framework/services/metrics/beta.py +0 -0
  196. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  197. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  198. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  199. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  200. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  201. investing_algorithm_framework/services/metrics/generate.py +358 -0
  202. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  203. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  204. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  205. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  206. investing_algorithm_framework/services/metrics/returns.py +452 -0
  207. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  208. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  209. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  210. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  211. investing_algorithm_framework/services/metrics/trades.py +473 -0
  212. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  213. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  214. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  215. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  216. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  217. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  218. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  219. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  220. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  221. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  222. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  223. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  224. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  225. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  226. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  227. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  228. investing_algorithm_framework/services/positions/__init__.py +7 -0
  229. investing_algorithm_framework/services/positions/position_service.py +210 -0
  230. investing_algorithm_framework/services/repository_service.py +8 -2
  231. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  232. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  233. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  234. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  235. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  237. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  238. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  239. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  240. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  241. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  242. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  243. investing_algorithm_framework/app/algorithm.py +0 -630
  244. investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
  245. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  246. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  247. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
  248. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  249. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  250. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  251. investing_algorithm_framework/domain/models/trade.py +0 -78
  252. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  253. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  254. investing_algorithm_framework/domain/singleton.py +0 -9
  255. investing_algorithm_framework/domain/utils/backtesting.py +0 -82
  256. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  257. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  258. investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
  259. investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
  260. investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
  261. investing_algorithm_framework/services/backtest_service.py +0 -268
  262. investing_algorithm_framework/services/market_data_service.py +0 -77
  263. investing_algorithm_framework/services/order_backtest_service.py +0 -122
  264. investing_algorithm_framework/services/order_service.py +0 -752
  265. investing_algorithm_framework/services/portfolio_service.py +0 -164
  266. investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
  267. investing_algorithm_framework/services/position_cost_service.py +0 -5
  268. investing_algorithm_framework/services/position_service.py +0 -63
  269. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
  270. investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
  271. investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
  272. investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
  273. investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
  274. /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
  275. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  276. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -1,40 +1,47 @@
1
1
  import logging
2
- from datetime import datetime
2
+ from datetime import datetime, timezone
3
+
4
+ from dateutil.parser import parse
3
5
 
4
6
  from investing_algorithm_framework.domain.exceptions import \
5
7
  OperationalException
6
8
  from investing_algorithm_framework.domain.models.base_model import BaseModel
7
- from investing_algorithm_framework.domain.models.order import OrderStatus, \
8
- OrderType, OrderSide
9
- from investing_algorithm_framework.domain.models.order.order_fee import \
10
- OrderFee
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
11
15
 
12
16
  logger = logging.getLogger("investing_algorithm_framework")
13
17
 
14
18
 
15
19
  class Order(BaseModel):
16
-
20
+ """
21
+ Order model class to represent an order of the trading bot
22
+ """
17
23
  def __init__(
18
24
  self,
19
25
  order_type,
20
26
  order_side,
21
- status,
22
27
  amount,
28
+ status: OrderStatus | None | str = OrderStatus.CREATED.value,
23
29
  target_symbol=None,
24
30
  trading_symbol=None,
25
31
  price=None,
26
- net_gain=0,
27
32
  created_at=None,
28
33
  updated_at=None,
29
- trade_closed_at=None,
30
- trade_closed_price=None,
31
- trade_closed_amount=None,
32
34
  external_id=None,
35
+ cost=None,
33
36
  filled=None,
34
37
  remaining=None,
35
- cost=None,
36
38
  fee=None,
37
39
  position_id=None,
40
+ order_fee=None,
41
+ order_fee_currency=None,
42
+ order_fee_rate=None,
43
+ id=None,
44
+ metadata=None,
38
45
  ):
39
46
  if target_symbol is None:
40
47
  raise OperationalException("Target symbol is not specified")
@@ -54,6 +61,15 @@ class Order(BaseModel):
54
61
  if status is None:
55
62
  raise OperationalException("Status is not set")
56
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
+
57
73
  self.external_id = external_id
58
74
  self.price = price
59
75
  self.order_side = OrderSide.from_value(order_side).value
@@ -61,16 +77,19 @@ class Order(BaseModel):
61
77
  self.status = OrderStatus.from_value(status).value
62
78
  self.position_id = position_id
63
79
  self.amount = amount
64
- self.net_gain = net_gain
65
- self.trade_closed_at = trade_closed_at
66
- self.trade_closed_price = trade_closed_price
67
- self.trade_closed_amount = trade_closed_amount
68
- self.created_at = created_at
69
- self.updated_at = updated_at
70
80
  self.filled = filled
71
81
  self.remaining = remaining
72
- self.cost = cost
73
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
74
93
 
75
94
  def get_external_id(self):
76
95
  return self.external_id
@@ -90,6 +109,24 @@ class Order(BaseModel):
90
109
  def set_price(self, price):
91
110
  self.price = price
92
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
+
93
130
  def get_order_size(self):
94
131
  return self.order_side
95
132
 
@@ -114,30 +151,6 @@ class Order(BaseModel):
114
151
  def set_external_id(self, external_id):
115
152
  self.external_id = external_id
116
153
 
117
- def get_net_gain(self):
118
- return self.net_gain
119
-
120
- def set_net_gain(self, net_gain):
121
- self.net_gain = net_gain
122
-
123
- def get_trade_closed_at(self):
124
- return self.trade_closed_at
125
-
126
- def set_trade_closed_at(self, trade_closed_at):
127
- self.trade_closed_at = trade_closed_at
128
-
129
- def get_trade_closed_price(self):
130
- return self.trade_closed_price
131
-
132
- def set_trade_closed_price(self, trade_closed_price):
133
- self.trade_closed_price = trade_closed_price
134
-
135
- def get_trade_closed_amount(self):
136
- return self.trade_closed_amount
137
-
138
- def set_trade_closed_amount(self, trade_closed_amount):
139
- self.trade_closed_amount = trade_closed_amount
140
-
141
154
  def get_created_at(self):
142
155
  return self.created_at
143
156
 
@@ -163,27 +176,74 @@ class Order(BaseModel):
163
176
  def get_remaining(self):
164
177
 
165
178
  if self.remaining is None:
166
- return '0'
179
+ return self.get_amount() - self.get_filled()
167
180
 
168
181
  return self.remaining
169
182
 
170
183
  def set_remaining(self, remaining):
171
184
  self.remaining = remaining
172
185
 
173
- def get_cost(self):
174
- return self.cost
175
-
176
- def set_cost(self, cost):
177
- self.cost = cost
178
-
179
186
  def get_fee(self):
180
187
  return self.fee
181
188
 
182
189
  def set_fee(self, order_fee):
183
190
  self.fee = order_fee
184
191
 
185
- def to_dict(self):
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
+
186
245
  return {
246
+ "id": self.id,
187
247
  "external_id": self.external_id,
188
248
  "target_symbol": self.target_symbol,
189
249
  "trading_symbol": self.trading_symbol,
@@ -192,22 +252,85 @@ class Order(BaseModel):
192
252
  "status": self.status,
193
253
  "price": self.price,
194
254
  "amount": self.amount,
195
- "net_gain": self.net_gain,
196
- "trade_closed_at": self.trade_closed_at,
197
- "trade_closed_price": self.trade_closed_price,
198
- "created_at": self.created_at,
199
- "updated_at": self.updated_at,
255
+ "created_at": created_at,
256
+ "updated_at": updated_at,
257
+ "cost": self.cost,
200
258
  "filled": self.filled,
201
259
  "remaining": self.remaining,
202
- "cost": self.cost,
203
- "fee": self.fee.to_dict() if self.fee is not None else None,
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 {},
204
264
  }
205
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
+
206
308
  @staticmethod
207
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
+ """
208
315
  status = OrderStatus.from_value(ccxt_order["status"])
209
316
  target_symbol = ccxt_order.get("symbol").split("/")[0]
210
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)
211
334
 
212
335
  return Order(
213
336
  external_id=ccxt_order.get("id", None),
@@ -216,15 +339,15 @@ class Order(BaseModel):
216
339
  price=ccxt_order.get("price", None),
217
340
  amount=ccxt_order.get("amount", None),
218
341
  status=status,
342
+ cost=ccxt_order.get("cost", None),
219
343
  order_type=ccxt_order.get("type", None),
220
344
  order_side=ccxt_order.get("side", None),
221
345
  filled=ccxt_order.get("filled", None),
222
346
  remaining=ccxt_order.get("remaining", None),
223
- cost=ccxt_order.get("cost", None),
224
- fee=OrderFee.from_ccxt_fee(ccxt_order.get("fee", None)),
225
- created_at=datetime.strptime(
226
- ccxt_order.get("datetime", None), "%Y-%m-%dT%H:%M:%S.%fZ"
227
- )
347
+ order_fee=order_fee,
348
+ order_fee_currency=order_fee_currency,
349
+ order_fee_rate=order_fee_rate,
350
+ created_at=created_at
228
351
  )
229
352
 
230
353
  def __repr__(self):
@@ -238,7 +361,6 @@ class Order(BaseModel):
238
361
  id=id_value,
239
362
  price=self.get_price(),
240
363
  amount=self.get_amount(),
241
- net_gain=self.get_net_gain(),
242
364
  external_id=self.external_id,
243
365
  status=self.status,
244
366
  target_symbol=self.target_symbol,
@@ -247,5 +369,16 @@ class Order(BaseModel):
247
369
  order_type=self.order_type,
248
370
  filled=self.get_filled(),
249
371
  remaining=self.get_remaining(),
250
- cost=self.get_cost(),
372
+ created_at=self.get_created_at(),
373
+ updated_at=self.get_updated_at(),
251
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
@@ -18,7 +18,7 @@ class OrderStatus(Enum):
18
18
  if value.upper() == order_type.value:
19
19
  return order_type
20
20
 
21
- raise ValueError("Could not convert value to OrderStatus")
21
+ raise ValueError(f"Could not convert value {value} to OrderStatus")
22
22
 
23
23
  @staticmethod
24
24
  def from_value(value):
@@ -31,7 +31,7 @@ class OrderStatus(Enum):
31
31
  elif isinstance(value, str):
32
32
  return OrderStatus.from_string(value)
33
33
 
34
- raise ValueError("Could not convert value to OrderStatus")
34
+ raise ValueError(f"Could not convert value: {value} to OrderStatus")
35
35
 
36
36
  def equals(self, other):
37
37
  return OrderStatus.from_value(other) == self
@@ -3,8 +3,6 @@ from enum import Enum
3
3
 
4
4
  class OrderType(Enum):
5
5
  LIMIT = 'LIMIT'
6
- MARKET = 'MARKET'
7
- STOP_LOSS_LIMIT = "STOP_LOSS_LIMIT"
8
6
 
9
7
  @staticmethod
10
8
  def from_string(value: str):
@@ -15,7 +13,7 @@ class OrderType(Enum):
15
13
  if value.upper() == order_type.value:
16
14
  return order_type
17
15
 
18
- raise ValueError("Could not convert value to OrderType")
16
+ raise ValueError(f"Could not convert value: {value} to OrderType")
19
17
 
20
18
  @staticmethod
21
19
  def from_value(value):
@@ -1,5 +1,9 @@
1
- from .portfolio_configuration import PortfolioConfiguration
2
1
  from .portfolio import Portfolio
2
+ from .portfolio_configuration import PortfolioConfiguration
3
3
  from .portfolio_snapshot import PortfolioSnapshot
4
4
 
5
- __all__ = ["PortfolioConfiguration", "Portfolio", "PortfolioSnapshot"]
5
+ __all__ = [
6
+ "PortfolioConfiguration",
7
+ "Portfolio",
8
+ "PortfolioSnapshot",
9
+ ]
@@ -2,6 +2,37 @@ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
2
 
3
3
 
4
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
+ """
5
36
 
6
37
  def __init__(
7
38
  self,
@@ -9,6 +40,7 @@ class Portfolio(BaseModel):
9
40
  trading_symbol,
10
41
  net_size,
11
42
  unallocated,
43
+ initial_balance,
12
44
  market,
13
45
  realized=0,
14
46
  total_revenue=0,
@@ -16,21 +48,25 @@ class Portfolio(BaseModel):
16
48
  total_net_gain=0,
17
49
  total_trade_volume=0,
18
50
  created_at=None,
19
- updated_at=None
51
+ updated_at=None,
52
+ initialized=False,
20
53
  ):
21
54
  self.identifier = identifier
22
55
  self.updated_at = None
23
56
  self.trading_symbol = trading_symbol.upper()
24
57
  self.net_size = net_size
25
58
  self.unallocated = unallocated
59
+ self.initial_balance = initial_balance
26
60
  self.realized = realized
27
61
  self.total_revenue = total_revenue
28
62
  self.total_cost = total_cost
29
63
  self.total_net_gain = total_net_gain
30
64
  self.total_trade_volume = total_trade_volume
31
- self.market = market
65
+ self.market = market.upper()
32
66
  self.created_at = created_at
33
67
  self.updated_at = updated_at
68
+ self.initialized = initialized
69
+ self._allocated = None
34
70
 
35
71
  def __repr__(self):
36
72
  return self.repr(
@@ -40,9 +76,14 @@ class Portfolio(BaseModel):
40
76
  unallocated=self.unallocated,
41
77
  realized=self.realized,
42
78
  total_revenue=self.total_revenue,
43
- total_cost=self.total_cost
79
+ total_cost=self.total_cost,
80
+ market=self.market,
81
+ initial_balance=self.initial_balance
44
82
  )
45
83
 
84
+ def get_identifier(self):
85
+ return self.identifier
86
+
46
87
  def get_unallocated(self):
47
88
  return self.unallocated
48
89
 
@@ -72,3 +113,57 @@ class Portfolio(BaseModel):
72
113
 
73
114
  def get_trading_symbol(self):
74
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
+ }