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.
- investing_algorithm_framework/__init__.py +197 -0
- investing_algorithm_framework/app/__init__.py +47 -0
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
- 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 +2204 -0
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1667 -0
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- 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 +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/__init__.py +35 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
- investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
- investing_algorithm_framework/app/strategy.py +675 -0
- investing_algorithm_framework/app/task.py +41 -0
- investing_algorithm_framework/app/web/__init__.py +5 -0
- investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
- investing_algorithm_framework/app/web/controllers/orders.py +20 -0
- investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
- investing_algorithm_framework/app/web/controllers/positions.py +18 -0
- investing_algorithm_framework/app/web/create_app.py +20 -0
- investing_algorithm_framework/app/web/error_handler.py +59 -0
- investing_algorithm_framework/app/web/responses.py +20 -0
- investing_algorithm_framework/app/web/run_strategies.py +4 -0
- investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
- investing_algorithm_framework/app/web/schemas/order.py +12 -0
- investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
- investing_algorithm_framework/app/web/schemas/position.py +15 -0
- investing_algorithm_framework/app/web/setup_cors.py +6 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +207 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- 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_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/create_app.py +54 -0
- investing_algorithm_framework/dependency_container.py +155 -0
- investing_algorithm_framework/domain/__init__.py +148 -0
- 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 +435 -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 +111 -0
- investing_algorithm_framework/domain/constants.py +83 -0
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +112 -0
- investing_algorithm_framework/domain/models/__init__.py +43 -0
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +25 -0
- 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/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +384 -0
- investing_algorithm_framework/domain/models/order/order_side.py +36 -0
- investing_algorithm_framework/domain/models/order/order_status.py +37 -0
- investing_algorithm_framework/domain/models/order/order_type.py +30 -0
- investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +4 -0
- investing_algorithm_framework/domain/models/position/position.py +68 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +153 -0
- investing_algorithm_framework/domain/models/time_interval.py +124 -0
- investing_algorithm_framework/domain/models/time_unit.py +149 -0
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +13 -0
- investing_algorithm_framework/domain/models/trade/trade.py +388 -0
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/positions/__init__.py +4 -0
- investing_algorithm_framework/domain/positions/position_size.py +41 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/stateless_actions.py +7 -0
- investing_algorithm_framework/domain/strategy.py +44 -0
- investing_algorithm_framework/domain/utils/__init__.py +27 -0
- investing_algorithm_framework/domain/utils/csv.py +104 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +41 -0
- investing_algorithm_framework/domain/utils/signatures.py +17 -0
- investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
- investing_algorithm_framework/domain/utils/synchronized.py +12 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +50 -0
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +10 -0
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
- investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +40 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/services/__init__.py +132 -0
- investing_algorithm_framework/services/backtesting/__init__.py +5 -0
- investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
- investing_algorithm_framework/services/configuration_service.py +96 -0
- 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/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +114 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +181 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +75 -0
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +40 -0
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
- investing_algorithm_framework/services/trade_service/__init__.py +3 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
- investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
- investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
- investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
- investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
- 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
|
+
)
|