investing-algorithm-framework 1.3.1__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 (282) hide show
  1. investing_algorithm_framework/__init__.py +195 -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 +31 -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 +2233 -264
  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/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/action_handlers/__init__.py +6 -3
  37. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
  41. investing_algorithm_framework/app/strategy.py +873 -52
  42. investing_algorithm_framework/app/task.py +5 -3
  43. investing_algorithm_framework/app/web/__init__.py +2 -1
  44. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  45. investing_algorithm_framework/app/web/controllers/orders.py +4 -3
  46. investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
  47. investing_algorithm_framework/app/web/controllers/positions.py +3 -3
  48. investing_algorithm_framework/app/web/create_app.py +4 -2
  49. investing_algorithm_framework/app/web/error_handler.py +1 -1
  50. investing_algorithm_framework/app/web/schemas/order.py +2 -2
  51. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  52. investing_algorithm_framework/cli/__init__.py +0 -0
  53. investing_algorithm_framework/cli/cli.py +231 -0
  54. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  55. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  56. investing_algorithm_framework/cli/initialize_app.py +603 -0
  57. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  58. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  59. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  60. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  61. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  62. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  63. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  64. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  65. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  66. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  67. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  68. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  69. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  70. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  71. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  72. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  73. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  74. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  75. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  76. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  77. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  78. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  79. investing_algorithm_framework/create_app.py +43 -9
  80. investing_algorithm_framework/dependency_container.py +121 -33
  81. investing_algorithm_framework/domain/__init__.py +109 -22
  82. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  83. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  84. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  87. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  88. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  92. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  93. investing_algorithm_framework/domain/config.py +60 -138
  94. investing_algorithm_framework/domain/constants.py +23 -34
  95. investing_algorithm_framework/domain/data_provider.py +334 -0
  96. investing_algorithm_framework/domain/data_structures.py +42 -0
  97. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  98. investing_algorithm_framework/domain/exceptions.py +51 -1
  99. investing_algorithm_framework/domain/models/__init__.py +29 -14
  100. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  101. investing_algorithm_framework/domain/models/base_model.py +3 -1
  102. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  104. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  105. investing_algorithm_framework/domain/models/event.py +35 -0
  106. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  107. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  108. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  109. investing_algorithm_framework/domain/models/order/order.py +243 -86
  110. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  111. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  112. investing_algorithm_framework/domain/models/portfolio/__init__.py +7 -2
  113. investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
  114. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
  115. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  116. investing_algorithm_framework/domain/models/position/__init__.py +3 -2
  117. investing_algorithm_framework/domain/models/position/position.py +29 -0
  118. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  119. investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
  120. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  121. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  122. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -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 +94 -98
  126. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +111 -2
  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 +11 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  132. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  133. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  134. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  135. investing_algorithm_framework/domain/order_executor.py +112 -0
  136. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  137. investing_algorithm_framework/domain/services/__init__.py +11 -0
  138. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  139. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  140. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  141. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  142. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  143. investing_algorithm_framework/domain/strategy.py +1 -29
  144. investing_algorithm_framework/domain/utils/__init__.py +16 -4
  145. investing_algorithm_framework/domain/utils/csv.py +22 -0
  146. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  147. investing_algorithm_framework/domain/utils/dates.py +57 -0
  148. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  149. investing_algorithm_framework/domain/utils/polars.py +53 -0
  150. investing_algorithm_framework/domain/utils/random.py +29 -0
  151. investing_algorithm_framework/download_data.py +244 -0
  152. investing_algorithm_framework/infrastructure/__init__.py +39 -11
  153. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  154. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  155. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  156. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  157. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  158. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +87 -13
  159. investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
  160. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  161. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  162. investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
  163. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  164. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  165. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +3 -2
  166. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  167. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
  168. investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
  169. investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
  170. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  171. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  172. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  173. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  174. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  175. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  176. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  177. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  178. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  179. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  180. investing_algorithm_framework/infrastructure/repositories/__init__.py +13 -5
  181. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  182. investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
  183. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  184. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  185. investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
  186. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  187. investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
  188. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  189. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  190. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  191. investing_algorithm_framework/infrastructure/services/__init__.py +9 -2
  192. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  193. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  194. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  195. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  196. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  197. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  198. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  199. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  200. investing_algorithm_framework/services/__init__.py +127 -10
  201. investing_algorithm_framework/services/configuration_service.py +95 -0
  202. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  203. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  204. investing_algorithm_framework/services/market_credential_service.py +40 -0
  205. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  206. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  207. investing_algorithm_framework/services/metrics/beta.py +0 -0
  208. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  209. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  210. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  211. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  212. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  213. investing_algorithm_framework/services/metrics/generate.py +358 -0
  214. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  215. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  216. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  217. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  218. investing_algorithm_framework/services/metrics/returns.py +452 -0
  219. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  220. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  221. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  222. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  223. investing_algorithm_framework/services/metrics/trades.py +473 -0
  224. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  225. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  226. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  227. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  228. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  229. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  230. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  231. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  232. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  233. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  234. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  235. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  236. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  237. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  238. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  239. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  240. investing_algorithm_framework/services/positions/__init__.py +7 -0
  241. investing_algorithm_framework/services/positions/position_service.py +210 -0
  242. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  243. investing_algorithm_framework/services/repository_service.py +8 -2
  244. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  245. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  246. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  247. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  248. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  249. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  250. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  251. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  252. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  253. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  254. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  255. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  256. investing_algorithm_framework/app/algorithm.py +0 -410
  257. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  258. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  259. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
  260. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  261. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  262. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  263. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  264. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
  265. investing_algorithm_framework/domain/singleton.py +0 -9
  266. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  267. investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
  268. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  269. investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
  270. investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
  271. investing_algorithm_framework/services/market_data_service.py +0 -75
  272. investing_algorithm_framework/services/order_service.py +0 -464
  273. investing_algorithm_framework/services/portfolio_service.py +0 -105
  274. investing_algorithm_framework/services/position_cost_service.py +0 -5
  275. investing_algorithm_framework/services/position_service.py +0 -50
  276. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
  277. investing_algorithm_framework/setup_logging.py +0 -40
  278. investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
  279. investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
  280. investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
  281. investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
  282. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -0,0 +1,389 @@
