From 4f0a8cf3054810aa05de9197db6b2e5c4ed623ea Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Mon, 20 Jan 2025 16:34:17 +0100 Subject: [PATCH 1/7] Issue #771 - The initial options multi-legs orders support was added: - The low-level JSON object extended with the new property and additional class for it - The order request class extended with ability to specify legs as part of object - The new order type added for supporting a new complex order types --- Alpaca.Markets/CompatibilitySuppressions.xml | 28 +++++ Alpaca.Markets/Enums/OrderClass.cs | 9 +- Alpaca.Markets/Helpers/Validation.cs | 4 + Alpaca.Markets/Messages/JsonNewOrder.cs | 13 ++- Alpaca.Markets/Messages/JsonOrderLeg.cs | 16 +++ Alpaca.Markets/Parameters/NewOrderRequest.cs | 51 ++++++++- Alpaca.Markets/Parameters/OptionLegRequest.cs | 106 ++++++++++++++++++ Alpaca.Markets/PublicAPI.Shipped.txt | 4 +- Alpaca.Markets/PublicAPI.Unshipped.txt | 12 ++ 9 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 Alpaca.Markets/Messages/JsonOrderLeg.cs create mode 100644 Alpaca.Markets/Parameters/OptionLegRequest.cs diff --git a/Alpaca.Markets/CompatibilitySuppressions.xml b/Alpaca.Markets/CompatibilitySuppressions.xml index f140f4eb7..b47e1b2a5 100644 --- a/Alpaca.Markets/CompatibilitySuppressions.xml +++ b/Alpaca.Markets/CompatibilitySuppressions.xml @@ -568,6 +568,13 @@ lib/net462/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.NewOrderRequest.get_Side + lib/net462/Alpaca.Markets.dll + lib/net462/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.PortfolioHistoryRequest.get_TimeInterval @@ -862,6 +869,13 @@ lib/netstandard2.1/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.NewOrderRequest.get_Side + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.PortfolioHistoryRequest.get_TimeInterval @@ -1156,6 +1170,13 @@ lib/netstandard2.0/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.NewOrderRequest.get_Side + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.PortfolioHistoryRequest.get_TimeInterval @@ -1450,6 +1471,13 @@ lib/netstandard2.1/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.NewOrderRequest.get_Side + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.PortfolioHistoryRequest.get_TimeInterval diff --git a/Alpaca.Markets/Enums/OrderClass.cs b/Alpaca.Markets/Enums/OrderClass.cs index a4ead0ec7..76eee591a 100644 --- a/Alpaca.Markets/Enums/OrderClass.cs +++ b/Alpaca.Markets/Enums/OrderClass.cs @@ -4,6 +4,7 @@ namespace Alpaca.Markets; /// Order class for advanced orders in the Alpaca REST API. /// [JsonConverter(typeof(StringEnumConverter))] +[SuppressMessage("ReSharper", "StringLiteralTypo")] public enum OrderClass { /// @@ -29,5 +30,11 @@ public enum OrderClass /// One Triggers Other order /// [EnumMember(Value = "oto")] - OneTriggersOther + OneTriggersOther, + + /// + /// Multi-leg options order + /// + [EnumMember(Value = "mleg")] + MultiLegOptions } diff --git a/Alpaca.Markets/Helpers/Validation.cs b/Alpaca.Markets/Helpers/Validation.cs index a49f49576..7eb90176d 100644 --- a/Alpaca.Markets/Helpers/Validation.cs +++ b/Alpaca.Markets/Helpers/Validation.cs @@ -53,6 +53,10 @@ public static TRequest Validate( return request; } + public static IEnumerable GetExceptions( + this IEnumerable items) => + items.OfType().SelectMany(request => request.GetExceptions()); + public static RequestValidationException? TryValidateSymbolName( this String symbolName, [CallerArgumentExpression(nameof(symbolName))] String propertyName = "") => diff --git a/Alpaca.Markets/Messages/JsonNewOrder.cs b/Alpaca.Markets/Messages/JsonNewOrder.cs index 86d259f35..1d2256edb 100644 --- a/Alpaca.Markets/Messages/JsonNewOrder.cs +++ b/Alpaca.Markets/Messages/JsonNewOrder.cs @@ -3,7 +3,7 @@ internal sealed class JsonNewOrder { [JsonProperty(PropertyName = "symbol", Required = Required.Always)] - public String Symbol { get; set; } = String.Empty; + public String? Symbol { get; set; } [JsonProperty(PropertyName = "qty", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public Decimal? Quantity { get; set; } @@ -11,11 +11,11 @@ internal sealed class JsonNewOrder [JsonProperty(PropertyName = "notional", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public Decimal? Notional { get; set; } - [JsonProperty(PropertyName = "side", Required = Required.Always)] - public OrderSide OrderSide { get; set; } + [JsonProperty(PropertyName = "side", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public OrderSide? OrderSide { get; set; } - [JsonProperty(PropertyName = "type", Required = Required.Always)] - public OrderType OrderType { get; set; } + [JsonProperty(PropertyName = "type", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public OrderType? OrderType { get; set; } [JsonProperty(PropertyName = "time_in_force", Required = Required.Always)] public TimeInForce TimeInForce { get; set; } @@ -49,4 +49,7 @@ internal sealed class JsonNewOrder [JsonProperty(PropertyName = "position_intent", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public PositionIntent? PositionIntent { get; set; } + + [JsonProperty(PropertyName = "legs", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public List? Legs { get; set; } } diff --git a/Alpaca.Markets/Messages/JsonOrderLeg.cs b/Alpaca.Markets/Messages/JsonOrderLeg.cs new file mode 100644 index 000000000..0ed9e72a0 --- /dev/null +++ b/Alpaca.Markets/Messages/JsonOrderLeg.cs @@ -0,0 +1,16 @@ +namespace Alpaca.Markets; + +internal sealed class JsonOrderLeg +{ + [JsonProperty(PropertyName = "symbol", Required = Required.Always)] + public String Symbol { get; set; } = String.Empty; + + [JsonProperty(PropertyName = "ratio_qty", Required = Required.Always)] + public Decimal RatioQuantity { get; set; } + + [JsonProperty(PropertyName = "side", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public OrderSide? OrderSide { get; set; } + + [JsonProperty(PropertyName = "position_intent", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public PositionIntent? PositionIntent { get; set; } +} diff --git a/Alpaca.Markets/Parameters/NewOrderRequest.cs b/Alpaca.Markets/Parameters/NewOrderRequest.cs index 0c6eb2372..2d1e45425 100644 --- a/Alpaca.Markets/Parameters/NewOrderRequest.cs +++ b/Alpaca.Markets/Parameters/NewOrderRequest.cs @@ -6,6 +6,8 @@ [UsedImplicitly] public sealed class NewOrderRequest : Validation.IRequest { + private readonly List _legs = []; + /// /// Creates new instance of object. /// @@ -26,16 +28,32 @@ public NewOrderRequest( { Symbol = symbol.EnsureNotNull(); Quantity = quantity; + Duration = duration; Side = side; Type = type; + } + + /// + /// Creates new instance of object. + /// + /// Order quantity. + /// Order duration. + /// Order type. + public NewOrderRequest( + OrderQuantity quantity, + OrderType type, + TimeInForce duration) + { + Quantity = quantity; Duration = duration; + Type = type; } /// /// Gets the new order asset symbol. /// [UsedImplicitly] - public String Symbol { get; } + public String? Symbol { get; } /// /// Gets the new order quantity. @@ -47,7 +65,7 @@ public NewOrderRequest( /// Gets the new order side (buy or sell). /// [UsedImplicitly] - public OrderSide Side { get; } + public OrderSide? Side { get; } /// /// Gets the new order type. @@ -127,12 +145,36 @@ public NewOrderRequest( [UsedImplicitly] public PositionIntent? PositionIntent { get; set; } + /// + /// Gets the list of order legs for option multi-legs order. + /// + [UsedImplicitly] + public IReadOnlyList Legs => _legs; + + /// + /// Adds the option multi-leg into the collection. + /// + /// The option multi-leg order leg information. + /// Original order request object with new leg. + public NewOrderRequest With( + OptionLegRequest leg) + { + _legs.Add(leg); + return this; + } + IEnumerable Validation.IRequest.GetExceptions() { ClientOrderId = ClientOrderId?.TrimClientOrderId(); - yield return Symbol.TryValidateSymbolName(); + yield return Symbol?.TryValidateSymbolName(); yield return Quantity.TryValidateQuantity(); + + foreach (var exception in Legs.GetExceptions()) + { + yield return exception; + } } + internal JsonNewOrder GetJsonRequest() => new() { @@ -163,6 +205,9 @@ StopLossLimitPrice is not null StopPrice = StopLossStopPrice, LimitPrice = StopLossLimitPrice } + : null, + Legs = Legs.Count != 0 + ? Legs.Select(leg => leg.GetJsonRequest()).ToList() : null }; } diff --git a/Alpaca.Markets/Parameters/OptionLegRequest.cs b/Alpaca.Markets/Parameters/OptionLegRequest.cs new file mode 100644 index 000000000..b0b6738c1 --- /dev/null +++ b/Alpaca.Markets/Parameters/OptionLegRequest.cs @@ -0,0 +1,106 @@ +namespace Alpaca.Markets; + +/// +/// Encapsulates request parameters for call. +/// +[UsedImplicitly] +public sealed class OptionLegRequest : Validation.IRequest +{ + /// + /// Creates new instance of object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order side (buy or sell). + /// Order position intent. + /// + /// The argument is null. + /// + public OptionLegRequest( + String symbol, + Decimal ratioQuantity, + OrderSide side, + PositionIntent positionIntent) + { + Symbol = symbol.EnsureNotNull(); + PositionIntent = positionIntent; + RatioQuantity = ratioQuantity; + Side = side; + } + + /// + /// Creates new instance of object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order side (buy or sell). + /// + /// The argument is null. + /// + public OptionLegRequest( + String symbol, + Decimal ratioQuantity, + OrderSide side) + { + Symbol = symbol.EnsureNotNull(); + RatioQuantity = ratioQuantity; + Side = side; + } + + /// + /// Creates new instance of object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order position intent. + /// + /// The argument is null. + /// + public OptionLegRequest( + String symbol, + Decimal ratioQuantity, + PositionIntent positionIntent) + { + Symbol = symbol.EnsureNotNull(); + PositionIntent = positionIntent; + RatioQuantity = ratioQuantity; + } + + /// + /// Gets the new order asset symbol. + /// + [UsedImplicitly] + public String Symbol { get; } + + /// + /// Gets the proportional quantity of this leg in relation to the overall multi-leg order quantity. + /// + [UsedImplicitly] + public Decimal RatioQuantity { get; } + + /// + /// Gets the new order side (buy or sell). + /// + [UsedImplicitly] + public OrderSide? Side { get; } + + /// + /// Gets the optional position intent for order placement. + /// + [UsedImplicitly] + public PositionIntent? PositionIntent { get; } + + IEnumerable Validation.IRequest.GetExceptions() + { + yield return Symbol.TryValidateSymbolName(); + } + + internal JsonOrderLeg GetJsonRequest() => + new() + { + Symbol = Symbol, + OrderSide = Side, + RatioQuantity = RatioQuantity, + PositionIntent = PositionIntent + }; +} diff --git a/Alpaca.Markets/PublicAPI.Shipped.txt b/Alpaca.Markets/PublicAPI.Shipped.txt index 8c7b718e1..8191ca20e 100644 --- a/Alpaca.Markets/PublicAPI.Shipped.txt +++ b/Alpaca.Markets/PublicAPI.Shipped.txt @@ -1076,14 +1076,14 @@ Alpaca.Markets.NewOrderRequest.OrderClass.set -> void Alpaca.Markets.NewOrderRequest.PositionIntent.get -> Alpaca.Markets.PositionIntent? Alpaca.Markets.NewOrderRequest.PositionIntent.set -> void Alpaca.Markets.NewOrderRequest.Quantity.get -> Alpaca.Markets.OrderQuantity -Alpaca.Markets.NewOrderRequest.Side.get -> Alpaca.Markets.OrderSide +Alpaca.Markets.NewOrderRequest.Side.get -> Alpaca.Markets.OrderSide? Alpaca.Markets.NewOrderRequest.StopLossLimitPrice.get -> decimal? Alpaca.Markets.NewOrderRequest.StopLossLimitPrice.set -> void Alpaca.Markets.NewOrderRequest.StopLossStopPrice.get -> decimal? Alpaca.Markets.NewOrderRequest.StopLossStopPrice.set -> void Alpaca.Markets.NewOrderRequest.StopPrice.get -> decimal? Alpaca.Markets.NewOrderRequest.StopPrice.set -> void -Alpaca.Markets.NewOrderRequest.Symbol.get -> string! +Alpaca.Markets.NewOrderRequest.Symbol.get -> string? Alpaca.Markets.NewOrderRequest.TakeProfitLimitPrice.get -> decimal? Alpaca.Markets.NewOrderRequest.TakeProfitLimitPrice.set -> void Alpaca.Markets.NewOrderRequest.TrailOffsetInDollars.get -> decimal? diff --git a/Alpaca.Markets/PublicAPI.Unshipped.txt b/Alpaca.Markets/PublicAPI.Unshipped.txt index ab058de62..1728105f5 100644 --- a/Alpaca.Markets/PublicAPI.Unshipped.txt +++ b/Alpaca.Markets/PublicAPI.Unshipped.txt @@ -1 +1,13 @@ #nullable enable +Alpaca.Markets.NewOrderRequest.Legs.get -> System.Collections.Generic.IReadOnlyList! +Alpaca.Markets.NewOrderRequest.NewOrderRequest(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderType type, Alpaca.Markets.TimeInForce duration) -> void +Alpaca.Markets.NewOrderRequest.With(Alpaca.Markets.OptionLegRequest! leg) -> Alpaca.Markets.NewOrderRequest! +Alpaca.Markets.OptionLegRequest +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OptionLegRequest.PositionIntent.get -> Alpaca.Markets.PositionIntent? +Alpaca.Markets.OptionLegRequest.RatioQuantity.get -> decimal +Alpaca.Markets.OptionLegRequest.Side.get -> Alpaca.Markets.OrderSide? +Alpaca.Markets.OptionLegRequest.Symbol.get -> string! +Alpaca.Markets.OrderClass.MultiLegOptions = 4 -> Alpaca.Markets.OrderClass From 0e25a40856a64192a3e828f7611acd8806ce4057 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Mon, 20 Jan 2025 23:46:12 +0100 Subject: [PATCH 2/7] Issue #771 - The initial options multi-legs orders support was added: - The new advanced order type added into strongly-typed orders hierarchy - Helper class for defining multi-leg order legs added into library --- .../Helpers/JsonNewOrderExtensions.cs | 22 +++ Alpaca.Markets/Orders/LimitOrder.cs | 4 +- Alpaca.Markets/Orders/MultiLegOrder.cs | 131 ++++++++++++++++++ Alpaca.Markets/Orders/OrderLeg.cs | 105 ++++++++++++++ Alpaca.Markets/PublicAPI.Unshipped.txt | 15 ++ 5 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 Alpaca.Markets/Orders/MultiLegOrder.cs create mode 100644 Alpaca.Markets/Orders/OrderLeg.cs diff --git a/Alpaca.Markets/Helpers/JsonNewOrderExtensions.cs b/Alpaca.Markets/Helpers/JsonNewOrderExtensions.cs index fe40a4f14..4ed3d8f1c 100644 --- a/Alpaca.Markets/Helpers/JsonNewOrderExtensions.cs +++ b/Alpaca.Markets/Helpers/JsonNewOrderExtensions.cs @@ -9,6 +9,20 @@ public static JsonNewOrder WithoutLimitPrice( return order; } + public static JsonNewOrder WithoutOrderSide( + this JsonNewOrder order) + { + order.OrderSide = null; + return order; + } + + public static JsonNewOrder WithoutSymbol( + this JsonNewOrder order) + { + order.Symbol = null; + return order; + } + public static JsonNewOrder WithStopPrice( this JsonNewOrder order, Decimal stopPrice) @@ -70,4 +84,12 @@ public static JsonNewOrder WithStopLoss( }; return order; } + + public static JsonNewOrder WithOrderLegs( + this JsonNewOrder order, + IEnumerable legs) + { + (order.Legs ??= []).AddRange(legs); + return order; + } } diff --git a/Alpaca.Markets/Orders/LimitOrder.cs b/Alpaca.Markets/Orders/LimitOrder.cs index 1d5120ffc..6454bf49f 100644 --- a/Alpaca.Markets/Orders/LimitOrder.cs +++ b/Alpaca.Markets/Orders/LimitOrder.cs @@ -59,7 +59,7 @@ public static LimitOrder Sell( /// Creates a new instance of the order from the current order. /// /// Stop loss order stop price. - /// New advanced order representing pair of original order and stop loss order. + /// New advanced order representing a pair of original order and stop loss order. [UsedImplicitly] public OneCancelsOtherOrder OneCancelsOther( Decimal stopLossStopPrice) => @@ -70,7 +70,7 @@ public OneCancelsOtherOrder OneCancelsOther( /// /// Stop loss order stop price. /// Stop loss order limit price. - /// New advanced order representing pair of original order and stop loss order. + /// New advanced order representing a pair of original order and stop loss order. [UsedImplicitly] public OneCancelsOtherOrder OneCancelsOther( Decimal stopLossStopPrice, diff --git a/Alpaca.Markets/Orders/MultiLegOrder.cs b/Alpaca.Markets/Orders/MultiLegOrder.cs new file mode 100644 index 000000000..68a866613 --- /dev/null +++ b/Alpaca.Markets/Orders/MultiLegOrder.cs @@ -0,0 +1,131 @@ +namespace Alpaca.Markets; + +/// +/// TBD +/// +public sealed class MultiLegOrder : AdvancedOrderBase +{ + private readonly List _legs; + + private MultiLegOrder( + SimpleOrderBase baseOrder, + params IReadOnlyList legs) + : base(baseOrder, OrderClass.MultiLegOptions) + { + Duration = TimeInForce.Day; + _legs = [.. legs]; + } + + /// + /// Creates a new instance of the market order with two legs. + /// + /// Order quantity. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Market( + OrderQuantity quantity, + OrderLeg orderLeg1, + OrderLeg orderLeg2) => + createMarket(quantity, orderLeg1, orderLeg2); + + /// + /// Creates a new instance of the market order with three legs. + /// + /// Order quantity. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// Third leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Market( + OrderQuantity quantity, + OrderLeg orderLeg1, + OrderLeg orderLeg2, + OrderLeg orderLeg3) => + createMarket(quantity, orderLeg1, orderLeg2, orderLeg3); + + /// + /// Creates a new instance of the market order with four legs. + /// + /// Order quantity. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// Third leg of the multi-leg order. + /// Fourth leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Market( + OrderQuantity quantity, + OrderLeg orderLeg1, + OrderLeg orderLeg2, + OrderLeg orderLeg3, + OrderLeg orderLeg4) => + createMarket(quantity, orderLeg1, orderLeg2, orderLeg3, orderLeg4); + + /// + /// Creates a new instance of the limit order with two legs. + /// + /// Order quantity. + /// Order limit price. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Limit( + OrderQuantity quantity, + Decimal limitPrice, + OrderLeg orderLeg1, + OrderLeg orderLeg2) => + createLimit(quantity, limitPrice, orderLeg1, orderLeg2); + + /// + /// Creates a new instance of the limit order with three legs. + /// + /// Order quantity. + /// Order limit price. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// Third leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Limit( + OrderQuantity quantity, + Decimal limitPrice, + OrderLeg orderLeg1, + OrderLeg orderLeg2, + OrderLeg orderLeg3) => + createLimit(quantity, limitPrice, orderLeg1, orderLeg2, orderLeg3); + + /// + /// Creates a new instance of the limit order with four legs. + /// + /// Order quantity. + /// Order limit price. + /// First leg of the multi-leg order. + /// Second leg of the multi-leg order. + /// Third leg of the multi-leg order. + /// Fourth leg of the multi-leg order. + /// A new advanced options order with several legs. + public static MultiLegOrder Limit( + OrderQuantity quantity, + Decimal limitPrice, + OrderLeg orderLeg1, + OrderLeg orderLeg2, + OrderLeg orderLeg3, + OrderLeg orderLeg4) => + createLimit(quantity, limitPrice, orderLeg1, orderLeg2, orderLeg3, orderLeg4); + + private static MultiLegOrder createLimit( + OrderQuantity quantity, + Decimal limitPrice, + params IReadOnlyList legs) => + new(new LimitOrder(String.Empty, quantity, OrderSide.Buy, limitPrice), legs); + + private static MultiLegOrder createMarket( + OrderQuantity quantity, + params IReadOnlyList legs) => + new(new MarketOrder(String.Empty, quantity, OrderSide.Buy), legs); + + internal override JsonNewOrder GetJsonRequest() => + base.GetJsonRequest() + .WithOrderLegs(_legs.Select(leg => leg.GetJsonRequest())) + .WithoutOrderSide() + .WithoutSymbol(); +} \ No newline at end of file diff --git a/Alpaca.Markets/Orders/OrderLeg.cs b/Alpaca.Markets/Orders/OrderLeg.cs new file mode 100644 index 000000000..9f937aff1 --- /dev/null +++ b/Alpaca.Markets/Orders/OrderLeg.cs @@ -0,0 +1,105 @@ +namespace Alpaca.Markets; + +/// +/// Represents the single leg of the option multi-leg order. +/// +public sealed record OrderLeg +{ + /// + /// Creates a new instance of the object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order side (buy or sell). + /// + /// The argument is null. + /// + public OrderLeg( + String symbol, + Decimal ratioQuantity, + OrderSide side) + : this(symbol, ratioQuantity, side, null) + { + } + + /// + /// Creates a new instance of the object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order position intent. + /// + /// The argument is null. + /// + public OrderLeg( + String symbol, + Decimal ratioQuantity, + PositionIntent positionIntent) + : this(symbol, ratioQuantity, null, positionIntent) + { + } + + /// + /// Creates a new instance of the object. + /// + /// Order asset symbol. + /// Order quantity. + /// Order side (buy or sell). + /// Order position intent. + /// + /// The argument is null. + /// + public OrderLeg( + String symbol, + Decimal ratioQuantity, + PositionIntent positionIntent, + OrderSide side) + : this(symbol, ratioQuantity, side, positionIntent) + { + } + + private OrderLeg( + String symbol, + Decimal ratioQuantity, + OrderSide? side, + PositionIntent? positionIntent) + { + Symbol = symbol.EnsureNotNull(); + RatioQuantity = ratioQuantity; + PositionIntent = positionIntent; + Side = side; + } + + /// + /// Gets the new order asset symbol. + /// + [UsedImplicitly] + public String Symbol { get; } + + /// + /// Gets the proportional quantity of this leg in relation to the overall multi-leg order quantity. + /// + [UsedImplicitly] + public Decimal RatioQuantity { get; } + + /// + /// Gets the new order side (buy or sell). + /// + [UsedImplicitly] + public OrderSide? Side { get; } + + /// + /// Gets the optional position intent for order placement. + /// + [UsedImplicitly] + public PositionIntent? PositionIntent { get; } + + internal JsonOrderLeg GetJsonRequest() => + new () + { + Symbol = Symbol, + OrderSide = Side, + RatioQuantity = RatioQuantity, + PositionIntent = PositionIntent + }; +} diff --git a/Alpaca.Markets/PublicAPI.Unshipped.txt b/Alpaca.Markets/PublicAPI.Unshipped.txt index 1728105f5..c81c12fc7 100644 --- a/Alpaca.Markets/PublicAPI.Unshipped.txt +++ b/Alpaca.Markets/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +Alpaca.Markets.MultiLegOrder Alpaca.Markets.NewOrderRequest.Legs.get -> System.Collections.Generic.IReadOnlyList! Alpaca.Markets.NewOrderRequest.NewOrderRequest(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderType type, Alpaca.Markets.TimeInForce duration) -> void Alpaca.Markets.NewOrderRequest.With(Alpaca.Markets.OptionLegRequest! leg) -> Alpaca.Markets.NewOrderRequest! @@ -11,3 +12,17 @@ Alpaca.Markets.OptionLegRequest.RatioQuantity.get -> decimal Alpaca.Markets.OptionLegRequest.Side.get -> Alpaca.Markets.OrderSide? Alpaca.Markets.OptionLegRequest.Symbol.get -> string! Alpaca.Markets.OrderClass.MultiLegOptions = 4 -> Alpaca.Markets.OrderClass +Alpaca.Markets.OrderLeg +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OrderLeg.PositionIntent.get -> Alpaca.Markets.PositionIntent? +Alpaca.Markets.OrderLeg.RatioQuantity.get -> decimal +Alpaca.Markets.OrderLeg.Side.get -> Alpaca.Markets.OrderSide? +Alpaca.Markets.OrderLeg.Symbol.get -> string! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! From 75c654795f93f6a23371975c10dc1f90cd412463 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Mon, 20 Jan 2025 23:58:12 +0100 Subject: [PATCH 3/7] Issue #771 - The initial options multi-legs orders support was added: - Several attributes of order object now marked as nullable for proper multi-leg orders support --- Alpaca.Markets/CompatibilitySuppressions.xml | 147 +++++++++++++++++++ Alpaca.Markets/Interfaces/IOrder.cs | 8 +- Alpaca.Markets/Messages/JsonOrder.cs | 16 +- Alpaca.Markets/PublicAPI.Shipped.txt | 8 +- 4 files changed, 163 insertions(+), 16 deletions(-) diff --git a/Alpaca.Markets/CompatibilitySuppressions.xml b/Alpaca.Markets/CompatibilitySuppressions.xml index b47e1b2a5..abb81a07f 100644 --- a/Alpaca.Markets/CompatibilitySuppressions.xml +++ b/Alpaca.Markets/CompatibilitySuppressions.xml @@ -554,6 +554,27 @@ lib/net462/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetClass + lib/net462/Alpaca.Markets.dll + lib/net462/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetId + lib/net462/Alpaca.Markets.dll + lib/net462/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_OrderSide + lib/net462/Alpaca.Markets.dll + lib/net462/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.LatestDataListRequest.#ctor(System.Collections.Generic.IEnumerable{System.String},Alpaca.Markets.CryptoExchange) @@ -855,6 +876,27 @@ lib/netstandard2.1/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetClass + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetId + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_OrderSide + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.LatestDataListRequest.#ctor(System.Collections.Generic.IEnumerable{System.String},Alpaca.Markets.CryptoExchange) @@ -1156,6 +1198,27 @@ lib/netstandard2.0/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetClass + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetId + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_OrderSide + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.LatestDataListRequest.#ctor(System.Collections.Generic.IEnumerable{System.String},Alpaca.Markets.CryptoExchange) @@ -1457,6 +1520,27 @@ lib/netstandard2.1/Alpaca.Markets.dll true + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetClass + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_AssetId + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0002 + M:Alpaca.Markets.IOrder.get_OrderSide + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + CP0002 M:Alpaca.Markets.LatestDataListRequest.#ctor(System.Collections.Generic.IEnumerable{System.String},Alpaca.Markets.CryptoExchange) @@ -1485,6 +1569,69 @@ lib/netstandard2.1/Alpaca.Markets.dll true + + CP0006 + P:Alpaca.Markets.IOrder.AssetClass + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.AssetId + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.OrderSide + lib/net6.0/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.AssetClass + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.AssetId + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.OrderSide + lib/netstandard2.0/Alpaca.Markets.dll + lib/netstandard2.0/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.AssetClass + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.AssetId + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + + + CP0006 + P:Alpaca.Markets.IOrder.OrderSide + lib/netstandard2.1/Alpaca.Markets.dll + lib/netstandard2.1/Alpaca.Markets.dll + true + CP0008 T:Alpaca.Markets.AccountActivityType diff --git a/Alpaca.Markets/Interfaces/IOrder.cs b/Alpaca.Markets/Interfaces/IOrder.cs index 0c418f297..e6573de77 100644 --- a/Alpaca.Markets/Interfaces/IOrder.cs +++ b/Alpaca.Markets/Interfaces/IOrder.cs @@ -70,19 +70,19 @@ public interface IOrder /// Gets unique asset identifier. /// [UsedImplicitly] - Guid AssetId { get; } + Guid? AssetId { get; } /// /// Gets asset symbol. /// [UsedImplicitly] - String Symbol { get; } + String? Symbol { get; } /// /// Gets asset class. /// [UsedImplicitly] - AssetClass AssetClass { get; } + AssetClass? AssetClass { get; } /// /// Gets original notional order quantity (with the fractional part). @@ -128,7 +128,7 @@ public interface IOrder /// Gets order side (buy or sell). /// [UsedImplicitly] - OrderSide OrderSide { get; } + OrderSide? OrderSide { get; } /// /// Gets order duration. diff --git a/Alpaca.Markets/Messages/JsonOrder.cs b/Alpaca.Markets/Messages/JsonOrder.cs index 1a7f27d3d..ed8841dcc 100644 --- a/Alpaca.Markets/Messages/JsonOrder.cs +++ b/Alpaca.Markets/Messages/JsonOrder.cs @@ -44,14 +44,14 @@ internal sealed class JsonOrder : IOrder [JsonConverter(typeof(AssumeUtcIsoDateTimeConverter))] public DateTime? ReplacedAtUtc { get; [ExcludeFromCodeCoverage] set; } - [JsonProperty(PropertyName = "asset_id", Required = Required.Always)] - public Guid AssetId { get; set; } + [JsonProperty(PropertyName = "asset_id", Required = Required.Default)] + public Guid? AssetId { get; set; } - [JsonProperty(PropertyName = "symbol", Required = Required.Always)] - public String Symbol { get; set; } = String.Empty; + [JsonProperty(PropertyName = "symbol", Required = Required.Default)] + public String? Symbol { get; set; } - [JsonProperty(PropertyName = "asset_class", Required = Required.Always)] - public AssetClass AssetClass { get; set; } + [JsonProperty(PropertyName = "asset_class", Required = Required.Default)] + public AssetClass? AssetClass { get; set; } [JsonProperty(PropertyName = "notional", Required = Required.Default)] public Decimal? Notional { get; [ExcludeFromCodeCoverage] set; } @@ -74,8 +74,8 @@ internal sealed class JsonOrder : IOrder [JsonProperty(PropertyName = "order_class", Required = Required.Always)] public OrderClass OrderClass { get; } - [JsonProperty(PropertyName = "side", Required = Required.Always)] - public OrderSide OrderSide { get; set; } + [JsonProperty(PropertyName = "side", Required = Required.Default)] + public OrderSide? OrderSide { get; set; } [JsonProperty(PropertyName = "time_in_force", Required = Required.Always)] public TimeInForce TimeInForce { get; set; } diff --git a/Alpaca.Markets/PublicAPI.Shipped.txt b/Alpaca.Markets/PublicAPI.Shipped.txt index 8191ca20e..d35ec8504 100644 --- a/Alpaca.Markets/PublicAPI.Shipped.txt +++ b/Alpaca.Markets/PublicAPI.Shipped.txt @@ -753,8 +753,8 @@ Alpaca.Markets.IOptionSnapshot.Quote.get -> Alpaca.Markets.IQuote? Alpaca.Markets.IOptionSnapshot.Symbol.get -> string! Alpaca.Markets.IOptionSnapshot.Trade.get -> Alpaca.Markets.ITrade? Alpaca.Markets.IOrder -Alpaca.Markets.IOrder.AssetClass.get -> Alpaca.Markets.AssetClass -Alpaca.Markets.IOrder.AssetId.get -> System.Guid +Alpaca.Markets.IOrder.AssetClass.get -> Alpaca.Markets.AssetClass? +Alpaca.Markets.IOrder.AssetId.get -> System.Guid? Alpaca.Markets.IOrder.AverageFillPrice.get -> decimal? Alpaca.Markets.IOrder.CancelledAtUtc.get -> System.DateTime? Alpaca.Markets.IOrder.ClientOrderId.get -> string? @@ -770,7 +770,7 @@ Alpaca.Markets.IOrder.Legs.get -> System.Collections.Generic.IReadOnlyList decimal? Alpaca.Markets.IOrder.OrderClass.get -> Alpaca.Markets.OrderClass Alpaca.Markets.IOrder.OrderId.get -> System.Guid -Alpaca.Markets.IOrder.OrderSide.get -> Alpaca.Markets.OrderSide +Alpaca.Markets.IOrder.OrderSide.get -> Alpaca.Markets.OrderSide? Alpaca.Markets.IOrder.OrderStatus.get -> Alpaca.Markets.OrderStatus Alpaca.Markets.IOrder.OrderType.get -> Alpaca.Markets.OrderType Alpaca.Markets.IOrder.ReplacedAtUtc.get -> System.DateTime? @@ -780,7 +780,7 @@ Alpaca.Markets.IOrder.Quantity.get -> decimal? Alpaca.Markets.IOrder.Notional.get -> decimal? Alpaca.Markets.IOrder.StopPrice.get -> decimal? Alpaca.Markets.IOrder.SubmittedAtUtc.get -> System.DateTime? -Alpaca.Markets.IOrder.Symbol.get -> string! +Alpaca.Markets.IOrder.Symbol.get -> string? Alpaca.Markets.IOrder.TimeInForce.get -> Alpaca.Markets.TimeInForce Alpaca.Markets.IOrder.TrailOffsetInDollars.get -> decimal? Alpaca.Markets.IOrder.TrailOffsetInPercent.get -> decimal? From 774b5a5cb1fb61aedaab3db7d7d6405f41c34ee3 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Tue, 21 Jan 2025 01:21:28 +0100 Subject: [PATCH 4/7] 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) { From 23928aa5149e45200107869377fc6270fe63bc0b Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Tue, 21 Jan 2025 01:27:51 +0100 Subject: [PATCH 5/7] Prepare release 8.0.0-alpha1 of SDK and Extensions packages --- Alpaca.Markets/Alpaca.Markets.csproj | 1 + Alpaca.Markets/PublicAPI.Shipped.txt | 27 ++++++++++++++++++++++++++ Alpaca.Markets/PublicAPI.Unshipped.txt | 27 -------------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Alpaca.Markets/Alpaca.Markets.csproj b/Alpaca.Markets/Alpaca.Markets.csproj index 00318cb94..6a67e7523 100644 --- a/Alpaca.Markets/Alpaca.Markets.csproj +++ b/Alpaca.Markets/Alpaca.Markets.csproj @@ -20,6 +20,7 @@ - Obsolete members cleanup - remove fully obsolete and convert warnings into errors. +- Initial support for the options multi-legs orders were added in experimental mode. C# SDK for Alpaca Trade API https://docs.alpaca.markets/ https://github.com/alpacahq/alpaca-trade-api-csharp diff --git a/Alpaca.Markets/PublicAPI.Shipped.txt b/Alpaca.Markets/PublicAPI.Shipped.txt index d35ec8504..2b673fb99 100644 --- a/Alpaca.Markets/PublicAPI.Shipped.txt +++ b/Alpaca.Markets/PublicAPI.Shipped.txt @@ -1057,6 +1057,7 @@ Alpaca.Markets.MarketDataFeed.Iex = 0 -> Alpaca.Markets.MarketDataFeed Alpaca.Markets.MarketDataFeed.Otc = 2 -> Alpaca.Markets.MarketDataFeed Alpaca.Markets.MarketDataFeed.Sip = 1 -> Alpaca.Markets.MarketDataFeed Alpaca.Markets.MarketOrder +Alpaca.Markets.MultiLegOrder Alpaca.Markets.Multiplier Alpaca.Markets.Multiplier.Double = 2 -> Alpaca.Markets.Multiplier Alpaca.Markets.Multiplier.None = 0 -> Alpaca.Markets.Multiplier @@ -1068,9 +1069,11 @@ Alpaca.Markets.NewOrderRequest.ClientOrderId.set -> void Alpaca.Markets.NewOrderRequest.Duration.get -> Alpaca.Markets.TimeInForce Alpaca.Markets.NewOrderRequest.ExtendedHours.get -> bool? Alpaca.Markets.NewOrderRequest.ExtendedHours.set -> void +Alpaca.Markets.NewOrderRequest.Legs.get -> System.Collections.Generic.IReadOnlyList! Alpaca.Markets.NewOrderRequest.LimitPrice.get -> decimal? Alpaca.Markets.NewOrderRequest.LimitPrice.set -> void Alpaca.Markets.NewOrderRequest.NewOrderRequest(string! symbol, Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderSide side, Alpaca.Markets.OrderType type, Alpaca.Markets.TimeInForce duration) -> void +Alpaca.Markets.NewOrderRequest.NewOrderRequest(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderType type, Alpaca.Markets.TimeInForce duration) -> void Alpaca.Markets.NewOrderRequest.OrderClass.get -> Alpaca.Markets.OrderClass? Alpaca.Markets.NewOrderRequest.OrderClass.set -> void Alpaca.Markets.NewOrderRequest.PositionIntent.get -> Alpaca.Markets.PositionIntent? @@ -1091,6 +1094,7 @@ Alpaca.Markets.NewOrderRequest.TrailOffsetInDollars.set -> void Alpaca.Markets.NewOrderRequest.TrailOffsetInPercent.get -> decimal? Alpaca.Markets.NewOrderRequest.TrailOffsetInPercent.set -> void Alpaca.Markets.NewOrderRequest.Type.get -> Alpaca.Markets.OrderType +Alpaca.Markets.NewOrderRequest.With(Alpaca.Markets.OptionLegRequest! leg) -> Alpaca.Markets.NewOrderRequest! Alpaca.Markets.NewsArticlesRequest Alpaca.Markets.NewsArticlesRequest.ExcludeItemsWithoutContent.get -> bool? Alpaca.Markets.NewsArticlesRequest.ExcludeItemsWithoutContent.set -> void @@ -1165,6 +1169,14 @@ Alpaca.Markets.OptionContractsRequest.StrikePriceGreaterThanOrEqualTo.set -> voi Alpaca.Markets.OptionContractsRequest.StrikePriceLessThanOrEqualTo.get -> decimal? Alpaca.Markets.OptionContractsRequest.StrikePriceLessThanOrEqualTo.set -> void Alpaca.Markets.OptionContractsRequest.UnderlyingSymbols.get -> System.Collections.Generic.IReadOnlyCollection! +Alpaca.Markets.OptionLegRequest +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OptionLegRequest.PositionIntent.get -> Alpaca.Markets.PositionIntent? +Alpaca.Markets.OptionLegRequest.RatioQuantity.get -> decimal +Alpaca.Markets.OptionLegRequest.Side.get -> Alpaca.Markets.OrderSide? +Alpaca.Markets.OptionLegRequest.Symbol.get -> string! Alpaca.Markets.OptionsFeed Alpaca.Markets.OptionsFeed.Indicative = 1 -> Alpaca.Markets.OptionsFeed Alpaca.Markets.OptionsFeed.Opra = 0 -> Alpaca.Markets.OptionsFeed @@ -1202,10 +1214,19 @@ Alpaca.Markets.OrderBase.Type.get -> Alpaca.Markets.OrderType Alpaca.Markets.OrderBaseExtensions Alpaca.Markets.OrderClass Alpaca.Markets.OrderClass.Bracket = 1 -> Alpaca.Markets.OrderClass +Alpaca.Markets.OrderClass.MultiLegOptions = 4 -> Alpaca.Markets.OrderClass Alpaca.Markets.OrderClass.OneCancelsOther = 2 -> Alpaca.Markets.OrderClass Alpaca.Markets.OrderClass.OneTriggersOther = 3 -> Alpaca.Markets.OrderClass Alpaca.Markets.OrderClass.Simple = 0 -> Alpaca.Markets.OrderClass Alpaca.Markets.OrderExtensions +Alpaca.Markets.OrderLeg +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void +Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent, Alpaca.Markets.OrderSide side) -> void +Alpaca.Markets.OrderLeg.PositionIntent.get -> Alpaca.Markets.PositionIntent? +Alpaca.Markets.OrderLeg.RatioQuantity.get -> decimal +Alpaca.Markets.OrderLeg.Side.get -> Alpaca.Markets.OrderSide? +Alpaca.Markets.OrderLeg.Symbol.get -> string! Alpaca.Markets.OrderSide Alpaca.Markets.OrderSide.Buy = 0 -> Alpaca.Markets.OrderSide Alpaca.Markets.OrderSide.Sell = 1 -> Alpaca.Markets.OrderSide @@ -1468,6 +1489,12 @@ static Alpaca.Markets.LimitOrder.Buy(string! symbol, Alpaca.Markets.OrderQuantit static Alpaca.Markets.LimitOrder.Sell(string! symbol, Alpaca.Markets.OrderQuantity quantity, decimal limitPrice) -> Alpaca.Markets.LimitOrder! static Alpaca.Markets.MarketOrder.Buy(string! symbol, Alpaca.Markets.OrderQuantity quantity) -> Alpaca.Markets.MarketOrder! static Alpaca.Markets.MarketOrder.Sell(string! symbol, Alpaca.Markets.OrderQuantity quantity) -> Alpaca.Markets.MarketOrder! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! +static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! static Alpaca.Markets.OpenClose.implicit operator Alpaca.Markets.Interval(Alpaca.Markets.OpenClose openClose) -> Alpaca.Markets.Interval static Alpaca.Markets.OrderBaseExtensions.WithClientOrderId(this TOrder! order, string! clientOrderId) -> TOrder! static Alpaca.Markets.OrderBaseExtensions.WithDuration(this TOrder! order, Alpaca.Markets.TimeInForce duration) -> TOrder! diff --git a/Alpaca.Markets/PublicAPI.Unshipped.txt b/Alpaca.Markets/PublicAPI.Unshipped.txt index c81c12fc7..ab058de62 100644 --- a/Alpaca.Markets/PublicAPI.Unshipped.txt +++ b/Alpaca.Markets/PublicAPI.Unshipped.txt @@ -1,28 +1 @@ #nullable enable -Alpaca.Markets.MultiLegOrder -Alpaca.Markets.NewOrderRequest.Legs.get -> System.Collections.Generic.IReadOnlyList! -Alpaca.Markets.NewOrderRequest.NewOrderRequest(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderType type, Alpaca.Markets.TimeInForce duration) -> void -Alpaca.Markets.NewOrderRequest.With(Alpaca.Markets.OptionLegRequest! leg) -> Alpaca.Markets.NewOrderRequest! -Alpaca.Markets.OptionLegRequest -Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void -Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side, Alpaca.Markets.PositionIntent positionIntent) -> void -Alpaca.Markets.OptionLegRequest.OptionLegRequest(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void -Alpaca.Markets.OptionLegRequest.PositionIntent.get -> Alpaca.Markets.PositionIntent? -Alpaca.Markets.OptionLegRequest.RatioQuantity.get -> decimal -Alpaca.Markets.OptionLegRequest.Side.get -> Alpaca.Markets.OrderSide? -Alpaca.Markets.OptionLegRequest.Symbol.get -> string! -Alpaca.Markets.OrderClass.MultiLegOptions = 4 -> Alpaca.Markets.OrderClass -Alpaca.Markets.OrderLeg -Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide side) -> void -Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> void -Alpaca.Markets.OrderLeg.OrderLeg(string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent, Alpaca.Markets.OrderSide side) -> void -Alpaca.Markets.OrderLeg.PositionIntent.get -> Alpaca.Markets.PositionIntent? -Alpaca.Markets.OrderLeg.RatioQuantity.get -> decimal -Alpaca.Markets.OrderLeg.Side.get -> Alpaca.Markets.OrderSide? -Alpaca.Markets.OrderLeg.Symbol.get -> string! -static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! -static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! -static Alpaca.Markets.MultiLegOrder.Limit(Alpaca.Markets.OrderQuantity quantity, decimal limitPrice, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! -static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2) -> Alpaca.Markets.MultiLegOrder! -static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3) -> Alpaca.Markets.MultiLegOrder! -static Alpaca.Markets.MultiLegOrder.Market(Alpaca.Markets.OrderQuantity quantity, Alpaca.Markets.OrderLeg! orderLeg1, Alpaca.Markets.OrderLeg! orderLeg2, Alpaca.Markets.OrderLeg! orderLeg3, Alpaca.Markets.OrderLeg! orderLeg4) -> Alpaca.Markets.MultiLegOrder! From 47b52c2adaa68a637037f149234f493fc5ba0957 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Tue, 21 Jan 2025 01:57:08 +0100 Subject: [PATCH 6/7] Prepare release 8.0.0-beta1 of SDK and Extensions packages --- Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj | 6 +++--- Alpaca.Markets/Alpaca.Markets.csproj | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj b/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj index a5215c7d1..18764cd02 100644 --- a/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj +++ b/Alpaca.Markets.Extensions/Alpaca.Markets.Extensions.csproj @@ -12,9 +12,9 @@ - 8.0.0.0 - 8.0.0.0 - 8.0.0-alpha1 + 8.0.0.1 + 8.0.0.1 + 8.0.0-beta1 diff --git a/Alpaca.Markets/Alpaca.Markets.csproj b/Alpaca.Markets/Alpaca.Markets.csproj index 6a67e7523..a3caf6ad6 100644 --- a/Alpaca.Markets/Alpaca.Markets.csproj +++ b/Alpaca.Markets/Alpaca.Markets.csproj @@ -12,9 +12,9 @@ - 8.0.0.0 - 8.0.0.0 - 8.0.0-alpha1 + 8.0.0.1 + 8.0.0.1 + 8.0.0-beta1 From 384ef85a8cea7a4449105672c8d740c88fe0ee45 Mon Sep 17 00:00:00 2001 From: Oleg Rakhmatulin Date: Wed, 29 Jan 2025 09:32:18 +0100 Subject: [PATCH 7/7] Issue #771 - The options multi-legs orders support was expanded: - Added several helper functions for creating the `OrderLeg` instance - The new methods were covered by tests for better cross-checking --- Alpaca.Markets.Tests/OrderTypeTest.cs | 41 ++++++----- Alpaca.Markets/Orders/OrderSideExtensions.cs | 71 +++++++++++++++++++- Alpaca.Markets/PublicAPI.Shipped.txt | 4 ++ 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/Alpaca.Markets.Tests/OrderTypeTest.cs b/Alpaca.Markets.Tests/OrderTypeTest.cs index 42d66356d..2e60dca98 100644 --- a/Alpaca.Markets.Tests/OrderTypeTest.cs +++ b/Alpaca.Markets.Tests/OrderTypeTest.cs @@ -131,28 +131,35 @@ 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)) + var lfi = getOrderLegCreationInfo() + .Select(info => info.Intent.Leg(Stock, ratioQuantity, info.Side)) + .ToList(); + var lfs = getOrderLegCreationInfo() + .Select(info => info.Side.Leg(Stock, ratioQuantity, info.Intent)) .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( + MultiLegOrder.Market(Quantity, lfi[0], lfi[1]), + MultiLegOrder.Market(Quantity, lfs[0], lfs[1])); + assertOrdersAreEqual( + MultiLegOrder.Market(Quantity, lfi[0], lfi[1], lfi[2]), + MultiLegOrder.Market(Quantity, lfs[0], lfs[1], lfs[2])); + assertOrdersAreEqual( + MultiLegOrder.Market(Quantity, lfi[0], lfi[1], lfi[2], lfi[3]), + MultiLegOrder.Market(Quantity, lfs[0], lfs[1], lfs[2], lfs[3])); - assertOrdersAreEqual(limitOrder1, limitOrder1); - assertOrdersAreEqual(limitOrder2, limitOrder2); - assertOrdersAreEqual(limitOrder3, limitOrder3); + assertOrdersAreEqual( + MultiLegOrder.Limit(Quantity, limitPrice, lfi[0], lfi[1]), + MultiLegOrder.Limit(Quantity, limitPrice, lfs[0], lfs[1])); + assertOrdersAreEqual( + MultiLegOrder.Limit(Quantity, limitPrice, lfi[0], lfi[1], lfi[2]), + MultiLegOrder.Limit(Quantity, limitPrice, lfs[0], lfs[1], lfs[2])); + assertOrdersAreEqual( + MultiLegOrder.Limit(Quantity, limitPrice, lfi[0], lfi[1], lfi[2], lfi[3]), + MultiLegOrder.Limit(Quantity, limitPrice, lfs[0], lfs[1], lfs[2], lfs[3])); } - private static IEnumerable<(PositionIntent, OrderSide)> getOrderLegs() + private static IEnumerable<(PositionIntent Intent, OrderSide Side)> getOrderLegCreationInfo() { yield return (PositionIntent.BuyToClose, OrderSide.Buy); yield return (PositionIntent.SellToClose, OrderSide.Sell); diff --git a/Alpaca.Markets/Orders/OrderSideExtensions.cs b/Alpaca.Markets/Orders/OrderSideExtensions.cs index 8d6d218ab..0782d55ab 100644 --- a/Alpaca.Markets/Orders/OrderSideExtensions.cs +++ b/Alpaca.Markets/Orders/OrderSideExtensions.cs @@ -99,4 +99,73 @@ public static TrailingStopOrder TrailingStop( OrderQuantity quantity, TrailOffset trailOffset) => new(symbol, quantity, orderSide, trailOffset); -} + + /// + /// Creates new leg for the options multi-leg order. + /// + /// Order side (buy or sell). + /// Order asset symbol. + /// Order quantity. + /// + /// The argument is null. + /// + /// The new object instance. + [UsedImplicitly] + public static OrderLeg Leg( + this OrderSide orderSide, + String symbol, + Decimal ratioQuantity) => + new (symbol, ratioQuantity, orderSide); + + /// + /// Creates new leg for the options multi-leg order. + /// + /// Order side (buy or sell). + /// Order asset symbol. + /// Order quantity. + /// Order position intent. + /// + /// The argument is null. + /// + /// The new object instance. + public static OrderLeg Leg( + this OrderSide orderSide, + String symbol, + Decimal ratioQuantity, + PositionIntent positionIntent) => + new (symbol, ratioQuantity, positionIntent, orderSide); + + /// + /// Creates new leg for the options multi-leg order. + /// + /// Order position intent. + /// Order asset symbol. + /// Order quantity. + /// + /// The argument is null. + /// + /// The new object instance. + [UsedImplicitly] + public static OrderLeg Leg( + this PositionIntent positionIntent, + String symbol, + Decimal ratioQuantity) => + new (symbol, ratioQuantity, positionIntent); + + /// + /// Creates new leg for the options multi-leg order. + /// + /// Order position intent. + /// Order asset symbol. + /// Order quantity. + /// Order side (buy or sell). + /// + /// The argument is null. + /// + /// The new object instance. + public static OrderLeg Leg( + this PositionIntent positionIntent, + String symbol, + Decimal ratioQuantity, + OrderSide orderSide) => + new (symbol, ratioQuantity, positionIntent, orderSide);} diff --git a/Alpaca.Markets/PublicAPI.Shipped.txt b/Alpaca.Markets/PublicAPI.Shipped.txt index 2b673fb99..b154498b7 100644 --- a/Alpaca.Markets/PublicAPI.Shipped.txt +++ b/Alpaca.Markets/PublicAPI.Shipped.txt @@ -1501,6 +1501,10 @@ static Alpaca.Markets.OrderBaseExtensions.WithDuration(this TOrder! orde static Alpaca.Markets.OrderBaseExtensions.WithExtendedHours(this TOrder! order, bool extendedHours) -> TOrder! static Alpaca.Markets.OrderBaseExtensions.WithPositionIntent(this TOrder! order, Alpaca.Markets.PositionIntent positionIntent) -> TOrder! static Alpaca.Markets.OrderExtensions.GetOrderQuantity(this Alpaca.Markets.IOrder! order) -> Alpaca.Markets.OrderQuantity +static Alpaca.Markets.OrderSideExtensions.Leg(this Alpaca.Markets.OrderSide orderSide, string! symbol, decimal ratioQuantity) -> Alpaca.Markets.OrderLeg! +static Alpaca.Markets.OrderSideExtensions.Leg(this Alpaca.Markets.OrderSide orderSide, string! symbol, decimal ratioQuantity, Alpaca.Markets.PositionIntent positionIntent) -> Alpaca.Markets.OrderLeg! +static Alpaca.Markets.OrderSideExtensions.Leg(this Alpaca.Markets.PositionIntent positionIntent, string! symbol, decimal ratioQuantity) -> Alpaca.Markets.OrderLeg! +static Alpaca.Markets.OrderSideExtensions.Leg(this Alpaca.Markets.PositionIntent positionIntent, string! symbol, decimal ratioQuantity, Alpaca.Markets.OrderSide orderSide) -> Alpaca.Markets.OrderLeg! static Alpaca.Markets.OrderSideExtensions.Limit(this Alpaca.Markets.OrderSide orderSide, string! symbol, Alpaca.Markets.OrderQuantity quantity, decimal limitPrice) -> Alpaca.Markets.LimitOrder! static Alpaca.Markets.OrderSideExtensions.Market(this Alpaca.Markets.OrderSide orderSide, string! symbol, Alpaca.Markets.OrderQuantity quantity) -> Alpaca.Markets.MarketOrder! static Alpaca.Markets.OrderSideExtensions.Stop(this Alpaca.Markets.OrderSide orderSide, string! symbol, Alpaca.Markets.OrderQuantity quantity, decimal stopPrice) -> Alpaca.Markets.StopOrder!