Skip to content

Commit

Permalink
Fix combo order quantity update (QuantConnect#7651)
Browse files Browse the repository at this point in the history
* Fix combo order quantity update

* Minor changes

* Fix unit tests

* Cleanup
  • Loading branch information
jhonabreul authored Jan 3, 2024
1 parent aaab65f commit b75ed58
Show file tree
Hide file tree
Showing 46 changed files with 297 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Estimated Strategy Capacity", "$70000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "61.31%"},
{"OrderListHash", "3adbc743489fe6902267726150770f82"}
{"OrderListHash", "a36c60c5fb020121d6541683138d8f28"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/BasicTemplateOptionStrategyAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Estimated Strategy Capacity", "$3000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZFMEBBB2E|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "338.60%"},
{"OrderListHash", "cbe76e0a8288fb9ccfc42b4c20142131"}
{"OrderListHash", "c9eb598f33939941206efc018eb6ee45"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Estimated Strategy Capacity", "$13000000.00"},
{"Lowest Capacity Asset", "SPXW XKX6S2GM9PGU|SPX 31"},
{"Portfolio Turnover", "0.28%"},
{"OrderListHash", "ddc78aa04824180bc36aac3472ae22b4"}
{"OrderListHash", "69b28e4f5b33ff54f173c14ea1e00c50"}
};
}
}
21 changes: 19 additions & 2 deletions Algorithm.CSharp/ComboLegLimitOrderAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,28 @@ namespace QuantConnect.Algorithm.CSharp
/// </summary>
public class ComboLegLimitOrderAlgorithm : ComboOrderAlgorithm
{
private List<decimal> _originalLimitPrices = new();

protected override IEnumerable<OrderTicket> PlaceComboOrder(List<Leg> legs, int quantity, decimal? limitPrice = null)
{
foreach (var leg in legs)
{
_originalLimitPrices.Add(leg.OrderPrice.Value);
leg.OrderPrice *= 2; // Won't fill
}

return ComboLegLimitOrder(legs, quantity);
}

protected override void UpdateComboOrder(List<OrderTicket> tickets)
{
// Let's updated the limit prices to the original values
for (int i = 0; i < tickets.Count; i++)
{
tickets[i].Update(new UpdateOrderFields { LimitPrice = _originalLimitPrices[i] });
}
}

public override void OnEndOfAlgorithm()
{
base.OnEndOfAlgorithm();
Expand Down Expand Up @@ -88,8 +105,8 @@ public override void OnEndOfAlgorithm()
{"Total Fees", "$26.00"},
{"Estimated Strategy Capacity", "$58000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "30.16%"},
{"OrderListHash", "c081092ac475a3aa95fc55a2718e2b25"}
{"Portfolio Turnover", "30.22%"},
{"OrderListHash", "216608fa33a238275b5f4f08e50e45b4"}
};
}
}
20 changes: 18 additions & 2 deletions Algorithm.CSharp/ComboLimitOrderAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public class ComboLimitOrderAlgorithm : ComboOrderAlgorithm
private decimal _limitPrice;
private int _comboQuantity;

private decimal _temporaryLimitPrice;
private int _temporaryComboQuantity;

private int _fillCount;

