Skip to content

Commit

Permalink
[TradingViewTradingMode] support multiple take profits
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeDSM committed Oct 21, 2024
1 parent 60da13b commit 1a40c84
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Trading/Mode/daily_trading_mode/daily_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ orders price syntax</a>.
- `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 <a target="_blank" rel="noopener" href="https://www.octobot.cloud/en/guides/octobot-trading-modes/order-price-syntax?utm_source=octobot&utm_medium=dk&utm_campaign=regular_open_source_content&utm_content=TradingViewSignalsTradingModeDocs">
orders price syntax</a>.
- `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 <a target="_blank" rel="noopener" href="https://www.octobot.cloud/en/guides/octobot-trading-modes/order-price-syntax?utm_source=octobot&utm_medium=dk&utm_campaign=regular_open_source_content&utm_content=TradingViewSignalsTradingModeDocs">
orders price syntax</a>.
orders price syntax</a>.
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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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"],
Expand All @@ -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,
Expand All @@ -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"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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:
Expand All @@ -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):
Expand Down

0 comments on commit 1a40c84

Please sign in to comment.