Files
2026-07-13 13:02:50 +08:00

185 lines
9.0 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 System;
using System.Linq;
using QuantConnect.Orders;
namespace QuantConnect.Securities.CryptoFuture
{
/// <summary>
/// The crypto future margin model which supports both Coin and USDT futures
/// </summary>
public class CryptoFutureMarginModel : SecurityMarginModel
{
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="leverage">The leverage to use, used on initial margin requirements, default 25x</param>
/// <param name="maintenanceMarginRate">The maintenance margin rate, default 5%</param>
/// <param name="maintenanceAmount">The maintenance amount which will reduce maintenance margin requirements, default 0</param>
[Obsolete("This constructor is deprecated, please use the overload without maintenanceMarginRate and maintenanceAmount parameters.")]
public CryptoFutureMarginModel(decimal leverage, decimal maintenanceMarginRate = 0.05m, decimal maintenanceAmount = 0)
: base(leverage, 0)
{
}
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="leverage">The leverage to use, used on initial margin requirements, default 25x</param>
public CryptoFutureMarginModel(decimal leverage = 25)
: base(leverage, 0)
{
}
/// <summary>
/// Gets the margin currently alloted to the specified holding.
/// </summary>
/// <param name="parameters">An object containing the security</param>
/// <returns>The maintenance margin required for the option</returns>
public override MaintenanceMargin GetMaintenanceMargin(MaintenanceMarginParameters parameters)
{
return new MaintenanceMargin(GetInitialMarginRequirement(new InitialMarginParameters(parameters.Security, parameters.Quantity)));
}
/// <summary>
/// The margin that must be held in order to increase the position by the provided quantity
/// </summary>
/// <param name="parameters">An object containing the security and quantity of shares</param>
/// <returns>The initial margin required for the option (i.e. the equity required to enter a position for this option)</returns>
public override InitialMargin GetInitialMarginRequirement(InitialMarginParameters parameters)
{
var security = parameters.Security;
var quantity = parameters.Quantity;
if (security?.GetLastData() == null || quantity == 0m)
{
return InitialMargin.Zero;
}
var positionValue = security.Holdings.GetQuantityValue(quantity, security.Price);
var marginRequirementInCollateral = Math.Abs(positionValue.Amount) / GetLeverage(security);
return new InitialMargin(marginRequirementInCollateral * positionValue.Cash.ConversionRate);
}
/// <summary>
/// Gets the margin cash available for a trade
/// </summary>
/// <param name="portfolio">The algorithm's portfolio</param>
/// <param name="security">The security to be traded</param>
/// <param name="direction">The direction of the trade</param>
/// <returns>The margin available for the trade</returns>
/// <remarks>What we do specially here is that instead of using the total portfolio value as potential margin remaining we only consider the collateral currency</remarks>
protected override decimal GetMarginRemaining(SecurityPortfolioManager portfolio, Security security, OrderDirection direction)
{
var collateralCurrency = GetCollateralCash(security);
var totalCollateralCurrency = GetTotalCollateralAmount(portfolio, security, collateralCurrency);
var result = totalCollateralCurrency;
foreach (var kvp in portfolio.Where(holdings => holdings.Value.Invested && holdings.Value.Type == SecurityType.CryptoFuture && holdings.Value.Symbol != security.Symbol))
{
var otherCryptoFuture = portfolio.Securities[kvp.Key];
// check if we share the collateral
if (SharesCollateral(portfolio, collateralCurrency, otherCryptoFuture))
{
// we reduce the available collateral based on total usage of all other positions too
result -= otherCryptoFuture.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForCurrentHoldings(otherCryptoFuture));
}
}
if (direction != OrderDirection.Hold)
{
var holdings = security.Holdings;
//If the order is in the same direction as holdings, our remaining cash is our cash
//In the opposite direction, our remaining cash is 2 x current value of assets + our cash
if (holdings.IsLong)
{
switch (direction)
{
case OrderDirection.Sell:
result +=
// portion of margin to close the existing position
this.GetMaintenanceMargin(security) +
// portion of margin to open the new position
this.GetInitialMarginRequirement(security, security.Holdings.AbsoluteQuantity);
break;
}
}
else if (holdings.IsShort)
{
switch (direction)
{
case OrderDirection.Buy:
result +=
// portion of margin to close the existing position
this.GetMaintenanceMargin(security) +
// portion of margin to open the new position
this.GetInitialMarginRequirement(security, security.Holdings.AbsoluteQuantity);
break;
}
}
}
result -= totalCollateralCurrency * RequiredFreeBuyingPowerPercent;
// convert into account currency
result *= collateralCurrency.ConversionRate;
return result < 0 ? 0 : result;
}
/// <summary>
/// Determines whether the given security shares collateral with another crypto future.
/// </summary>
/// <param name="portfolio">The algorithm's portfolio</param>
/// <param name="collateralCurrency">The collateral cash for the current security</param>
/// <param name="otherCryptoFuture">The other crypto future security to check</param>
/// <returns>True if both securities share the same collateral</returns>
protected virtual bool SharesCollateral(SecurityPortfolioManager portfolio, Cash collateralCurrency, Security otherCryptoFuture)
{
return collateralCurrency == GetCollateralCash(otherCryptoFuture);
}
/// <summary>
/// Helper method to determine what's the collateral currency for the given crypto future
/// </summary>
private static Cash GetCollateralCash(Security security)
{
var cryptoFuture = (CryptoFuture)security;
var collateralCurrency = cryptoFuture.BaseCurrency;
if (!cryptoFuture.IsCryptoCoinFuture())
{
collateralCurrency = cryptoFuture.QuoteCurrency;
}
return collateralCurrency;
}
/// <summary>
/// Gets the total collateral amount for the given crypto future position.
/// The base implementation returns only the primary collateral amount.
/// Override in subclasses to include supplementary collateral currencies.
/// </summary>
/// <param name="portfolio">The algorithm's portfolio</param>
/// <param name="security">The crypto future security</param>
/// <param name="primaryCollateral">The primary collateral cash (e.g. USDT for non-coin futures, BTC for coin futures)</param>
/// <returns>Total collateral amount in terms of the primary collateral currency</returns>
protected virtual decimal GetTotalCollateralAmount(SecurityPortfolioManager portfolio, Security security, Cash primaryCollateral)
{
return primaryCollateral.Amount;
}
}
}