private decimal _liquidatedQuantity;
Expand All @@ -47,10 +50,23 @@ protected override IEnumerable<OrderTicket> PlaceComboOrder(List<Leg> legs, int
{
_limitPrice = limitPrice.Value;
_comboQuantity = quantity;
_temporaryLimitPrice = limitPrice.Value - Math.Sign(quantity) * limitPrice.Value * 0.5m; // Won't fill
_temporaryComboQuantity = quantity * 10;

legs.ForEach(x => { x.OrderPrice = null; });

return ComboLimitOrder(legs, quantity, _limitPrice);
// First, let's place a limit order that won't fill so we can update it later
return ComboLimitOrder(legs, _temporaryComboQuantity, _temporaryLimitPrice);
}

protected override void UpdateComboOrder(List<OrderTicket> tickets)
{
// Let's update the quantity and limit price to the real values
tickets[0].Update(new UpdateOrderFields
{
Quantity = _comboQuantity,
LimitPrice = _limitPrice
});
}

public override void OnOrderEvent(OrderEvent orderEvent)
Expand Down Expand Up @@ -156,7 +172,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$5000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "60.91%"},
{"OrderListHash", "6ca75024c436ecca6efbe1ddaace4c71"}
{"OrderListHash", "0a8f9edaff4857d0e7731c7b936e4288"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/ComboMarketOrderAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Estimated Strategy Capacity", "$70000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "30.35%"},
{"OrderListHash", "3c43fa5b49373e3c77e000d2283fe4bc"}
{"OrderListHash", "71cb9fdd55ff925f70677ae01a18b90d"}
};
}
}
20 changes: 19 additions & 1 deletion Algorithm.CSharp/ComboOrderAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public abstract class ComboOrderAlgorithm : QCAlgorithm, IRegressionAlgorithmDef
{
private Symbol _optionSymbol;

private List<OrderTicket> Tickets { get; set; }
private bool _updated;

protected List<OrderEvent> FillOrderEvents { get; private set; } = new();

protected List<Leg> OrderLegs { get; private set; }
Expand Down Expand Up @@ -85,9 +88,19 @@ public override void OnData(Slice slice)
Leg.Create(callContracts[1].Symbol, -2, 14.6m),
Leg.Create(callContracts[2].Symbol, 1, 14.0m)
};
PlaceComboOrder(OrderLegs, ComboOrderQuantity, 1.9m);
Tickets = PlaceComboOrder(OrderLegs, ComboOrderQuantity, 1.9m).ToList();
}
}
// Let's test order updates
else if (Tickets.All(ticket => ticket.OrderType != OrderType.ComboMarket) && FillOrderEvents.Count == 0 && !_updated)
{
UpdateComboOrder(Tickets);
_updated = true;
}
}

protected virtual void UpdateComboOrder(List<OrderTicket> tickets)
{
}

public override void OnOrderEvent(OrderEvent orderEvent)
Expand All @@ -107,6 +120,11 @@ public override void OnEndOfAlgorithm()
throw new Exception("Combo order legs were not initialized");
}

if (Tickets.All(ticket => ticket.OrderType != OrderType.ComboMarket) && !_updated)
{
throw new Exception("Combo order was not updated");
}

