investing-algorithm-framework 7.19.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of investing-algorithm-framework might be problematic. Click here for more details.

Files changed (260) hide show
  1. investing_algorithm_framework/__init__.py +197 -0
  2. investing_algorithm_framework/app/__init__.py +47 -0
  3. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  4. investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
  5. investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
  6. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  7. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  8. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  9. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  10. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  11. investing_algorithm_framework/app/app.py +2204 -0
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1667 -0
  14. investing_algorithm_framework/app/eventloop.py +590 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  31. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  32. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  33. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  34. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  35. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  36. investing_algorithm_framework/app/stateless/__init__.py +35 -0
  37. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
  38. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
  39. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
  40. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
  41. investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
  42. investing_algorithm_framework/app/strategy.py +675 -0
  43. investing_algorithm_framework/app/task.py +41 -0
  44. investing_algorithm_framework/app/web/__init__.py +5 -0
  45. investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
  46. investing_algorithm_framework/app/web/controllers/orders.py +20 -0
  47. investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
  48. investing_algorithm_framework/app/web/controllers/positions.py +18 -0
  49. investing_algorithm_framework/app/web/create_app.py +20 -0
  50. investing_algorithm_framework/app/web/error_handler.py +59 -0
  51. investing_algorithm_framework/app/web/responses.py +20 -0
  52. investing_algorithm_framework/app/web/run_strategies.py +4 -0
  53. investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
  54. investing_algorithm_framework/app/web/schemas/order.py +12 -0
  55. investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
  56. investing_algorithm_framework/app/web/schemas/position.py +15 -0
  57. investing_algorithm_framework/app/web/setup_cors.py +6 -0
  58. investing_algorithm_framework/cli/__init__.py +0 -0
  59. investing_algorithm_framework/cli/cli.py +207 -0
  60. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
  61. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  62. investing_algorithm_framework/cli/initialize_app.py +603 -0
  63. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  64. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  65. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  66. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  67. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  68. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  69. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  70. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  71. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  72. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  73. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  74. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  75. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  76. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  77. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  78. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  79. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  80. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  81. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  82. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  83. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  84. investing_algorithm_framework/create_app.py +54 -0
  85. investing_algorithm_framework/dependency_container.py +155 -0
  86. investing_algorithm_framework/domain/__init__.py +148 -0
  87. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  88. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  92. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  93. investing_algorithm_framework/domain/backtesting/backtest_run.py +435 -0
  94. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  95. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  96. investing_algorithm_framework/domain/config.py +111 -0
  97. investing_algorithm_framework/domain/constants.py +83 -0
  98. investing_algorithm_framework/domain/data_provider.py +334 -0
  99. investing_algorithm_framework/domain/data_structures.py +42 -0
  100. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  101. investing_algorithm_framework/domain/exceptions.py +112 -0
  102. investing_algorithm_framework/domain/models/__init__.py +43 -0
  103. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  104. investing_algorithm_framework/domain/models/base_model.py +25 -0
  105. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  106. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  107. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  108. investing_algorithm_framework/domain/models/event.py +35 -0
  109. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  110. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  111. investing_algorithm_framework/domain/models/order/__init__.py +6 -0
  112. investing_algorithm_framework/domain/models/order/order.py +384 -0
  113. investing_algorithm_framework/domain/models/order/order_side.py +36 -0
  114. investing_algorithm_framework/domain/models/order/order_status.py +37 -0
  115. investing_algorithm_framework/domain/models/order/order_type.py +30 -0
  116. investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
  117. investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
  118. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
  119. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  120. investing_algorithm_framework/domain/models/position/__init__.py +4 -0
  121. investing_algorithm_framework/domain/models/position/position.py +68 -0
  122. investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
  123. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  124. investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
  125. investing_algorithm_framework/domain/models/time_frame.py +153 -0
  126. investing_algorithm_framework/domain/models/time_interval.py +124 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +149 -0
  128. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  129. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  130. investing_algorithm_framework/domain/models/trade/__init__.py +13 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +388 -0
  132. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
  133. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  134. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
  135. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
  136. investing_algorithm_framework/domain/order_executor.py +112 -0
  137. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  138. investing_algorithm_framework/domain/positions/__init__.py +4 -0
  139. investing_algorithm_framework/domain/positions/position_size.py +41 -0
  140. investing_algorithm_framework/domain/services/__init__.py +11 -0
  141. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  142. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  143. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  144. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  145. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  146. investing_algorithm_framework/domain/stateless_actions.py +7 -0
  147. investing_algorithm_framework/domain/strategy.py +44 -0
  148. investing_algorithm_framework/domain/utils/__init__.py +27 -0
  149. investing_algorithm_framework/domain/utils/csv.py +104 -0
  150. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  151. investing_algorithm_framework/domain/utils/dates.py +57 -0
  152. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  153. investing_algorithm_framework/domain/utils/polars.py +53 -0
  154. investing_algorithm_framework/domain/utils/random.py +41 -0
  155. investing_algorithm_framework/domain/utils/signatures.py +17 -0
  156. investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
  157. investing_algorithm_framework/domain/utils/synchronized.py +12 -0
  158. investing_algorithm_framework/download_data.py +108 -0
  159. investing_algorithm_framework/infrastructure/__init__.py +50 -0
  160. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  161. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  162. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  163. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  164. investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
  165. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
  166. investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
  167. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  168. investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
  169. investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
  170. investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
  171. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  172. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  173. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
  174. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  175. investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
  176. investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
  177. investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
  178. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  179. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  180. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  181. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
  182. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
  183. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  184. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  185. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  186. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  187. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  188. investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
  189. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  190. investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
  191. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
  192. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  193. investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
  194. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  195. investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
  196. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  197. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
  198. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
  199. investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
  200. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  201. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  202. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  203. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  204. investing_algorithm_framework/services/__init__.py +132 -0
  205. investing_algorithm_framework/services/backtesting/__init__.py +5 -0
  206. investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
  207. investing_algorithm_framework/services/configuration_service.py +96 -0
  208. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  209. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  210. investing_algorithm_framework/services/market_credential_service.py +40 -0
  211. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  212. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  213. investing_algorithm_framework/services/metrics/beta.py +0 -0
  214. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  215. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  216. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  217. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  218. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  219. investing_algorithm_framework/services/metrics/generate.py +358 -0
  220. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  221. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  222. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  223. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  224. investing_algorithm_framework/services/metrics/returns.py +452 -0
  225. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  226. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  227. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  228. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  229. investing_algorithm_framework/services/metrics/trades.py +500 -0
  230. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  231. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  232. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  233. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  234. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  235. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  237. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  238. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  239. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  240. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  241. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
  242. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  243. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  244. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  245. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  246. investing_algorithm_framework/services/positions/__init__.py +7 -0
  247. investing_algorithm_framework/services/positions/position_service.py +210 -0
  248. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  249. investing_algorithm_framework/services/repository_service.py +40 -0
  250. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  251. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
  252. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
  253. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
  254. investing_algorithm_framework/services/trade_service/__init__.py +3 -0
  255. investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
  256. investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
  257. investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
  258. investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
  259. investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
  260. investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,34 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TradeRiskType(Enum):
