227 lines
8.6 KiB
C#
227 lines
8.6 KiB
C#
/*
|
||
* 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 QuantConnect.Securities;
|
||
|
||
namespace QuantConnect.Orders.Fees
|
||
{
|
||
/// <summary>
|
||
/// Represents a fee model specific to Webull.
|
||
/// </summary>
|
||
/// <see href="https://www.webull.com/pricing"/>
|
||
/// <remarks>
|
||
/// Equity and standard options trades are commission-free on Webull.
|
||
/// Index options carry a flat $0.50 Webull contract fee plus a variable exchange proprietary fee
|
||
/// that depends on the underlying index symbol and the option's market price.
|
||
/// </remarks>
|
||
public class WebullFeeModel : FeeModel
|
||
{
|
||
/// <summary>
|
||
/// Webull contract fee applied to every index option contract, regardless of underlying.
|
||
/// </summary>
|
||
private const decimal _webullIndexOptionContractFee = 0.50m;
|
||
|
||
/// <summary>
|
||
/// Exchange proprietary fee for SPX options priced below $1.00.
|
||
/// </summary>
|
||
private const decimal _spxExchangeFeeBelow1 = 0.57m;
|
||
|
||
/// <summary>
|
||
/// Exchange proprietary fee for SPX options priced at or above $1.00.
|
||
/// </summary>
|
||
private const decimal _spxExchangeFeeAbove1 = 0.66m;
|
||
|
||
/// <summary>
|
||
/// Exchange proprietary fee for SPXW options priced below $1.00.
|
||
/// </summary>
|
||
private const decimal _spxwExchangeFeeBelow1 = 0.50m;
|
||
|
||
/// <summary>
|
||
/// Exchange proprietary fee for SPXW options priced at or above $1.00.
|
||
/// </summary>
|
||
private const decimal _spxwExchangeFeeAbove1 = 0.59m;
|
||
|
||
/// <summary>
|
||
/// VIX/VIXW exchange fee tier 1: option price at or below $0.10.
|
||
/// </summary>
|
||
private const decimal _vixExchangeFeeTier1 = 0.10m;
|
||
|
||
/// <summary>
|
||
/// VIX/VIXW exchange fee tier 2: option price between $0.11 and $0.99.
|
||
/// </summary>
|
||
private const decimal _vixExchangeFeeTier2 = 0.25m;
|
||
|
||
/// <summary>
|
||
/// VIX/VIXW exchange fee tier 3: option price between $1.00 and $1.99.
|
||
/// </summary>
|
||
private const decimal _vixExchangeFeeTier3 = 0.40m;
|
||
|
||
/// <summary>
|
||
/// VIX/VIXW exchange fee tier 4: option price at or above $2.00.
|
||
/// </summary>
|
||
private const decimal _vixExchangeFeeTier4 = 0.45m;
|
||
|
||
/// <summary>
|
||
/// XSP exchange fee for orders with fewer than 10 contracts.
|
||
/// </summary>
|
||
private const decimal _xspExchangeFeeSmall = 0.00m;
|
||
|
||
/// <summary>
|
||
/// XSP exchange fee for orders with 10 or more contracts.
|
||
/// </summary>
|
||
private const decimal _xspExchangeFeeLarge = 0.07m;
|
||
|
||
/// <summary>
|
||
/// DJX flat exchange proprietary fee per contract.
|
||
/// </summary>
|
||
private const decimal _djxExchangeFee = 0.18m;
|
||
|
||
/// <summary>
|
||
/// NDX/NDXP exchange fee for single-leg orders with premium below $25.
|
||
/// </summary>
|
||
private const decimal _ndxSingleLegFeeBelow25 = 0.50m;
|
||
|
||
/// <summary>
|
||
/// NDX/NDXP exchange fee for single-leg orders with premium at or above $25.
|
||
/// </summary>
|
||
private const decimal _ndxSingleLegFeeAbove25 = 0.75m;
|
||
|
||
/// <summary>
|
||
/// NDX/NDXP exchange fee for multi-leg orders with premium below $25.
|
||
/// </summary>
|
||
private const decimal _ndxMultiLegFeeBelow25 = 0.65m;
|
||
|
||
/// <summary>
|
||
/// NDX/NDXP exchange fee for multi-leg orders with premium at or above $25.
|
||
/// </summary>
|
||
private const decimal _ndxMultiLegFeeAbove25 = 0.90m;
|
||
|
||
/// <summary>
|
||
/// Gets the order fee for a given security and order.
|
||
/// </summary>
|
||
/// <param name="parameters">The parameters including the security and order details.</param>
|
||
/// <returns>
|
||
/// <see cref="OrderFee.Zero"/> for equity and standard options;
|
||
/// a per-contract fee for index options.
|
||
/// </returns>
|
||
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
|
||
{
|
||
switch (parameters.Security.Type)
|
||
{
|
||
case SecurityType.IndexOption:
|
||
return new OrderFee(new CashAmount(GetIndexOptionFee(parameters), Currencies.USD));
|
||
default:
|
||
// Equity and Option are commission-free on Webull.
|
||
return OrderFee.Zero;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Calculates the total per-contract fee for an index option order.
|
||
/// The total fee = (exchange proprietary fee + Webull contract fee) × quantity.
|
||
/// </summary>
|
||
/// <param name="parameters">Order fee parameters containing the security and order.</param>
|
||
/// <returns>Total fee amount in USD.</returns>
|
||
private static decimal GetIndexOptionFee(OrderFeeParameters parameters)
|
||
{
|
||
var order = parameters.Order;
|
||
var security = parameters.Security;
|
||
var quantity = order.AbsoluteQuantity;
|
||
var price = security.Price;
|
||
var underlying = security.Symbol.Underlying?.Value?.ToUpperInvariant() ?? string.Empty;
|
||
var isMultiLeg = order.Type == OrderType.ComboMarket
|
||
|| order.Type == OrderType.ComboLimit
|
||
|| order.Type == OrderType.ComboLegLimit;
|
||
|
||
var exchangeFee = GetIndexOptionExchangeFee(underlying, price, quantity, isMultiLeg);
|
||
return quantity * (exchangeFee + _webullIndexOptionContractFee);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns the exchange proprietary fee per contract for an index option, based on
|
||
/// the underlying ticker, the option's current price, order quantity, and leg type.
|
||
/// </summary>
|
||
/// <param name="underlying">Uppercase underlying ticker (e.g. "SPX", "VIX").</param>
|
||
/// <param name="price">Current market price of the option.</param>
|
||
/// <param name="quantity">Absolute number of contracts in the order.</param>
|
||
/// <param name="isMultiLeg">True when the order is a combo/multi-leg order.</param>
|
||
/// <returns>Exchange fee per contract in USD.</returns>
|
||
private static decimal GetIndexOptionExchangeFee(string underlying, decimal price, decimal quantity, bool isMultiLeg)
|
||
{
|
||
switch (underlying)
|
||
{
|
||
case "SPX":
|
||
return price < 1m ? _spxExchangeFeeBelow1 : _spxExchangeFeeAbove1;
|
||
|
||
case "SPXW":
|
||
return price < 1m ? _spxwExchangeFeeBelow1 : _spxwExchangeFeeAbove1;
|
||
|
||
case "VIX":
|
||
case "VIXW":
|
||
return GetVixExchangeFee(price);
|
||
|
||
case "XSP":
|
||
return quantity < 10m ? _xspExchangeFeeSmall : _xspExchangeFeeLarge;
|
||
|
||
case "DJX":
|
||
return _djxExchangeFee;
|
||
|
||
case "NDX":
|
||
case "NDXP":
|
||
return GetNdxExchangeFee(price, isMultiLeg);
|
||
|
||
default:
|
||
return 0m;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns the VIX/VIXW exchange fee for a simple order based on the option price tier.
|
||
/// </summary>
|
||
private static decimal GetVixExchangeFee(decimal price)
|
||
{
|
||
if (price <= 0.10m)
|
||
{
|
||
return _vixExchangeFeeTier1;
|
||
}
|
||
|
||
if (price <= 0.99m)
|
||
{
|
||
return _vixExchangeFeeTier2;
|
||
}
|
||
|
||
if (price <= 1.99m)
|
||
{
|
||
return _vixExchangeFeeTier3;
|
||
}
|
||
|
||
return _vixExchangeFeeTier4;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns the NDX/NDXP exchange fee per contract based on premium tier and order leg type.
|
||
/// </summary>
|
||
private static decimal GetNdxExchangeFee(decimal price, bool isMultiLeg)
|
||
{
|
||
if (isMultiLeg)
|
||
{
|
||
return price < 25m ? _ndxMultiLegFeeBelow25 : _ndxMultiLegFeeAbove25;
|
||
}
|
||
|
||
return price < 25m ? _ndxSingleLegFeeBelow25 : _ndxSingleLegFeeAbove25;
|
||
}
|
||
}
|
||
}
|