if (FillOrderEvents.Count != ExpectedFillCount)
{
throw new Exception($"Expected {ExpectedFillCount} fill order events, found {FillOrderEvents.Count}");
Expand Down
6 changes: 3 additions & 3 deletions Algorithm.CSharp/ComboOrderTicketDemoAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private void ComboLegLimitOrders()

foreach (var ticket in combo1)
{
var newLimit = ticket.Get(OrderField.LimitPrice) + (ticket.Quantity > 0 ? 1m : -1m) * 0.01m;
var newLimit = Math.Round(ticket.Get(OrderField.LimitPrice) + (ticket.Quantity > 0 ? 1m : -1m) * 0.01m, 2);
Debug($"Updating limits - Combo #1: {newLimit.ToStringInvariant("0.00")}");

ticket.Update(new UpdateOrderFields
Expand All @@ -221,7 +221,7 @@ private void ComboLegLimitOrders()

foreach (var ticket in combo2)
{
var newLimit = ticket.Get(OrderField.LimitPrice) + (ticket.Quantity > 0 ? 1m : -1m) * 0.01m;
var newLimit = Math.Round(ticket.Get(OrderField.LimitPrice) + (ticket.Quantity > 0 ? 1m : -1m) * 0.01m, 2);
Debug($"Updating limits - Combo #2: {newLimit.ToStringInvariant("0.00")}");

ticket.Update(new UpdateOrderFields
Expand Down Expand Up @@ -367,7 +367,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$2000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "58.98%"},
{"OrderListHash", "1254db09e88826bed15bbce3aff11365"}
{"OrderListHash", "c455fd803ce5f4b7902a97d84c14629a"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/ComboOrdersFillModelAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$250000.00"},
{"Lowest Capacity Asset", "IBM R735QTJ8XC9X"},
{"Portfolio Turnover", "9.81%"},
{"OrderListHash", "411685f7f8454b5ee59a3fe42ddec90a"}
{"OrderListHash", "ea77edfba185b1bee9961fcbcd3a20ef"}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$120000.00"},
{"Lowest Capacity Asset", "GOOCV WBGM92QHIYO6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "32.18%"},
{"OrderListHash", "8e62cd986a2ba74341db4f17816a1583"}
{"OrderListHash", "a22851491e8b6a12e1a2f741b084781c"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$160000.00"},
{"Lowest Capacity Asset", "GOOCV 30AKMEIPOSS1Y|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "32.12%"},
{"OrderListHash", "dc343e84c50c44cb1ee72aaea67f5bfb"}
{"OrderListHash", "87079f2dfad700b5362ce26a1f80c1ef"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/CoveredCallComboLimitOrderAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Estimated Strategy Capacity", "$8000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZFMEBBB2E|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "227.27%"},
{"OrderListHash", "5d9434c81e36c12d3e34b0d2c39468b2"}
{"OrderListHash", "4521f5e6f5d0929a907e9832ce31c4a3"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ protected override void AssertState(OrderTicket ticket, int expectedGroupCount,
{"Estimated Strategy Capacity", "$0"},
{"Lowest Capacity Asset", ""},
{"Portfolio Turnover", "0%"},
{"OrderListHash", "2c50d5dba71820a101e55e61236b5caa"}
{"OrderListHash", "a0ccd30c00ec046a942c6e2885dfb237"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/IronCondorStrategyAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$4000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2.00%"},
{"OrderListHash", "4d6ce6322499eb3f564c973fdc2a6fc7"}
{"OrderListHash", "95337d99685b140f2534ffb21afed10b"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/LargeQuantityOptionStrategyAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$6000.00"},
{"Lowest Capacity Asset", "GOOCV 30AKMELSHQVZA|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "208.51%"},
{"OrderListHash", "3b38055cdc7e0643cc32f0ce24d651a9"}
{"OrderListHash", "f53992823027e3b976ab8de0b3f96100"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$13000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "1.31%"},
{"OrderListHash", "85d15c33656b556a9bae6304ec8d6193"}
{"OrderListHash", "5c3873497b1108a1caeb6c6ddc941c56"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$7000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZFMEBBB2E|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2.17%"},
{"OrderListHash", "8d756e8d2da7e9afeb2bb09a6ee29cf3"}
{"OrderListHash", "d03d3bf7e0247f8c41fb58639d0bcd03"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$4000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2.23%"},
{"OrderListHash", "b0cddb103e05acf099ab78f92fff9cbb"}
{"OrderListHash", "64404785e4781b384f457e3661c41393"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$7000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZEOEHQRYE|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "1.85%"},
{"OrderListHash", "713df4f63925b9193a81f3e5e6136870"}
{"OrderListHash", "5de301650de7e202677b008e52c0f9a1"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$14000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZK4DP0LC6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "1.87%"},
{"OrderListHash", "f870b902db2b022768f705a1befa4f93"}
{"OrderListHash", "2d36b79f33d5637b18f77a0dd4bb2b18"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$16000.00"},
{"Lowest Capacity Asset", "GOOCV WBGM92QHIYO6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "4.31%"},
{"OrderListHash", "ba72827dda76607a57eeca6f8e87e862"}
{"OrderListHash", "cdf4b3ca6cae4aba68be24deb93ff0a3"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$15000.00"},
{"Lowest Capacity Asset", "GOOCV 30AKMELSHQVZA|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "4.21%"},
{"OrderListHash", "e2227791e3ca5bf42a6286ef8014744e"}
{"OrderListHash", "dff4c8a7a24047e7a857fda355502b36"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/NakedCallStrategyAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$8000.00"},
{"Lowest Capacity Asset", "GOOCV WBGM92QHIYO6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2.19%"},
{"OrderListHash", "01b1231544147c79964ba9c0c4ede790"}
{"OrderListHash", "44e53ac6f2dff1ad4a7c34bcd4e5f0d4"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/NakedPutStrategyAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected override void LiquidateStrategy()
{"Estimated Strategy Capacity", "$10000.00"},
{"Lowest Capacity Asset", "GOOCV 30AKMEIPOSS1Y|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2.13%"},
{"OrderListHash", "c9e0862ca284ca35a4d1abb9ce58ea46"}
{"OrderListHash", "27cefe3a419018bb6b0bd26735cad576"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$1800000.00"},
{"Lowest Capacity Asset", "GOOCV 30AKMEIPOSS1Y|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "7.50%"},
{"OrderListHash", "0418745669e8e8c3c98ff75c10deeab5"}
{"OrderListHash", "e2e0ddfc52c963683da287cf7600fcc5"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected override void PlaceTrades(OptionContract optionContract)
{"Estimated Strategy Capacity", "$8800000.00"},
{"Lowest Capacity Asset", "GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "7580.62%"},
{"OrderListHash", "ea97bdd2b85abb9e628d44788bad0eba"}
{"OrderListHash", "d19c696bc30e5d6ea743d7ba33b07925"}
};
}
}
2 changes: 1 addition & 1 deletion Algorithm.CSharp/RevertComboOrderPositionsAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private decimal GetComboOrderFillPrice(List<OrderTicket> orderTickets)
{"Estimated Strategy Capacity", "$16000.00"},
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "2088.83%"},
{"OrderListHash", "28d2da503142331e4129ea11cd36857a"}
{"OrderListHash", "f2b37471b38c6ee668024690407a2131"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public override void OnEndOfAlgorithm()
{"Estimated Strategy Capacity", "$190000.00"},
{"Lowest Capacity Asset", "GOOCV 306CZK4DP0LC6|GOOCV VP83T1ZUHROL"},
{"Portfolio Turnover", "1.19%"},
{"OrderListHash", "c7672fdeef16c3c524e1e10557e2e4e3"}
{"OrderListHash", "4a5e3aac203b4a44532db1e657de3245"}
};
}
}
4 changes: 2 additions & 2 deletions Algorithm.Python/ComboOrderTicketDemoAlgorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ def ComboLegLimitOrders(self):
# if neither order has filled, bring in the limits by a penny

for ticket in combo1:
newLimit = ticket.Get(OrderField.LimitPrice) + (1 if ticket.Quantity > 0 else -1) * 0.01
newLimit = round(ticket.Get(OrderField.LimitPrice) + (1 if ticket.Quantity > 0 else -1) * 0.01, 2)
self.Debug(f"Updating limits - Combo #1: {newLimit:.2f}")
fields = UpdateOrderFields()
fields.LimitPrice = newLimit
fields.Tag = f"Update #{len(ticket.UpdateRequests) + 1}"
ticket.Update(fields)

for ticket in combo2:
newLimit = ticket.Get(OrderField.LimitPrice) + (1 if ticket.Quantity > 0 else -1) * 0.01
newLimit = round(ticket.Get(OrderField.LimitPrice) + (1 if ticket.Quantity > 0 else -1) * 0.01, 2)
self.Debug(f"Updating limits - Combo #2: {newLimit:.2f}")
fields.LimitPrice = newLimit
fields.Tag = f"Update #{len(ticket.UpdateRequests) + 1}"
Expand Down
Loading

0 comments on commit b75ed58

Please sign in to comment.