From 774b5a5cb1fb61aedaab3db7d7d6405f41c34ee3 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Tue, 21 Jan 2025 01:21:28 +0100 Subject: [PATCH] Issue #771 - The initial options multi-legs orders support was added: - Basic unit tests added to cover advanced orders creation/serialization logic --- Alpaca.Markets.Tests/OrderTypeTest.cs | 43 ++++++++++++++++++++ Alpaca.Markets/Messages/JsonNewOrder.cs | 6 +-- Alpaca.Markets/Parameters/NewOrderRequest.cs | 1 + 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Alpaca.Markets.Tests/OrderTypeTest.cs b/Alpaca.Markets.Tests/OrderTypeTest.cs index 2930b1b9e..42d66356d 100644 --- a/Alpaca.Markets.Tests/OrderTypeTest.cs +++ b/Alpaca.Markets.Tests/OrderTypeTest.cs @@ -125,6 +125,41 @@ public void BracketOrderCreationWorks() .StopLoss(stopLossStopPrice, stopLossLimitPrice)); } + [Fact] + public void MultiLegOrderCreationWorks() + { + const Decimal limitPrice = 100M; + const Decimal ratioQuantity = 0.25M; + + var legs = getOrderLegs() + .Select(tuple => new OrderLeg(Stock, ratioQuantity, tuple.Item1, tuple.Item2)) + .ToList(); + + var marketOrder1 = MultiLegOrder.Market(Quantity, legs[0], legs[1]); + var marketOrder2 = MultiLegOrder.Market(Quantity, legs[0], legs[1], legs[2]); + var marketOrder3 = MultiLegOrder.Market(Quantity, legs[0], legs[1], legs[2], legs[3]); + + var limitOrder1 = MultiLegOrder.Limit(Quantity, limitPrice, legs[0], legs[1]); + var limitOrder2 = MultiLegOrder.Limit(Quantity, limitPrice, legs[0], legs[1], legs[2]); + var limitOrder3 = MultiLegOrder.Limit(Quantity, limitPrice, legs[0], legs[1], legs[2], legs[3]); + + assertOrdersAreEqual(marketOrder1, marketOrder1); + assertOrdersAreEqual(marketOrder2, marketOrder2); + assertOrdersAreEqual(marketOrder3, marketOrder3); + + assertOrdersAreEqual(limitOrder1, limitOrder1); + assertOrdersAreEqual(limitOrder2, limitOrder2); + assertOrdersAreEqual(limitOrder3, limitOrder3); + } + + private static IEnumerable<(PositionIntent, OrderSide)> getOrderLegs() + { + yield return (PositionIntent.BuyToClose, OrderSide.Buy); + yield return (PositionIntent.SellToClose, OrderSide.Sell); + yield return (PositionIntent.BuyToOpen, OrderSide.Buy); + yield return (PositionIntent.SellToOpen, OrderSide.Sell); + } + private static void assertOrdersAreEqual( OrderBase lhs, OrderBase rhs) @@ -227,6 +262,14 @@ private static void assertOrderBasePropertiesAreEqual( Assert.Equal(lhs.Type, rhs.Type); } + private static void assertOrdersAreEqual( + MultiLegOrder lhs, + MultiLegOrder rhs) + { + assertOrderBasePropertiesAreEqual(lhs, rhs); + assertJsonSerializedOrdersAreEqual(lhs, rhs); + } + private static void assertJsonSerializedOrdersAreEqual( OrderBase lhs, OrderBase rhs) => diff --git a/Alpaca.Markets/Messages/JsonNewOrder.cs b/Alpaca.Markets/Messages/JsonNewOrder.cs index 1d2256edb..078b2060e 100644 --- a/Alpaca.Markets/Messages/JsonNewOrder.cs +++ b/Alpaca.Markets/Messages/JsonNewOrder.cs @@ -2,7 +2,7 @@ internal sealed class JsonNewOrder { - [JsonProperty(PropertyName = "symbol", Required = Required.Always)] + [JsonProperty(PropertyName = "symbol", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public String? Symbol { get; set; } [JsonProperty(PropertyName = "qty", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] @@ -14,8 +14,8 @@ internal sealed class JsonNewOrder [JsonProperty(PropertyName = "side", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public OrderSide? OrderSide { get; set; } - [JsonProperty(PropertyName = "type", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] - public OrderType? OrderType { get; set; } + [JsonProperty(PropertyName = "type", Required = Required.Always)] + public OrderType OrderType { get; set; } [JsonProperty(PropertyName = "time_in_force", Required = Required.Always)] public TimeInForce TimeInForce { get; set; } diff --git a/Alpaca.Markets/Parameters/NewOrderRequest.cs b/Alpaca.Markets/Parameters/NewOrderRequest.cs index 2d1e45425..6f8a38b80 100644 --- a/Alpaca.Markets/Parameters/NewOrderRequest.cs +++ b/Alpaca.Markets/Parameters/NewOrderRequest.cs @@ -156,6 +156,7 @@ public NewOrderRequest( /// /// The option multi-leg order leg information. /// Original order request object with new leg. + [UsedImplicitly] public NewOrderRequest With( OptionLegRequest leg) {