chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,39 @@
/*
* 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.Collections.Generic;
using System.Linq;
using QuantConnect.Interfaces;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// An implementation of <see cref="IFutureChainProvider"/> that always returns an empty list of contracts
/// </summary>
public class EmptyFutureChainProvider : IFutureChainProvider
{
/// <summary>
/// Gets the list of future contracts for a given underlying symbol
/// </summary>
/// <param name="symbol">The underlying symbol</param>
/// <param name="date">The date for which to request the future chain (only used in backtesting)</param>
/// <returns>The list of future contracts</returns>
public IEnumerable<Symbol> GetFutureContractList(Symbol symbol, DateTime date)
{
return Enumerable.Empty<Symbol>();
}
}
}
+269
View File
@@ -0,0 +1,269 @@
/*
* 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.Data;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using Python.Runtime;
using QuantConnect.Util;
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Futures Security Object Implementation for Futures Assets
/// </summary>
/// <seealso cref="Security"/>
public class Future : Security, IContinuousSecurity
{
private bool _isTradable;
/// <summary>
/// Gets or sets whether or not this security should be considered tradable
/// </summary>
/// <remarks>Canonical futures are not tradable</remarks>
public override bool IsTradable
{
get
{
// once a future is removed it is no longer tradable
return _isTradable && !Symbol.IsCanonical();
}
set
{
_isTradable = value;
}
}
/// <summary>
/// The default number of days required to settle a futures sale
/// </summary>
public const int DefaultSettlementDays = 1;
/// <summary>
/// The default time of day for settlement
/// </summary>
public static readonly TimeSpan DefaultSettlementTime = new TimeSpan(6, 0, 0);
/// <summary>
/// Constructor for the Future security
/// </summary>
/// <param name="exchangeHours">Defines the hours this exchange is open</param>
/// <param name="quoteCurrency">The cash object that represent the quote currency</param>
/// <param name="config">The subscription configuration for this security</param>
/// <param name="symbolProperties">The symbol properties for this security</param>
/// <param name="currencyConverter">Currency converter used to convert <see cref="CashAmount"/>
/// instances into units of the account currency</param>
/// <param name="registeredTypes">Provides all data types registered in the algorithm</param>
public Future(SecurityExchangeHours exchangeHours,
SubscriptionDataConfig config,
Cash quoteCurrency,
SymbolProperties symbolProperties,
ICurrencyConverter currencyConverter,
IRegisteredSecurityDataTypesProvider registeredTypes
)
: base(config,
quoteCurrency,
symbolProperties,
new FutureExchange(exchangeHours),
new FutureCache(),
new SecurityPortfolioModel(),
new FutureFillModel(),
new InteractiveBrokersFeeModel(),
NullSlippageModel.Instance,
new FutureSettlementModel(),
Securities.VolatilityModel.Null,
null,
new SecurityDataFilter(),
new SecurityPriceVariationModel(),
currencyConverter,
registeredTypes,
Securities.MarginInterestRateModel.Null
)
{
BuyingPowerModel = new FutureMarginModel(0, this);
// for now all futures are cash settled as we don't allow underlying (Live Cattle?) to be posted on the account
SettlementType = SettlementType.Cash;
Holdings = new FutureHolding(this, currencyConverter);
ContractFilter = new EmptyContractFilter<FutureUniverse>();
}
/// <summary>
/// Constructor for the Future security
/// </summary>
/// <param name="symbol">The subscription security symbol</param>
/// <param name="exchangeHours">Defines the hours this exchange is open</param>
/// <param name="quoteCurrency">The cash object that represent the quote currency</param>
/// <param name="symbolProperties">The symbol properties for this security</param>
/// <param name="currencyConverter">Currency converter used to convert <see cref="CashAmount"/>
/// instances into units of the account currency</param>
/// <param name="registeredTypes">Provides all data types registered in the algorithm</param>
/// <param name="securityCache">Cache to store security information</param>
public Future(Symbol symbol,
SecurityExchangeHours exchangeHours,
Cash quoteCurrency,
SymbolProperties symbolProperties,
ICurrencyConverter currencyConverter,
IRegisteredSecurityDataTypesProvider registeredTypes,
SecurityCache securityCache)
: base(symbol,
quoteCurrency,
symbolProperties,
new FutureExchange(exchangeHours),
securityCache,
new SecurityPortfolioModel(),
new FutureFillModel(),
new InteractiveBrokersFeeModel(),
NullSlippageModel.Instance,
new FutureSettlementModel(),
Securities.VolatilityModel.Null,
null,
new SecurityDataFilter(),
new SecurityPriceVariationModel(),
currencyConverter,
registeredTypes,
Securities.MarginInterestRateModel.Null
)
{
BuyingPowerModel = new FutureMarginModel(0, this);
// for now all futures are cash settled as we don't allow underlying (Live Cattle?) to be posted on the account
SettlementType = SettlementType.Cash;
Holdings = new FutureHolding(this, currencyConverter);
ContractFilter = new EmptyContractFilter<FutureUniverse>();
}
/// <summary>
/// Returns true if this is the future chain security, false if it is a specific future contract
/// </summary>
public bool IsFutureChain => Symbol.IsCanonical();
/// <summary>
/// Returns true if this is a specific future contract security, false if it is the future chain security
/// </summary>
public bool IsFutureContract => !Symbol.IsCanonical();
/// <summary>
/// Gets the expiration date
/// </summary>
public DateTime Expiry
{
get { return Symbol.ID.Date; }
}
/// <summary>
/// Specifies if futures contract has physical or cash settlement on settlement
/// </summary>
public SettlementType SettlementType
{
get; set;
}
/// <summary>
/// Gets or sets the currently mapped symbol for the security
/// </summary>
public Symbol Mapped
{
get; set;
}
/// <summary>
/// Gets or sets the contract filter
/// </summary>
public IDerivativeSecurityFilter<FutureUniverse> ContractFilter
{
get; set;
}
/// <summary>
/// Sets the <see cref="LocalTimeKeeper"/> to be used for this <see cref="Security"/>.
/// This is the source of this instance's time.
/// </summary>
/// <param name="localTimeKeeper">The source of this <see cref="Security"/>'s time.</param>
public override void SetLocalTimeKeeper(LocalTimeKeeper localTimeKeeper)
{
base.SetLocalTimeKeeper(localTimeKeeper);
var model = SettlementModel as FutureSettlementModel;
if (model != null)
{
model.SetLocalDateTimeFrontier(LocalTime);
}
}
/// <summary>
/// Sets the <see cref="ContractFilter"/> to a new instance of the filter
/// using the specified expiration range values
/// </summary>
/// <param name="minExpiry">The minimum time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in less than 10 days</param>
/// <param name="maxExpiry">The maximum time until expiry to include, for example, TimeSpan.FromDays(10)
/// would exclude contracts expiring in more than 10 days</param>
public void SetFilter(TimeSpan minExpiry, TimeSpan maxExpiry)
{
SetFilterImp(universe => universe.Expiration(minExpiry, maxExpiry));
}
/// <summary>
/// Sets the <see cref="ContractFilter"/> to a new instance of the filter
/// using the specified expiration range values
/// </summary>
/// <param name="minExpiryDays">The minimum time, expressed in days, until expiry to include, for example, 10
/// would exclude contracts expiring in less than 10 days</param>
/// <param name="maxExpiryDays">The maximum time, expressed in days, until expiry to include, for example, 10
/// would exclude contracts expiring in more than 10 days</param>
public void SetFilter(int minExpiryDays, int maxExpiryDays)
{
SetFilterImp(universe => universe.Expiration(minExpiryDays, maxExpiryDays));
}
/// <summary>
/// Sets the <see cref="ContractFilter"/> to a new universe selection function
/// </summary>
/// <param name="universeFunc">new universe selection function</param>
public void SetFilter(Func<FutureFilterUniverse, FutureFilterUniverse> universeFunc)
{
SetFilterImp(universeFunc);
ContractFilter.Asynchronous = false;
}
/// <summary>
/// Sets the <see cref="ContractFilter"/> to a new universe selection function
/// </summary>
/// <param name="universeFunc">new universe selection function</param>
public void SetFilter(PyObject universeFunc)
{
var pyUniverseFunc = PythonUtil.ToFunc<FutureFilterUniverse, FutureFilterUniverse>(universeFunc);
SetFilter(pyUniverseFunc);
}
private void SetFilterImp(Func<FutureFilterUniverse, FutureFilterUniverse> universeFunc)
{
Func<IDerivativeSecurityFilterUniverse<FutureUniverse>, IDerivativeSecurityFilterUniverse<FutureUniverse>> func = universe =>
{
var futureUniverse = universe as FutureFilterUniverse;
var result = universeFunc(futureUniverse);
return result.ApplyTypesFilter();
};
ContractFilter = new FuncSecurityDerivativeFilter<FutureUniverse>(func);
}
/// <summary>
/// Returns the securities symbol
/// </summary>
public static implicit operator Symbol(Future security) => security.Symbol;
}
}
+56
View File
@@ -0,0 +1,56 @@
/*
* 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.Collections.Generic;
using QuantConnect.Data;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Future specific caching support
/// </summary>
/// <seealso cref="SecurityCache"/>
public class FutureCache : SecurityCache
{
/// <summary>
/// The current settlement price
/// </summary>
public decimal SettlementPrice { get; set; }
/// <summary>
/// Will consume the given data point updating the cache state and it's properties
/// </summary>
/// <param name="data">The data point to process</param>
/// <param name="cacheByType">True if this data point should be cached by type</param>
protected override void ProcessDataPoint(BaseData data, bool cacheByType)
{
base.ProcessDataPoint(data, cacheByType);
SettlementPrice = Price;
}
/// <summary>
/// Stores the specified data list in the cache, updating the open interest from any chain universe data
/// </summary>
/// <param name="data">The collection of data to store in this cache</param>
/// <param name="dataType">The data type</param>
public override void StoreData(IReadOnlyList<BaseData> data, Type dataType)
{
UpdateOpenInterest(data);
base.StoreData(data, dataType);
}
}
}
@@ -0,0 +1,43 @@
/*
* 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.
*/
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Future exchange class - information and helper tools for future exchange properties
/// </summary>
/// <seealso cref="SecurityExchange"/>
public class FutureExchange : SecurityExchange
{
/// <summary>
/// Number of trading days per year for this security, 252.
/// </summary>
/// <remarks>Used for performance statistics to calculate sharpe ratio accurately</remarks>
public override int TradingDaysPerYear
{
get { return 252; }
}
/// <summary>
/// Initializes a new instance of the <see cref="FutureExchange"/> class using the specified
/// exchange hours to determine open/close times
/// </summary>
/// <param name="exchangeHours">Contains the weekly exchange schedule plus holidays</param>
public FutureExchange(SecurityExchangeHours exchangeHours)
: base(exchangeHours)
{
}
}
}
@@ -0,0 +1,113 @@
/*
* 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.
*/
namespace QuantConnect.Securities
{
/// <summary>
/// Static class contains definitions of popular futures expiration cycles
/// </summary>
public static class FutureExpirationCycles
{
/// <summary>
/// January Cycle: Expirations in January, April, July, October (the first month of each quarter)
/// </summary>
public static readonly int[] January = { 1, 4, 7, 10 };
/// <summary>
/// February Cycle: Expirations in February, May, August, November (second month)
/// </summary>
public static readonly int[] February = { 2, 5, 8, 11 };
/// <summary>
/// March Cycle: Expirations in March, June, September, December (third month)
/// </summary>
public static readonly int[] March = { 3, 6, 9, 12 };
/// <summary>
/// December Cycle: Expirations in December
/// </summary>
public static readonly int[] December = { 12 };
/// <summary>
/// All Year Cycle: Expirations in every month of the year
/// </summary>
public static readonly int[] AllYear = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
/// <summary>
/// GJMQVZ Cycle
/// </summary>
public static readonly int[] GJMQVZ = { 2, 4, 6, 8, 10, 12 };
/// <summary>
/// GJKMNQVZ Cycle
/// </summary>
public static readonly int[] GJKMNQVZ = { 2, 4, 5, 6, 7, 8, 10, 12 };
/// <summary>
/// HMUZ Cycle
/// </summary>
public static readonly int[] HMUZ = March;
/// <summary>
/// HKNUZ Cycle
/// </summary>
public static readonly int[] HKNUZ = { 3, 5, 7, 9, 12 };
/// <summary>
/// HKNV Cycle
/// </summary>
public static readonly int[] HKNV = { 3, 5, 7, 10 };
/// <summary>
/// HKNVZ Cycle
/// </summary>
public static readonly int[] HKNVZ = { 3, 5, 7, 10, 12 };
/// <summary>
/// FHKNUX Cycle
/// </summary>
public static readonly int[] FHKNUX = { 1, 3, 5, 7, 9, 11 };
/// <summary>
/// FHJKQUVX Cycle
/// </summary>
public static readonly int[] FHJKQUVX = { 1, 3, 4, 5, 8, 9, 10, 11 };
/// <summary>
/// HKNUVZ Cycle
/// </summary>
public static readonly int[] HKNUVZ = { 3, 5, 7, 9, 10, 12 };
/// <summary>
/// FHKNQUVZ Cycle
/// </summary>
public static readonly int[] FHKNUVZ = { 1, 3, 5, 7, 9, 10, 12 };
/// <summary>
/// FHKMQUVZ Cycle
/// </summary>
public static readonly int[] FHKNQUVZ = { 1, 3, 5, 7, 8, 9, 10, 12 };
/// <summary>
/// FHKNQUX Cycle
/// </summary>
public static readonly int[] FHKNQUX = { 1, 3, 5, 7, 8, 9, 11 };
/// <summary>
/// FGHJKMNQUVXZ Cycle
/// </summary>
public static readonly int[] FGHJKMNQUVXZ = AllYear;
}
}
@@ -0,0 +1,114 @@
/*
* 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.Collections.Generic;
using System.Linq;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Future;
using QuantConnect.Util;
namespace QuantConnect.Securities
{
/// <summary>
/// Represents futures symbols universe used in filtering.
/// </summary>
public class FutureFilterUniverse : ContractSecurityFilterUniverse<FutureFilterUniverse, FutureUniverse>
{
/// <summary>
/// Constructs FutureFilterUniverse
/// </summary>
public FutureFilterUniverse(IReadOnlyList<FutureUniverse> allData, DateTime localTime)
: base(allData, localTime)
{
}
/// <summary>
/// Determine if the given Future contract symbol is standard
/// </summary>
/// <returns>True if contract is standard</returns>
protected override bool IsStandard(Symbol symbol)
{
return FutureSymbol.IsStandard(symbol);
}
/// <summary>
/// Creates a new instance of the data type for the given symbol
/// </summary>
/// <returns>A data instance for the given symbol, which is just the symbol itself</returns>
protected override FutureUniverse CreateDataInstance(Symbol symbol)
{
return new FutureUniverse()
{
Symbol = symbol,
Time = LocalTime
};
}
/// <summary>
/// Applies filter selecting futures contracts based on expiration cycles. See <see cref="FutureExpirationCycles"/> for details
/// </summary>
/// <param name="months">Months to select contracts from</param>
/// <returns>Universe with filter applied</returns>
public FutureFilterUniverse ExpirationCycle(int[] months)
{
var monthHashSet = months.ToHashSet();
return this.Where(x => monthHashSet.Contains(x.ID.Date.Month));
}
}
/// <summary>
/// Extensions for Linq support
/// </summary>
public static class FutureFilterUniverseEx
{
/// <summary>
/// Filters universe
/// </summary>
/// <param name="universe">Universe to apply the filter too</param>
/// <param name="predicate">Bool function to determine which Symbol are filtered</param>
/// <returns><see cref="FutureFilterUniverse"/> with filter applied</returns>
public static FutureFilterUniverse Where(this FutureFilterUniverse universe, Func<FutureUniverse, bool> predicate)
{
universe.Data = universe.Data.Where(predicate).ToList();
return universe;
}
/// <summary>
/// Maps universe
/// </summary>
/// <param name="universe">Universe to apply the filter too</param>
/// <param name="mapFunc">Symbol function to determine which Symbols are filtered</param>
/// <returns><see cref="FutureFilterUniverse"/> with filter applied</returns>
public static FutureFilterUniverse Select(this FutureFilterUniverse universe, Func<FutureUniverse, Symbol> mapFunc)
{
universe.AllSymbols = universe.Data.Select(mapFunc).ToList();
return universe;
}
/// <summary>
/// Binds universe
/// </summary>
/// <param name="universe">Universe to apply the filter too</param>
/// <param name="mapFunc">Symbols function to determine which Symbols are filtered</param>
/// <returns><see cref="FutureFilterUniverse"/> with filter applied</returns>
public static FutureFilterUniverse SelectMany(this FutureFilterUniverse universe, Func<FutureUniverse, IEnumerable<Symbol>> mapFunc)
{
universe.AllSymbols = universe.Data.SelectMany(mapFunc).ToList();
return universe;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*/
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Future holdings implementation of the base securities class
/// </summary>
/// <seealso cref="SecurityHolding"/>
public class FutureHolding : SecurityHolding
{
/// <summary>
/// The cash settled profit for the current open position
/// </summary>
public virtual decimal SettledProfit { get; set; }
/// <summary>
/// Unsettled profit for the current open position <see cref="SettledProfit"/>
/// </summary>
public virtual decimal UnsettledProfit
{
get
{
return TotalCloseProfit() - SettledProfit;
}
}
/// <summary>
/// Future Holding Class constructor
/// </summary>
/// <param name="security">The future security being held</param>
/// <param name="currencyConverter">A currency converter instance</param>
public FutureHolding(Security security, ICurrencyConverter currencyConverter)
: base(security, currencyConverter)
{
}
}
}
@@ -0,0 +1,305 @@
/*
* 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.IO;
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Logging;
using System.Threading.Tasks;
using QuantConnect.Interfaces;
using QuantConnect.Orders.Fees;
using QuantConnect.Configuration;
using System.Collections.Generic;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Represents a simple margin model for margin futures. Margin file contains Initial and Maintenance margins
/// </summary>
public class FutureMarginModel : SecurityMarginModel
{
private static IDataProvider _dataProvider;
private static readonly object _locker = new();
private static Dictionary<string, MarginRequirementsEntry[]> _marginRequirementsCache = new();
// historical database of margin requirements
private int _marginCurrentIndex;
private readonly Security _security;
/// <summary>
/// True will enable usage of intraday margins.
/// </summary>
/// <remarks>Disabled by default. Note that intraday margins are less than overnight margins
/// and could lead to margin calls</remarks>
public bool EnableIntradayMargins { get; set; }
/// <summary>
/// Initial Overnight margin requirement for the contract effective from the date of change
/// </summary>
public virtual decimal InitialOvernightMarginRequirement => GetCurrentMarginRequirements(_security)?.InitialOvernight ?? 0m;
/// <summary>
/// Maintenance Overnight margin requirement for the contract effective from the date of change
/// </summary>
public virtual decimal MaintenanceOvernightMarginRequirement => GetCurrentMarginRequirements(_security)?.MaintenanceOvernight ?? 0m;
/// <summary>
/// Initial Intraday margin for the contract effective from the date of change
/// </summary>
public virtual decimal InitialIntradayMarginRequirement => GetCurrentMarginRequirements(_security)?.InitialIntraday ?? 0m;
/// <summary>
/// Maintenance Intraday margin requirement for the contract effective from the date of change
/// </summary>
public virtual decimal MaintenanceIntradayMarginRequirement => GetCurrentMarginRequirements(_security)?.MaintenanceIntraday ?? 0m;
/// <summary>
/// Initializes a new instance of the <see cref="FutureMarginModel"/>
/// </summary>
/// <param name="requiredFreeBuyingPowerPercent">The percentage used to determine the required unused buying power for the account.</param>
/// <param name="security">The security that this model belongs to</param>
public FutureMarginModel(decimal requiredFreeBuyingPowerPercent = 0, Security security = null)
{
RequiredFreeBuyingPowerPercent = requiredFreeBuyingPowerPercent;
_security = security;
}
/// <summary>
/// Gets the current leverage of the security
/// </summary>
/// <param name="security">The security to get leverage for</param>
/// <returns>The current leverage in the security</returns>
public override decimal GetLeverage(Security security)
{
return 1;
}
/// <summary>
/// Sets the leverage for the applicable securities, i.e, futures
/// </summary>
/// <remarks>
/// This is added to maintain backwards compatibility with the old margin/leverage system
/// </remarks>
/// <param name="security"></param>
/// <param name="leverage">The new leverage</param>
public override void SetLeverage(Security security, decimal leverage)
{
// Futures are leveraged products and different leverage cannot be set by user.
throw new InvalidOperationException("Futures are leveraged products and different leverage cannot be set by user");
}
/// <summary>
/// Get the maximum market order quantity to obtain a position with a given buying power percentage.
/// Will not take into account free buying power.
/// </summary>
/// <param name="parameters">An object containing the portfolio, the security and the target signed buying power percentage</param>
/// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
{
if (Math.Abs(parameters.TargetBuyingPower) > 1)
{
throw new InvalidOperationException(
"Futures do not allow specifying a leveraged target, since they are traded using margin which already is leveraged. " +
$"Possible target buying power goes from -1 to 1, target provided is: {parameters.TargetBuyingPower}");
}
return base.GetMaximumOrderQuantityForTargetBuyingPower(parameters);
}
/// <summary>
/// Gets the total margin required to execute the specified order in units of the account currency including fees
/// </summary>
/// <param name="parameters">An object containing the portfolio, the security and the order</param>
/// <returns>The total margin in terms of the currency quoted in the order</returns>
public override InitialMargin GetInitialMarginRequiredForOrder(
InitialMarginRequiredForOrderParameters parameters
)
{
//Get the order value from the non-abstract order classes (MarketOrder, LimitOrder, StopMarketOrder)
//Market order is approximated from the current security price and set in the MarketOrder Method in QCAlgorithm.
var fees = parameters.Security.FeeModel.GetOrderFee(
new OrderFeeParameters(parameters.Security,
parameters.Order)).Value;
var feesInAccountCurrency = parameters.CurrencyConverter.
ConvertToAccountCurrency(fees).Amount;
var orderMargin = this.GetInitialMarginRequirement(parameters.Security, parameters.Order.Quantity);
return new InitialMargin(orderMargin + Math.Sign(orderMargin) * feesInAccountCurrency);
}
/// <summary>
/// Gets the margin currently allotted to the specified holding
/// </summary>
/// <param name="parameters">An object containing the security</param>
/// <returns>The maintenance margin required for the </returns>
public override MaintenanceMargin GetMaintenanceMargin(MaintenanceMarginParameters parameters)
{
if (parameters.Quantity == 0m)
{
return 0m;
}
var security = parameters.Security;
var marginReq = GetCurrentMarginRequirements(security);
if (marginReq == null)
{
return 0m;
}
if (EnableIntradayMargins
&& security.Exchange.ExchangeOpen
&& !security.Exchange.ClosingSoon)
{
return marginReq.MaintenanceIntraday * parameters.AbsoluteQuantity * security.QuoteCurrency.ConversionRate;
}
// margin is per contract
return marginReq.MaintenanceOvernight * parameters.AbsoluteQuantity * security.QuoteCurrency.ConversionRate;
}
/// <summary>
/// The margin that must be held in order to increase the position by the provided quantity
/// </summary>
public override InitialMargin GetInitialMarginRequirement(InitialMarginParameters parameters)
{
var security = parameters.Security;
var quantity = parameters.Quantity;
if (quantity == 0m)
{
return InitialMargin.Zero;
}
var marginReq = GetCurrentMarginRequirements(security);
if (marginReq == null)
{
return InitialMargin.Zero;
}
if (EnableIntradayMargins
&& security.Exchange.ExchangeOpen
&& !security.Exchange.ClosingSoon)
{
return new InitialMargin(marginReq.InitialIntraday * quantity * security.QuoteCurrency.ConversionRate);
}
// margin is per contract
return new InitialMargin(marginReq.InitialOvernight * quantity * security.QuoteCurrency.ConversionRate);
}
private MarginRequirementsEntry GetCurrentMarginRequirements(Security security)
{
var lastData = security?.GetLastData();
if (lastData == null)
{
return null;
}
var marginRequirementsHistory = LoadMarginRequirementsHistory(security.Symbol);
var date = lastData.Time.Date;
while (_marginCurrentIndex + 1 < marginRequirementsHistory.Length &&
marginRequirementsHistory[_marginCurrentIndex + 1].Date <= date)
{
_marginCurrentIndex++;
}
return marginRequirementsHistory[_marginCurrentIndex];
}
/// <summary>
/// Gets the sorted list of historical margin changes produced by reading in the margin requirements
/// data found in /Data/symbol-margin/
/// </summary>
/// <returns>Sorted list of historical margin changes</returns>
private static MarginRequirementsEntry[] LoadMarginRequirementsHistory(Symbol symbol)
{
if (!_marginRequirementsCache.TryGetValue(symbol.ID.Symbol, out var marginRequirementsEntries))
{
lock (_locker)
{
if (!_marginRequirementsCache.TryGetValue(symbol.ID.Symbol, out marginRequirementsEntries))
{
Dictionary<string, MarginRequirementsEntry[]> marginRequirementsCache = new(_marginRequirementsCache)
{
[symbol.ID.Symbol] = marginRequirementsEntries = FromCsvFile(symbol)
};
// we change the reference so we can read without a lock
_marginRequirementsCache = marginRequirementsCache;
}
}
}
return marginRequirementsEntries;
}
/// <summary>
/// Reads margin requirements file and returns a sorted list of historical margin changes
/// </summary>
/// <param name="symbol">The symbol to fetch margin requirements for</param>
/// <returns>Sorted list of historical margin changes</returns>
private static MarginRequirementsEntry[] FromCsvFile(Symbol symbol)
{
var file = Path.Combine(Globals.DataFolder,
symbol.SecurityType.ToLower(),
symbol.ID.Market.ToLowerInvariant(),
"margins", symbol.ID.Symbol + ".csv");
if(_dataProvider == null)
{
ClearMarginCache();
_dataProvider = Composer.Instance.GetPart<IDataProvider>();
}
// skip the first header line, also skip #'s as these are comment lines
var marginRequirementsEntries = _dataProvider.ReadLines(file)
.Where(x => !x.StartsWith("#") && !string.IsNullOrWhiteSpace(x))
.Skip(1)
.Select(MarginRequirementsEntry.Create)
.OrderBy(x => x.Date)
.ToArray();
if (marginRequirementsEntries.Length == 0)
{
Log.Error($"FutureMarginModel.FromCsvFile(): Unable to locate future margin requirements file. Defaulting to zero margin for this symbol. File: {file}");
marginRequirementsEntries = new[] {
new MarginRequirementsEntry
{
Date = DateTime.MinValue
}
};
}
return marginRequirementsEntries;
}
/// <summary>
/// For live deployments we don't want to have stale margin requirements to we refresh them every day
/// </summary>
private static void ClearMarginCache()
{
Task.Delay(Time.OneDay).ContinueWith((_) =>
{
lock (_locker)
{
_marginRequirementsCache = new();
}
ClearMarginCache();
});
}
}
}
@@ -0,0 +1,103 @@
/*
* 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.Logging;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Settlement model which can handle daily profit and loss settlement
/// </summary>
public class FutureSettlementModel : ImmediateSettlementModel
{
private DateTime _lastSettlementDate;
private decimal _settledFutureQuantity;
private decimal _settlementPrice;
/// <summary>
/// Applies unsettledContractsTodaysProfit settlement rules
/// </summary>
/// <param name="applyFundsParameters">The funds application parameters</param>
public override void ApplyFunds(ApplyFundsSettlementModelParameters applyFundsParameters)
{
if(_settledFutureQuantity != 0)
{
var fill = applyFundsParameters.Fill;
var security = applyFundsParameters.Security;
var futureHolding = (FutureHolding)security.Holdings;
var absoluteQuantityClosed = Math.Min(fill.AbsoluteFillQuantity, security.Holdings.AbsoluteQuantity);
var absoluteQuantityClosedSettled = Math.Min(absoluteQuantityClosed, Math.Abs(_settledFutureQuantity));
var quantityClosedSettled = Math.Sign(-fill.FillQuantity) * absoluteQuantityClosedSettled;
// reduce our settled future quantity proportionally too
var factor = quantityClosedSettled / _settledFutureQuantity;
_settledFutureQuantity -= quantityClosedSettled;
// the passed in cash amount will hold the complete profit/loss of the trade, so we need to substract the settled profit we were given or taken from
var removedSettledProfit = factor * futureHolding.SettledProfit;
futureHolding.SettledProfit -= removedSettledProfit;
applyFundsParameters.CashAmount = new CashAmount(applyFundsParameters.CashAmount.Amount - removedSettledProfit, applyFundsParameters.CashAmount.Currency);
}
base.ApplyFunds(applyFundsParameters);
}
/// <summary>
/// Scan for pending settlements
/// </summary>
/// <param name="settlementParameters">The settlement parameters</param>
public override void Scan(ScanSettlementModelParameters settlementParameters)
{
var security = settlementParameters.Security;
// In the futures markets, losers pay winners every day. So once a day after the settlement time has passed we will update the cash book to reflect this
if (_lastSettlementDate.Date < security.LocalTime.Date)
{
if ((_lastSettlementDate != default) && security.Invested)
{
var futureHolding = (FutureHolding)security.Holdings;
var futureCache = (FutureCache)security.Cache;
_settlementPrice = futureCache.SettlementPrice;
_settledFutureQuantity = security.Holdings.Quantity;
// We settled the daily P&L, losers pay winners
var dailyProfitLoss = futureHolding.TotalCloseProfit(includeFees: false, exitPrice: _settlementPrice) - futureHolding.SettledProfit;
if (dailyProfitLoss != 0)
{
futureHolding.SettledProfit += dailyProfitLoss;
settlementParameters.Portfolio.CashBook[security.QuoteCurrency.Symbol].AddAmount(dailyProfitLoss);
Log.Trace($"FutureSettlementModel.Scan({security.Symbol}): {security.LocalTime} Daily P&L: {dailyProfitLoss} " +
$"Quantity: {_settledFutureQuantity} Settlement: {_settlementPrice} UnrealizedProfit: {futureHolding.UnrealizedProfit}");
}
}
_lastSettlementDate = security.LocalTime.Date;
}
}
/// <summary>
/// Set the current datetime in terms of the exchange's local time zone
/// </summary>
/// <param name="newLocalTime">Current local time</param>
public void SetLocalDateTimeFrontier(DateTime newLocalTime)
{
_lastSettlementDate = newLocalTime.Date;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* 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.Logging;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Static class contains common utility methods specific to symbols representing the future contracts
/// </summary>
public static class FutureSymbol
{
/// <summary>
/// Determine if a given Futures contract is a standard contract.
/// </summary>
/// <param name="symbol">Future symbol</param>
/// <returns>True if symbol expiration matches standard expiration</returns>
public static bool IsStandard(Symbol symbol)
{
var contractExpirationDate = symbol.ID.Date.Date;
try
{
// Use our FutureExpiryFunctions to determine standard contracts dates.
var expiryFunction = FuturesExpiryFunctions.FuturesExpiryFunction(symbol);
var contractMonth = FuturesExpiryUtilityFunctions.GetFutureContractMonth(symbol);
var standardExpirationDate = expiryFunction(contractMonth);
// Return true if the dates match
return contractExpirationDate == standardExpirationDate.Date;
}
catch
{
Log.Error($"FutureSymbol.IsStandard(): Could not find standard date for {symbol}, will be classified as standard");
return true;
}
}
/// <summary>
/// Returns true if the future contract is a weekly contract
/// </summary>
/// <param name="symbol">Future symbol</param>
/// <returns>True if symbol is non-standard contract</returns>
public static bool IsWeekly(Symbol symbol)
{
return !IsStandard(symbol);
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,422 @@
/*
* 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.Collections.Generic;
using System.Linq;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Class to implement common functions used in FuturesExpiryFunctions
/// </summary>
public static class FuturesExpiryUtilityFunctions
{
private static readonly MarketHoursDatabase MarketHoursDatabase = MarketHoursDatabase.FromDataFolder();
/// <summary>
/// Get holiday list from the MHDB given the market and the symbol of the security
/// </summary>
/// <param name="market">The market the exchange resides in, i.e, 'usa', 'fxcm', ect...</param>
/// <param name="symbol">The particular symbol being traded</param>s
internal static HashSet<DateTime> GetExpirationHolidays(string market, string symbol)
{
var exchangeHours = MarketHoursDatabase.FromDataFolder()
.GetEntry(market, symbol, SecurityType.Future)
.ExchangeHours;
return exchangeHours.Holidays.Concat(exchangeHours.BankHolidays).ToHashSet();
}
/// <summary>
/// Method to retrieve n^th succeeding/preceding business day for a given day
/// </summary>
/// <param name="time">The current Time</param>
/// <param name="n">Number of business days succeeding current time. Use negative value for preceding business days</param>
/// <param name="holidays">Set of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
/// <returns>The date-time after adding n business days</returns>
public static DateTime AddBusinessDays(DateTime time, int n, HashSet<DateTime> holidays)
{
if (n < 0)
{
var businessDays = -n;
var totalDays = 1;
do
{
var previousDay = time.AddDays(-totalDays);
if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
{
businessDays--;
}
if (businessDays > 0) totalDays++;
} while (businessDays > 0);
return time.AddDays(-totalDays);
}
else
{
var businessDays = n;
var totalDays = 1;
do
{
var previousDay = time.AddDays(totalDays);
if (!holidays.Contains(previousDay.Date) && previousDay.IsCommonBusinessDay())
{
businessDays--;
}
if (businessDays > 0) totalDays++;
} while (businessDays > 0);
return time.AddDays(totalDays);
}
}
/// <summary>
/// Method to retrieve n^th succeeding/preceding business day for a given day if there was a holiday on that day
/// </summary>
/// <param name="time">The current Time</param>
/// <param name="n">Number of business days succeeding current time. Use negative value for preceding business days</param>
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
/// <returns>The date-time after adding n business days</returns>
public static DateTime AddBusinessDaysIfHoliday(DateTime time, int n, HashSet<DateTime> holidayList)
{
if (holidayList.Contains(time))
{
return AddBusinessDays(time, n, holidayList);
}
else
{
return time;
}
}
/// <summary>
/// Method to retrieve the n^th last business day of the delivery month.
/// </summary>
/// <param name="time">DateTime for delivery month</param>
/// <param name="n">Number of days</param>
/// <param name="holidayList">Holidays to use while calculating n^th business day. Useful for MHDB entries</param>
/// <returns>Nth Last Business day of the month</returns>
public static DateTime NthLastBusinessDay(DateTime time, int n, IEnumerable<DateTime> holidayList)
{
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
var lastDayOfMonth = new DateTime(time.Year, time.Month, daysInMonth);
var holidays = holidayList.Select(x => x.Date);
if (n > daysInMonth)
{
throw new ArgumentOutOfRangeException(nameof(n), Invariant(
$"Number of days ({n}) is larger than the size of month({daysInMonth})"
));
}
// Count the number of days in the month after the third to last business day
var businessDays = n;
var totalDays = 0;
do
{
var previousDay = lastDayOfMonth.AddDays(-totalDays);
if (NotHoliday(previousDay, holidays) && !holidays.Contains(previousDay))
{
businessDays--;
}
if (businessDays > 0) totalDays++;
} while (businessDays > 0);
return lastDayOfMonth.AddDays(-totalDays);
}
/// <summary>
/// Calculates the n^th business day of the month (includes checking for holidays)
/// </summary>
/// <param name="time">Month to calculate business day for</param>
/// <param name="nthBusinessDay">n^th business day to get</param>
/// <param name="holidayList"> Holidays to not count as business days</param>
/// <returns>Nth business day of the month</returns>
public static DateTime NthBusinessDay(DateTime time, int nthBusinessDay, IEnumerable<DateTime> holidayList)
{
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
var holidays = holidayList.Select(x => x.Date);
if (nthBusinessDay > daysInMonth)
{
throw new ArgumentOutOfRangeException(Invariant(
$"Argument nthBusinessDay (${nthBusinessDay}) is larger than the amount of days in the current month (${daysInMonth})"
));
}
if (nthBusinessDay < 1)
{
throw new ArgumentOutOfRangeException(Invariant(
$"Argument nthBusinessDay (${nthBusinessDay}) is less than one. Provide a number greater than one and less than the days in month"
));
}
var calculatedTime = new DateTime(time.Year, time.Month, 1);
var daysCounted = calculatedTime.IsCommonBusinessDay() ? 1 : 0;
var i = 0;
// Check for holiday up here in case we want the first business day and it is a holiday so that we don't skip over it.
// We also want to make sure that we don't stop on a weekend.
while (daysCounted < nthBusinessDay || holidays.Contains(calculatedTime) || !calculatedTime.IsCommonBusinessDay())
{
// The asset continues trading on days contained within `USHoliday.Dates`, but
// the last trade date is affected by those holidays. We check for
// both MHDB entries and holidays to get accurate business days
if (holidays.Contains(calculatedTime))
{
// Catches edge case where first day is on a friday
if (i == 0 && calculatedTime.DayOfWeek == DayOfWeek.Friday)
{
daysCounted = 0;
}
calculatedTime = calculatedTime.AddDays(1);
if (i != 0 && calculatedTime.IsCommonBusinessDay())
{
daysCounted++;
}
i++;
continue;
}
calculatedTime = calculatedTime.AddDays(1);
if (!holidays.Contains(calculatedTime) && NotHoliday(calculatedTime, holidays))
{
daysCounted++;
}
i++;
}
return calculatedTime;
}
/// <summary>
/// Method to retrieve the 2nd Friday of the given month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>2nd Friday of given month</returns>
public static DateTime SecondFriday(DateTime time) => NthFriday(time, 2);
/// <summary>
/// Method to retrieve the 3rd Friday of the given month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>3rd Friday of given month</returns>
public static DateTime ThirdFriday(DateTime time) => NthFriday(time, 3);
/// <summary>
/// Method to retrieve the Nth Friday of the given month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <param name="n">The order of the Friday in the period</param>
/// <returns>Nth Friday of given month</returns>
public static DateTime NthFriday(DateTime time, int n) => NthWeekday(time, n, DayOfWeek.Friday);
/// <summary>
/// Method to retrieve third Wednesday of the given month (usually Monday).
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>Third Wednesday of the given month</returns>
public static DateTime ThirdWednesday(DateTime time) => NthWeekday(time, 3, DayOfWeek.Wednesday);
/// <summary>
/// Method to retrieve the Nth Weekday of the given month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <param name="n">The order of the Weekday in the period</param>
/// <param name="dayOfWeek">The day of the week</param>
/// <returns>Nth Weekday of given month</returns>
public static DateTime NthWeekday(DateTime time, int n, DayOfWeek dayOfWeek)
{
if (n < 1 || n > 5)
{
throw new ArgumentOutOfRangeException(nameof(n), "'n' lower than 1 or greater than 5");
}
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
return (from day in Enumerable.Range(1, daysInMonth)
where new DateTime(time.Year, time.Month, day).DayOfWeek == dayOfWeek
select new DateTime(time.Year, time.Month, day)).ElementAt(n - 1);
}
/// <summary>
/// Method to retrieve the last weekday of any month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <param name="dayOfWeek">the last weekday to be found</param>
/// <returns>Last day of the we</returns>
public static DateTime LastWeekday(DateTime time, DayOfWeek dayOfWeek)
{
var daysInMonth = DateTime.DaysInMonth(time.Year, time.Month);
return (from day in Enumerable.Range(1, daysInMonth).Reverse()
where new DateTime(time.Year, time.Month, day).DayOfWeek == dayOfWeek
select new DateTime(time.Year, time.Month, day)).First();
}
/// <summary>
/// Method to retrieve the last Thursday of any month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>Last Thursday of the given month</returns>
public static DateTime LastThursday(DateTime time) => LastWeekday(time, DayOfWeek.Thursday);
/// <summary>
/// Method to retrieve the last Friday of any month
/// </summary>
/// <param name="time">Date from the given month</param>
/// <returns>Last Friday of the given month</returns>
public static DateTime LastFriday(DateTime time) => LastWeekday(time, DayOfWeek.Friday);
/// <summary>
/// Method to check whether a given time is holiday or not
/// </summary>
/// <param name="time">The DateTime for consideration</param>
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
/// <returns>True if the time is not a holidays, otherwise returns false</returns>
public static bool NotHoliday(DateTime time, IEnumerable<DateTime> holidayList)
{
return time.IsCommonBusinessDay() && !holidayList.Contains(time.Date);
}
/// <summary>
/// This function takes Thursday as input and returns true if four weekdays preceding it are not Holidays
/// </summary>
/// <param name="thursday">DateTime of a given Thursday</param>
/// <param name="holidayList">Enumerable of holidays to exclude. These should be sourced from the <see cref="MarketHoursDatabase"/></param>
/// <returns>False if DayOfWeek is not Thursday or is not preceded by four weekdays,Otherwise returns True</returns>
public static bool NotPrecededByHoliday(DateTime thursday, IEnumerable<DateTime> holidayList)
{
if (thursday.DayOfWeek != DayOfWeek.Thursday)
{
throw new ArgumentException("Input to NotPrecededByHolidays must be a Thursday");
}
var result = true;
// for Monday, Tuesday and Wednesday
for (var i = 1; i <= 3; i++)
{
if (!NotHoliday(thursday.AddDays(-i), holidayList))
{
result = false;
}
}
// for Friday
if (!NotHoliday(thursday.AddDays(-6), holidayList))
{
result = false;
}
return result;
}
/// <summary>
/// Gets the number of months between the contract month and the expiry date.
/// </summary>
/// <param name="underlying">The future symbol ticker</param>
/// <param name="futureExpiryDate">Expiry date to use to look up contract month delta. Only used for dairy, since we need to lookup its contract month in a pre-defined table.</param>
/// <returns>The number of months between the contract month and the contract expiry</returns>
public static int GetDeltaBetweenContractMonthAndContractExpiry(string underlying, DateTime? futureExpiryDate = null)
{
return ExpiriesPriorMonth.TryGetValue(underlying, out int value) ? value : 0;
}
/// <summary>
/// Helper method to retrieve the futures contract month
/// </summary>
public static DateTime GetFutureContractMonth(Symbol symbol)
{
if (symbol.SecurityType == SecurityType.FutureOption)
{
symbol = symbol.Underlying;
}
var contractExpirationDate = symbol.ID.Date.Date;
var monthsToAdd = GetDeltaBetweenContractMonthAndContractExpiry(symbol.ID.Symbol, contractExpirationDate);
var contractMonth = contractExpirationDate.AddDays(-(contractExpirationDate.Day - 1))
.AddMonths(monthsToAdd);
return contractMonth;
}
/// <summary>
/// Helper method to resolve a future expiration from it's contract month
/// </summary>
public static DateTime GetFutureExpirationFromContractMonth(string symbol, string market, DateTime contractMonth)
{
return GetFutureExpirationFromContractMonth(Symbol.CreateFuture(symbol, market, SecurityIdentifier.DefaultDate), contractMonth);
}
/// <summary>
/// Helper method to resolve a future expiration from it's contract month
/// </summary>
public static DateTime GetFutureExpirationFromContractMonth(Symbol future, DateTime contractMonth)
{
var futureExpiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(future.Canonical);
var futureExpiry = futureExpiryFunc(contractMonth);
return futureExpiry;
}
/// <summary>
/// This function returns the third Friday of the month, adjusted for holidays and weekends.
/// </summary>
public static DateTime ThirdFriday(DateTime time, Symbol contract)
{
if (contract.ID.SecurityType.IsOption())
{
return ThirdFriday(time, contract.Underlying);
}
var thirdFriday = ThirdFriday(time);
var holidays = GetExpirationHolidays(contract.ID.Market, contract.ID.Symbol);
return AddBusinessDaysIfHoliday(thirdFriday, -1, holidays);
}
/// <summary>
/// Checks if the future contract is expired.
/// </summary>
public static bool IsFutureContractExpired(Symbol symbol, DateTime currentUtcTime, MarketHoursDatabase marketHoursDatabase = null)
{
var exchangeHours = (marketHoursDatabase ?? MarketHoursDatabase).GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
var currentTimeInExchangeTz = currentUtcTime.ConvertFromUtc(exchangeHours.TimeZone);
if (currentTimeInExchangeTz >= symbol.ID.Date)
{
return true;
}
return false;
}
private static readonly Dictionary<string, int> ExpiriesPriorMonth = new Dictionary<string, int>
{
{ Futures.Energy.ArgusLLSvsWTIArgusTradeMonth, 1 },
{ Futures.Energy.ArgusPropaneSaudiAramco, 1 },
{ Futures.Energy.BrentCrude, 2 },
{ Futures.Energy.BrentLastDayFinancial, 2 },
{ Futures.Energy.CrudeOilWTI, 1 },
{ Futures.Energy.MicroCrudeOilWTI, 1 },
{ Futures.Energy.Gasoline, 1 },
{ Futures.Energy.HeatingOil, 1 },
{ Futures.Energy.MarsArgusVsWTITradeMonth, 1 },
{ Futures.Energy.NaturalGas, 1 },
{ Futures.Energy.NaturalGasHenryHubLastDayFinancial, 1 },
{ Futures.Energy.NaturalGasHenryHubPenultimateFinancial, 1 },
{ Futures.Energy.WTIHoustonArgusVsWTITradeMonth, 1 },
{ Futures.Energy.WTIHoustonCrudeOil, 1 },
{ Futures.Softs.Sugar11, 1 },
{ Futures.Softs.Sugar11CME, 1 }
};
}
}
+257
View File
@@ -0,0 +1,257 @@
/*
* 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.Collections.Generic;
using System.Linq;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// Helpers for getting the futures contracts that are trading on a given date.
/// This is a substitute for the BacktestingFutureChainProvider, but
/// does not outright replace it because of missing entries. This will resolve
/// the listed contracts without having any data in place. We follow the listing rules
/// set forth by the exchange to get the <see cref="Symbol"/>s that are listed at a given date.
/// </summary>
public static class FuturesListings
{
private static readonly Symbol _zb = Symbol.Create("ZB", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zc = Symbol.Create("ZC", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zs = Symbol.Create("ZS", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zm = Symbol.Create("ZM", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zt = Symbol.Create("ZT", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zl = Symbol.Create("ZL", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zw = Symbol.Create("ZW", SecurityType.Future, Market.CBOT);
private static readonly Symbol _tn = Symbol.Create("TN", SecurityType.Future, Market.CBOT);
private static readonly Symbol _aud = Symbol.Create("6A", SecurityType.Future, Market.CME);
private static readonly Symbol _gbp = Symbol.Create("6B", SecurityType.Future, Market.CME);
private static readonly Symbol _mxn = Symbol.Create("6M", SecurityType.Future, Market.CME);
private static readonly Symbol _jpy = Symbol.Create("6J", SecurityType.Future, Market.CME);
private static readonly Symbol _eur = Symbol.Create("6E", SecurityType.Future, Market.CME);
private static readonly Symbol _cad = Symbol.Create("6C", SecurityType.Future, Market.CME);
private static Dictionary<string, Func<DateTime, List<Symbol>>> _futuresListingRules = new Dictionary<string, Func<DateTime, List<Symbol>>>
{
{ "ZB", t => QuarterlyContracts(_zb, t, 3) },
{ "ZC", t => MonthlyContractListings(
_zc,
t,
12,
new FuturesListingCycles(new[] { 3, 5, 9 }, 9),
new FuturesListingCycles(new[] { 7, 12 }, 8)) },
{ "ZN", t => QuarterlyContracts(_zt, t, 3) },
{ "TN", t => QuarterlyContracts(_tn, t, 3) },
{ "ZS", t => MonthlyContractListings(
_zs,
t,
11,
new FuturesListingCycles(new[] { 1, 3, 5, 8, 9 }, 15),
new FuturesListingCycles(new[] { 7, 11 }, 8)) },
{ "ZM", t => MonthlyContractListings(
_zm,
t,
12,
new FuturesListingCycles(new[] { 1, 3, 5, 8, 9 }, 15),
new FuturesListingCycles(new[] { 7, 10, 12 }, 12)) },
{ "ZL", t => MonthlyContractListings(
_zl,
t,
12,
new FuturesListingCycles(new[] { 1, 3, 5, 8, 9 }, 15),
new FuturesListingCycles(new[] { 7, 10, 12 }, 12)) },
{ "ZT", t => QuarterlyContracts(_zt, t, 3) },
{ "ZW", t => MonthlyContractListings(
_zw,
t,
7,
new FuturesListingCycles(new[] { 3, 5, 7, 9, 12 }, 15)) },
{ "6A", t => QuarterlyContracts(_aud, t, 8) },
{ "6B", t => QuarterlyContracts(_gbp, t, 8) },
{ "6M", t => QuarterlyContracts(_mxn, t, 8) },
{ "6J", t => QuarterlyContracts(_jpy, t, 8) },
{ "6E", t => QuarterlyContracts(_eur, t, 8) },
{ "6C", t => QuarterlyContracts(_cad, t, 8) },
};
/// <summary>
/// Gets the listed futures contracts on a given date
/// </summary>
/// <param name="futureTicker">Ticker of the future contract</param>
/// <param name="time">Contracts to look up that are listed at that time</param>
/// <returns>The currently trading contracts on the exchange</returns>
public static List<Symbol> ListedContracts(string futureTicker, DateTime time)
{
if (!_futuresListingRules.ContainsKey(futureTicker))
{
// No entries found. This differs from entries being returned as an empty array, where
// that would mean that no listings were found.
return null;
}
return _futuresListingRules[futureTicker](time);
}
/// <summary>
/// Gets contracts following a quarterly listing procedure, with a limit of
/// how many contracts are listed at once.
/// </summary>
/// <param name="canonicalFuture">Canonical Futures Symbol</param>
/// <param name="time">Contracts to look up that are listed at that time</param>
/// <param name="limit">Number of Symbols we get back/are listed at a given time</param>
/// <returns>Symbols that are listed at the given time</returns>
private static List<Symbol> QuarterlyContracts(Symbol canonicalFuture, DateTime time, int limit)
{
var contractMonth = new DateTime(time.Year, time.Month, 1);
var futureExpiry = DateTime.MinValue;
var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture);
// Skip any contracts that have already expired.
while (futureExpiry < time)
{
futureExpiry = expiryFunc(contractMonth);
contractMonth = contractMonth.AddMonths(1);
}
// Negate the last incrementation from the while loop to get the actual contract month of the future.
var firstFutureContractMonth = contractMonth.AddMonths(-1);
var quarterlyContracts = new List<Symbol>();
// Gets the next closest month from the current month in multiples of 3
var quarterlyContractMonth = (int)Math.Ceiling((double)firstFutureContractMonth.Month / 3) * 3;
for (var i = 0; i < limit; i++)
{
// We're past the expiration frontier due to the while loop above, which means
// that any contracts from here on out will be greater than the current time.
var currentContractMonth = firstFutureContractMonth.AddMonths(-firstFutureContractMonth.Month + quarterlyContractMonth);
var currentFutureExpiry = expiryFunc(currentContractMonth);
quarterlyContracts.Add(Symbol.CreateFuture(canonicalFuture.ID.Symbol, canonicalFuture.ID.Market, currentFutureExpiry));
quarterlyContractMonth += 3;
}
return quarterlyContracts;
}
/// <summary>
/// Gets Futures contracts that follow a limited cyclical pattern
/// </summary>
/// <param name="canonicalFuture">Canonical Futures Symbol</param>
/// <param name="time">Contracts to look up that are listed at that time</param>
/// <param name="contractMonthForNewListings">Contract month that results in new listings after this contract's expiry</param>
/// <param name="futureListingCycles">
/// Cycles that define the number of contracts and the months the contracts are listed on, including
/// the limit of how many contracts will be listed.
/// </param>
/// <returns>Symbols that are listed at the given time</returns>
private static List<Symbol> MonthlyContractListings(
Symbol canonicalFuture,
DateTime time,
int contractMonthForNewListings,
params FuturesListingCycles[] futureListingCycles)
{
var listings = new List<Symbol>();
var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture);
var yearDelta = 0;
var contractMonthForNewListingCycle = new DateTime(time.Year, contractMonthForNewListings, 1);
var contractMonthForNewListingCycleExpiry = expiryFunc(contractMonthForNewListingCycle);
if (time <= contractMonthForNewListingCycleExpiry)
{
// Go back a year if we haven't yet crossed this year's contract renewal expiration date.
contractMonthForNewListingCycleExpiry = expiryFunc(contractMonthForNewListingCycle.AddYears(-1));
yearDelta = -1;
}
foreach (var listingCycle in futureListingCycles)
{
var year = yearDelta;
var count = 0;
var initialListings = true;
while (count != listingCycle.Limit)
{
var monthStartIndex = 0;
if (initialListings)
{
// For the initial listing, we want to start counting at some month that might not be the first
// index of the collection. The index is discovered here and used as the starting point for listed contracts.
monthStartIndex = listingCycle.Cycle.Length - listingCycle.Cycle.Count(c => c > contractMonthForNewListingCycleExpiry.Month);
initialListings = false;
}
for (var m = monthStartIndex; m < listingCycle.Cycle.Length; m++)
{
// Add the future's expiration to the listings
var currentContractMonth = new DateTime(time.Year + year, listingCycle.Cycle[m], 1);
var currentFutureExpiry = expiryFunc(currentContractMonth);
if (currentFutureExpiry >= time)
{
listings.Add(Symbol.CreateFuture(canonicalFuture.ID.Symbol, canonicalFuture.ID.Market, currentFutureExpiry));
}
if (++count == listingCycle.Limit)
{
break;
}
}
year++;
}
}
return listings;
}
/// <summary>
/// Listing Cycles, i.e. the months and number of contracts that are renewed whenever
/// the specified renewal expiration contract expires.
/// </summary>
/// <remarks>
/// Example:
///
/// (from: https://www.cmegroup.com/trading/agricultural/grain-and-oilseed/wheat_contract_specifications.html)
/// "15 monthly contracts of Mar, May, Jul, Sep, Dec listed annually following the termination of trading in the July contract of the current year."
///
/// This would equate to a cycle of [3, 5, 7, 9, 12], a limit of 15, and the contract month == 7.
/// </remarks>
private class FuturesListingCycles
{
/// <summary>
/// Monthly cycles that the futures listings rule follows
/// </summary>
public int[] Cycle { get; }
/// <summary>
/// Max number of contracts returned by this rule
/// </summary>
public int Limit { get; }
/// <summary>
/// Creates a listing cycle rule
/// </summary>
/// <param name="cycle">New contract listing cycles</param>
/// <param name="limit">Max number of contracts to return in this rule</param>
public FuturesListingCycles(int[] cycle, int limit)
{
Cycle = cycle;
Limit = limit;
}
}
}
}
@@ -0,0 +1,105 @@
/*
* 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.Globalization;
using QuantConnect.Logging;
namespace QuantConnect.Securities.Future
{
/// <summary>
/// POCO class for modeling margin requirements at given date
/// </summary>
public class MarginRequirementsEntry
{
/// <summary>
/// Date of margin requirements change
/// </summary>
public DateTime Date { get; init; }
/// <summary>
/// Initial overnight margin for the contract effective from the date of change
/// </summary>
public decimal InitialOvernight { get; init; }
/// <summary>
/// Maintenance overnight margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceOvernight { get; init; }
/// <summary>
/// Initial intraday margin for the contract effective from the date of change
/// </summary>
public decimal InitialIntraday { get; init; }
/// <summary>
/// Maintenance intraday margin for the contract effective from the date of change
/// </summary>
public decimal MaintenanceIntraday { get; init; }
/// <summary>
/// Creates a new instance of <see cref="MarginRequirementsEntry"/> from the specified csv line
/// </summary>
/// <param name="csvLine">The csv line to be parsed</param>
/// <returns>A new <see cref="MarginRequirementsEntry"/> for the specified csv line</returns>
public static MarginRequirementsEntry Create(string csvLine)
{
var line = csvLine.Split(',');
DateTime date;
if (!DateTime.TryParseExact(line[0], DateFormat.EightCharacter, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
Log.Trace($"Couldn't parse date/time while reading future margin requirement file. Line: {csvLine}");
}
decimal initialOvernight;
if (!decimal.TryParse(line[1], out initialOvernight))
{
Log.Trace($"Couldn't parse Initial Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
decimal maintenanceOvernight;
if (!decimal.TryParse(line[2], out maintenanceOvernight))
{
Log.Trace($"Couldn't parse Maintenance Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal initialIntraday = initialOvernight * 0.4m;
if (line.Length >= 4
&& !decimal.TryParse(line[3], out initialIntraday))
{
Log.Trace($"Couldn't parse Initial Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
// default value, if present in file we try to parse
decimal maintenanceIntraday = maintenanceOvernight * 0.4m;
if (line.Length >= 5
&& !decimal.TryParse(line[4], out maintenanceIntraday))
{
Log.Trace($"Couldn't parse Maintenance Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
}
return new MarginRequirementsEntry
{
Date = date,
InitialOvernight = initialOvernight,
MaintenanceOvernight = maintenanceOvernight,
InitialIntraday = initialIntraday,
MaintenanceIntraday = maintenanceIntraday
};
}
}
}