Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ea5830f
feat: support new OrderType in TastytradeBrokerageModel
Romazes Sep 29, 2025
c03e413
feat: extension GetGroupQuantity
Romazes Sep 29, 2025
6d9138d
refactor: WaitOneAssertFail to return bool
Romazes Sep 30, 2025
24cfc45
refactor: GetOpenOrders in BrokerageTests
Romazes Sep 30, 2025
3474b25
feat: display holdings when begin/end test run
Romazes Sep 30, 2025
dddf92c
refactor: brokerageTest class to support handle combo order
Romazes Sep 30, 2025
3f65c12
refactor: Tastytrade Algo BullCallSpread
Romazes Sep 30, 2025
b842247
feat: Bull/Bear-CallSpread test cases in BrokerageTests
Romazes Sep 30, 2025
531f78e
fix: remove static in ComboLimitOrderTestParameters of ExpectedStatus
Romazes Sep 30, 2025
c9907b2
feat: restrict submit combo limit cross zero orders in Tastytrade
Romazes Oct 1, 2025
831765b
test:fix: missed test helper to c9907b23
Romazes Oct 1, 2025
925c214
refactor: GetGroupQuantityByEachLegQuantity
Romazes Oct 1, 2025
fdf996a
test:feat: extra test cases to GetGroupQuantity to 925c214a
Romazes Oct 1, 2025
345cb96
test:refactor: take out Cancel Status update in HandleEvents()
Romazes Oct 2, 2025
4f92f97
feat: prop Direction in Leg
Romazes Oct 2, 2025
20d90a7
Revert "feat: prop Direction in Leg"
Romazes Oct 2, 2025
a076c7e
refactor: ComboLimitOrderTestParameters
Romazes Oct 3, 2025
632816f
test:refactor: CancelOrders
Romazes Oct 3, 2025
cba8ded
test:feat: add missed xml description
Romazes Oct 3, 2025
cf01e0b
test:feat: support ModifyUntilFilled Combo orders
Romazes Oct 3, 2025
c1adea4
test:feat: Long/Short-Combo unit tests
Romazes Oct 3, 2025
dd0d708
test:fix: compare all status if it is combo order type
Romazes Oct 3, 2025
a5108cf
test:refactor: applyOrderUpdate in gracefully way in LimitOrderTestPa…
Romazes Oct 3, 2025
f802d68
remover: Tastytrade algo
Romazes Oct 3, 2025
ec86e08
test:feat: create new BaseOrderTestParameters with helper methods
Romazes Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions Common/Brokerages/TastytradeBrokerageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,18 @@ public class TastytradeBrokerageModel : DefaultBrokerageModel
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit
OrderType.StopLimit,
OrderType.ComboLimit
});

/// <summary>
/// The set of <see cref="OrderType"/> values that cannot be used for cross-zero execution.
/// </summary>
private static IReadOnlySet<OrderType> NotSupportedCrossZeroOrderTypes => new HashSet<OrderType>()
{
OrderType.ComboLimit
};

/// <summary>
/// Constructor for Tastytrade brokerage model
/// </summary>
Expand Down Expand Up @@ -95,7 +104,7 @@ public override bool CanSubmitOrder(Security security, Order order, out Brokerag
return false;
}

if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message))
if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message, NotSupportedCrossZeroOrderTypes))
{
return false;
}
Expand Down
10 changes: 10 additions & 0 deletions Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4344,6 +4344,16 @@ public static ConvertibleCashAmount InTheMoneyAmount(this Option option, decimal
return option.Holdings.GetQuantityValue(Math.Abs(quantity), option.GetPayOff(option.Underlying.Price));
}

/// <summary>
/// Gets the greatest common divisor of a list of numbers
/// </summary>
/// <param name="values">List of numbers which greatest common divisor is requested</param>
/// <returns>The greatest common divisor for the given list of numbers</returns>
public static decimal GreatestCommonDivisor(this IEnumerable<decimal> values)
{
return GreatestCommonDivisor(values.Select(Convert.ToInt32));
}

