diff --git a/Trading/Mode/daily_trading_mode/daily_trading.py b/Trading/Mode/daily_trading_mode/daily_trading.py
index e035ed3fd..a658fc1c2 100644
--- a/Trading/Mode/daily_trading_mode/daily_trading.py
+++ b/Trading/Mode/daily_trading_mode/daily_trading.py
@@ -609,7 +609,7 @@ async def create_new_orders(self, symbol, final_note, state, **kwargs):
symbol_market,
price
)
- for price in data.get(self.ADDITIONAL_TAKE_PROFIT_PRICES_KEY, [])
+ for price in (data.get(self.ADDITIONAL_TAKE_PROFIT_PRICES_KEY) or [])
]
user_stop_price = trading_personal_data.decimal_adapt_price(
symbol_market,
diff --git a/Trading/Mode/daily_trading_mode/tests/test_daily_trading_mode_consumer.py b/Trading/Mode/daily_trading_mode/tests/test_daily_trading_mode_consumer.py
index a1848ca24..1c12a772a 100644
--- a/Trading/Mode/daily_trading_mode/tests/test_daily_trading_mode_consumer.py
+++ b/Trading/Mode/daily_trading_mode/tests/test_daily_trading_mode_consumer.py
@@ -963,6 +963,24 @@ async def test_chained_stop_loss_and_take_profit_orders(tools):
# take profit only
data = {
consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal("100000"),
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [],
+ consumer.VOLUME_KEY: decimal.Decimal("0.01"),
+ }
+ orders_with_tp = await consumer.create_new_orders(symbol, decimal.Decimal(str(-1)), state, data=data)
+ buy_order = orders_with_tp[0]
+ assert len(buy_order.chained_orders) == 1
+ take_profit_order = buy_order.chained_orders[0]
+ assert isinstance(take_profit_order, trading_personal_data.SellLimitOrder)
+ assert take_profit_order.origin_quantity == decimal.Decimal("0.01") \
+ - trading_personal_data.get_fees_for_currency(buy_order.fee, take_profit_order.quantity_currency)
+ assert take_profit_order.origin_price == decimal.Decimal("100000")
+ assert take_profit_order.is_waiting_for_chained_trigger
+ assert take_profit_order.associated_entry_ids == [buy_order.order_id]
+ assert not take_profit_order.is_open()
+ assert not take_profit_order.is_created()
+ # take profit only using ADDITIONAL_TAKE_PROFIT_PRICES_KEY
+ data = {
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [decimal.Decimal("100000")],
consumer.VOLUME_KEY: decimal.Decimal("0.01"),
}
orders_with_tp = await consumer.create_new_orders(symbol, decimal.Decimal(str(-1)), state, data=data)
diff --git a/Trading/Mode/trading_view_signals_trading_mode/resources/TradingViewSignalsTradingMode.md b/Trading/Mode/trading_view_signals_trading_mode/resources/TradingViewSignalsTradingMode.md
index 256f9265e..648b96170 100644
--- a/Trading/Mode/trading_view_signals_trading_mode/resources/TradingViewSignalsTradingMode.md
+++ b/Trading/Mode/trading_view_signals_trading_mode/resources/TradingViewSignalsTradingMode.md
@@ -51,7 +51,8 @@ orders price syntax.
- `STOP_PRICE` is the price of the stop order to create. Can also be a delta or % delta like `PRICE`. When increasing the position or buying in spot trading, the stop loss will automatically be created once the initial order is filled. When decreasing the position (or selling in spot) using a LIMIT `ORDER_TYPE`, the stop loss will be created instantly. *Orders crated this way are compatible with PNL history.* It follows the
orders price syntax.
- `TAKE_PROFIT_PRICE` is the price of the take profit order to create. Can also be a delta or % delta like `PRICE`. When increasing the position or buying in spot trading, the take profit will automatically be created once the initial order is filled. When decreasing the position (or selling in spot) using a LIMIT `ORDER_TYPE`, the take profit will be created instantly. *Orders crated this way are compatible with PNL history.* It follows the
-orders price syntax.
+orders price syntax.
+Multiple take profit prices can be used from `TAKE_PROFIT_PRICE_1`, `TAKE_PROFIT_PRICE_2`, ...
- `REDUCE_ONLY` when true, only reduce the current position (avoid accidental short position opening when reducing a long position). **Only used in futures trading**. Default is false
- `TAG` is an identifier to give to the orders to create.
diff --git a/Trading/Mode/trading_view_signals_trading_mode/tests/test_trading_view_signals_trading.py b/Trading/Mode/trading_view_signals_trading_mode/tests/test_trading_view_signals_trading.py
index 4351625f8..c6fb0d99c 100644
--- a/Trading/Mode/trading_view_signals_trading_mode/tests/test_trading_view_signals_trading.py
+++ b/Trading/Mode/trading_view_signals_trading_mode/tests/test_trading_view_signals_trading.py
@@ -162,11 +162,13 @@ async def test_parse_signal_data():
errors = []
assert Mode.TradingViewSignalsTradingMode.parse_signal_data(
- "KEY=value;EXCHANGE;PLOp=ABC",
+ "KEY=value;EXCHANGE;PLOp=ABC;TAKE_PROFIT_PRICE=1;TAKE_PROFIT_PRICE_2=3",
errors
) == {
"KEY": "value",
"PLOp": "ABC",
+ "TAKE_PROFIT_PRICE": "1",
+ "TAKE_PROFIT_PRICE_2": "3",
}
assert len(errors) == 1
assert "EXCHANGE" in str(errors[0])
@@ -301,6 +303,7 @@ async def test_signal_callback(tools):
consumer.STOP_PRICE_KEY: decimal.Decimal(math.nan),
consumer.STOP_ONLY: False,
consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal(math.nan),
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [],
consumer.REDUCE_ONLY_KEY: False,
consumer.TAG_KEY: None,
consumer.EXCHANGE_ORDER_IDS: None,
@@ -327,6 +330,7 @@ async def test_signal_callback(tools):
consumer.STOP_PRICE_KEY: decimal.Decimal("25000"),
consumer.STOP_ONLY: True,
consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal(math.nan),
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [],
consumer.REDUCE_ONLY_KEY: False,
consumer.TAG_KEY: "stop_1_tag",
consumer.EXCHANGE_ORDER_IDS: None,
@@ -357,6 +361,7 @@ async def test_signal_callback(tools):
consumer.STOP_PRICE_KEY: decimal.Decimal("12"),
consumer.STOP_ONLY: False,
consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal("22222"),
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [],
consumer.REDUCE_ONLY_KEY: True,
consumer.TAG_KEY: None,
mode.EXCHANGE_ORDER_IDS: ["ab1", "aaaaa"],
@@ -376,7 +381,9 @@ async def test_signal_callback(tools):
mode.REDUCE_ONLY_KEY: True,
mode.ORDER_TYPE_SIGNAL: "LiMiT",
mode.STOP_PRICE_KEY: "-10%", # price - 10%
- mode.TAKE_PROFIT_PRICE_KEY: "120.333333333333333d", # price + 120.333333333333333
+ f"{mode.TAKE_PROFIT_PRICE_KEY}_0": "120.333333333333333d", # price + 120.333333333333333
+ f"{mode.TAKE_PROFIT_PRICE_KEY}_1": "122.333333333333333d", # price + 122.333333333333333
+ f"{mode.TAKE_PROFIT_PRICE_KEY}_2": "4444d", # price + 4444
mode.EXCHANGE_ORDER_IDS: ["ab1", "aaaaa"],
"PARAM_TAG_1": "ttt",
"PARAM_Plop": False,
@@ -389,7 +396,10 @@ async def test_signal_callback(tools):
consumer.VOLUME_KEY: decimal.Decimal("1"),
consumer.STOP_PRICE_KEY: decimal.Decimal("6308.27549999"),
consumer.STOP_ONLY: False,
- consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal("7129.52833333"),
+ consumer.TAKE_PROFIT_PRICE_KEY: decimal.Decimal("nan"), # only additional TP orders are provided
+ consumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: [
+ decimal.Decimal("7129.52833333"), decimal.Decimal("7131.52833333"), decimal.Decimal('11453.19499999')
+ ],
consumer.REDUCE_ONLY_KEY: True,
consumer.TAG_KEY: None,
mode.EXCHANGE_ORDER_IDS: ["ab1", "aaaaa"],
diff --git a/Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py b/Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py
index 1582d58d8..0a2265f22 100644
--- a/Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py
+++ b/Trading/Mode/trading_view_signals_trading_mode/trading_view_signals_trading.py
@@ -273,6 +273,9 @@ async def _parse_order_details(self, ctx, parsed_data):
tp_price = await self._parse_price(
ctx, parsed_data, TradingViewSignalsTradingMode.TAKE_PROFIT_PRICE_KEY, math.nan
)
+ additional_tp_prices = await self._parse_additional_prices(
+ ctx, parsed_data, f"{TradingViewSignalsTradingMode.TAKE_PROFIT_PRICE_KEY}_", math.nan
+ )
allow_holdings_adaptation = parsed_data.get(TradingViewSignalsTradingMode.ALLOW_HOLDINGS_ADAPTATION_KEY, False)
order_data = {
@@ -283,6 +286,7 @@ async def _parse_order_details(self, ctx, parsed_data):
TradingViewSignalsModeConsumer.STOP_PRICE_KEY: stop_price,
TradingViewSignalsModeConsumer.STOP_ONLY: order_type == TradingViewSignalsTradingMode.STOP_SIGNAL,
TradingViewSignalsModeConsumer.TAKE_PROFIT_PRICE_KEY: tp_price,
+ TradingViewSignalsModeConsumer.ADDITIONAL_TAKE_PROFIT_PRICES_KEY: additional_tp_prices,
TradingViewSignalsModeConsumer.REDUCE_ONLY_KEY:
parsed_data.get(TradingViewSignalsTradingMode.REDUCE_ONLY_KEY, False),
TradingViewSignalsModeConsumer.TAG_KEY:
@@ -293,6 +297,13 @@ async def _parse_order_details(self, ctx, parsed_data):
}
return state, order_data
+ async def _parse_additional_prices(self, ctx, parsed_data, price_prefix, default):
+ prices = []
+ for key, value in parsed_data.items():
+ if key.startswith(price_prefix) and len(key.split(price_prefix)) == 2:
+ prices.append(await self._parse_price(ctx, parsed_data, key, default))
+ return prices
+
async def _parse_price(self, ctx, parsed_data, key, default):
target_price = decimal.Decimal(str(default))
if input_price_or_offset := parsed_data.get(key, 0):