diff --git a/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/02 Place Orders.html b/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/02 Place Orders.html index 9b225a99c9..986ffab6aa 100644 --- a/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/02 Place Orders.html +++ b/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/02 Place Orders.html @@ -16,7 +16,7 @@ ]) -
The child orders are independent of each other and can be for different securities. To relate the child orders, pass them through the OrderFactory.OneCancelsOtherorder_factory.one_cancels_other or OrderFactory.OneUpdatesOtherorder_factory.one_updates_other method first, as described in Custom Contingent Orders. A child order can trigger other orders in turn. To create a chain, call the Triggerstriggers method on the child order request.
The child orders are independent of each other and can be for different securities. To relate the child orders, pass them through the OrderFactory.OneCancelsOtherorder_factory.one_cancels_other or OrderFactory.OneUpdatesOtherorder_factory.one_updates_other method first, as described in Custom Contingent Orders. A child order can trigger its own child orders, which are grandchild orders of the parent. To create a chain, call the Triggerstriggers method on the child order request.
// The IBM order triggers the BAC order once it fills. @@ -27,6 +27,8 @@ tickets = self.one_triggers_other_order(parent, [child])
The brokerage holds the grandchild order until the child order completely fills. The method returns the order tickets in the order parent, child, and grandchild, with each parent before the orders it triggers. If you cancel the parent, LEAN cancels the child and the grandchild.
+The following images show the two outcomes of an OTO set. In the first image, the parent order fills and triggers the child order, which fills later:
diff --git a/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/99 Examples.html b/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/99 Examples.html
index 5531652dc3..776e36f7a0 100644
--- a/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/99 Examples.html
+++ b/03 Writing Algorithms/22 Trading and Orders/03 Order Types/13 Contingent Orders/03 One Triggers Other Orders/99 Examples.html
@@ -43,3 +43,57 @@ The following algorithm trades SPY in a price range with a chain of three orders. The parent buys SPY 1% below the close price. When the parent fills, its child sells the position 1% above the close price. When the child fills, the grandchild buys SPY again at the entry price. While the algorithm holds the position of the grandchild, it places a new take-profit order.
+public class OneTriggersOtherChainExampleAlgorithm : QCAlgorithm
+{
+ private Symbol _spy;
+
+ public override void Initialize()
+ {
+ SetStartDate(2024, 1, 1);
+ SetEndDate(2024, 12, 31);
+ _spy = AddEquity("SPY", Resolution.Daily).Symbol;
+ }
+
+ public override void OnData(Slice slice)
+ {
+ if (Transactions.GetOpenOrders(_spy).Count > 0 || !slice.Bars.TryGetValue(_spy, out var bar))
+ {
+ return;
+ }
+ var entryPrice = Math.Round(bar.Close * 0.99m, 2);
+ var exitPrice = Math.Round(bar.Close * 1.01m, 2);
+ if (Portfolio[_spy].Invested)
+ {
+ LimitOrder(_spy, -Portfolio[_spy].Quantity, exitPrice);
+ return;
+ }
+ // The take profit triggers the re-entry, which is a grandchild of the entry.
+ var takeProfit = OrderFactory.LimitOrder(_spy, -100, exitPrice).Triggers(OrderFactory.LimitOrder(_spy, 100, entryPrice));
+ OneTriggersOtherOrder(OrderFactory.LimitOrder(_spy, 100, entryPrice), new List<SubmitOrderRequest> { takeProfit });
+ }
+}
+class OneTriggersOtherChainExampleAlgorithm(QCAlgorithm):
+
+ def initialize(self) -> None:
+ self.set_start_date(2024, 1, 1)
+ self.set_end_date(2024, 12, 31)
+ self._spy = self.add_equity("SPY", Resolution.DAILY).symbol
+
+ def on_data(self, slice: Slice) -> None:
+ bar = slice.bars.get(self._spy)
+ if self.transactions.get_open_orders(self._spy) or not bar:
+ return
+ entry_price = round(bar.close * 0.99, 2)
+ exit_price = round(bar.close * 1.01, 2)
+ if self.portfolio[self._spy].invested:
+ self.limit_order(self._spy, -self.portfolio[self._spy].quantity, exit_price)
+ return
+ # The take profit triggers the re-entry, which is a grandchild of the entry.
+ take_profit = self.order_factory.limit_order(self._spy, -100, exit_price).triggers(
+ self.order_factory.limit_order(self._spy, 100, entry_price))
+ self.one_triggers_other_order(self.order_factory.limit_order(self._spy, 100, entry_price), [take_profit])
+