investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__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.
- investing_algorithm_framework/__init__.py +147 -44
- investing_algorithm_framework/app/__init__.py +23 -6
- investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +1322 -707
- investing_algorithm_framework/app/context.py +196 -88
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +16 -5
- investing_algorithm_framework/app/reporting/ascii.py +57 -202
- investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
- investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
- investing_algorithm_framework/app/reporting/generate.py +100 -114
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
- investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
- investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
- investing_algorithm_framework/app/strategy.py +315 -175
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/cli/cli.py +30 -12
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
- investing_algorithm_framework/cli/initialize_app.py +20 -1
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
- investing_algorithm_framework/create_app.py +3 -5
- investing_algorithm_framework/dependency_container.py +25 -39
- investing_algorithm_framework/domain/__init__.py +45 -38
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +27 -0
- investing_algorithm_framework/domain/constants.py +6 -34
- investing_algorithm_framework/domain/data_provider.py +200 -56
- investing_algorithm_framework/domain/exceptions.py +34 -1
- investing_algorithm_framework/domain/models/__init__.py +10 -19
- investing_algorithm_framework/domain/models/base_model.py +0 -6
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -0
- investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
- investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +34 -13
- investing_algorithm_framework/domain/models/order/order_status.py +1 -1
- investing_algorithm_framework/domain/models/order/order_type.py +1 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +9 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +7 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +63 -1
- investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
- investing_algorithm_framework/domain/models/trade/trade.py +56 -32
- investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
- investing_algorithm_framework/domain/order_executor.py +19 -0
- investing_algorithm_framework/domain/portfolio_provider.py +20 -1
- investing_algorithm_framework/domain/services/__init__.py +0 -13
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +5 -1
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +17 -14
- investing_algorithm_framework/download_data.py +40 -10
- investing_algorithm_framework/infrastructure/__init__.py +13 -25
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
- investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
- investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
- investing_algorithm_framework/services/__init__.py +105 -8
- investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
- investing_algorithm_framework/services/configuration_service.py +14 -4
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
- investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
- investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
- investing_algorithm_framework/services/order_service/order_service.py +9 -71
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
- investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
- investing_algorithm_framework/services/repository_service.py +5 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
- investing_algorithm_framework/app/reporting/evaluation.py +0 -243
- investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
- investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
- investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
- investing_algorithm_framework/domain/models/data_source.py +0 -21
- investing_algorithm_framework/domain/models/date_range.py +0 -64
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/services/observable.py +0 -51
- investing_algorithm_framework/domain/services/observer.py +0 -19
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
- investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
- investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
- investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
- /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
|
@@ -1,29 +1,15 @@
|
|
|
1
|
+
from dateutil.parser import parse
|
|
2
|
+
from datetime import timezone
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
1
5
|
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
6
|
|
|
5
7
|
|
|
6
8
|
class TradeStopLoss(BaseModel):
|
|
7
9
|
"""
|
|
8
10
|
TradeStopLoss represents a stop loss strategy for a trade.
|
|
9
11
|
|
|
10
|
-
|
|
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:
|
|
12
|
+
if trailing is set to False, the stop loss price is calculated as follows:
|
|
27
13
|
You buy a stock at $100.
|
|
28
14
|
You set a 5% stop loss, meaning you will sell if
|
|
29
15
|
the price drops to $95.
|
|
@@ -31,7 +17,7 @@ class TradeStopLoss(BaseModel):
|
|
|
31
17
|
But if the price keeps falling to $95, the stop loss triggers,
|
|
32
18
|
and you exit with a $5 loss.
|
|
33
19
|
|
|
34
|
-
if
|
|
20
|
+
if trailing is set to True, the stop loss price is
|
|
35
21
|
calculated as follows:
|
|
36
22
|
You buy a stock at $100.
|
|
37
23
|
You set a 5% trailing stop loss, meaning you will sell if
|
|
@@ -44,31 +30,65 @@ class TradeStopLoss(BaseModel):
|
|
|
44
30
|
loss moves up to $142.50.
|
|
45
31
|
If the price drops from $150 to $142.50, the stop
|
|
46
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
|
|
47
51
|
"""
|
|
48
52
|
|
|
49
53
|
def __init__(
|
|
50
54
|
self,
|
|
51
55
|
trade_id: int,
|
|
52
|
-
trade_risk_type: TradeRiskType,
|
|
53
56
|
percentage: float,
|
|
54
57
|
open_price: float,
|
|
58
|
+
trailing: bool = False,
|
|
55
59
|
total_amount_trade: float = None,
|
|
56
60
|
sell_percentage: float = 100,
|
|
57
61
|
active: bool = True,
|
|
62
|
+
triggered: bool = False,
|
|
63
|
+
triggered_at: datetime = None,
|
|
58
64
|
sell_prices: str = None,
|
|
59
65
|
sell_dates: str = None,
|
|
60
66
|
sell_amount: float = None,
|
|
67
|
+
high_water_mark: float = None,
|
|
61
68
|
high_water_mark_date: str = None,
|
|
69
|
+
created_at: datetime = None,
|
|
70
|
+
updated_at: datetime = None
|
|
62
71
|
):
|
|
63
72
|
self.trade_id = trade_id
|
|
64
|
-
self.
|
|
73
|
+
self.trailing = trailing
|
|
65
74
|
self.percentage = percentage
|
|
75
|
+
self.triggered = triggered
|
|
76
|
+
self.triggered_at = triggered_at
|
|
66
77
|
self.sell_percentage = sell_percentage
|
|
67
|
-
self.high_water_mark =
|
|
78
|
+
self.high_water_mark = high_water_mark
|
|
68
79
|
self.high_water_mark_date = high_water_mark_date
|
|
69
80
|
self.open_price = open_price
|
|
70
|
-
self.
|
|
71
|
-
|
|
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))
|
|
72
92
|
|
|
73
93
|
if sell_amount is not None:
|
|
74
94
|
self.sell_amount = sell_amount
|
|
@@ -91,13 +111,17 @@ class TradeStopLoss(BaseModel):
|
|
|
91
111
|
and the percentage of the take profit.
|
|
92
112
|
|
|
93
113
|
Args:
|
|
94
|
-
current_price
|
|
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
|
|
95
119
|
"""
|
|
96
120
|
|
|
97
121
|
if not self.active or self.sold_amount == self.sell_amount:
|
|
98
122
|
return
|
|
99
123
|
|
|
100
|
-
if
|
|
124
|
+
if not self.trailing:
|
|
101
125
|
# Check if the current price is less than the high water mark
|
|
102
126
|
if current_price > self.high_water_mark:
|
|
103
127
|
self.high_water_mark = current_price
|
|
@@ -127,8 +151,8 @@ class TradeStopLoss(BaseModel):
|
|
|
127
151
|
if not self.active or self.sold_amount == self.sell_amount:
|
|
128
152
|
return False
|
|
129
153
|
|
|
130
|
-
if
|
|
131
|
-
# Check if the current price is less than the high
|
|
154
|
+
if not self.trailing:
|
|
155
|
+
# Check if the current price is less than the high watermark
|
|
132
156
|
return current_price <= self.stop_loss_price
|
|
133
157
|
else:
|
|
134
158
|
# Check if the current price is less than the stop loss price
|
|
@@ -151,10 +175,6 @@ class TradeStopLoss(BaseModel):
|
|
|
151
175
|
trade stop loss stays active. The client that uses the
|
|
152
176
|
trade stop loss is responsible for setting the trade stop
|
|
153
177
|
loss to inactive.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
trade: Trade - the trade to calculate the sell amount for
|
|
157
|
-
|
|
158
178
|
"""
|
|
159
179
|
|
|
160
180
|
if not self.active:
|
|
@@ -218,27 +238,62 @@ class TradeStopLoss(BaseModel):
|
|
|
218
238
|
self.sell_dates = None
|
|
219
239
|
|
|
220
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
|
+
|
|
221
252
|
return {
|
|
222
253
|
"trade_id": self.trade_id,
|
|
223
|
-
"
|
|
254
|
+
"trailing": self.trailing,
|
|
224
255
|
"percentage": self.percentage,
|
|
225
256
|
"open_price": self.open_price,
|
|
226
257
|
"sell_percentage": self.sell_percentage,
|
|
227
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)),
|
|
228
262
|
"stop_loss_price": self.stop_loss_price,
|
|
229
263
|
"sell_amount": self.sell_amount,
|
|
230
264
|
"sold_amount": self.sold_amount,
|
|
231
265
|
"active": self.active,
|
|
232
|
-
"sell_prices": self.sell_prices
|
|
266
|
+
"sell_prices": self.sell_prices,
|
|
267
|
+
"created_at": ensure_iso(self.created_at),
|
|
268
|
+
"updated_at": ensure_iso(self.updated_at)
|
|
233
269
|
}
|
|
234
270
|
|
|
235
271
|
@staticmethod
|
|
236
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
|
+
|
|
237
294
|
return TradeStopLoss(
|
|
238
295
|
trade_id=data.get("trade_id"),
|
|
239
|
-
|
|
240
|
-
data.get("trade_risk_type")
|
|
241
|
-
),
|
|
296
|
+
trailing=data.get("trailing"),
|
|
242
297
|
percentage=data.get("percentage"),
|
|
243
298
|
open_price=data.get("open_price"),
|
|
244
299
|
total_amount_trade=data.get("sell_amount", 0) /
|
|
@@ -248,20 +303,30 @@ class TradeStopLoss(BaseModel):
|
|
|
248
303
|
sell_prices=data.get("sell_prices"),
|
|
249
304
|
sell_dates=data.get("sell_dates"),
|
|
250
305
|
sell_amount=data.get("sell_amount"),
|
|
251
|
-
|
|
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
|
|
252
312
|
)
|
|
253
313
|
|
|
254
314
|
def __repr__(self):
|
|
255
315
|
return self.repr(
|
|
256
316
|
trade_id=self.trade_id,
|
|
257
|
-
|
|
317
|
+
trailing=self.trailing,
|
|
258
318
|
percentage=self.percentage,
|
|
259
319
|
sell_percentage=self.sell_percentage,
|
|
260
320
|
high_water_mark=self.high_water_mark,
|
|
321
|
+
high_water_mark_date=self.high_water_mark_date,
|
|
261
322
|
open_price=self.open_price,
|
|
262
323
|
stop_loss_price=self.stop_loss_price,
|
|
263
324
|
sell_amount=self.sell_amount,
|
|
264
325
|
sold_amount=self.sold_amount,
|
|
265
326
|
sell_prices=self.sell_prices,
|
|
266
|
-
active=self.active
|
|
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
|
|
267
332
|
)
|
|
@@ -1,25 +1,14 @@
|
|
|
1
|
+
from datetime import timezone, datetime
|
|
2
|
+
from dateutil.parser import parse
|
|
3
|
+
|
|
1
4
|
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
|
|
|
5
6
|
|
|
6
7
|
class TradeTakeProfit(BaseModel):
|
|
7
8
|
"""
|
|
8
9
|
TradeTakeProfit represents a take profit strategy for a trade.
|
|
9
10
|
|
|
10
|
-
|
|
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
|
|
11
|
+
if trailing is set to False, the take profit price is
|
|
23
12
|
calculated as follows:
|
|
24
13
|
You buy a stock at $100.
|
|
25
14
|
You set a 5% take profit, meaning you will sell if the price
|
|
@@ -29,7 +18,7 @@ class TradeTakeProfit(BaseModel):
|
|
|
29
18
|
But if the price keeps falling below $105, the take profit is not
|
|
30
19
|
triggered.
|
|
31
20
|
|
|
32
|
-
if
|
|
21
|
+
if trailing is set to True, the take profit price is
|
|
33
22
|
calculated as follows:
|
|
34
23
|
You buy a stock at $100.
|
|
35
24
|
You set a 5% trailing take profit, the moment the price rises
|
|
@@ -44,31 +33,65 @@ class TradeTakeProfit(BaseModel):
|
|
|
44
33
|
securing a $14 profit.
|
|
45
34
|
But if the price keeps rising to $150, the take profit
|
|
46
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
|
|
47
57
|
"""
|
|
48
58
|
|
|
49
59
|
def __init__(
|
|
50
60
|
self,
|
|
51
61
|
trade_id: int,
|
|
52
|
-
trade_risk_type: TradeRiskType,
|
|
53
62
|
percentage: float,
|
|
54
63
|
open_price: float,
|
|
64
|
+
trailing: bool = False,
|
|
55
65
|
total_amount_trade: float = None,
|
|
56
66
|
sell_percentage: float = 100,
|
|
57
67
|
active: bool = True,
|
|
68
|
+
triggered: bool = False,
|
|
69
|
+
triggered_at: datetime = None,
|
|
58
70
|
sell_prices: str = None,
|
|
59
71
|
sell_dates: str = None,
|
|
60
72
|
sell_amount: float = None,
|
|
73
|
+
high_water_mark: float = None,
|
|
61
74
|
high_water_mark_date: str = None,
|
|
75
|
+
created_at: datetime = None,
|
|
76
|
+
updated_at: datetime = None
|
|
62
77
|
):
|
|
63
78
|
self.trade_id = trade_id
|
|
64
|
-
self.
|
|
79
|
+
self.trailing = trailing
|
|
65
80
|
self.percentage = percentage
|
|
66
81
|
self.sell_percentage = sell_percentage
|
|
67
|
-
self.
|
|
82
|
+
self.triggered = triggered
|
|
83
|
+
self.triggered_at = triggered_at
|
|
84
|
+
self.high_water_mark = high_water_mark
|
|
68
85
|
self.high_water_mark_date = high_water_mark_date
|
|
69
86
|
self.open_price = open_price
|
|
70
|
-
self.
|
|
71
|
-
|
|
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
|
|
72
95
|
|
|
73
96
|
if sell_amount is not None:
|
|
74
97
|
self.sell_amount = sell_amount
|
|
@@ -84,100 +107,99 @@ class TradeTakeProfit(BaseModel):
|
|
|
84
107
|
"""
|
|
85
108
|
Function to update the take profit price based on
|
|
86
109
|
the last reported price.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
current price and the percentage of the take profit.
|
|
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.
|
|
91
114
|
|
|
92
115
|
Args:
|
|
93
116
|
current_price: float - the last reported price of the trade
|
|
117
|
+
date: the date of the price update
|
|
94
118
|
"""
|
|
95
119
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.high_water_mark = current_price
|
|
102
|
-
self.high_water_mark_date = date
|
|
103
|
-
else:
|
|
104
|
-
if current_price >= self.take_profit_price:
|
|
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):
|
|
105
125
|
self.high_water_mark = current_price
|
|
106
126
|
self.high_water_mark_date = date
|
|
107
|
-
return
|
|
108
|
-
|
|
109
127
|
return
|
|
110
|
-
else:
|
|
111
128
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
new_take_profit_price = self.high_water_mark * \
|
|
118
|
-
(1 - (self.percentage / 100))
|
|
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))
|
|
119
134
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
#
|
|
130
|
-
|
|
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:
|
|
131
147
|
self.high_water_mark = current_price
|
|
132
148
|
self.high_water_mark_date = date
|
|
149
|
+
# Recalculate take profit price based on new high water mark
|
|
133
150
|
new_take_profit_price = self.high_water_mark * \
|
|
134
151
|
(1 - (self.percentage / 100))
|
|
135
|
-
|
|
136
|
-
|
|
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:
|
|
152
|
+
# Update take profit price if it's higher than current
|
|
153
|
+
if new_take_profit_price > self.take_profit_price:
|
|
140
154
|
self.take_profit_price = new_take_profit_price
|
|
141
155
|
|
|
142
|
-
return
|
|
143
|
-
|
|
144
156
|
def has_triggered(self, current_price: float = None) -> bool:
|
|
145
157
|
|
|
146
|
-
if
|
|
147
|
-
#
|
|
158
|
+
if not self.trailing:
|
|
159
|
+
# Fixed take profit: trigger when price reaches take_profit_price
|
|
148
160
|
return current_price >= self.take_profit_price
|
|
149
161
|
else:
|
|
150
|
-
#
|
|
151
|
-
# But check if we can set the high water mark
|
|
162
|
+
# Trailing take profit logic
|
|
152
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
|
|
153
188
|
|
|
154
|
-
if
|
|
189
|
+
# Check if price has risen above high
|
|
190
|
+
# water mark (adjust upward)
|
|
191
|
+
if current_price > self.high_water_mark:
|
|
155
192
|
self.high_water_mark = current_price
|
|
193
|
+
# Recalculate take profit price based on
|
|
194
|
+
# new high water mark
|
|
156
195
|
new_take_profit_price = self.high_water_mark * \
|
|
157
196
|
(1 - (self.percentage / 100))
|
|
158
|
-
if
|
|
197
|
+
# Update take profit price if it's higher than current
|
|
198
|
+
if new_take_profit_price > self.take_profit_price:
|
|
159
199
|
self.take_profit_price = new_take_profit_price
|
|
160
200
|
|
|
161
201
|
return False
|
|
162
202
|
|
|
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
203
|
def get_sell_amount(self) -> float:
|
|
182
204
|
"""
|
|
183
205
|
Function to calculate the amount to sell based on the
|
|
@@ -189,9 +211,8 @@ class TradeTakeProfit(BaseModel):
|
|
|
189
211
|
trade stop loss is responsible for setting the trade stop
|
|
190
212
|
loss to inactive.
|
|
191
213
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
Returns:
|
|
215
|
+
float - the amount to sell
|
|
195
216
|
"""
|
|
196
217
|
|
|
197
218
|
if not self.active:
|
|
@@ -206,8 +227,8 @@ class TradeTakeProfit(BaseModel):
|
|
|
206
227
|
date is added to the list of sell dates.
|
|
207
228
|
|
|
208
229
|
Args:
|
|
209
|
-
price
|
|
210
|
-
date:
|
|
230
|
+
price (float): the price at which the trade was sold
|
|
231
|
+
date (datetime): the date at which the trade was sold
|
|
211
232
|
|
|
212
233
|
Returns:
|
|
213
234
|
None
|
|
@@ -255,9 +276,16 @@ class TradeTakeProfit(BaseModel):
|
|
|
255
276
|
self.sell_dates = None
|
|
256
277
|
|
|
257
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
|
+
|
|
258
286
|
return {
|
|
259
287
|
"trade_id": self.trade_id,
|
|
260
|
-
"
|
|
288
|
+
"trailing": self.trailing,
|
|
261
289
|
"percentage": self.percentage,
|
|
262
290
|
"open_price": self.open_price,
|
|
263
291
|
"sell_percentage": self.sell_percentage,
|
|
@@ -266,38 +294,72 @@ class TradeTakeProfit(BaseModel):
|
|
|
266
294
|
"sell_amount": self.sell_amount,
|
|
267
295
|
"sold_amount": self.sold_amount,
|
|
268
296
|
"active": self.active,
|
|
269
|
-
"
|
|
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)
|
|
270
303
|
}
|
|
271
304
|
|
|
272
305
|
@staticmethod
|
|
273
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
|
+
|
|
274
328
|
return TradeTakeProfit(
|
|
275
329
|
trade_id=data.get("trade_id"),
|
|
276
|
-
|
|
277
|
-
data.get("trade_risk_type")
|
|
278
|
-
),
|
|
330
|
+
trailing=data.get("trailing"),
|
|
279
331
|
percentage=data.get("percentage"),
|
|
280
332
|
open_price=data.get("open_price"),
|
|
281
333
|
total_amount_trade=data.get("total_amount_trade"),
|
|
282
334
|
sell_percentage=data.get("sell_percentage", 100),
|
|
283
335
|
active=data.get("active", True),
|
|
336
|
+
triggered=data.get("triggered", False),
|
|
337
|
+
triggered_at=triggered_at,
|
|
284
338
|
sell_prices=data.get("sell_prices"),
|
|
285
339
|
sell_dates=data.get("sell_dates"),
|
|
286
340
|
sell_amount=data.get("sell_amount"),
|
|
287
|
-
|
|
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
|
|
288
345
|
)
|
|
289
346
|
|
|
290
347
|
def __repr__(self):
|
|
291
348
|
return self.repr(
|
|
292
349
|
trade_id=self.trade_id,
|
|
293
|
-
|
|
350
|
+
trailing=self.trailing,
|
|
294
351
|
percentage=self.percentage,
|
|
295
352
|
open_price=self.open_price,
|
|
296
353
|
sell_percentage=self.sell_percentage,
|
|
297
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,
|
|
298
358
|
take_profit_price=self.take_profit_price,
|
|
299
359
|
sell_amount=self.sell_amount,
|
|
300
360
|
sold_amount=self.sold_amount,
|
|
301
361
|
active=self.active,
|
|
302
|
-
sell_prices=self.sell_prices
|
|
362
|
+
sell_prices=self.sell_prices,
|
|
363
|
+
created_at=self.created_at,
|
|
364
|
+
updated_at=self.updated_at
|
|
303
365
|
)
|