Skip to content

Commit

Permalink
Issue #771 - The options multi-legs orders support was expanded:
Browse files Browse the repository at this point in the history
- Added several helper functions for creating the `OrderLeg` instance
- The new methods were covered by tests for better cross-checking
  • Loading branch information
OlegRa committed Jan 29, 2025
1 parent 47b52c2 commit 384ef85
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 18 deletions.
41 changes: 24 additions & 17 deletions Alpaca.Markets.Tests/OrderTypeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 70 additions & 1 deletion Alpaca.Markets/Orders/OrderSideExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,73 @@ public static TrailingStopOrder TrailingStop(
OrderQuantity quantity,
TrailOffset trailOffset) =>
new(symbol, quantity, orderSide, trailOffset);
}

/// <summary>
/// Creates new leg for the options multi-leg order.
/// </summary>
/// <param name="orderSide">Order side (buy or sell).</param>
/// <param name="symbol">Order asset symbol.</param>
/// <param name="ratioQuantity">Order quantity.</param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="symbol"/> argument is <c>null</c>.
/// </exception>
/// <returns>The new <see cref="OrderLeg"/> object instance.</returns>
[UsedImplicitly]
public static OrderLeg Leg(
this OrderSide orderSide,
String symbol,
Decimal ratioQuantity) =>
new (symbol, ratioQuantity, orderSide);

/// <summary>
/// Creates new leg for the options multi-leg order.
/// </summary>
/// <param name="orderSide">Order side (buy or sell).</param>
/// <param name="symbol">Order asset symbol.</param>
/// <param name="ratioQuantity">Order quantity.</param>
/// <param name="positionIntent">Order position intent.</param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="symbol"/> argument is <c>null</c>.
/// </exception>
/// <returns>The new <see cref="OrderLeg"/> object instance.</returns>
public static OrderLeg Leg(
this OrderSide orderSide,
String symbol,
Decimal ratioQuantity,
PositionIntent positionIntent) =>
new (symbol, ratioQuantity, positionIntent, orderSide);

/// <summary>
/// Creates new leg for the options multi-leg order.
/// </summary>
/// <param name="positionIntent">Order position intent.</param>
/// <param name="symbol">Order asset symbol.</param>
/// <param name="ratioQuantity">Order quantity.</param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="symbol"/> argument is <c>null</c>.
/// </exception>
/// <returns>The new <see cref="OrderLeg"/> object instance.</returns>
[UsedImplicitly]
public static OrderLeg Leg(
this PositionIntent positionIntent,
String symbol,
Decimal ratioQuantity) =>
new (symbol, ratioQuantity, positionIntent);

/// <summary>
/// Creates new leg for the options multi-leg order.
/// </summary>
/// <param name="positionIntent">Order position intent.</param>
/// <param name="symbol">Order asset symbol.</param>
/// <param name="ratioQuantity">Order quantity.</param>
/// <param name="orderSide">Order side (buy or sell).</param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="symbol"/> argument is <c>null</c>.
/// </exception>
/// <returns>The new <see cref="OrderLeg"/> object instance.</returns>
public static OrderLeg Leg(
this PositionIntent positionIntent,
String symbol,
Decimal ratioQuantity,
OrderSide orderSide) =>
new (symbol, ratioQuantity, positionIntent, orderSide);}
4 changes: 4 additions & 0 deletions Alpaca.Markets/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,10 @@ static Alpaca.Markets.OrderBaseExtensions.WithDuration<TOrder>(this TOrder! orde
static Alpaca.Markets.OrderBaseExtensions.WithExtendedHours<TOrder>(this TOrder! order, bool extendedHours) -> TOrder!
static Alpaca.Markets.OrderBaseExtensions.WithPositionIntent<TOrder>(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!
Expand Down

0 comments on commit 384ef85

Please sign in to comment.