/// <summary>
/// Gets the greatest common divisor of a list of numbers
/// </summary>
Expand Down
27 changes: 27 additions & 0 deletions Common/Orders/GroupOrderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,32 @@ public static decimal GetOrderLegRatio(this decimal legGroupQuantity, GroupOrder
{
return groupOrderManager != null ? legGroupQuantity / groupOrderManager.Quantity : legGroupQuantity;
}

/// <summary>
/// Calculates the greatest common divisor (GCD) of the provided leg quantities
/// and returns it as a signed quantity based on the <see cref="OrderDirection"/>.
/// </summary>
/// <param name="legQuantity">A collection of leg quantities.</param>
/// <param name="orderDirection">
/// Determines the sign of the returned quantity:
/// <see cref="OrderDirection.Buy"/> returns a positive quantity,
/// <see cref="OrderDirection.Sell"/> returns a negative quantity.
/// </param>
/// <returns>
/// The greatest common divisor of the leg quantities, signed according to <paramref name="orderDirection"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="orderDirection"/> has an unsupported value.
/// </exception>
public static decimal GetGroupQuantityByEachLegQuantity(IEnumerable<decimal> legQuantity, OrderDirection orderDirection)
{
var groupQuantity = Extensions.GreatestCommonDivisor(legQuantity.Select(Math.Abs));
return orderDirection switch
{
OrderDirection.Buy => groupQuantity,
OrderDirection.Sell => decimal.Negate(groupQuantity),
_ => throw new ArgumentException($"Unsupported {nameof(OrderDirection)} value: '{orderDirection}'.", nameof(orderDirection))
};
}
}
}
82 changes: 82 additions & 0 deletions Tests/Brokerages/BaseOrderTestParameters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using QuantConnect.Orders;
using QuantConnect.Logging;
using System.Collections.Generic;

namespace QuantConnect.Tests.Brokerages
{
public abstract class BaseOrderTestParameters
{
/// <summary>
/// Calculates the adjusted limit price for an order based on its direction
/// and a price adjustment factor, ensuring the price moves toward being filled.
/// </summary>
/// <param name="orderDirection">The direction of the order (Buy or Sell).</param>
/// <param name="previousLimitPrice">The previous limit price of the order.</param>
/// <param name="targetMarketPrice">The target market price used to adjust the limit price.</param>
/// <param name="priceAdjustmentFactor">The factor by which the price is adjusted.</param>
/// <returns>The new, adjusted limit price.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the order direction is not Buy or Sell.</exception>
protected virtual decimal CalculateAdjustedLimitPrice(OrderDirection orderDirection, decimal previousLimitPrice, decimal targetMarketPrice, decimal priceAdjustmentFactor)
{
var adjustmentLimitPrice = orderDirection switch
{
OrderDirection.Buy => Math.Max(previousLimitPrice * priceAdjustmentFactor, targetMarketPrice * priceAdjustmentFactor),
OrderDirection.Sell => Math.Min(previousLimitPrice / priceAdjustmentFactor, targetMarketPrice / priceAdjustmentFactor),
_ => throw new NotSupportedException("Unsupported order direction: " + orderDirection)
};
Log.Trace($"{nameof(CalculateAdjustedLimitPrice)}: {orderDirection} | Prev: {previousLimitPrice}, Target: {targetMarketPrice}, AdjustmentFactor: {priceAdjustmentFactor}, Result: {adjustmentLimitPrice}");
return adjustmentLimitPrice;
}

/// <summary>
/// Rounds the given price to the nearest increment defined by the underlying symbol's minimum price variation.
/// </summary>
/// <param name="price">The original price to round.</param>
/// <param name="minimumPriceVariation">The minimum tick size or price increment for the symbol.</param>
/// <returns>The price rounded to the nearest valid increment.</returns>
protected virtual decimal RoundPrice(decimal price, decimal minimumPriceVariation)
{
var roundOffPlaces = minimumPriceVariation.GetDecimalPlaces();
var roundedPrice = Math.Round(price / roundOffPlaces) * roundOffPlaces;
Log.Trace($"{nameof(BaseOrderTestParameters)}.{nameof(RoundPrice)}: Price = {price}, Minimum Price increment = {minimumPriceVariation}, Rounded price = {roundedPrice}");
return roundedPrice;
}

protected void ApplyUpdateOrderRequests(IReadOnlyCollection<Order> orders, UpdateOrderFields fields)
{
foreach (var order in orders)
{
ApplyUpdateOrderRequest(order, fields);
}
}

protected void ApplyUpdateOrderRequest(Order order, UpdateOrderFields fields)
{
order.ApplyUpdateOrderRequest(new UpdateOrderRequest(DateTime.UtcNow, order.Id, fields));
}

/// <summary>
/// Base class for defining order test parameters.
/// Implement <see cref="ToString"/> to provide a descriptive name
/// for displaying the test case in <c>Visual Studio Test Explorer</c>.
/// </summary>
/// <returns>A string representing the test parameters for display purposes.</returns>
public abstract override string ToString();
}
}
Loading