Entry that triggers OCO with TP and SL


 

Would anyone have a template for the following order:

Entry that triggers OCO for Take Profit and Stop Loss?

Regards,

Javed


 

Here's one of mine. Can't figure out how to fix the indents though.
 
def place_trailing_oca_bracket_order(self, order_details):
"""
Place a bracket-type OCA order with a trailing stop loss and an adjustable take profit order.
The stop loss is a trailing stop placed at a certain percentage away from the entry price.
The take profit starts as a stop order and converts to a trailing stop upon reaching a profit trigger.
"""
symbol = order_details["symbol"]
entry_quantity = order_details["quantity"]
time_bound = order_details.get("minutes_until_market_close", self.max_allowed_position_minutes)

# Max allowed time in mins
time_bound = min(time_bound, self.max_allowed_position_minutes)

# Determine position direction
action = order_details.get("side", "BUY").upper()
is_long = action == "BUY"
reverse_action = "SELL" if is_long else "BUY"

# Get minimum tick size
min_tick = self.get_min_tick(symbol)

strategy_params = order_details.get("strategy_params", {})

# Extract standard strategy parameters
stop_loss_percent = strategy_params.get("stop_loss_percent")
trailing_stop_percent = strategy_params.get("trailing_stop_percent")
profit_trigger_percent = strategy_params.get("profit_trigger_percent")
profit_lock_in_percent = strategy_params.get("profit_lock_in_percent")
trailing_profit_percent = strategy_params.get("trailing_profit_percent")

# Create entry order with adjusted price based on direction
if is_long:
entry_price = self.get_ask_price(symbol)
else:
entry_price = self.get_bid_price(symbol)

# Calculate price levels based on position direction
if is_long:
# Price adjustments for long positions
# Initial stop loss below entry price
initial_stop_loss_price = self.adjust_price_to_tick_size(entry_price * (1 - stop_loss_percent), min_tick)

initial_stop_loss_protection_price = (
self.adjust_price_to_tick_size(entry_price * (1 - stop_loss_percent), min_tick) - 1
)

# Profit trigger above entry price
profit_trigger_price = self.adjust_price_to_tick_size(entry_price * (1 + profit_trigger_percent), min_tick)

# Lock in price slightly below trigger price
profit_lock_in_price = self.adjust_price_to_tick_size(
entry_price * (1 + profit_lock_in_percent), min_tick
) # Must be lower than trigger for longs to protect profits

# Amount to trail by
trailing_stop_amount = self.adjust_price_to_tick_size(entry_price * trailing_profit_percent, min_tick)
else:
# Price adjustments for short positions
# Initial stop loss above entry price
initial_stop_loss_price = self.adjust_price_to_tick_size(entry_price * (1 + stop_loss_percent), min_tick)
initial_stop_loss_protection_price = (
self.adjust_price_to_tick_size(entry_price * (1 + stop_loss_percent), min_tick) + 1
)

# Profit trigger below entry price
profit_trigger_price = self.adjust_price_to_tick_size(entry_price * (1 - profit_trigger_percent), min_tick)

# Lock in price slightly above trigger price
profit_lock_in_price = self.adjust_price_to_tick_size(
entry_price * (1 - profit_lock_in_percent), min_tick
) # Must be higher than trigger for shorts to protect profits

# Amount to trail by
trailing_stop_amount = self.adjust_price_to_tick_size(entry_price * trailing_stop_percent, min_tick)

contract = self.get_contract(symbol)
prediction_id = order_details.get("prediction_id")

bracket_order = []

# Create entry order
entry_order = LimitOrder(
action=action,
totalQuantity=entry_quantity,
lmtPrice=entry_price,
orderId=self.ib.client.getReqId(),
transmit=False,
orderRef=f"{prediction_id}_entry",
tif="IOC",
)
bracket_order.append(entry_order)

# Create initial trailing stop loss order
stop_loss_order = Order(
action=reverse_action,
orderType="TRAIL",
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
orderRef=f"{prediction_id}_stop_loss",
ocaType=2,
outsideRth=True,
ocaGroup=prediction_id,
auxPrice=trailing_stop_amount, # Trailing amount using trailing_stop_percent
trailStopPrice=initial_stop_loss_price, # Initial stop price using stop_loss_percent
)
bracket_order.append(stop_loss_order)

# Create first take profit order (triggers at profit_trigger_percent and stops at profit_lock_in_percent)
take_profit_order = Order(
action=reverse_action,
orderType="STP",
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
orderRef=f"{prediction_id}_take_profit_stop",
ocaType=2,
outsideRth=True,
ocaGroup=prediction_id,
auxPrice=initial_stop_loss_protection_price,
triggerPrice=profit_trigger_price,
adjustedOrderType="STP",
adjustedStopPrice=profit_lock_in_price,
)
bracket_order.append(take_profit_order)

