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,332 @@
1
+ from dateutil.parser import parse
2
+ from datetime import timezone
3
+ from datetime import datetime
4
+
5
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
6
+
7
+
8
+ class TradeStopLoss(BaseModel):
9
+ """
10
+ TradeStopLoss represents a stop loss strategy for a trade.
11
+
12
+ if trailing is set to False, the stop loss price is calculated as follows:
13
+ You buy a stock at $100.
14
+ You set a 5% stop loss, meaning you will sell if
15
+ the price drops to $95.
16
+ If the price rises to $120, the stop loss is not triggered.
17
+ But if the price keeps falling to $95, the stop loss triggers,
18
+ and you exit with a $5 loss.
19
+
20
+ if trailing is set to True, the stop loss price is
21
+ calculated as follows:
22
+ You buy a stock at $100.
23
+ You set a 5% trailing stop loss, meaning you will sell if
24
+ the price drops 5% from its peak at $96
25
+ If the price rises to $120, the stop loss adjusts
26
+ to $114 (5% below $120).
27
+ If the price falls to $114, the position is
28
+ closed, securing a $14 profit.
29
+ But if the price keeps rising to $150, the stop
30
+ loss moves up to $142.50.
31
+ If the price drops from $150 to $142.50, the stop
32
+ loss triggers, and you exit with a $42.50 profit.
33
+
34
+ Attributes:
35
+ - trade (Trade): the trade that the take profit is for
36
+ - trailing (bool): whether the stop loss is trailing or fixed
37
+ - percentage (float): the stop loss percentage
38
+ - sell_percentage (float): the percentage of the trade to sell when the
39
+ take profit is hit. Default is 100% of the trade. If the
40
+ take profit percentage is lower than 100% a check must
41
+ be made that the combined sell percentage of all
42
+ take profits is less or equal than 100%.
43
+ - open_price (float): the price at which the trade was opened
44
+ - high_water_mark_date (str): the date at which the high water mark
45
+ was reached
46
+ - active (bool): whether the stop loss is active
47
+ - sell_amount (float): the amount to sell when the stop loss triggers
48
+ - sold_amount (float): the amount that has been sold
49
+ - high_water_mark (float) the highest price of the trade
50
+ - stop_loss_price (float) the price at which the stop loss triggers
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ trade_id: int,
56
+ percentage: float,
57
+ open_price: float,
58
+ trailing: bool = False,
59
+ total_amount_trade: float = None,
60
+ sell_percentage: float = 100,
61
+ active: bool = True,
62
+ triggered: bool = False,
63
+ triggered_at: datetime = None,
64
+ sell_prices: str = None,
65
+ sell_dates: str = None,
66
+ sell_amount: float = None,
67
+ high_water_mark: float = None,
68
+ high_water_mark_date: str = None,
69
+ created_at: datetime = None,
70
+ updated_at: datetime = None
71
+ ):
72
+ self.trade_id = trade_id
73
+ self.trailing = trailing
74
+ self.percentage = percentage
75
+ self.triggered = triggered
76
+ self.triggered_at = triggered_at
77
+ self.sell_percentage = sell_percentage
78
+ self.high_water_mark = high_water_mark
79
+ self.high_water_mark_date = high_water_mark_date
80
+ self.open_price = open_price
81
+ self.created_at = created_at
82
+ self.updated_at = updated_at
83
+
84
+ if high_water_mark is None:
85
+ self.high_water_mark = open_price
86
+ self.stop_loss_price = self.open_price * \
87
+ (1 - (self.percentage / 100))
88
+ self.high_water_mark_date = created_at
89
+ else:
90
+ self.stop_loss_price = high_water_mark * \
91
+ (1 - (self.percentage / 100))
92
+
93
+ if sell_amount is not None:
94
+ self.sell_amount = sell_amount
95
+ else:
96
+ self.sell_amount = total_amount_trade * \
97
+ (self.sell_percentage / 100)
98
+
99
+ self.sold_amount = 0
100
+ self.active = active
101
+ self.sell_prices = sell_prices
102
+ self.sell_dates = sell_dates
103
+
104
+ def update_with_last_reported_price(self, current_price: float, date):
105
+ """
106
+ Function to update the take profit price based on the last
107
+ reported price.
108
+ The take profit price is only updated when the trade risk
109
+ type is trailing.
110
+ The take profit price is updated based on the current price
111
+ and the percentage of the take profit.
112
+
113
+ Args:
114
+ current_price (float): the last reported price of the trade
115
+ date (datetime): the date of the last reported price
116
+
117
+ Returns:
118
+ None
119
+ """
120
+
121
+ if not self.active or self.sold_amount == self.sell_amount:
122
+ return
123
+
124
+ if not self.trailing:
125
+ # Check if the current price is less than the high water mark
126
+ if current_price > self.high_water_mark:
127
+ self.high_water_mark = current_price
128
+ return
129
+ else:
130
+ # Check if the current price is less than the stop loss price
131
+ if current_price <= self.stop_loss_price:
132
+ return
133
+ elif current_price > self.high_water_mark:
134
+ self.high_water_mark = current_price
135
+ self.high_water_mark_date = date
136
+ self.stop_loss_price = self.high_water_mark * \
137
+ (1 - (self.percentage / 100))
138
+
139
+ def has_triggered(self, current_price: float) -> bool:
140
+ """
141
+ Function to check if the stop loss has triggered.
142
+ Function always returns False if the stop loss is not active or
143
+ the sold amount is equal to the sell amount.
144
+
145
+ Args:
146
+ current_price: float - the current price of the trade
147
+
148
+ Returns:
149
+ bool - True if the stop loss has triggered, False otherwise
150
+ """
151
+ if not self.active or self.sold_amount == self.sell_amount:
152
+ return False
153
+
154
+ if not self.trailing:
155
+ # Check if the current price is less than the high watermark
156
+ return current_price <= self.stop_loss_price
157
+ else:
158
+ # Check if the current price is less than the stop loss price
159
+ if current_price <= self.stop_loss_price:
160
+ return True
161
+ elif current_price > self.high_water_mark:
162
+ self.high_water_mark = current_price
163
+ self.stop_loss_price = self.high_water_mark * \
164
+ (1 - (self.percentage / 100))
165
+
166
+ return False
167
+
168
+ def get_sell_amount(self) -> float:
169
+ """
170
+ Function to calculate the amount to sell based on the
171
+ sell percentage and the remaining amount of the trade.
172
+ Keep in mind the moment the take profit triggers, the remaining
173
+ amount of the trade is used to calculate the sell amount.
174
+ If the remaining amount is smaller than the trade amount, the
175
+ trade stop loss stays active. The client that uses the
176
+ trade stop loss is responsible for setting the trade stop
177
+ loss to inactive.
178
+ """
179
+
180
+ if not self.active:
181
+ return 0
182
+
183
+ return self.sell_amount - self.sold_amount
184
+
185
+ def add_sell_price(self, price: float, date: str):
186
+ """
187
+ Function to add a sell price to the list of sell prices.
188
+ The sell price is added to the list of sell prices and the
189
+ date is added to the list of sell dates.
190
+
191
+ Args:
192
+ price: float - the price at which the trade was sold
193
+ date: str - the date at which the trade was sold
194
+
195
+ Returns:
196
+ None
197
+ """
198
+ if self.sell_prices is None:
199
+ self.sell_prices = str(price)
200
+ self.sell_dates = str(date)
201
+ else:
202
+ self.sell_prices += f", {price}"
203
+ self.sell_dates += f", {date}"
204
+
205
+ def remove_sell_price(self, price: float, date: str):
206
+ """
207
+ Function to remove a sell price from the list of sell prices.
208
+ The sell price is removed from the list of sell prices and the
209
+ date is removed from the list of sell dates.
210
+
211
+ Args:
212
+ price: float - the price at which the trade was sold
213
+ date: str - the date at which the trade was sold
214
+
215
+ Returns:
216
+ None
217
+ """
218
+ if self.sell_prices is not None:
219
+
220
+ # Split the sell prices into a list and convert to float
221
+ sell_prices_list = self.sell_prices.split(", ")
222
+ sell_prices_list = [float(p) for p in sell_prices_list]
223
+
224
+ if price in sell_prices_list:
225
+ sell_prices_list.remove(price)
226
+ self.sell_prices = ", ".join(sell_prices_list)
227
+
228
+ if self.sell_prices == "":
229
+ self.sell_prices = None
230
+
231
+ # Split the sell dates into a list
232
+ sell_dates_list = self.sell_dates.split(", ")
233
+ if date in sell_dates_list:
234
+ sell_dates_list.remove(date)
235
+ self.sell_dates = ", ".join(sell_dates_list)
236
+ else:
237
+ self.sell_prices = None
238
+ self.sell_dates = None
239
+
240
+ def to_dict(self, datetime_format=None):
241
+ def ensure_iso(value):
242
+
243
+ if value is None:
244
+ return value
245
+
246
+ if hasattr(value, "isoformat"):
247
+ if value.tzinfo is None:
248
+ value = value.replace(tzinfo=timezone.utc)
249
+ return value.isoformat()
250
+ return value
251
+
252
+ return {
253
+ "trade_id": self.trade_id,
254
+ "trailing": self.trailing,
255
+ "percentage": self.percentage,
256
+ "open_price": self.open_price,
257
+ "sell_percentage": self.sell_percentage,
258
+ "high_water_mark": self.high_water_mark,
259
+ "high_water_mark_date": self.high_water_mark_date,
260
+ "triggered": self.triggered,
261
+ "triggered_at": ensure_iso(getattr(self, "triggered_at", None)),
262
+ "stop_loss_price": self.stop_loss_price,
263
+ "sell_amount": self.sell_amount,
264
+ "sold_amount": self.sold_amount,
265
+ "active": self.active,
266
+ "sell_prices": self.sell_prices,
267
+ "created_at": ensure_iso(self.created_at),
268
+ "updated_at": ensure_iso(self.updated_at)
269
+ }
270
+
271
+ @staticmethod
272
+ def from_dict(data: dict):
273
+ created_at = parse(data["created_at"]) \
274
+ if data.get("created_at") is not None else None
275
+ updated_at = parse(data["updated_at"]) \
276
+ if data.get("updated_at") is not None else None
277
+ triggered_at = parse(data["triggered_at"]) \
278
+ if data.get("triggered_at") is not None else None
279
+ high_water_mark_date = parse(data.get("high_water_mark_date")) \
280
+ if data.get("high_water_mark_date") is not None else None
281
+
282
+ # Make sure all the dates are timezone utc aware
283
+ if created_at and created_at.tzinfo is None:
284
+ created_at = created_at.replace(tzinfo=timezone.utc)
285
+ if updated_at and updated_at.tzinfo is None:
286
+ updated_at = updated_at.replace(tzinfo=timezone.utc)
287
+ if triggered_at and triggered_at.tzinfo is None:
288
+ triggered_at = triggered_at.replace(tzinfo=timezone.utc)
289
+ if high_water_mark_date and high_water_mark_date.tzinfo is None:
290
+ high_water_mark_date = high_water_mark_date.replace(
291
+ tzinfo=timezone.utc
292
+ )
293
+
294
+ return TradeStopLoss(
295
+ trade_id=data.get("trade_id"),
296
+ trailing=data.get("trailing"),
297
+ percentage=data.get("percentage"),
298
+ open_price=data.get("open_price"),
299
+ total_amount_trade=data.get("sell_amount", 0) /
300
+ (data.get("sell_percentage", 100) / 100),
301
+ sell_percentage=data.get("sell_percentage", 100),
302
+ active=data.get("active", True),
303
+ sell_prices=data.get("sell_prices"),
304
+ sell_dates=data.get("sell_dates"),
305
+ sell_amount=data.get("sell_amount"),
306
+ high_water_mark=data.get("high_water_mark"),
307
+ high_water_mark_date=high_water_mark_date,
308
+ triggered=data.get("triggered", False),
309
+ triggered_at=triggered_at,
310
+ created_at=created_at,
311
+ updated_at=updated_at
312
+ )
313
+
314
+ def __repr__(self):
315
+ return self.repr(
316
+ trade_id=self.trade_id,
317
+ trailing=self.trailing,
318
+ percentage=self.percentage,
319
+ sell_percentage=self.sell_percentage,
320
+ high_water_mark=self.high_water_mark,
321
+ high_water_mark_date=self.high_water_mark_date,
322
+ open_price=self.open_price,
323
+ stop_loss_price=self.stop_loss_price,
324
+ sell_amount=self.sell_amount,
325
+ sold_amount=self.sold_amount,
326
+ sell_prices=self.sell_prices,
327
+ active=self.active,
328
+ triggered=self.triggered,
329
+ triggered_at=self.triggered_at,
330
+ created_at=self.created_at,
331
+ updated_at=self.updated_at
332
+ )
@@ -0,0 +1,365 @@
1
+ from datetime import timezone, datetime
2
+ from dateutil.parser import parse
3
+
4
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
5
+
6
+
7
+ class TradeTakeProfit(BaseModel):
8
+ """
9
+ TradeTakeProfit represents a take profit strategy for a trade.
10
+
11
+ if trailing is set to False, the take profit price is
12
+ calculated as follows:
13
+ You buy a stock at $100.
14
+ You set a 5% take profit, meaning you will sell if the price
15
+ rises to $105.
16
+ If the price rises to $120, the take profit triggers,
17
+ and you exit with a $20 profit.
18
+ But if the price keeps falling below $105, the take profit is not
19
+ triggered.
20
+
21
+ if trailing is set to True, the take profit price is
22
+ calculated as follows:
23
+ You buy a stock at $100.
24
+ You set a 5% trailing take profit, the moment the price rises
25
+ 5% the initial take profit mark will be set. This means you
26
+ will set the take_profit_price initially at none and
27
+ only if the price hits $105, you will set the
28
+ take_profit_price to $105.
29
+ if the price drops below $105, the take profit is triggered.
30
+ If the price rises to $120, the take profit adjusts to
31
+ $114 (5% below $120).
32
+ If the price falls to $114, the position is closed,
33
+ securing a $14 profit.
34
+ But if the price keeps rising to $150, the take profit
35
+ moves up to $142.50.
36
+
37
+ Attributes:
38
+ - trade (Trade): the trade that the take profit is for
39
+ - trailing (bool): whether the take profit is trailing or fixed
40
+ - percentage (float): the stop loss percentage
41
+ - sell_percentage (float): the percentage of the trade to sell when the
42
+ take profit is hit. Default is 100% of the trade. If the
43
+ take profit percentage is lower than 100% a check must
44
+ be made that the combined sell percentage of all
45
+ take profits is less or equal than 100%.
46
+ - open_price (float): the price at which the trade was opened
47
+ - take_profit_price (float): the price at which the take profit
48
+ triggers
49
+ - high_water_mark_date (str): the date at which the high water mark
50
+ was reached
51
+ - active (bool): whether the take profit is active
52
+ - triggered (bool): whether the take profit has been triggered
53
+ - sell_amount (float): the amount to sell when the stop loss triggers
54
+ - sold_amount (float): the amount that has been sold
55
+ - high_water_mark (float) the highest price of the trade
56
+ - stop_loss_price (float) the price at which the stop loss triggers
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ trade_id: int,
62
+ percentage: float,
63
+ open_price: float,
64
+ trailing: bool = False,
65
+ total_amount_trade: float = None,
66
+ sell_percentage: float = 100,
67
+ active: bool = True,
68
+ triggered: bool = False,
69
+ triggered_at: datetime = None,
70
+ sell_prices: str = None,
71
+ sell_dates: str = None,
72
+ sell_amount: float = None,
73
+ high_water_mark: float = None,
74
+ high_water_mark_date: str = None,
75
+ created_at: datetime = None,
76
+ updated_at: datetime = None
77
+ ):
78
+ self.trade_id = trade_id
79
+ self.trailing = trailing
80
+ self.percentage = percentage
81
+ self.sell_percentage = sell_percentage
82
+ self.triggered = triggered
83
+ self.triggered_at = triggered_at
84
+ self.high_water_mark = high_water_mark
85
+ self.high_water_mark_date = high_water_mark_date
86
+ self.open_price = open_price
87
+ self.created_at = created_at
88
+ self.updated_at = updated_at
89
+
90
+ if high_water_mark is None and not self.trailing:
91
+ self.take_profit_price = self.open_price * \
92
+ (1 + (self.percentage / 100))
93
+ else:
94
+ self.take_profit_price = None
95
+
96
+ if sell_amount is not None:
97
+ self.sell_amount = sell_amount
98
+ else:
99
+ self.sell_amount = total_amount_trade * \
100
+ (self.sell_percentage / 100)
101
+ self.sold_amount = 0
102
+ self.active = active
103
+ self.sell_prices = sell_prices
104
+ self.sell_dates = sell_dates
105
+
106
+ def update_with_last_reported_price(self, current_price: float, date):
107
+ """
108
+ Function to update the take profit price based on
109
+ the last reported price.
110
+ For fixed take profits: track the high water mark when price
111
+ exceeds the take profit price.
112
+ For trailing take profits: update the take profit price based on
113
+ the current price and the percentage of the take profit.
114
+
115
+ Args:
116
+ current_price: float - the last reported price of the trade
117
+ date: the date of the price update
118
+ """
119
+
120
+ if not self.trailing:
121
+ # Fixed take profit: track high watermark
122
+ if current_price >= self.take_profit_price:
123
+ if (self.high_water_mark is None
124
+ or current_price > self.high_water_mark):
125
+ self.high_water_mark = current_price
126
+ self.high_water_mark_date = date
127
+ return
128
+
129
+ # Trailing take profit logic
130
+ if self.high_water_mark is None:
131
+ # High water mark not set yet
132
+ # Calculate the initial take profit threshold
133
+ initial_threshold = self.open_price * (1 + (self.percentage / 100))
134
+
135
+ # Wait for price to reach the initial take profit threshold
136
+ if current_price >= initial_threshold:
137
+ # Initial threshold reached, set high watermark
138
+ self.high_water_mark = current_price
139
+ self.high_water_mark_date = date
140
+ # Calculate new take profit price based on high watermark
141
+ self.take_profit_price = self.high_water_mark * \
142
+ (1 - (self.percentage / 100))
143
+ else:
144
+ # High watermark is set, check for updates
145
+ # Check if price has risen above high watermark (adjust upward)
146
+ if current_price > self.high_water_mark:
147
+ self.high_water_mark = current_price
148
+ self.high_water_mark_date = date
149
+ # Recalculate take profit price based on new high water mark
150
+ new_take_profit_price = self.high_water_mark * \
151
+ (1 - (self.percentage / 100))
152
+ # Update take profit price if it's higher than current
153
+ if new_take_profit_price > self.take_profit_price:
154
+ self.take_profit_price = new_take_profit_price
155
+
156
+ def has_triggered(self, current_price: float = None) -> bool:
157
+
158
+ if not self.trailing:
159
+ # Fixed take profit: trigger when price reaches take_profit_price
160
+ return current_price >= self.take_profit_price
161
+ else:
162
+ # Trailing take profit logic
163
+ if self.high_water_mark is None:
164
+ # High water mark not set yet
165
+ # Calculate the initial take profit threshold
166
+ # (open_price * (1 + percentage))
167
+ initial_threshold = (self.open_price
168
+ * (1 + (self.percentage / 100)))
169
+
170
+ # Wait for price to reach the initial take profit threshold
171
+ if current_price >= initial_threshold:
172
+ # Initial threshold reached, set high water mark
173
+ self.high_water_mark = current_price
174
+ # Calculate new take profit price based on high water mark
175
+ # This is the pullback level
176
+ # (high_water_mark * (1 - percentage))
177
+ self.take_profit_price = self.high_water_mark * \
178
+ (1 - (self.percentage / 100))
179
+ # Don't trigger yet, wait for pullback
180
+ return False
181
+ else:
182
+ # High watermark is set, check for triggers and updates
183
+
184
+ # Check if price has pulled back below take profit
185
+ # price (trigger condition)
186
+ if current_price < self.take_profit_price:
187
+ return True
188
+
189
+ # Check if price has risen above high
190
+ # water mark (adjust upward)
191
+ if current_price > self.high_water_mark:
192
+ self.high_water_mark = current_price
193
+ # Recalculate take profit price based on
194
+ # new high water mark
195
+ new_take_profit_price = self.high_water_mark * \
196
+ (1 - (self.percentage / 100))
197
+ # Update take profit price if it's higher than current
198
+ if new_take_profit_price > self.take_profit_price:
199
+ self.take_profit_price = new_take_profit_price
200
+
201
+ return False
202
+
203
+ def get_sell_amount(self) -> float:
204
+ """
205
+ Function to calculate the amount to sell based on the
206
+ sell percentage and the remaining amount of the trade.
207
+ Keep in mind the moment the take profit triggers, the remaining
208
+ amount of the trade is used to calculate the sell amount.
209
+ If the remaining amount is smaller than the trade amount, the
210
+ trade stop loss stays active. The client that uses the
211
+ trade stop loss is responsible for setting the trade stop
212
+ loss to inactive.
213
+
214
+ Returns:
215
+ float - the amount to sell
216
+ """
217
+
218
+ if not self.active:
219
+ return 0
220
+
221
+ return self.sell_amount - self.sold_amount
222
+
223
+ def add_sell_price(self, price: float, date: str):
224
+ """
225
+ Function to add a sell price to the list of sell prices.
226
+ The sell price is added to the list of sell prices and the
227
+ date is added to the list of sell dates.
228
+
229
+ Args:
230
+ price (float): the price at which the trade was sold
231
+ date (datetime): the date at which the trade was sold
232
+
233
+ Returns:
234
+ None
235
+ """
236
+ if self.sell_prices is None:
237
+ self.sell_prices = str(price)
238
+ self.sell_dates = str(date)
239
+ else:
240
+ self.sell_prices += f", {price}"
241
+ self.sell_dates += f", {date}"
242
+
243
+ def remove_sell_price(self, price: float, date: str):
244
+ """
245
+ Function to remove a sell price from the list of sell prices.
246
+ The sell price is removed from the list of sell prices and the
247
+ date is removed from the list of sell dates.
248
+
249
+ Args:
250
+ price: float - the price at which the trade was sold
251
+ date: str - the date at which the trade was sold
252
+
253
+ Returns:
254
+ None
255
+ """
256
+ if self.sell_prices is not None:
257
+
258
+ # Split the sell prices into a list and convert to float
259
+ sell_prices_list = self.sell_prices.split(", ")
260
+ sell_prices_list = [float(p) for p in sell_prices_list]
261
+
262
+ if price in sell_prices_list:
263
+ sell_prices_list.remove(price)
264
+ self.sell_prices = ", ".join(sell_prices_list)
265
+
266
+ if self.sell_prices == "":
267
+ self.sell_prices = None
268
+
269
+ # Split the sell dates into a list
270
+ sell_dates_list = self.sell_dates.split(", ")
271
+ if date in sell_dates_list:
272
+ sell_dates_list.remove(date)
273
+ self.sell_dates = ", ".join(sell_dates_list)
274
+ else:
275
+ self.sell_prices = None
276
+ self.sell_dates = None
277
+
278
+ def to_dict(self, datetime_format=None):
279
+ def ensure_iso(value):
280
+ if hasattr(value, "isoformat"):
281
+ if value.tzinfo is None:
282
+ value = value.replace(tzinfo=timezone.utc)
283
+ return value.isoformat()
284
+ return value
285
+
286
+ return {
287
+ "trade_id": self.trade_id,
288
+ "trailing": self.trailing,
289
+ "percentage": self.percentage,
290
+ "open_price": self.open_price,
291
+ "sell_percentage": self.sell_percentage,
292
+ "high_water_mark": self.high_water_mark,
293
+ "take_profit_price": self.take_profit_price,
294
+ "sell_amount": self.sell_amount,
295
+ "sold_amount": self.sold_amount,
296
+ "active": self.active,
297
+ "triggered": self.triggered,
298
+ "triggered_at": ensure_iso(self.triggered_at),
299
+ "high_water_mark_date": self.high_water_mark_date,
300
+ "sell_prices": self.sell_prices,
301
+ "created_at": ensure_iso(self.created_at),
302
+ "updated_at": ensure_iso(self.updated_at)
303
+ }
304
+
305
+ @staticmethod
306
+ def from_dict(data: dict):
307
+ created_at = parse(data["created_at"]) \
308
+ if data.get("created_at") is not None else None
309
+ updated_at = parse(data["updated_at"]) \
310
+ if data.get("updated_at") is not None else None
311
+ triggered_at = parse(data["triggered_at"]) \
312
+ if data.get("triggered_at") is not None else None
313
+ high_water_mark_date = parse(data.get("high_water_mark_date")) \
314
+ if data.get("high_water_mark_date") is not None else None
315
+
316
+ # Make sure all the dates are timezone utc aware
317
+ if created_at and created_at.tzinfo is None:
318
+ created_at = created_at.replace(tzinfo=timezone.utc)
319
+ if updated_at and updated_at.tzinfo is None:
320
+ updated_at = updated_at.replace(tzinfo=timezone.utc)
321
+ if triggered_at and triggered_at.tzinfo is None:
322
+ triggered_at = triggered_at.replace(tzinfo=timezone.utc)
323
+ if high_water_mark_date and high_water_mark_date.tzinfo is None:
324
+ high_water_mark_date = high_water_mark_date.replace(
325
+ tzinfo=timezone.utc
326
+ )
327
+
328
+ return TradeTakeProfit(
329
+ trade_id=data.get("trade_id"),
330
+ trailing=data.get("trailing"),
331
+ percentage=data.get("percentage"),
332
+ open_price=data.get("open_price"),
333
+ total_amount_trade=data.get("total_amount_trade"),
334
+ sell_percentage=data.get("sell_percentage", 100),
335
+ active=data.get("active", True),
336
+ triggered=data.get("triggered", False),
337
+ triggered_at=triggered_at,
338
+ sell_prices=data.get("sell_prices"),
339
+ sell_dates=data.get("sell_dates"),
340
+ sell_amount=data.get("sell_amount"),
341
+ high_water_mark=data.get("high_water_mark"),
342
+ high_water_mark_date=high_water_mark_date,
343
+ created_at=created_at,
344
+ updated_at=updated_at
345
+ )
346
+
347
+ def __repr__(self):
348
+ return self.repr(
349
+ trade_id=self.trade_id,
350
+ trailing=self.trailing,
351
+ percentage=self.percentage,
352
+ open_price=self.open_price,
353
+ sell_percentage=self.sell_percentage,
354
+ high_water_mark=self.high_water_mark,
355
+ high_water_mark_date=self.high_water_mark_date,
356
+ triggered=self.triggered,
357
+ triggered_at=self.triggered_at,
358
+ take_profit_price=self.take_profit_price,
359
+ sell_amount=self.sell_amount,
360
+ sold_amount=self.sold_amount,
361
+ active=self.active,
362
+ sell_prices=self.sell_prices,
363
+ created_at=self.created_at,
364
+ updated_at=self.updated_at
365
+ )