1
+ from dateutil.parser import parse
2
+ from datetime import timezone
3
+
4
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
5
+ from investing_algorithm_framework.domain.models.order import OrderSide, Order
6
+ from investing_algorithm_framework.domain.models.trade.trade_status import \
7
+ TradeStatus
8
+ from investing_algorithm_framework.domain.models.trade.trade_stop_loss import \
9
+ TradeStopLoss
10
+ from investing_algorithm_framework.domain.models.trade\
11
+ .trade_take_profit import TradeTakeProfit
12
+
13
+
14
+ class Trade(BaseModel):
15
+ """
16
+ Trade model
17
+
18
+ A trade is a combination of a buy and sell order that has been opened or
19
+ closed.
20
+
21
+ A trade is considered opened when a buy order is executed and there is
22
+ no corresponding sell order. A trade is considered closed when a sell
23
+ order is executed and the amount of the sell order is equal or larger
24
+ to the amount of the buy order.
25
+
26
+ A single sell order can close multiple buy orders. Also, a single
27
+ buy order can be closed by multiple sell orders.
28
+
29
+ Attributes:
30
+ orders: str, the id of the buy order
31
+ target_symbol: str, the target symbol of the trade
32
+ trading_symbol: str, the trading symbol of the trade
33
+ closed_at: datetime, the datetime when the trade was closed
34
+ amount: float, the amount of the trade
35
+ available_amount: float, the available amount of the trade
36
+ remaining: float, the remaining amount that is not filled by the
37
+ buy order that opened the trade.
38
+ filled_amount: float, the filled amount of the trade by the buy
39
+ order that opened the trade.
40
+ net_gain: float, the net gain of the trade
41
+ last_reported_price: float, the last reported price of the trade
42
+ last_reported_price_datetime: datetime, the datetime when the last
43
+ reported price was reported
44
+ updated_at: datetime, the datetime when the trade was last updated
45
+ status: str, the status of the trade
46
+ metadata: dict, the metadata of the trade, this can be used to store
47
+ additional information about the trade.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ id,
53
+ orders,
54
+ target_symbol,
55
+ trading_symbol,
56
+ closed_at,
57
+ opened_at,
58
+ open_price,
59
+ amount,
60
+ available_amount,
61
+ cost,
62
+ remaining,
63
+ filled_amount,
64
+ status,
65
+ net_gain=0,
66
+ last_reported_price=None,
67
+ last_reported_price_datetime=None,
68
+ high_water_mark=None,
69
+ high_water_mark_datetime=None,
70
+ updated_at=None,
71
+ stop_losses=None,
72
+ take_profits=None,
73
+ metadata=None,
74
+ ):
75
+ self.id = id
76
+ self.orders = orders
77
+ self.target_symbol = target_symbol
78
+ self.trading_symbol = trading_symbol
79
+ self.closed_at = closed_at
80
+ self.opened_at = opened_at
81
+ self.open_price = open_price
82
+ self.amount = amount
83
+ self.available_amount = available_amount
84
+ self.cost = cost
85
+ self.remaining = remaining
86
+ self.filled_amount = filled_amount
87
+ self.net_gain = net_gain
88
+ self.last_reported_price = last_reported_price
89
+ self.last_reported_price_datetime = last_reported_price_datetime
90
+ self.high_water_mark = high_water_mark
91
+ self.high_water_mark_datetime = high_water_mark_datetime
92
+ self.status = TradeStatus.from_value(status).value
93
+ self.updated_at = updated_at
94
+ self.stop_losses = stop_losses
95
+ self.take_profits = take_profits
96
+ self.metadata = metadata if metadata is not None else {}
97
+
98
+ def update(self, data):
99
+
100
+ if "status" in data:
101
+ self.status = TradeStatus.from_value(data["status"]).value
102
+
103
+ if TradeStatus.CLOSED.equals(self.status):
104
+
105
+ # Set all stop losses to inactive
106
+ if self.stop_losses is not None:
107
+ for stop_loss in self.stop_losses:
108
+ stop_loss.active = False
109
+
110
+ # set all take profits to inactive
111
+ if self.take_profits is not None:
112
+ for take_profit in self.take_profits:
113
+ take_profit.active = False
114
+
115
+ if "last_reported_price" in data:
116
+ self.last_reported_price = data["last_reported_price"]
117
+
118
+ if self.high_water_mark is None:
119
+ self.high_water_mark = data["last_reported_price"]
120
+ self.high_water_mark_datetime = \
121
+ data["last_reported_price_datetime"]
122
+ else:
123
+
124
+ if data["last_reported_price"] > self.high_water_mark:
125
+ self.high_water_mark = data["last_reported_price"]
126
+ self.high_water_mark_datetime = \
127
+ data["last_reported_price_datetime"]
128
+
129
+ return super().update(data)
130
+
131
+ @property
132
+ def closed_prices(self):
133
+ return [
134
+ order.price for order in self.orders
135
+ if order.order_side == OrderSide.SELL.value
136
+ ]
137
+
138
+ @property
139
+ def buy_order(self):
140
+
141
+ if self.orders is None:
142
+ return
143
+
144
+ return [
145
+ order for order in self.orders
146
+ if order.order_side == OrderSide.BUY.value
147
+ ][0]
148
+
149
+ @property
150
+ def symbol(self):
151
+ return f"{self.target_symbol.upper()}/{self.trading_symbol.upper()}"
152
+
153
+ @property
154
+ def duration(self):
155
+ """
156
+ Calculate the duration of the trade in hours.
157
+
158
+ Returns:
159
+ float: The duration of the trade in hours.
160
+ """
161
+ if TradeStatus.CLOSED.equals(self.status):
162
+ # Get the total hours between the closed and opened datetime
163
+ diff = self.closed_at - self.opened_at
164
+ return diff.total_seconds() / 3600
165
+
166
+ if self.opened_at is None:
167
+ return None
168
+
169
+ if self.updated_at is None:
170
+ return None
171
+
172
+ diff = self.updated_at - self.opened_at
173
+ return diff.total_seconds() / 3600
174
+
175
+ @property
176
+ def size(self):
177
+ return self.amount * self.open_price
178
+
179
+ @property
180
+ def change(self):
181
+ """
182
+ Property to calculate the change in value of the trade.
183
+
184
+ This is the difference between the current value of the trade
185
+ and the cost of the trade.
186
+ """
187
+ if TradeStatus.CLOSED.equals(self.status):
188
+ return self.net_gain
189
+
190
+ if self.last_reported_price is None:
191
+ return 0
192
+
193
+ if self.remaining is None or self.remaining == 0:
194
+ amount = self.amount
195
+ else:
196
+ amount = self.amount - self.remaining
197
+
198
+ cost = amount * self.open_price
199
+ gain = (amount * self.last_reported_price) - cost
200
+ return gain
201
+
202
+ @property
203
+ def net_gain_absolute(self):
204
+
205
+ if TradeStatus.CLOSED.equals(self.status):
206
+ return self.net_gain
207
+ else:
208
+ gain = 0
209
+
210
+ if self.last_reported_price is not None:
211
+ gain = (
212
+ self.available_amount *
213
+ (self.last_reported_price - self.open_price)
214
+ )
215
+
216
+ gain += self.net_gain
217
+ return gain
218
+
219
+ @property
220
+ def net_gain_percentage(self):
221
+
222
+ if TradeStatus.CLOSED.equals(self.status):
223
+
224
+ if self.cost != 0:
225
+ return (self.net_gain / self.cost) * 100
226
+
227
+ else:
228
+ gain = 0
229
+
230
+ if self.last_reported_price is not None:
231
+ gain = (
232
+ self.available_amount *
233
+ (self.last_reported_price - self.open_price)
234
+ )
235
+
236
+ gain += self.net_gain
237
+
238
+ if self.cost != 0:
239
+ return (gain / self.cost) * 100
240
+
241
+ return 0
242
+
243
+ @property
244
+ def percentage_change(self):
245
+
246
+ if TradeStatus.CLOSED.equals(self.status):
247
+
248
+ if self.cost != 0:
249
+ return (self.net_gain / self.cost) * 100
250
+
251
+ if self.last_reported_price is None:
252
+ return 0
253
+
254
+ cost = self.available_amount * self.open_price
255
+ gain = (self.available_amount * self.last_reported_price) - cost
256
+ gain += self.net_gain
257
+
258
+ if cost != 0:
259
+ return (gain / cost) * 100
260
+
261
+ return 0
262
+
263
+ def to_dict(self, datetime_format=None):
264
+ def ensure_iso(value):
265
+ if hasattr(value, "isoformat"):
266
+ if value.tzinfo is None:
267
+ value = value.replace(tzinfo=timezone.utc)
268
+ return value.isoformat()
269
+ return value
270
+
271
+ opened_at = ensure_iso(self.opened_at) if self.opened_at else None
272
+ closed_at = ensure_iso(self.closed_at) if self.closed_at else None
273
+ updated_at = ensure_iso(self.updated_at) if self.updated_at else None
274
+
275
+ # Ensure status is a string
276
+ self.status = TradeStatus.from_value(self.status).value
277
+
278
+ return {
279
+ "id": self.id,
280
+ "orders": [
281
+ order.to_dict(datetime_format=datetime_format)
282
+ for order in self.orders
283
+ ],
284
+ "target_symbol": self.target_symbol,
285
+ "trading_symbol": self.trading_symbol,
286
+ "status": self.status,
287
+ "amount": self.amount,
288
+ "remaining": self.remaining if self.remaining is not None else 0,
289
+ "open_price": self.open_price,
290
+ "last_reported_price": self.last_reported_price,
291
+ "opened_at": opened_at,
292
+ "closed_at": closed_at,
293
+ "updated_at": updated_at,
294
+ "net_gain": self.net_gain if self.net_gain is not None else 0,
295
+ "cost": self.cost if self.cost is not None else 0,
296
+ "stop_losses": [
297
+ stop_loss.to_dict(datetime_format=datetime_format)
298
+ for stop_loss in self.stop_losses
299
+ ] if self.stop_losses else None,
300
+ "take_profits": [
301
+ take_profit.to_dict(datetime_format=datetime_format)
302
+ for take_profit in self.take_profits
303
+ ] if self.take_profits else None,
304
+ "filled_amount": self.filled_amount,
305
+ "available_amount": self.available_amount,
306
+ "metadata": self.metadata if self.metadata else {},
307
+ }
308
+
309
+ @staticmethod
310
+ def from_dict(data):
311
+ opened_at = None
312
+ closed_at = None
313
+ updated_at = None
314
+ stop_losses = None
315
+ take_profits = None
316
+ orders = None
317
+
318
+ if "opened_at" in data and data["opened_at"] is not None:
319
+ opened_at = parse(data["opened_at"])
320
+
321
+ if "closed_at" in data and data["closed_at"] is not None:
322
+ closed_at = parse(data["closed_at"])
323
+
324
+ if "updated_at" in data and data["updated_at"] is not None:
325
+ updated_at = parse(data["updated_at"])
326
+
327
+ if "stop_losses" in data and data["stop_losses"] is not None:
328
+ stop_losses = [
329
+ TradeStopLoss.from_dict(stop_loss)
330
+ for stop_loss in data["stop_losses"]
331
+ ]
332
+
333
+ if "take_profits" in data and data["take_profits"] is not None:
334
+ take_profits = [
335
+ TradeTakeProfit.from_dict(take_profit)
336
+ for take_profit in data["take_profits"]
337
+ ]
338
+
339
+ if "orders" in data and data["orders"] is not None:
340
+ orders = [
341
+ Order.from_dict(order)
342
+ for order in data["orders"]
343
+ ]
344
+
345
+ return Trade(
346
+ id=data.get("id", None),
347
+ orders=orders,
348
+ target_symbol=data.get("target_symbol", ""),
349
+ trading_symbol=data.get("trading_symbol", ""),
350
+ amount=data.get("amount", 0),
351
+ open_price=data.get("open_price", 0),
352
+ opened_at=opened_at,
353
+ closed_at=closed_at,
354
+ filled_amount=data.get("filled_amount", 0),
355
+ available_amount=data.get("available_amount", 0),
356
+ remaining=data.get("remaining", 0),
357
+ net_gain=data.get("net_gain", 0),
358
+ last_reported_price=data.get("last_reported_price"),
359
+ status=TradeStatus.from_value(data.get("status", "OPEN")).value,
360
+ cost=data.get("cost", 0),
361
+ updated_at=updated_at,
362
+ stop_losses=stop_losses,
363
+ take_profits=take_profits,
364
+ metadata=data.get("metadata", {}),
365
+ )
366
+
367
+ def __repr__(self):
368
+ return self.repr(
369
+ id=self.id,
370
+ symbol=self.symbol,
371
+ target_symbol=self.target_symbol,
372
+ trading_symbol=self.trading_symbol,
373
+ status=self.status,
374
+ amount=self.amount,
375
+ available_amount=self.available_amount,
376
+ filled_amount=self.filled_amount,
377
+ remaining=self.remaining,
378
+ open_price=self.open_price,
379
+ opened_at=self.opened_at,
380
+ closed_at=self.closed_at,
381
+ net_gain=self.net_gain,
382
+ last_reported_price=self.last_reported_price,
383
+ updated_at=self.updated_at,
384
+ metadata=self.metadata,
385
+ )
386
+
387
+ def __lt__(self, other):
388
+ # Define the less-than comparison based on created_at attribute
389
+ return self.opened_at < other.opened_at
@@ -0,0 +1,40 @@
1
+ from enum import Enum
2
+ from investing_algorithm_framework.domain.exceptions import \
3
+ OperationalException
4
+
5
+
6
+ class TradeStatus(Enum):
7
+ CREATED = "CREATED"
8
+ OPEN = "OPEN"
9
+ CLOSED = "CLOSED"
10
+
11
+ @staticmethod
12
+ def from_string(value: str):
13
+
14
+ if isinstance(value, str):
15
+ for status in TradeStatus:
16
+
17
+ if value.upper() == status.value:
18
+ return status
19
+
20
+ raise OperationalException(
21
+ f"Could not convert value: '{value}' to TradeStatus"
22
+ )
23
+
24
+ @staticmethod
25
+ def from_value(value):
26
+
27
+ if isinstance(value, TradeStatus):
28
+ for status in TradeStatus:
29
+
30
+ if value == status:
31
+ return status
32
+ elif isinstance(value, str):
33
+ return TradeStatus.from_string(value)
34
+
35
+ raise OperationalException(
36
+ f"Could not convert value: {value} to TradeStatus"
37
+ )
38
+
39
+ def equals(self, other):
40
+ return TradeStatus.from_value(other) == self