# Create time bound order (same for both market conditions)
time_bound_order = MarketOrder(
action=reverse_action,
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
outsideRth=True,
orderRef=f"{prediction_id}_time_bound",
ocaType=2,
ocaGroup=prediction_id,
)

# Add time condition to the time_bound_order
time_condition_after = TimeCondition()
time_condition_after.time = format_date(add_minutes_to_date(get_utc_now(), time_bound), "%Y%m%d-%H:%M:%S")
time_condition_after.isMore = True
time_bound_order.conditions.append(time_condition_after)

bracket_order.append(time_bound_order)

# Set the last order to transmit
bracket_order[-1].transmit = True

# Place all orders
trades = []
for order in bracket_order:
log.info(f"Placing {order.orderRef} for {'long' if is_long else 'short'} position")
trade = self.ib.placeOrder(contract, order)
trades.append(trade)
log.info(f"Order placed: {order.orderRef} with ID {order.orderId}")

return trades
 
 


 

This is incredibly helpful.  Thank you so much!  Let me know if you ever need anything help from my end.

Javed

On Tue, Dec 17, 2024 at 4:32 PM wordd via groups.io <howtoreached=gmail.com@groups.io> wrote:
Here's one of mine. Can't figure out how to fix the indents though.
 
def place_trailing_oca_bracket_order(self, order_details):
"""
Place a bracket-type OCA order with a trailing stop loss and an adjustable take profit order.
The stop loss is a trailing stop placed at a certain percentage away from the entry price.
The take profit starts as a stop order and converts to a trailing stop upon reaching a profit trigger.
"""
symbol = order_details["symbol"]
entry_quantity = order_details["quantity"]
time_bound = order_details.get("minutes_until_market_close", self.max_allowed_position_minutes)

# Max allowed time in mins
time_bound = min(time_bound, self.max_allowed_position_minutes)

# Determine position direction
action = order_details.get("side", "BUY").upper()
is_long = action == "BUY"
reverse_action = "SELL" if is_long else "BUY"

# Get minimum tick size
min_tick = self.get_min_tick(symbol)

strategy_params = order_details.get("strategy_params", {})

# Extract standard strategy parameters
stop_loss_percent = strategy_params.get("stop_loss_percent")
trailing_stop_percent = strategy_params.get("trailing_stop_percent")
profit_trigger_percent = strategy_params.get("profit_trigger_percent")
profit_lock_in_percent = strategy_params.get("profit_lock_in_percent")
trailing_profit_percent = strategy_params.get("trailing_profit_percent")

# Create entry order with adjusted price based on direction
if is_long:
entry_price = self.get_ask_price(symbol)
else:
entry_price = self.get_bid_price(symbol)

# Calculate price levels based on position direction
if is_long:
# Price adjustments for long positions
# Initial stop loss below entry price
initial_stop_loss_price = self.adjust_price_to_tick_size(entry_price * (1 - stop_loss_percent), min_tick)

initial_stop_loss_protection_price = (
self.adjust_price_to_tick_size(entry_price * (1 - stop_loss_percent), min_tick) - 1
)

# Profit trigger above entry price
profit_trigger_price = self.adjust_price_to_tick_size(entry_price * (1 + profit_trigger_percent), min_tick)

# Lock in price slightly below trigger price
profit_lock_in_price = self.adjust_price_to_tick_size(
entry_price * (1 + profit_lock_in_percent), min_tick
) # Must be lower than trigger for longs to protect profits

# Amount to trail by
trailing_stop_amount = self.adjust_price_to_tick_size(entry_price * trailing_profit_percent, min_tick)
else:
# Price adjustments for short positions
# Initial stop loss above entry price
initial_stop_loss_price = self.adjust_price_to_tick_size(entry_price * (1 + stop_loss_percent), min_tick)
initial_stop_loss_protection_price = (
self.adjust_price_to_tick_size(entry_price * (1 + stop_loss_percent), min_tick) + 1
)

# Profit trigger below entry price
profit_trigger_price = self.adjust_price_to_tick_size(entry_price * (1 - profit_trigger_percent), min_tick)

# Lock in price slightly above trigger price
profit_lock_in_price = self.adjust_price_to_tick_size(
entry_price * (1 - profit_lock_in_percent), min_tick
) # Must be higher than trigger for shorts to protect profits

# Amount to trail by
trailing_stop_amount = self.adjust_price_to_tick_size(entry_price * trailing_stop_percent, min_tick)

contract = self.get_contract(symbol)
prediction_id = order_details.get("prediction_id")

