/*
* 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 NUnit.Framework;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;
namespace QuantConnect.Tests.Brokerages.Tastytrade
{
///
/// Contains unit tests for the to ensure correct fee calculations
/// for different security types such as equities, options, futures, and future options.
///
[TestFixture]
public class TastytradeFeeModelTests
{
private TastytradeFeeModel _feeModel;
///
/// Initializes the instance once before any tests are run.
///
[OneTimeSetUp]
public void OneTimeSetUp()
{
_feeModel = new TastytradeFeeModel();
}
///
/// Provides test cases for fee calculation across various symbols, order quantities,
/// and holding quantities.
///
private static IEnumerable FeeTestParameters
{
get
{
var aapl = Symbols.AAPL;
yield return new(aapl, 1m, 0m, 0m);
yield return new(aapl, 10m, 0m, 0m);
var aaplOptionContract = Symbol.CreateOption(aapl, aapl.ID.Market, SecurityType.Option.DefaultOptionStyle(), OptionRight.Call, 200m, new DateTime(2025, 06, 20));
yield return new(aaplOptionContract, 10m, 0m, 10m);
yield return new(aaplOptionContract, 10m, 1m, 10m);
yield return new(aaplOptionContract, 1m, -1m, 0m);
yield return new(aaplOptionContract, 10m, -10m, 0m);
yield return new(aaplOptionContract, 11m, -10m, 0m);
var SP500EMini = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2025, 06, 20));
yield return new(SP500EMini, 1m, 0m, 1.25m);
yield return new(SP500EMini, 10m, 1m, 12.5m);
yield return new(SP500EMini, -10m, 1m, 12.5m);
var SP500EMini_OptionContract = Symbol.CreateOption(SP500EMini, SP500EMini.ID.Market, SecurityType.FutureOption.DefaultOptionStyle(), OptionRight.Put, 900m, new DateTime(2025, 09, 19));
yield return new(SP500EMini_OptionContract, 1m, 0m, 2.5m);
yield return new(SP500EMini_OptionContract, 2m, 0m, 5m);
yield return new(SP500EMini_OptionContract, 4m, 0m, 10m);
yield return new(SP500EMini_OptionContract, 4m, 0m, 10m);
yield return new(SP500EMini_OptionContract, 1m, -1m, 0m);
yield return new(SP500EMini_OptionContract, 10m, -10m, 0m);
yield return new(SP500EMini_OptionContract, 11m, -11m, 0m);
yield return new(SP500EMini_OptionContract, -10m, -10m, 25m);
}
}
[Test, TestCaseSource(nameof(FeeTestParameters))]
public void CalculateRightOrderFeeBasedOnSecurity(Symbol symbol, decimal orderQuantity, decimal symbolHoldingQuantity, decimal expectedFeeQuantity)
{
var parameters = GetOrderFeeParameters(symbol, orderQuantity, symbolHoldingQuantity);
var fee = _feeModel.GetOrderFee(parameters);
Assert.AreEqual(expectedFeeQuantity, fee.Value.Amount);
Assert.AreEqual(Currencies.USD, fee.Value.Currency);
}
///
/// Creates using a and given
/// symbol and holding quantity.
///
/// The symbol of the security being ordered.
/// The order quantity.
/// The current holding quantity.
/// A populated object.
private static OrderFeeParameters GetOrderFeeParameters(Symbol symbol, decimal orderQuantity, decimal symbolHoldingQuantity)
{
var security = new Security(symbol, null, new Cash("USD", 0m, 1m), SymbolProperties.GetDefault("USD"), null, null, new SecurityCache());
security.Holdings.SetHoldings(100, symbolHoldingQuantity);
var marketOrder = new MarketOrder(symbol, orderQuantity, DateTime.UtcNow);
return new OrderFeeParameters(security, marketOrder);
}
}
}