5
+ FIXED = "FIXED"
6
+ TRAILING = "TRAILING"
7
+
8
+ @staticmethod
9
+ def from_string(value: str):
10
+
11
+ if isinstance(value, str):
12
+ for status in TradeRiskType:
13
+
14
+ if value.upper() == status.value:
15
+ return status
16
+
17
+ raise ValueError("Could not convert value to TradeRiskType")
18
+
19
+ @staticmethod
20
+ def from_value(value):
21
+
22
+ if isinstance(value, TradeRiskType):
23
+ for risk_type in TradeRiskType:
24
+
25
+ if value == risk_type:
26
+ return risk_type
27
+
28
+ elif isinstance(value, str):
29
+ return TradeRiskType.from_string(value)
30
+
31
+ raise ValueError("Could not convert value to TradeRiskType")
32
+
33
+ def equals(self, other):
34
+ return TradeRiskType.from_value(other) == self
@@ -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
@@ -0,0 +1,267 @@
1
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
+ from investing_algorithm_framework.domain.models.trade.trade_risk_type import \
3
+ TradeRiskType
4
+
5
+
6
+ class TradeStopLoss(BaseModel):
7
+ """
8
+ TradeStopLoss represents a stop loss strategy for a trade.
9
+
10
+ Attributes:
11
+ trade: Trade - the trade that the take profit is for
12
+ take_profit: float - the take profit percentage
13
+ trade_risk_type: TradeRiskType - the type of trade risk, either
14
+ trailing or fixed
15
+ percentage: float - the stop loss percentage
16
+ sell_percentage: float - the percentage of the trade to sell when the
17
+ take profit is hit. Default is 100% of the trade. If the
18
+ take profit percentage is lower than 100% a check must
19
+ be made that the combined sell percentage of all
20
+ take profits is less or equal than 100%.
21
+ sell_amount: float - the amount to sell when the stop loss triggers
22
+ sold_amount: float - the amount that has been sold
23
+ high_water_mark: float - the highest price of the trade
24
+ stop_loss_price: float - the price at which the stop loss triggers
25
+
26
+ if trade_risk_type is fixed, the stop loss price is calculated as follows:
27
+ You buy a stock at $100.
28
+ You set a 5% stop loss, meaning you will sell if
29
+ the price drops to $95.
30
+ If the price rises to $120, the stop loss is not triggered.
31
+ But if the price keeps falling to $95, the stop loss triggers,
32
+ and you exit with a $5 loss.
33
+
34
+ if trade_risk_type is trailing, the stop loss price is
35
+ calculated as follows:
36
+ You buy a stock at $100.
37
+ You set a 5% trailing stop loss, meaning you will sell if
38
+ the price drops 5% from its peak at $96
39
+ If the price rises to $120, the stop loss adjusts
40
+ to $114 (5% below $120).
41
+ If the price falls to $114, the position is
42
+ closed, securing a $14 profit.
43
+ But if the price keeps rising to $150, the stop
44
+ loss moves up to $142.50.
45
+ If the price drops from $150 to $142.50, the stop
46
+ loss triggers, and you exit with a $42.50 profit.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ trade_id: int,
52
+ trade_risk_type: TradeRiskType,
53
+ percentage: float,
54
+ open_price: float,
55
+ total_amount_trade: float = None,
56
+ sell_percentage: float = 100,
57
+ active: bool = True,
58
+ sell_prices: str = None,
59
+ sell_dates: str = None,
60
+ sell_amount: float = None,
61
+ high_water_mark_date: str = None,
62
+ ):
63
+ self.trade_id = trade_id
64
+ self.trade_risk_type = TradeRiskType.from_value(trade_risk_type).value
65
+ self.percentage = percentage
66
+ self.sell_percentage = sell_percentage
67
+ self.high_water_mark = open_price
68
+ self.high_water_mark_date = high_water_mark_date
69
+ self.open_price = open_price
70
+ self.stop_loss_price = self.high_water_mark * \
71
+ (1 - (self.percentage / 100))
72
+
73
+ if sell_amount is not None:
74
+ self.sell_amount = sell_amount
75
+ else:
76
+ self.sell_amount = total_amount_trade * \
77
+ (self.sell_percentage / 100)
78
+
79
+ self.sold_amount = 0
80
+ self.active = active
81
+ self.sell_prices = sell_prices
82
+ self.sell_dates = sell_dates
83
+
84
+ def update_with_last_reported_price(self, current_price: float, date):
85
+ """
86
+ Function to update the take profit price based on the last
87
+ reported price.
88
+ The take profit price is only updated when the trade risk
89
+ type is trailing.
90
+ The take profit price is updated based on the current price
91
+ and the percentage of the take profit.
92
+
93
+ Args:
94
+ current_price: float - the last reported price of the trade
95
+ """
96
+
97
+ if not self.active or self.sold_amount == self.sell_amount:
98
+ return
99
+
100
+ if TradeRiskType.FIXED.equals(self.trade_risk_type):
101
+ # Check if the current price is less than the high water mark
102
+ if current_price > self.high_water_mark:
103
+ self.high_water_mark = current_price
104
+ return
105
+ else:
106
+ # Check if the current price is less than the stop loss price
107
+ if current_price <= self.stop_loss_price:
108
+ return
109
+ elif current_price > self.high_water_mark:
110
+ self.high_water_mark = current_price
111
+ self.high_water_mark_date = date
112
+ self.stop_loss_price = self.high_water_mark * \
113
+ (1 - (self.percentage / 100))
114
+
115
+ def has_triggered(self, current_price: float) -> bool:
116
+ """
117
+ Function to check if the stop loss has triggered.
118
+ Function always returns False if the stop loss is not active or
119
+ the sold amount is equal to the sell amount.
120
+
121
+ Args:
122
+ current_price: float - the current price of the trade
123
+
124
+ Returns:
125
+ bool - True if the stop loss has triggered, False otherwise
126
+ """
127
+ if not self.active or self.sold_amount == self.sell_amount:
128
+ return False
129
+
130
+ if TradeRiskType.FIXED.equals(self.trade_risk_type):
131
+ # Check if the current price is less than the high watermark
132
+ return current_price <= self.stop_loss_price
133
+ else:
134
+ # Check if the current price is less than the stop loss price
135
+ if current_price <= self.stop_loss_price:
136
+ return True
137
+ elif current_price > self.high_water_mark:
138
+ self.high_water_mark = current_price
139
+ self.stop_loss_price = self.high_water_mark * \
140
+ (1 - (self.percentage / 100))
141
+
142
+ return False
143
+
144
+ def get_sell_amount(self) -> float:
145
+ """
146
+ Function to calculate the amount to sell based on the
147
+ sell percentage and the remaining amount of the trade.
148
+ Keep in mind the moment the take profit triggers, the remaining
149
+ amount of the trade is used to calculate the sell amount.
150
+ If the remaining amount is smaller than the trade amount, the
151
+ trade stop loss stays active. The client that uses the
152
+ trade stop loss is responsible for setting the trade stop
153
+ loss to inactive.
154
+
155
+ Args:
156
+ trade: Trade - the trade to calculate the sell amount for
157
+
158
+ """
159
+
160
+ if not self.active:
161
+ return 0
162
+
163
+ return self.sell_amount - self.sold_amount
164
+
165
+ def add_sell_price(self, price: float, date: str):
166
+ """
167
+ Function to add a sell price to the list of sell prices.
168
+ The sell price is added to the list of sell prices and the
169
+ date is added to the list of sell dates.
170
+
171
+ Args:
172
+ price: float - the price at which the trade was sold
173
+ date: str - the date at which the trade was sold
174
+
175
+ Returns:
176
+ None
177
+ """
178
+ if self.sell_prices is None:
179
+ self.sell_prices = str(price)
180
+ self.sell_dates = str(date)
181
+ else:
182
+ self.sell_prices += f", {price}"
183
+ self.sell_dates += f", {date}"
184
+
185
+ def remove_sell_price(self, price: float, date: str):
186
+ """
187
+ Function to remove a sell price from the list of sell prices.
188
+ The sell price is removed from the list of sell prices and the
189
+ date is removed from 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 not None:
199
+
200
+ # Split the sell prices into a list and convert to float
201
+ sell_prices_list = self.sell_prices.split(", ")
202
+ sell_prices_list = [float(p) for p in sell_prices_list]
203
+
204
+ if price in sell_prices_list:
205
+ sell_prices_list.remove(price)
206
+ self.sell_prices = ", ".join(sell_prices_list)
207
+
208
+ if self.sell_prices == "":
209
+ self.sell_prices = None
210
+
211
+ # Split the sell dates into a list
212
+ sell_dates_list = self.sell_dates.split(", ")
213
+ if date in sell_dates_list:
214
+ sell_dates_list.remove(date)
215
+ self.sell_dates = ", ".join(sell_dates_list)
216
+ else:
217
+ self.sell_prices = None
218
+ self.sell_dates = None
219
+
220
+ def to_dict(self, datetime_format=None):
221
+ return {
222
+ "trade_id": self.trade_id,
223
+ "trade_risk_type": self.trade_risk_type,
224
+ "percentage": self.percentage,
225
+ "open_price": self.open_price,
226
+ "sell_percentage": self.sell_percentage,
227
+ "high_water_mark": self.high_water_mark,
228
+ "stop_loss_price": self.stop_loss_price,
229
+ "sell_amount": self.sell_amount,
230
+ "sold_amount": self.sold_amount,
231
+ "active": self.active,
232
+ "sell_prices": self.sell_prices
233
+ }
234
+
235
+ @staticmethod
236
+ def from_dict(data: dict):
237
+ return TradeStopLoss(
238
+ trade_id=data.get("trade_id"),
239
+ trade_risk_type=TradeRiskType.from_string(
240
+ data.get("trade_risk_type")
241
+ ),
242
+ percentage=data.get("percentage"),
243
+ open_price=data.get("open_price"),
244
+ total_amount_trade=data.get("sell_amount", 0) /
245
+ (data.get("sell_percentage", 100) / 100),
246
+ sell_percentage=data.get("sell_percentage", 100),
247
+ active=data.get("active", True),
248
+ sell_prices=data.get("sell_prices"),
249
+ sell_dates=data.get("sell_dates"),
250
+ sell_amount=data.get("sell_amount"),
251
+ high_water_mark_date=data.get("high_water_mark_date")
252
+ )
253
+
254
+ def __repr__(self):
255
+ return self.repr(
256
+ trade_id=self.trade_id,
257
+ trade_risk_type=self.trade_risk_type,
258
+ percentage=self.percentage,
259
+ sell_percentage=self.sell_percentage,
260
+ high_water_mark=self.high_water_mark,
261
+ open_price=self.open_price,
262
+ stop_loss_price=self.stop_loss_price,
263
+ sell_amount=self.sell_amount,
264
+ sold_amount=self.sold_amount,
265
+ sell_prices=self.sell_prices,
266
+ active=self.active
267
+ )
@@ -0,0 +1,303 @@
1
+ from investing_algorithm_framework.domain.models.base_model import BaseModel
2
+ from investing_algorithm_framework.domain.models.trade.trade_risk_type import \
3
+ TradeRiskType
4
+
5
+
6
+ class TradeTakeProfit(BaseModel):
7
+ """
8
+ TradeTakeProfit represents a take profit strategy for a trade.
9
+
10
+ Attributes:
11
+ trade: Trade - the trade that the take profit is for
12
+ take_profit: float - the take profit percentage
13
+ trade_risk_type: TradeRiskType - the type of trade risk, either
14
+ trailing or fixed
15
+ percentage: float - the take profit percentage
16
+ sell_percentage: float - the percentage of the trade to sell when the
17
+ take profit is hit. Default is 100% of the trade.
18
+ If the take profit percentage is lower than 100% a check
19
+ must be made that the combined sell percentage of
20
+ all take profits is less or equal than 100%.
21
+
22
+ if trade_risk_type is fixed, the take profit price is
23
+ calculated as follows:
24
+ You buy a stock at $100.
25
+ You set a 5% take profit, meaning you will sell if the price
26
+ rises to $105.
27
+ If the price rises to $120, the take profit triggers,
28
+ and you exit with a $20 profit.
29
+ But if the price keeps falling below $105, the take profit is not
30
+ triggered.
31
+
32
+ if trade_risk_type is trailing, the take profit price is
33
+ calculated as follows:
34
+ You buy a stock at $100.
35
+ You set a 5% trailing take profit, the moment the price rises
36
+ 5% the initial take profit mark will be set. This means you
37
+ will set the take_profit_price initially at none and
38
+ only if the price hits $105, you will set the
39
+ take_profit_price to $105.
40
+ if the price drops below $105, the take profit is triggered.
41
+ If the price rises to $120, the take profit adjusts to
42
+ $114 (5% below $120).
43
+ If the price falls to $114, the position is closed,
44
+ securing a $14 profit.
45
+ But if the price keeps rising to $150, the take profit
46
+ moves up to $142.50.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ trade_id: int,
52
+ trade_risk_type: TradeRiskType,
53
+ percentage: float,
54
+ open_price: float,
55
+ total_amount_trade: float = None,
56
+ sell_percentage: float = 100,
57
+ active: bool = True,
58
+ sell_prices: str = None,
59
+ sell_dates: str = None,
60
+ sell_amount: float = None,
61
+ high_water_mark_date: str = None,
62
+ ):
63
+ self.trade_id = trade_id
64
+ self.trade_risk_type = TradeRiskType.from_value(trade_risk_type).value
65
+ self.percentage = percentage
66
+ self.sell_percentage = sell_percentage
67
+ self.high_water_mark = None
68
+ self.high_water_mark_date = high_water_mark_date
69
+ self.open_price = open_price
70
+ self.take_profit_price = open_price * \
71
+ (1 + (self.percentage / 100))
72
+
73
+ if sell_amount is not None:
74
+ self.sell_amount = sell_amount
75
+ else:
76
+ self.sell_amount = total_amount_trade * \
77
+ (self.sell_percentage / 100)
78
+ self.sold_amount = 0
79
+ self.active = active
80
+ self.sell_prices = sell_prices
81
+ self.sell_dates = sell_dates
82
+
83
+ def update_with_last_reported_price(self, current_price: float, date):
84
+ """
85
+ Function to update the take profit price based on
86
+ the last reported price.
87
+ The take profit price is only updated when the
88
+ trade risk type is trailing.
89
+ The take profit price is updated based on the
90
+ current price and the percentage of the take profit.
91
+
92
+ Args:
93
+ current_price: float - the last reported price of the trade
94
+ """
95
+
96
+ # Do nothing for fixed take profit
97
+ if TradeRiskType.FIXED.equals(self.trade_risk_type):
98
+
99
+ if self.high_water_mark is not None:
100
+ if current_price > self.high_water_mark:
101
+ self.high_water_mark = current_price
102
+ self.high_water_mark_date = date
103
+ else:
104
+ if current_price >= self.take_profit_price:
105
+ self.high_water_mark = current_price
106
+ self.high_water_mark_date = date
107
+ return
108
+
109
+ return
110
+ else:
111
+
112
+ if self.high_water_mark is None:
113
+
114
+ if current_price >= self.take_profit_price:
115
+ self.high_water_mark = current_price
116
+ self.high_water_mark_date = date
117
+ new_take_profit_price = self.high_water_mark * \
118
+ (1 - (self.percentage / 100))
119
+
120
+ if self.take_profit_price <= new_take_profit_price:
121
+ self.take_profit_price = new_take_profit_price
122
+
123
+ return
124
+
125
+ # Check if the current price is less than the take profit price
126
+ if current_price < self.take_profit_price:
127
+ return
128
+
129
+ # Increase the high water mark and take profit price
130
+ elif current_price > self.high_water_mark:
131
+ self.high_water_mark = current_price
132
+ self.high_water_mark_date = date
133
+ new_take_profit_price = self.high_water_mark * \
134
+ (1 - (self.percentage / 100))
135
+
136
+ # Only increase the take profit price if the new take
137
+ # profit price based on the new high water mark is higher
138
+ # then the current take profit price
139
+ if self.take_profit_price <= new_take_profit_price:
140
+ self.take_profit_price = new_take_profit_price
141
+
142
+ return
143
+
144
+ def has_triggered(self, current_price: float = None) -> bool:
145
+
146
+ if TradeRiskType.FIXED.equals(self.trade_risk_type):
147
+ # Check if the current price is less than the high water mark
148
+ return current_price >= self.take_profit_price
149
+ else:
150
+ # Always return false, when the high water mark is not set
151
+ # But check if we can set the high water mark
152
+ if self.high_water_mark is None:
153
+
154
+ if current_price >= self.take_profit_price:
155
+ self.high_water_mark = current_price
156
+ new_take_profit_price = self.high_water_mark * \
157
+ (1 - (self.percentage / 100))
158
+ if self.take_profit_price <= new_take_profit_price:
159
+ self.take_profit_price = new_take_profit_price
160
+
161
+ return False
162
+
163
+ # Check if the current price is less than the take profit price
164
+ if current_price < self.take_profit_price:
165
+ return True
166
+
167
+ # Increase the high watermark and take profit price
168
+ elif current_price > self.high_water_mark:
169
+ self.high_water_mark = current_price
170
+ new_take_profit_price = self.high_water_mark * \
171
+ (1 - (self.percentage / 100))
172
+
173
+ # Only increase the take profit price if the new take
174
+ # profit price based on the new high water mark is higher
175
+ # then the current take profit price
176
+ if self.take_profit_price <= new_take_profit_price:
177
+ self.take_profit_price = new_take_profit_price
178
+
179
+ return False
180
+
181
+ def get_sell_amount(self) -> float:
182
+ """
183
+ Function to calculate the amount to sell based on the
184
+ sell percentage and the remaining amount of the trade.
185
+ Keep in mind the moment the take profit triggers, the remaining
186
+ amount of the trade is used to calculate the sell amount.
187
+ If the remaining amount is smaller than the trade amount, the
188
+ trade stop loss stays active. The client that uses the
189
+ trade stop loss is responsible for setting the trade stop
190
+ loss to inactive.
191
+
192
+ Args:
193
+ trade: Trade - the trade to calculate the sell amount for
194
+
195
+ """
196
+
197
+ if not self.active:
198
+ return 0
199
+
200
+ return self.sell_amount - self.sold_amount
201
+
202
+ def add_sell_price(self, price: float, date: str):
203
+ """
204
+ Function to add a sell price to the list of sell prices.
205
+ The sell price is added to the list of sell prices and the
206
+ date is added to the list of sell dates.
207
+
208
+ Args:
209
+ price: float - the price at which the trade was sold
210
+ date: str - the date at which the trade was sold
211
+
212
+ Returns:
213
+ None
214
+ """
215
+ if self.sell_prices is None:
216
+ self.sell_prices = str(price)
217
+ self.sell_dates = str(date)
218
+ else:
219
+ self.sell_prices += f", {price}"
220
+ self.sell_dates += f", {date}"
221
+
222
+ def remove_sell_price(self, price: float, date: str):
223
+ """
224
+ Function to remove a sell price from the list of sell prices.
225
+ The sell price is removed from the list of sell prices and the
226
+ date is removed from the list of sell dates.
227
+
228
+ Args:
229
+ price: float - the price at which the trade was sold
230
+ date: str - the date at which the trade was sold
231
+
232
+ Returns:
233
+ None
234
+ """
235
+ if self.sell_prices is not None:
236
+
237
+ # Split the sell prices into a list and convert to float
238
+ sell_prices_list = self.sell_prices.split(", ")
239
+ sell_prices_list = [float(p) for p in sell_prices_list]
240
+
241
+ if price in sell_prices_list:
242
+ sell_prices_list.remove(price)
243
+ self.sell_prices = ", ".join(sell_prices_list)
244
+
245
+ if self.sell_prices == "":
246
+ self.sell_prices = None
247
+
248
+ # Split the sell dates into a list
249
+ sell_dates_list = self.sell_dates.split(", ")
250
+ if date in sell_dates_list:
251
+ sell_dates_list.remove(date)
252
+ self.sell_dates = ", ".join(sell_dates_list)
253
+ else:
254
+ self.sell_prices = None
255
+ self.sell_dates = None
256
+
257
+ def to_dict(self, datetime_format=None):
258
+ return {
259
+ "trade_id": self.trade_id,
260
+ "trade_risk_type": self.trade_risk_type,
261
+ "percentage": self.percentage,
262
+ "open_price": self.open_price,
263
+ "sell_percentage": self.sell_percentage,
264
+ "high_water_mark": self.high_water_mark,
265
+ "take_profit_price": self.take_profit_price,
266
+ "sell_amount": self.sell_amount,
267
+ "sold_amount": self.sold_amount,
268
+ "active": self.active,
269
+ "sell_prices": self.sell_prices
270
+ }
271
+
272
+ @staticmethod
273
+ def from_dict(data: dict):
274
+ return TradeTakeProfit(
275
+ trade_id=data.get("trade_id"),
276
+ trade_risk_type=TradeRiskType.from_string(
277
+ data.get("trade_risk_type")
278
+ ),
279
+ percentage=data.get("percentage"),
280
+ open_price=data.get("open_price"),
281
+ total_amount_trade=data.get("total_amount_trade"),
282
+ sell_percentage=data.get("sell_percentage", 100),
283
+ active=data.get("active", True),
284
+ sell_prices=data.get("sell_prices"),
285
+ sell_dates=data.get("sell_dates"),
286
+ sell_amount=data.get("sell_amount"),
287
+ high_water_mark_date=data.get("high_water_mark_date")
288
+ )
289
+
290
+ def __repr__(self):
291
+ return self.repr(
292
+ trade_id=self.trade_id,
293
+ trade_risk_type=self.trade_risk_type,
294
+ percentage=self.percentage,
295
+ open_price=self.open_price,
296
+ sell_percentage=self.sell_percentage,
297
+ high_water_mark=self.high_water_mark,
298
+ take_profit_price=self.take_profit_price,
299
+ sell_amount=self.sell_amount,
300
+ sold_amount=self.sold_amount,
301
+ active=self.active,
302
+ sell_prices=self.sell_prices
303
+ )