bracket_order = []

# Create entry order
entry_order = LimitOrder(
action=action,
totalQuantity=entry_quantity,
lmtPrice=entry_price,
orderId=self.ib.client.getReqId(),
transmit=False,
orderRef=f"{prediction_id}_entry",
tif="IOC",
)
bracket_order.append(entry_order)

# Create initial trailing stop loss order
stop_loss_order = Order(
action=reverse_action,
orderType="TRAIL",
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
orderRef=f"{prediction_id}_stop_loss",
ocaType=2,
outsideRth=True,
ocaGroup=prediction_id,
auxPrice=trailing_stop_amount, # Trailing amount using trailing_stop_percent
trailStopPrice=initial_stop_loss_price, # Initial stop price using stop_loss_percent
)
bracket_order.append(stop_loss_order)

# Create first take profit order (triggers at profit_trigger_percent and stops at profit_lock_in_percent)
take_profit_order = Order(
action=reverse_action,
orderType="STP",
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
orderRef=f"{prediction_id}_take_profit_stop",
ocaType=2,
outsideRth=True,
ocaGroup=prediction_id,
auxPrice=initial_stop_loss_protection_price,
triggerPrice=profit_trigger_price,
adjustedOrderType="STP",
adjustedStopPrice=profit_lock_in_price,
)
bracket_order.append(take_profit_order)

# Create time bound order (same for both market conditions)
time_bound_order = MarketOrder(
action=reverse_action,
totalQuantity=entry_quantity,
orderId=self.ib.client.getReqId(),
transmit=False,
parentId=entry_order.orderId,
outsideRth=True,
orderRef=f"{prediction_id}_time_bound",
ocaType=2,
ocaGroup=prediction_id,
)

# Add time condition to the time_bound_order
time_condition_after = TimeCondition()
time_condition_after.time = format_date(add_minutes_to_date(get_utc_now(), time_bound), "%Y%m%d-%H:%M:%S")
time_condition_after.isMore = True
time_bound_order.conditions.append(time_condition_after)

bracket_order.append(time_bound_order)

# Set the last order to transmit
bracket_order[-1].transmit = True

# Place all orders
trades = []
for order in bracket_order:
log.info(f"Placing {order.orderRef} for {'long' if is_long else 'short'} position")
trade = self.ib.placeOrder(contract, order)
trades.append(trade)
log.info(f"Order placed: {order.orderRef} with ID {order.orderId}")

return trades
 
 


 

No prob!


 

On Tue, Dec 17, 2024 at 09:32 PM, wordd wrote:

Can't figure out how to fix the indents though.

Just fyi, you can use "Github markdown" for code... wrap it in single backticks for code co-mingled within a line of text, and triple backticks for blocks of code. Attachments should work too (with the obvious side-effect of being separated from the message).


 

Create 3 separate orders.
TP and SL orders will have property "OrderId" that should be the same as the main order ID. 
The first two orders will have property "Transmit" set to false, the last one is true. 


 

Thank you.  I will try this.

On Thu, Dec 19, 2024 at 12:28 AM Andy Sanders via groups.io <arteinvolo=gmail.com@groups.io> wrote:
Create 3 separate orders.
TP and SL orders will have property "OrderId" that should be the same as the main order ID. 
The first two orders will have property "Transmit" set to false, the last one is true. 


 

This is not correct: all three orders must have unique order ids. But the TP and SL orders must have “ParentId” set to the “OrderId” of the main order.

 

 

From: twsapi@groups.io <twsapi@groups.io> On Behalf Of Andy Sanders via groups.io
Sent: 19 December 2024 05:28
To: twsapi@groups.io
Subject: Re: [TWS API] Entry that triggers OCO with TP and SL

 

Create 3 separate orders.

TP and SL orders will have property "OrderId" that should be the same as the main order ID. 

The first two orders will have property "Transmit" set to false, the last one is true. 


 

omg thank you. Didn't realize this was markdown compatible.


 

Got it


On Sat, Dec 21, 2024 at 12:27 PM Richard L King via groups.io <rlking=aultan.com@groups.io> wrote:

This is not correct: all three orders must have unique order ids. But the TP and SL orders must have “ParentId” set to the “OrderId” of the main order.

 

 

From: twsapi@groups.io <twsapi@groups.io> On Behalf Of Andy Sanders via groups.io
Sent: 19 December 2024 05:28
To: twsapi@groups.io
Subject: Re: [TWS API] Entry that triggers OCO with TP and SL

 

Create 3 separate orders.

TP and SL orders will have property "OrderId" that should be the same as the main order ID. 

The first two orders will have property "Transmit" set to false, the last one is true.