Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6351773a01 | ||
|
|
bf4b08e202 | ||
|
|
fa9ff399cc | ||
|
|
16c4259342 | ||
|
|
724d0b06a5 | ||
|
|
ba7fe05574 | ||
|
|
50437946e2 | ||
|
|
418970bb48 | ||
|
|
bef045a360 | ||
|
|
49bf436aa2 | ||
|
|
e29bb2c5e0 | ||
|
|
8fcc9f7d4e | ||
|
|
5209332074 | ||
|
|
adfad475cc | ||
|
|
21fcadf0f8 | ||
|
|
e893e67e9b | ||
|
|
3b588d04fb | ||
|
|
e81bcbb987 | ||
|
|
96e91b446d | ||
|
|
403f0348bd | ||
|
|
eeeb310438 | ||
|
|
271f0bb08e |
@@ -40,8 +40,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
|
||||
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
_contract = OptionChain(aapl)
|
||||
.OrderBy(x => x.ID.Symbol)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(_contract);
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var aapl = AddEquity("AAPL").Symbol;
|
||||
|
||||
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
_contract = OptionChain(aapl)
|
||||
.OrderBy(x => x.ID.Symbol)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
foreach (var contract in futuresContracts)
|
||||
{
|
||||
var option_contract_symbols = OptionChainProvider.GetOptionContractList(contract.Symbol, Time).ToList();
|
||||
var option_contract_symbols = OptionChain(contract.Symbol).ToList();
|
||||
if(option_contract_symbols.Count == 0)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -46,8 +46,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
if (_option == null)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(_twx, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
var option = OptionChain(_twx)
|
||||
.OrderBy(x => x.ID.Symbol)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -110,14 +110,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
foreach (var addedSecurity in changes.AddedSecurities)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
var option = OptionChain(addedSecurity.Symbol)
|
||||
.OrderBy(contractData => contractData.ID.Symbol)
|
||||
.First(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(option);
|
||||
|
||||
foreach (var symbol in new[] { option, option.Underlying })
|
||||
foreach (var symbol in new[] { option.Symbol, option.Underlying.Symbol })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
|
||||
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
|
||||
.OrderBy(symbol => symbol.ID.StrikePrice)
|
||||
_contract = OptionChain(aapl)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(_contract);
|
||||
|
||||
@@ -41,8 +41,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(aapl, Time)
|
||||
.OrderBy(symbol => symbol.ID.StrikePrice)
|
||||
var contracts = OptionChain(aapl)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Where(optionContract => optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American)
|
||||
.Take(2)
|
||||
|
||||
239
Algorithm.CSharp/BasicTemplateEurexFuturesAlgorithm.cs
Normal file
239
Algorithm.CSharp/BasicTemplateEurexFuturesAlgorithm.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This algorithm tests and demonstrates EUREX futures subscription and trading:
|
||||
/// - It tests contracts rollover by adding a continuous future and asserting that mapping happens at some point.
|
||||
/// - It tests basic trading by buying a contract and holding it until expiration.
|
||||
/// - It tests delisting and asserts the holdings are liquidated after that.
|
||||
/// </summary>
|
||||
public class BasicTemplateEurexFuturesAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Future _continuousContract;
|
||||
private Symbol _mappedSymbol;
|
||||
private Symbol _contractToTrade;
|
||||
private int _mappingsCount;
|
||||
private decimal _boughtQuantity;
|
||||
private decimal _liquidatedQuantity;
|
||||
private bool _delisted;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2024, 5, 30);
|
||||
SetEndDate(2024, 6, 23);
|
||||
|
||||
SetAccountCurrency(Currencies.EUR);
|
||||
SetCash(1000000);
|
||||
|
||||
_continuousContract = AddFuture(Futures.Indices.EuroStoxx50, Resolution.Minute,
|
||||
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
|
||||
dataMappingMode: DataMappingMode.FirstDayMonth,
|
||||
contractDepthOffset: 0);
|
||||
_continuousContract.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(180));
|
||||
_mappedSymbol = _continuousContract.Mapped;
|
||||
|
||||
var benchmark = AddIndex("SX5E", market: Market.EUREX);
|
||||
SetBenchmark(benchmark.Symbol);
|
||||
|
||||
var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
|
||||
SetSecurityInitializer(security => seeder.SeedSecurity(security));
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
foreach (var changedEvent in slice.SymbolChangedEvents.Values)
|
||||
{
|
||||
if (++_mappingsCount > 1)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected number of symbol changed events (mappings): {_mappingsCount}. " +
|
||||
$"Expected only 1.");
|
||||
}
|
||||
|
||||
Debug($"{Time} - SymbolChanged event: {changedEvent}");
|
||||
|
||||
if (changedEvent.OldSymbol != _mappedSymbol.ID.ToString())
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected symbol changed event old symbol: {changedEvent}");
|
||||
}
|
||||
|
||||
if (changedEvent.NewSymbol != _continuousContract.Mapped.ID.ToString())
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected symbol changed event new symbol: {changedEvent}");
|
||||
}
|
||||
|
||||
// Let's trade the previous mapped contract, so we can hold it until expiration for testing
|
||||
// (will be sooner than the new mapped contract)
|
||||
_contractToTrade = _mappedSymbol;
|
||||
_mappedSymbol = _continuousContract.Mapped;
|
||||
}
|
||||
|
||||
// Let's trade after the mapping is done
|
||||
if (_contractToTrade != null && _boughtQuantity == 0 && Securities[_contractToTrade].Exchange.ExchangeOpen)
|
||||
{
|
||||
Buy(_contractToTrade, 1);
|
||||
}
|
||||
|
||||
if (_contractToTrade != null && slice.Delistings.TryGetValue(_contractToTrade, out var delisting))
|
||||
{
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
_delisted = true;
|
||||
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Portfolio should not be invested after the traded contract is delisted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Symbol != _contractToTrade)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected order event symbol: {orderEvent.Symbol}. Expected {_contractToTrade}");
|
||||
}
|
||||
|
||||
if (orderEvent.Direction == OrderDirection.Buy)
|
||||
{
|
||||
if (orderEvent.Status == OrderStatus.Filled)
|
||||
{
|
||||
if (_boughtQuantity != 0 && _liquidatedQuantity != 0)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected buy order event status: {orderEvent.Status}");
|
||||
}
|
||||
_boughtQuantity = orderEvent.Quantity;
|
||||
}
|
||||
}
|
||||
else if (orderEvent.Direction == OrderDirection.Sell)
|
||||
{
|
||||
if (orderEvent.Status == OrderStatus.Filled)
|
||||
{
|
||||
if (_boughtQuantity <= 0 && _liquidatedQuantity != 0)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected sell order event status: {orderEvent.Status}");
|
||||
}
|
||||
_liquidatedQuantity = orderEvent.Quantity;
|
||||
|
||||
if (_liquidatedQuantity != -_boughtQuantity)
|
||||
{
|
||||
throw new RegressionTestException($"{Time} - Unexpected liquidated quantity: {_liquidatedQuantity}. Expected: {-_boughtQuantity}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
foreach (var addedSecurity in changes.AddedSecurities)
|
||||
{
|
||||
if (addedSecurity.Symbol.SecurityType == SecurityType.Future && addedSecurity.Symbol.IsCanonical())
|
||||
{
|
||||
_mappedSymbol = _continuousContract.Mapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_mappingsCount == 0)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected number of symbol changed events (mappings): {_mappingsCount}. Expected 1.");
|
||||
}
|
||||
|
||||
if (!_delisted)
|
||||
{
|
||||
throw new RegressionTestException("Contract was not delisted");
|
||||
}
|
||||
|
||||
// Make sure we traded and that the position was liquidated on delisting
|
||||
if (_boughtQuantity <= 0 || _liquidatedQuantity >= 0)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected sold quantity: {_boughtQuantity} and liquidated quantity: {_liquidatedQuantity}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 133945;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 26;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.11%"},
|
||||
{"Compounding Annual Return", "-1.667%"},
|
||||
{"Drawdown", "0.100%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Start Equity", "1000000"},
|
||||
{"End Equity", "998849.48"},
|
||||
{"Net Profit", "-0.115%"},
|
||||
{"Sharpe Ratio", "-34.455"},
|
||||
{"Sortino Ratio", "-57.336"},
|
||||
{"Probabilistic Sharpe Ratio", "0.002%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0.002"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-6.176"},
|
||||
{"Tracking Error", "0.002"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "€1.02"},
|
||||
{"Estimated Strategy Capacity", "€2300000000.00"},
|
||||
{"Lowest Capacity Asset", "FESX YJHOAMPYKRS5"},
|
||||
{"Portfolio Turnover", "0.40%"},
|
||||
{"OrderListHash", "54040d29a467becaedcf59d79323321b"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -43,14 +43,12 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
SetEndDate(2021, 1, 10);
|
||||
SetCash(1000000);
|
||||
|
||||
var spx = AddIndex("SPX").Symbol;
|
||||
|
||||
// regular option SPX contracts
|
||||
var spxOptions = AddIndexOption(spx);
|
||||
var spxOptions = AddIndexOption("SPX");
|
||||
spxOptions.SetFilter(u => u.Strikes(0, 1).Expiration(0, 30));
|
||||
|
||||
// weekly option SPX contracts
|
||||
var spxw = AddIndexOption(spx, "SPXW");
|
||||
var spxw = AddIndexOption("SPX", "SPXW");
|
||||
spxw.SetFilter(u => u.Strikes(0, 1)
|
||||
// single week ahead since there are many SPXW contracts and we want to preserve performance
|
||||
.Expiration(0, 7)
|
||||
|
||||
@@ -37,9 +37,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var equity = AddEquity("GOOG");
|
||||
|
||||
_optionSymbol = OptionChainProvider.GetOptionContractList(equity.Symbol, Time)
|
||||
.OrderBy(symbol => symbol.ID.StrikePrice)
|
||||
.ThenByDescending(symbol => symbol.ID.Date)
|
||||
_optionSymbol = OptionChain(equity.Symbol)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.ThenByDescending(x => x.ID.Date)
|
||||
.First(optionContract => optionContract.ID.OptionRight == OptionRight.Call);
|
||||
var option = AddOptionContract(_optionSymbol);
|
||||
|
||||
|
||||
@@ -51,10 +51,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
if (_addOption)
|
||||
{
|
||||
var contracts = OptionChainProvider.GetOptionContractList(_spx, Time);
|
||||
contracts = contracts.Where(x =>
|
||||
x.ID.OptionRight == OptionRight.Put &&
|
||||
x.ID.Date.Date == new DateTime(2021, 1, 15));
|
||||
var contracts = OptionChain(_spx).Where(x => x.ID.OptionRight == OptionRight.Put && x.ID.Date.Date == new DateTime(2021, 1, 15));
|
||||
|
||||
var option = AddIndexOptionContract(contracts.First(), Resolution.Minute);
|
||||
_optionExpiry = option.Expiry;
|
||||
|
||||
@@ -58,10 +58,10 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
var esOptions = OptionChainProvider.GetOptionContractList(es20m20, Time)
|
||||
.Concat(OptionChainProvider.GetOptionContractList(es20h20, Time))
|
||||
.Where(x => x.ID.StrikePrice == 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.Select(x => AddFutureOptionContract(x, Resolution.Minute).Symbol)
|
||||
var esOptions = OptionChain(es20m20)
|
||||
.Concat(OptionChain(es20h20))
|
||||
.Where(contractData => contractData.ID.StrikePrice == 3200m && contractData.ID.OptionRight == OptionRight.Call)
|
||||
.Select(contractData => AddFutureOptionContract(contractData, Resolution.Minute).Symbol)
|
||||
.ToList();
|
||||
|
||||
var expectedContracts = new[]
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
TimeSpan.FromMinutes(1));
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20.Symbol, new DateTime(2020, 1, 5))
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20.Symbol)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -59,9 +59,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option call expiring OTM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(contractData => contractData.ID.StrikePrice >= 3300m && contractData.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(contractData => contractData.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution).Symbol;
|
||||
|
||||
// Attempt to fetch a specific future option contract
|
||||
DcOption = OptionChainProvider.GetOptionContractList(dc, Time)
|
||||
DcOption = OptionChain(dc)
|
||||
.Where(x => x.ID.StrikePrice == 17m && x.ID.OptionRight == OptionRight.Call)
|
||||
.Select(x => AddFutureOptionContract(x, Resolution).Symbol)
|
||||
.FirstOrDefault();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
var underlying = AddFutureContract(QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
var option = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(underlying, Time)
|
||||
var option = AddFutureOptionContract(OptionChain(underlying)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice <= 3150m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice <= 3100m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice >= 3400m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice <= 3400m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice <= 3000m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
var spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
var spxOptions = OptionChainProvider.GetOptionContractList(spx, Time)
|
||||
var spxOptions = OptionChain(spx)
|
||||
.Where(x => (x.ID.StrikePrice == 3700m || x.ID.StrikePrice == 3800m) && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.Select(x => AddIndexOptionContract(x, Resolution.Minute).Symbol)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution).Symbol;
|
||||
|
||||
// Select an index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = spx.Symbol;
|
||||
|
||||
// Select an index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution).Symbol;
|
||||
|
||||
// Select a index option call expiring OTM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice >= 4250m && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice >= 4200m && x.ID.OptionRight == OptionRight.Put && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Put && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -60,9 +60,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
_esOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(contractData => contractData.ID.StrikePrice <= 3200m && contractData.ID.OptionRight == OptionRight.Call && contractData.ID.Date.Year == 2021 && contractData.ID.Date.Month == 1)
|
||||
.OrderByDescending(contractData => contractData.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice >= 4250m && x.ID.OptionRight == OptionRight.Call && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -59,9 +59,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 4200m && x.ID.OptionRight == OptionRight.Put && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(contractData => contractData.ID.StrikePrice <= 4200m && contractData.ID.OptionRight == OptionRight.Put && contractData.ID.Date.Year == 2021 && contractData.ID.Date.Month == 1)
|
||||
.OrderByDescending(contractData => contractData.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_spx = AddIndex("SPX", Resolution.Minute).Symbol;
|
||||
|
||||
// Select a index option expiring ITM, and adds it to the algorithm.
|
||||
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
|
||||
_spxOption = AddIndexOptionContract(OptionChain(_spx)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Put && x.ID.Date.Year == 2021 && x.ID.Date.Month == 1)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
_stock = AddEquity("GOOG").Symbol;
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(_stock, UtcTime).ToList();
|
||||
var contracts = OptionChain(_stock).ToList();
|
||||
_option = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Put)
|
||||
.OrderBy(c => c.ID.Date)
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 6;
|
||||
public int AlgorithmHistoryDataPoints => 7;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
SetCash(100000);
|
||||
Stock = AddEquity("GOOG", Resolution.Minute);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(Stock.Symbol, UtcTime).ToList();
|
||||
var contracts = OptionChain(Stock.Symbol).ToList();
|
||||
|
||||
PutOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Put)
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
_goog = AddEquity("GOOG", Resolution.Minute);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(_goog.Symbol, UtcTime).ToList();
|
||||
var contracts = OptionChain(_goog.Symbol).ToList();
|
||||
|
||||
_googCall600Symbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Call)
|
||||
|
||||
127
Algorithm.CSharp/OptionChainFullDataRegressionAlgorithm.cs
Normal file
127
Algorithm.CSharp/OptionChainFullDataRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm illustrating the usage of the <see cref="QCAlgorithm.OptionChain(Symbol)"/> method
|
||||
/// to get an option chain, which contains additional data besides the symbols, including prices, implied volatility and greeks.
|
||||
/// It also shows how this data can be used to filter the contracts based on certain criteria.
|
||||
/// </summary>
|
||||
public class OptionChainFullDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _optionContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2015, 12, 24);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(100000);
|
||||
|
||||
var goog = AddEquity("GOOG").Symbol;
|
||||
|
||||
_optionContract = OptionChain(goog)
|
||||
// Get contracts expiring within 10 days, with an implied volatility greater than 0.5 and a delta less than 0.5
|
||||
.Where(contractData => contractData.ID.Date - Time <= TimeSpan.FromDays(10) &&
|
||||
contractData.ImpliedVolatility > 0.5m &&
|
||||
contractData.Greeks.Delta < 0.5m)
|
||||
// Get the contract with the latest expiration date
|
||||
.OrderByDescending(x => x.ID.Date)
|
||||
.First();
|
||||
|
||||
AddOptionContract(_optionContract);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
// Do some trading with the selected contract for sample purposes
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
MarketOrder(_optionContract, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Liquidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public virtual List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1057;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 1;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "210"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "96041"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$209.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "GOOCV W6U7PD1F2WYU|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "85.46%"},
|
||||
{"OrderListHash", "a7ab1a9e64fe9ba76ea33a40a78a4e3b"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 19700;
|
||||
public long DataPoints => 19701;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option call expiring OTM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
_esOption = AddFutureOptionContract(OptionChain(_es19m20)
|
||||
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
|
||||
137
Algorithm.CSharp/OptionResolutionRegressionAlgorithm.cs
Normal file
137
Algorithm.CSharp/OptionResolutionRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Data.Market;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting the resolution being used for options universe and it's data respecting universe settings
|
||||
/// </summary>
|
||||
public class OptionResolutionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _optionSymbol;
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2015, 12, 24);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(100000);
|
||||
|
||||
UniverseSettings.Resolution = Resolution.Daily;
|
||||
|
||||
var option = AddOption("GOOG");
|
||||
option.SetFilter(u => u.Strikes(-2, +2).Expiration(0, 180));
|
||||
_optionSymbol = option.Symbol;
|
||||
|
||||
if (UniverseManager.TryGetValue(option.Symbol, out var universe)
|
||||
&& (universe.Configuration.Resolution != Resolution.Daily || universe.UniverseSettings.Resolution != Resolution.Daily))
|
||||
{
|
||||
throw new RegressionTestException("Unexpected universe resolution configuration!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event - v3.0 DATA EVENT HANDLER: (Pattern) Basic template for user to override for receiving all subscription data in a single event
|
||||
/// </summary>
|
||||
/// <param name="slice">The current slice of data keyed by symbol string</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
OptionChain chain;
|
||||
if (slice.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
// we find at the money (ATM) put contract with farthest expiration
|
||||
var atmContract = chain
|
||||
.OrderByDescending(x => x.Expiry)
|
||||
.ThenBy(x => Math.Abs(chain.Underlying.Price - x.Strike))
|
||||
.ThenByDescending(x => x.Right)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (atmContract != null)
|
||||
{
|
||||
// if found, trade it
|
||||
MarketOrder(atmContract.Symbol, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 4274;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "ce4cdd4d05199b633559cd14bc6db237"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
SetEndDate(2014, 06, 09);
|
||||
|
||||
var equitySymbol = AddEquity("TWX").Symbol;
|
||||
var contracts = OptionChainProvider.GetOptionContractList(equitySymbol, UtcTime).ToList();
|
||||
var contracts = OptionChain(equitySymbol).ToList();
|
||||
|
||||
var callOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Call)
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_lastSliceTime = Time;
|
||||
|
||||
var underlyingPrice = Securities[_symbol].Price;
|
||||
var contractSymbol = OptionChainProvider.GetOptionContractList(_symbol, Time)
|
||||
var contractSymbol = OptionChain(_symbol)
|
||||
.Where(x => x.ID.StrikePrice - underlyingPrice > 0)
|
||||
.OrderBy(x => x.ID.Date)
|
||||
.FirstOrDefault();
|
||||
@@ -91,7 +91,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 3;
|
||||
public int AlgorithmHistoryDataPoints => 787;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
|
||||
@@ -71,9 +71,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
var option = AddOption("AAPL");
|
||||
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(option.Symbol)
|
||||
.Any(config => config.Resolution != Resolution.Minute))
|
||||
.Any(config => config.Resolution != Resolution.Daily))
|
||||
{
|
||||
throw new RegressionTestException("Was expecting resolution to be set to Minute");
|
||||
throw new RegressionTestException("Was expecting resolution to be set to Daily");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
@@ -43,7 +43,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
_equitySymbol = AddEquity("TWX", Resolution.Minute).Symbol;
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(_equitySymbol, UtcTime).ToList();
|
||||
var contracts = OptionChain(_equitySymbol).ToList();
|
||||
|
||||
var callOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Call)
|
||||
|
||||
147
Algorithm.CSharp/ZeroDTEOptionsRegressionAlgorithm.cs
Normal file
147
Algorithm.CSharp/ZeroDTEOptionsRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Algorithm asserting that options are selected every day and that selection for 0DTE contracts works as expected,
|
||||
/// always including the contracts that expire the same date the option chain belongs to.
|
||||
/// </summary>
|
||||
public class ZeroDTEOptionsRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private List<DateTime> _selectionDays;
|
||||
private int _currentSelectionDayIndex;
|
||||
|
||||
private int _previouslyAddedContracts;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2024, 01, 01);
|
||||
SetEndDate(2024, 01, 10);
|
||||
SetCash(100000);
|
||||
|
||||
var equity = AddEquity("SPY");
|
||||
var option = AddOption(equity.Symbol);
|
||||
|
||||
option.SetFilter(u => u.IncludeWeeklys().Expiration(0, 0));
|
||||
|
||||
// use the underlying equity as the benchmark
|
||||
SetBenchmark(equity.Symbol);
|
||||
|
||||
_selectionDays = new List<DateTime>()
|
||||
{
|
||||
new DateTime(2024, 01, 01), // Sunday midnight, already Monday 1st, it's a holiday. Selection happens for Tuesday here
|
||||
new DateTime(2024, 01, 03), // Wednesday, midnight
|
||||
new DateTime(2024, 01, 04),
|
||||
new DateTime(2024, 01, 05),
|
||||
new DateTime(2024, 01, 06), // Friday midnight, selection happens for Monday here
|
||||
new DateTime(2024, 01, 09), // Monday midnight, already Tuesday, selection happens for Tuesday here
|
||||
new DateTime(2024, 01, 10),
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
// We expect selection every trading day
|
||||
if (Time.Date != _selectionDays[_currentSelectionDayIndex++])
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected date. Expected {_selectionDays[_currentSelectionDayIndex]} but was {Time.Date}");
|
||||
}
|
||||
|
||||
var addedOptions = changes.AddedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
if (addedOptions.Count == 0)
|
||||
{
|
||||
throw new RegressionTestException("No options were added");
|
||||
}
|
||||
|
||||
var removedOptions = changes.RemovedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
// Since we are selecting only 0DTE contracts, they must be deselected that same day
|
||||
if (removedOptions.Count != _previouslyAddedContracts)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected number of removed contracts. Expected {_previouslyAddedContracts} but was {removedOptions.Count}");
|
||||
}
|
||||
_previouslyAddedContracts = addedOptions.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 227;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class AddOptionContractExpiresRegressionAlgorithm(QCAlgorithm):
|
||||
data: Slice object keyed by symbol containing the stock data
|
||||
'''
|
||||
if self._option == None:
|
||||
options = self.option_chain_provider.get_option_contract_list(self._twx, self.time)
|
||||
options = self.option_chain(self._twx)
|
||||
options = sorted(options, key=lambda x: x.id.symbol)
|
||||
|
||||
option = next((option
|
||||
|
||||
@@ -67,7 +67,7 @@ class AddOptionContractFromUniverseRegressionAlgorithm(QCAlgorithm):
|
||||
return
|
||||
|
||||
for addedSecurity in changes.added_securities:
|
||||
options = self.option_chain_provider.get_option_contract_list(addedSecurity.symbol, self.time)
|
||||
options = self.option_chain(addedSecurity.symbol)
|
||||
options = sorted(options, key=lambda x: x.id.symbol)
|
||||
|
||||
option = next((option
|
||||
|
||||
120
Algorithm.Python/BasicTemplateEurexFuturesAlgorithm.py
Normal file
120
Algorithm.Python/BasicTemplateEurexFuturesAlgorithm.py
Normal file
@@ -0,0 +1,120 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
### <summary>
|
||||
### This algorithm tests and demonstrates EUREX futures subscription and trading:
|
||||
### - It tests contracts rollover by adding a continuous future and asserting that mapping happens at some point.
|
||||
### - It tests basic trading by buying a contract and holding it until expiration.
|
||||
### - It tests delisting and asserts the holdings are liquidated after that.
|
||||
### </summary>
|
||||
class BasicTemplateEurexFuturesAlgorithm(QCAlgorithm):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._continuous_contract = None
|
||||
self._mapped_symbol = None
|
||||
self._contract_to_trade = None
|
||||
self._mappings_count = 0
|
||||
self._bought_quantity = 0
|
||||
self._liquidated_quantity = 0
|
||||
self._delisted = False
|
||||
|
||||
def initialize(self):
|
||||
self.set_start_date(2024, 5, 30)
|
||||
self.set_end_date(2024, 6, 23)
|
||||
|
||||
self.set_account_currency(Currencies.EUR);
|
||||
self.set_cash(1000000)
|
||||
|
||||
self._continuous_contract = self.add_future(
|
||||
Futures.Indices.EURO_STOXX_50,
|
||||
Resolution.MINUTE,
|
||||
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
|
||||
data_mapping_mode=DataMappingMode.FIRST_DAY_MONTH,
|
||||
contract_depth_offset=0,
|
||||
)
|
||||
self._continuous_contract.set_filter(timedelta(days=0), timedelta(days=180))
|
||||
self._mapped_symbol = self._continuous_contract.mapped
|
||||
|
||||
benchmark = self.add_index("SX5E", market=Market.EUREX)
|
||||
self.set_benchmark(benchmark.symbol)
|
||||
|
||||
func_seeder = FuncSecuritySeeder(self.get_last_known_prices)
|
||||
self.set_security_initializer(lambda security: func_seeder.seed_security(security))
|
||||
|
||||
def on_data(self, slice):
|
||||
for changed_event in slice.symbol_changed_events.values():
|
||||
self._mappings_count += 1
|
||||
if self._mappings_count > 1:
|
||||
raise Exception(f"{self.time} - Unexpected number of symbol changed events (mappings): {self._mappings_count}. Expected only 1.")
|
||||
|
||||
self.debug(f"{self.time} - SymbolChanged event: {changed_event}")
|
||||
|
||||
if changed_event.old_symbol != str(self._mapped_symbol.id):
|
||||
raise Exception(f"{self.time} - Unexpected symbol changed event old symbol: {changed_event}")
|
||||
|
||||
if changed_event.new_symbol != str(self._continuous_contract.mapped.id):
|
||||
raise Exception(f"{self.time} - Unexpected symbol changed event new symbol: {changed_event}")
|
||||
|
||||
# Let's trade the previous mapped contract, so we can hold it until expiration for testing
|
||||
# (will be sooner than the new mapped contract)
|
||||
self._contract_to_trade = self._mapped_symbol
|
||||
self._mapped_symbol = self._continuous_contract.mapped
|
||||
|
||||
# Let's trade after the mapping is done
|
||||
if self._contract_to_trade is not None and self._bought_quantity == 0 and self.securities[self._contract_to_trade].exchange.exchange_open:
|
||||
self.buy(self._contract_to_trade, 1)
|
||||
|
||||
if self._contract_to_trade is not None and slice.delistings.contains_key(self._contract_to_trade):
|
||||
delisting = slice.delistings[self._contract_to_trade]
|
||||
if delisting.type == DelistingType.DELISTED:
|
||||
self._delisted = True
|
||||
|
||||
if self.portfolio.invested:
|
||||
raise Exception(f"{self.time} - Portfolio should not be invested after the traded contract is delisted.")
|
||||
|
||||
def on_order_event(self, order_event):
|
||||
if order_event.symbol != self._contract_to_trade:
|
||||
raise Exception(f"{self.time} - Unexpected order event symbol: {order_event.symbol}. Expected {self._contract_to_trade}")
|
||||
|
||||
if order_event.direction == OrderDirection.BUY:
|
||||
if order_event.status == OrderStatus.FILLED:
|
||||
if self._bought_quantity != 0 and self._liquidated_quantity != 0:
|
||||
raise Exception(f"{self.time} - Unexpected buy order event status: {order_event.status}")
|
||||
|
||||
self._bought_quantity = order_event.quantity
|
||||
elif order_event.direction == OrderDirection.SELL:
|
||||
if order_event.status == OrderStatus.FILLED:
|
||||
if self._bought_quantity <= 0 and self._liquidated_quantity != 0:
|
||||
raise Exception(f"{self.time} - Unexpected sell order event status: {order_event.status}")
|
||||
|
||||
self._liquidated_quantity = order_event.quantity
|
||||
if self._liquidated_quantity != -self._bought_quantity:
|
||||
raise Exception(f"{self.time} - Unexpected liquidated quantity: {self._liquidated_quantity}. Expected: {-self._bought_quantity}")
|
||||
|
||||
def on_securities_changed(self, changes):
|
||||
for added_security in changes.added_securities:
|
||||
if added_security.symbol.security_type == SecurityType.FUTURE and added_security.symbol.is_canonical():
|
||||
self._mapped_symbol = self._continuous_contract.mapped
|
||||
|
||||
def on_end_of_algorithm(self):
|
||||
if self._mappings_count == 0:
|
||||
raise Exception(f"Unexpected number of symbol changed events (mappings): {self._mappings_count}. Expected 1.")
|
||||
|
||||
if not self._delisted:
|
||||
raise Exception("Contract was not delisted")
|
||||
|
||||
# Make sure we traded and that the position was liquidated on delisting
|
||||
if self._bought_quantity <= 0 or self._liquidated_quantity >= 0:
|
||||
raise Exception(f"Unexpected sold quantity: {self._bought_quantity} and liquidated quantity: {self._liquidated_quantity}")
|
||||
@@ -25,14 +25,12 @@ class BasicTemplateSPXWeeklyIndexOptionsAlgorithm(QCAlgorithm):
|
||||
self.set_end_date(2021, 1, 10)
|
||||
self.set_cash(1000000)
|
||||
|
||||
self.spx = self.add_index("SPX").symbol
|
||||
|
||||
# regular option SPX contracts
|
||||
self.spx_options = self.add_index_option(self.spx)
|
||||
self.spx_options = self.add_index_option("SPX")
|
||||
self.spx_options.set_filter(lambda u: (u.strikes(0, 1).expiration(0, 30)))
|
||||
|
||||
# weekly option SPX contracts
|
||||
spxw = self.add_index_option(self.spx, "SPXW")
|
||||
spxw = self.add_index_option("SPX", "SPXW")
|
||||
# set our strike/expiry filter for this option chain
|
||||
spxw.set_filter(lambda u: (u.strikes(0, 1)
|
||||
# single week ahead since there are many SPXW contracts and we want to preserve performance
|
||||
|
||||
@@ -52,8 +52,7 @@ class FutureOptionBuySellCallIntradayRegressionAlgorithm(QCAlgorithm):
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.es_options = [
|
||||
self.add_future_option_contract(i, Resolution.MINUTE).symbol
|
||||
for i in (self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) +
|
||||
self.option_chain_provider.get_option_contract_list(self.es20h20, self.time))
|
||||
for i in (list(self.option_chain(self.es19m20)) + list(self.option_chain(self.es20h20)))
|
||||
if i.id.strike_price == 3200.0 and i.id.option_right == OptionRight.CALL
|
||||
]
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ class FutureOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted([x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price <= 3200.0 and x.id.option_right == OptionRight.CALL], key=lambda x: x.id.strike_price, reverse=True)
|
||||
sorted([x for x in self.option_chain(self.es19m20) if x.id.strike_price <= 3200.0 and x.id.option_right == OptionRight.CALL],
|
||||
key=lambda x: x.id.strike_price, reverse=True)
|
||||
)[0], Resolution.MINUTE).symbol
|
||||
|
||||
self.expected_contract = Symbol.create_option(self.es19m20, Market.CME, OptionStyle.AMERICAN, OptionRight.CALL, 3200.0, datetime(2020, 6, 19))
|
||||
|
||||
@@ -45,7 +45,8 @@ class FutureOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price >= 3300.0 and x.id.option_right == OptionRight.CALL],
|
||||
[x for x in self.option_chain(self.es19m20)
|
||||
if x.id.strike_price >= 3300.0 and x.id.option_right == OptionRight.CALL],
|
||||
key=lambda x: x.id.strike_price
|
||||
)
|
||||
)[0], Resolution.MINUTE).symbol
|
||||
|
||||
@@ -34,7 +34,9 @@ class FutureOptionDailyRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
# Attempt to fetch a specific ITM future option contract
|
||||
dc_options = [
|
||||
self.add_future_option_contract(x, resolution).symbol for x in (self.option_chain_provider.get_option_contract_list(self.dc, self.time)) if x.id.strike_price == 17 and x.id.option_right == OptionRight.CALL
|
||||
self.add_future_option_contract(x, resolution).symbol
|
||||
for x in self.option_chain(self.dc)
|
||||
if x.id.strike_price == 17 and x.id.option_right == OptionRight.CALL
|
||||
]
|
||||
self.dc_option = dc_options[0]
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ class FutureOptionPutITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted([x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price >= 3300.0 and x.id.option_right == OptionRight.PUT], key=lambda x: x.id.strike_price)
|
||||
sorted([x for x in self.option_chain(self.es19m20) if x.id.strike_price >= 3300.0 and x.id.option_right == OptionRight.PUT],
|
||||
key=lambda x: x.id.strike_price)
|
||||
)[0], Resolution.MINUTE).symbol
|
||||
|
||||
self.expected_contract = Symbol.create_option(self.es19m20, Market.CME, OptionStyle.AMERICAN, OptionRight.PUT, 3300.0, datetime(2020, 6, 19))
|
||||
|
||||
@@ -45,7 +45,7 @@ class FutureOptionPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price <= 3150.0 and x.id.option_right == OptionRight.PUT],
|
||||
[x for x in self.option_chain(self.es19m20) if x.id.strike_price <= 3150.0 and x.id.option_right == OptionRight.PUT],
|
||||
key=lambda x: x.id.strike_price,
|
||||
reverse=True
|
||||
)
|
||||
@@ -71,7 +71,7 @@ class FutureOptionPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
if delisting.type == DelistingType.DELISTED:
|
||||
if delisting.time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.time}")
|
||||
|
||||
|
||||
def on_order_event(self, order_event: OrderEvent):
|
||||
if order_event.status != OrderStatus.FILLED:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
|
||||
@@ -41,7 +41,7 @@ class FutureOptionShortCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price <= 3100.0 and x.id.option_right == OptionRight.CALL],
|
||||
[x for x in self.option_chain(self.es19m20) if x.id.strike_price <= 3100.0 and x.id.option_right == OptionRight.CALL],
|
||||
key=lambda x: x.id.strike_price,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ class FutureOptionShortCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price >= 3400.0 and x.id.option_right == OptionRight.CALL],
|
||||
[x for x in self.option_chain(self.es19m20) if x.id.strike_price >= 3400.0 and x.id.option_right == OptionRight.CALL],
|
||||
key=lambda x: x.id.strike_price
|
||||
)
|
||||
)[0], Resolution.MINUTE).symbol
|
||||
|
||||
@@ -41,7 +41,7 @@ class FutureOptionShortPutITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price <= 3400.0 and x.id.option_right == OptionRight.PUT],
|
||||
[x for x in self.option_chain(self.es19m20) if x.id.strike_price <= 3400.0 and x.id.option_right == OptionRight.PUT],
|
||||
key=lambda x: x.id.strike_price,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ class FutureOptionShortPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.es_option = self.add_future_option_contract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.option_chain_provider.get_option_contract_list(self.es19m20, self.time) if x.id.strike_price <= 3000.0 and x.id.option_right == OptionRight.PUT],
|
||||
[x for x in self.option_chain(self.es19m20) if x.id.strike_price <= 3000.0 and x.id.option_right == OptionRight.PUT],
|
||||
key=lambda x: x.id.strike_price,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ class IndexOptionBuySellCallIntradayRegressionAlgorithm(QCAlgorithm):
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
spx_options = list(sorted([
|
||||
self.add_index_option_contract(i, Resolution.MINUTE).symbol \
|
||||
for i in self.option_chain_provider.get_option_contract_list(spx, self.time)\
|
||||
for i in self.option_chain(spx)\
|
||||
if (i.id.strike_price == 3700 or i.id.strike_price == 3800) and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1],
|
||||
key=lambda x: x.id.strike_price
|
||||
))
|
||||
@@ -66,7 +66,7 @@ class IndexOptionBuySellCallIntradayRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
if spx_options[0] != expectedContract3700:
|
||||
raise Exception(f"Contract {expectedContract3700} was not found in the chain, found instead: {spx_options[0]}")
|
||||
|
||||
|
||||
if spx_options[1] != expectedContract3800:
|
||||
raise Exception(f"Contract {expectedContract3800} was not found in the chain, found instead: {spx_options[1]}")
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class IndexOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select an index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 3200 and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
@@ -43,8 +43,8 @@ class IndexOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
raise Exception(f"Contract {self.expected_option_contract} was not found in the chain")
|
||||
|
||||
self.schedule.on(
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
lambda: self.market_order(self.spx_option, 1)
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ class IndexOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
if delisting.type == DelistingType.WARNING:
|
||||
if delisting.time != datetime(2021, 1, 15):
|
||||
raise Exception(f"Delisting warning issued at unexpected date: {delisting.time}")
|
||||
|
||||
|
||||
if delisting.type == DelistingType.DELISTED:
|
||||
if delisting.time != datetime(2021, 1, 16):
|
||||
raise Exception(f"Delisting happened at unexpected date: {delisting.time}")
|
||||
|
||||
@@ -30,7 +30,7 @@ class IndexOptionCallITMGreeksExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = spx.symbol
|
||||
|
||||
# Select a index option call expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 3200 and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE)
|
||||
@@ -81,7 +81,7 @@ class IndexOptionCallITMGreeksExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
if any([i for i in rho if i == 0]):
|
||||
raise Exception("Option contract Rho was equal to zero")
|
||||
|
||||
|
||||
if any([i for i in theta if i == 0]):
|
||||
raise Exception("Option contract Theta was equal to zero")
|
||||
|
||||
|
||||
@@ -38,17 +38,17 @@ class IndexOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option call expiring OTM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price >= 4250 and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
self.expected_contract = Symbol.create_option(
|
||||
self.spx,
|
||||
Market.USA,
|
||||
OptionStyle.EUROPEAN,
|
||||
OptionRight.CALL,
|
||||
4250,
|
||||
self.spx,
|
||||
Market.USA,
|
||||
OptionStyle.EUROPEAN,
|
||||
OptionRight.CALL,
|
||||
4250,
|
||||
datetime(2021, 1, 15)
|
||||
)
|
||||
|
||||
@@ -56,8 +56,8 @@ class IndexOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
raise Exception(f"Contract {self.expected_contract} was not found in the chain")
|
||||
|
||||
self.schedule.on(
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
lambda: self.market_order(self.spx_option, 1)
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class IndexOptionPutITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price >= 4200 and i.id.option_right == OptionRight.PUT and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
@@ -38,17 +38,17 @@ class IndexOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option call expiring OTM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 3200 and i.id.option_right == OptionRight.PUT and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
self.expected_contract = Symbol.create_option(
|
||||
self.spx,
|
||||
Market.USA,
|
||||
OptionStyle.EUROPEAN,
|
||||
OptionRight.PUT,
|
||||
3200,
|
||||
self.spx,
|
||||
Market.USA,
|
||||
OptionStyle.EUROPEAN,
|
||||
OptionRight.PUT,
|
||||
3200,
|
||||
datetime(2021, 1, 15)
|
||||
)
|
||||
|
||||
@@ -56,8 +56,8 @@ class IndexOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
raise Exception(f"Contract {self.expected_contract} was not found in the chain")
|
||||
|
||||
self.schedule.on(
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
self.date_rules.tomorrow,
|
||||
self.time_rules.after_market_open(self.spx, 1),
|
||||
lambda: self.market_order(self.spx_option, 1)
|
||||
)
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class IndexOptionShortCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 3200 and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
@@ -34,7 +34,7 @@ class IndexOptionShortCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price >= 4250 and i.id.option_right == OptionRight.CALL and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
@@ -38,7 +38,7 @@ class IndexOptionShortCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 4200 and i.id.option_right == OptionRight.PUT and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
@@ -34,7 +34,7 @@ class IndexOptionShortCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
self.spx = self.add_index("SPX", Resolution.MINUTE).symbol
|
||||
|
||||
# Select a index option expiring ITM, and adds it to the algorithm.
|
||||
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
|
||||
self.spx_option = list(self.option_chain(self.spx))
|
||||
self.spx_option = [i for i in self.spx_option if i.id.strike_price <= 3200 and i.id.option_right == OptionRight.PUT and i.id.date.year == 2021 and i.id.date.month == 1]
|
||||
self.spx_option = list(sorted(self.spx_option, key=lambda x: x.id.strike_price, reverse=True))[0]
|
||||
self.spx_option = self.add_index_option_contract(self.spx_option, Resolution.MINUTE).symbol
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
class IndicatorExtensionsSMAWithCustomIndicatorsRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def initialize(self):
|
||||
self.set_start_date(2020, 2, 20)
|
||||
self.set_end_date(2020, 4, 20)
|
||||
self.qqq = self.add_equity("QQQ", Resolution.DAILY).symbol
|
||||
self.range_indicator = RangeIndicator("range1")
|
||||
self.range_sma = IndicatorExtensions.sma(self.range_indicator, 5)
|
||||
|
||||
self.range_indicator_2 = RangeIndicator2("range2")
|
||||
self.range_sma_2 = IndicatorExtensions.sma(self.range_indicator_2, 5)
|
||||
|
||||
def on_data(self, data):
|
||||
self.range_indicator.update(data.bars.get(self.qqq))
|
||||
self.range_indicator_2.update(data.bars.get(self.qqq))
|
||||
|
||||
self.debug(f"{self.range_indicator.name} {self.range_indicator.value}")
|
||||
self.debug(f"{self.range_sma.name} {self.range_sma.current.value}")
|
||||
|
||||
self.debug(f"{self.range_indicator_2.name} {self.range_indicator_2.value}")
|
||||
self.debug(f"{self.range_sma_2.name} {self.range_sma_2.current.value}")
|
||||
|
||||
def on_end_of_algorithm(self):
|
||||
if not self.range_sma.is_ready:
|
||||
raise Exception(f"{self.range_sma.name} should have been ready at the end of the algorithm, but it wasn't. The indicator received {self.range_sma.samples} samples.")
|
||||
|
||||
if not self.range_sma_2.is_ready:
|
||||
raise Exception(f"{self.range_sma_2.name} should have been ready at the end of the algorithm, but it wasn't. The indicator received {self.range_sma_2.samples} samples.")
|
||||
|
||||
class RangeIndicator(PythonIndicator):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.time = datetime.min
|
||||
self.value = 0
|
||||
self.is_ready = False;
|
||||
|
||||
@property
|
||||
def is_ready(self):
|
||||
return self._is_ready
|
||||
|
||||
@is_ready.setter
|
||||
def is_ready(self, value):
|
||||
self._is_ready = value
|
||||
|
||||
def update(self, bar: TradeBar):
|
||||
if bar is None:
|
||||
return False
|
||||
|
||||
self.value = bar.high - bar.low
|
||||
self.time = bar.time
|
||||
self.is_ready = True
|
||||
self.on_updated(IndicatorDataPoint(bar.end_time, self.value))
|
||||
return True
|
||||
|
||||
class RangeIndicator2(PythonIndicator):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.time = datetime.min
|
||||
self.value = 0
|
||||
self._is_ready = False;
|
||||
|
||||
@property
|
||||
def is_ready(self):
|
||||
return self._is_ready
|
||||
|
||||
def update(self, bar: TradeBar):
|
||||
if bar is None:
|
||||
return False
|
||||
|
||||
self.value = bar.high - bar.low
|
||||
self.time = bar.time
|
||||
self._is_ready = True
|
||||
self.on_updated(IndicatorDataPoint(bar.end_time, self.value))
|
||||
return True
|
||||
46
Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py
Normal file
46
Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm illustrating the usage of the <see cref="QCAlgorithm.OptionChain(Symbol)"/> method
|
||||
### to get an option chain, which contains additional data besides the symbols, including prices, implied volatility and greeks.
|
||||
### It also shows how this data can be used to filter the contracts based on certain criteria.
|
||||
### </summary>
|
||||
class OptionChainFullDataRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def initialize(self):
|
||||
self.set_start_date(2015, 12, 24)
|
||||
self.set_end_date(2015, 12, 24)
|
||||
self.set_cash(100000)
|
||||
|
||||
goog = self.add_equity("GOOG").symbol
|
||||
|
||||
# Get contracts expiring within 10 days, with an implied volatility greater than 0.5 and a delta less than 0.5
|
||||
contracts = [
|
||||
contract_data
|
||||
for contract_data in self.option_chain(goog)
|
||||
if contract_data.id.date - self.time <= timedelta(days=10) and contract_data.implied_volatility > 0.5 and contract_data.greeks.delta < 0.5
|
||||
]
|
||||
# Get the contract with the latest expiration date
|
||||
self._option_contract = sorted(contracts, key=lambda x: x.id.date, reverse=True)[0]
|
||||
|
||||
self.add_option_contract(self._option_contract)
|
||||
|
||||
def on_data(self, data):
|
||||
# Do some trading with the selected contract for sample purposes
|
||||
if not self.portfolio.invested:
|
||||
self.market_order(self._option_contract, 1)
|
||||
else:
|
||||
self.liquidate()
|
||||
@@ -92,7 +92,15 @@ namespace QuantConnect.Algorithm
|
||||
{
|
||||
Security underlyingSecurity;
|
||||
var underlyingSymbol = security.Symbol.Underlying;
|
||||
|
||||
var resolution = configs.GetHighestResolution();
|
||||
var isFillForward = configs.IsFillForward();
|
||||
if (UniverseManager.TryGetValue(security.Symbol, out var universe))
|
||||
{
|
||||
// as if the universe had selected this asset, the configuration of the canonical can be different
|
||||
resolution = universe.UniverseSettings.Resolution;
|
||||
isFillForward = universe.UniverseSettings.FillForward;
|
||||
}
|
||||
|
||||
// create the underlying security object if it doesn't already exist
|
||||
if (!Securities.TryGetValue(underlyingSymbol, out underlyingSecurity))
|
||||
@@ -101,7 +109,7 @@ namespace QuantConnect.Algorithm
|
||||
underlyingSymbol.Value,
|
||||
resolution,
|
||||
underlyingSymbol.ID.Market,
|
||||
configs.IsFillForward(),
|
||||
isFillForward,
|
||||
Security.NullLeverage,
|
||||
configs.IsExtendedMarketHours(),
|
||||
dataNormalizationMode: DataNormalizationMode.Raw);
|
||||
|
||||
@@ -33,6 +33,7 @@ using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Cfd;
|
||||
using QuantConnect.Securities.Equity;
|
||||
using QuantConnect.Securities.Forex;
|
||||
using QuantConnect.Securities.IndexOption;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Statistics;
|
||||
using QuantConnect.Util;
|
||||
@@ -52,6 +53,7 @@ using Index = QuantConnect.Securities.Index.Index;
|
||||
using QuantConnect.Securities.CryptoFuture;
|
||||
using QuantConnect.Algorithm.Framework.Alphas.Analysis;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Algorithm
|
||||
{
|
||||
@@ -466,6 +468,9 @@ namespace QuantConnect.Algorithm
|
||||
/// Gets the option chain provider, used to get the list of option contracts for an underlying symbol
|
||||
/// </summary>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
[Obsolete("OptionChainProvider property is will soon be deprecated. " +
|
||||
"The new OptionChain() method should be used to fetch equity and index option chains, " +
|
||||
"which will contain additional data per contract, like daily price data, implied volatility and greeks.")]
|
||||
public IOptionChainProvider OptionChainProvider { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -1934,6 +1939,15 @@ namespace QuantConnect.Algorithm
|
||||
return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
|
||||
}
|
||||
|
||||
var securityResolution = resolution;
|
||||
var securityFillForward = fillForward;
|
||||
if (isCanonical && symbol.SecurityType.IsOption() && symbol.SecurityType != SecurityType.FutureOption)
|
||||
{
|
||||
// option is daily only, for now exclude FOPs
|
||||
securityResolution = Resolution.Daily;
|
||||
securityFillForward = false;
|
||||
}
|
||||
|
||||
var isFilteredSubscription = !isCanonical;
|
||||
List<SubscriptionDataConfig> configs;
|
||||
// we pass dataNormalizationMode to SubscriptionManager.SubscriptionDataConfigService.Add conditionally,
|
||||
@@ -1941,8 +1955,8 @@ namespace QuantConnect.Algorithm
|
||||
if (dataNormalizationMode.HasValue)
|
||||
{
|
||||
configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol,
|
||||
resolution,
|
||||
fillForward,
|
||||
securityResolution,
|
||||
securityFillForward,
|
||||
extendedMarketHours,
|
||||
isFilteredSubscription,
|
||||
dataNormalizationMode: dataNormalizationMode.Value,
|
||||
@@ -1951,8 +1965,8 @@ namespace QuantConnect.Algorithm
|
||||
else
|
||||
{
|
||||
configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol,
|
||||
resolution,
|
||||
fillForward,
|
||||
securityResolution,
|
||||
securityFillForward,
|
||||
extendedMarketHours,
|
||||
isFilteredSubscription,
|
||||
contractDepthOffset: (uint)contractDepthOffset);
|
||||
@@ -1970,10 +1984,16 @@ namespace QuantConnect.Algorithm
|
||||
if (!UniverseManager.ContainsKey(symbol))
|
||||
{
|
||||
var canonicalConfig = configs.First();
|
||||
var settings = new UniverseSettings(canonicalConfig.Resolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse)
|
||||
var universeSettingsResolution = canonicalConfig.Resolution;
|
||||
if (symbol.SecurityType.IsOption())
|
||||
{
|
||||
universeSettingsResolution = resolution ?? UniverseSettings.Resolution;
|
||||
}
|
||||
var settings = new UniverseSettings(universeSettingsResolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse)
|
||||
{
|
||||
Asynchronous = UniverseSettings.Asynchronous
|
||||
};
|
||||
|
||||
if (symbol.SecurityType.IsOption())
|
||||
{
|
||||
universe = new OptionChainUniverse((Option)security, settings);
|
||||
@@ -2221,18 +2241,15 @@ namespace QuantConnect.Algorithm
|
||||
/// <summary>
|
||||
/// Creates and adds index options to the algorithm.
|
||||
/// </summary>
|
||||
/// <param name="ticker">The ticker of the Index Option</param>
|
||||
/// <param name="underlying">The underlying ticker of the Index Option</param>
|
||||
/// <param name="resolution">Resolution of the index option contracts, i.e. the granularity of the data</param>
|
||||
/// <param name="market">Market of the index option. If no market is provided, we default to <see cref="Market.USA"/> </param>
|
||||
/// <param name="market">The foreign exchange trading market, <seealso cref="Market"/>. Default value is null and looked up using BrokerageModel.DefaultMarkets in <see cref="AddSecurity{T}"/></param>
|
||||
/// <param name="fillForward">If true, this will fill in missing data points with the previous data point</param>
|
||||
/// <returns>Canonical Option security</returns>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public Option AddIndexOption(string ticker, Resolution? resolution = null, string market = Market.USA, bool fillForward = true)
|
||||
public IndexOption AddIndexOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true)
|
||||
{
|
||||
return AddIndexOption(
|
||||
QuantConnect.Symbol.Create(ticker, SecurityType.Index, market),
|
||||
resolution,
|
||||
fillForward);
|
||||
return AddIndexOption(underlying, null, resolution, market, fillForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2243,7 +2260,7 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="fillForward">If true, this will fill in missing data points with the previous data point</param>
|
||||
/// <returns>Canonical Option security</returns>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public Option AddIndexOption(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
|
||||
public IndexOption AddIndexOption(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
|
||||
{
|
||||
return AddIndexOption(symbol, null, resolution, fillForward);
|
||||
}
|
||||
@@ -2257,14 +2274,36 @@ namespace QuantConnect.Algorithm
|
||||
/// <param name="fillForward">If true, this will fill in missing data points with the previous data point</param>
|
||||
/// <returns>Canonical Option security</returns>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public Option AddIndexOption(Symbol symbol, string targetOption, Resolution? resolution = null, bool fillForward = true)
|
||||
public IndexOption AddIndexOption(Symbol symbol, string targetOption, Resolution? resolution = null, bool fillForward = true)
|
||||
{
|
||||
if (symbol.SecurityType != SecurityType.Index)
|
||||
{
|
||||
throw new ArgumentException("Symbol provided must be of type SecurityType.Index");
|
||||
}
|
||||
|
||||
return AddOption(symbol, targetOption, resolution, symbol.ID.Market, fillForward);
|
||||
return (IndexOption)AddOption(symbol, targetOption, resolution, symbol.ID.Market, fillForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds index options to the algorithm.
|
||||
/// </summary>
|
||||
/// <param name="underlying">The underlying ticker of the Index Option</param>
|
||||
/// <param name="targetOption">The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying</param>
|
||||
/// <param name="resolution">Resolution of the index option contracts, i.e. the granularity of the data</param>
|
||||
/// <param name="market">The foreign exchange trading market, <seealso cref="Market"/>. Default value is null and looked up using BrokerageModel.DefaultMarkets in <see cref="AddSecurity{T}"/></param>
|
||||
/// <param name="fillForward">If true, this will fill in missing data points with the previous data point</param>
|
||||
/// <returns>Canonical Option security</returns>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public IndexOption AddIndexOption(string underlying, string targetOption, Resolution? resolution = null, string market = null, bool fillForward = true)
|
||||
{
|
||||
if (market == null && !BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Index, out market))
|
||||
{
|
||||
throw new KeyNotFoundException($"No default market set for underlying security type: {SecurityType.Index}");
|
||||
}
|
||||
|
||||
return AddIndexOption(
|
||||
QuantConnect.Symbol.Create(underlying, SecurityType.Index, market),
|
||||
targetOption, resolution, fillForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2276,14 +2315,14 @@ namespace QuantConnect.Algorithm
|
||||
/// <returns>Index Option Contract</returns>
|
||||
/// <exception cref="ArgumentException">The provided Symbol is not an Index Option</exception>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public Option AddIndexOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
|
||||
public IndexOption AddIndexOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
|
||||
{
|
||||
if (symbol.SecurityType != SecurityType.IndexOption)
|
||||
if (symbol.SecurityType != SecurityType.IndexOption || symbol.IsCanonical())
|
||||
{
|
||||
throw new ArgumentException("Symbol provided must be of type SecurityType.IndexOption");
|
||||
throw new ArgumentException("Symbol provided must be non-canonical and of type SecurityType.IndexOption");
|
||||
}
|
||||
|
||||
return AddOptionContract(symbol, resolution, fillForward);
|
||||
return (IndexOption)AddOptionContract(symbol, resolution, fillForward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3305,6 +3344,63 @@ namespace QuantConnect.Algorithm
|
||||
return symbols.Select(symbol => Fundamentals(symbol)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the option chain for the specified symbol at the current time (<see cref="Time"/>)
|
||||
/// </summary>
|
||||
/// <param name="symbol">
|
||||
/// The symbol for which the option chain is asked for.
|
||||
/// It can be either the canonical option or the underlying symbol.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The option chain as an enumerable of <see cref="OptionUniverse"/>,
|
||||
/// each containing the contract symbol along with additional data, including daily price data,
|
||||
/// implied volatility and greeks.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// As of 2024/09/11, future options chain will not contain any additional data (e.g. daily price data, implied volatility and greeks),
|
||||
/// it will be populated with the contract symbol only. This is expected to change in the future.
|
||||
/// </remarks>
|
||||
[DocumentationAttribute(AddingData)]
|
||||
public DataHistory<OptionUniverse> OptionChain(Symbol symbol)
|
||||
{
|
||||
var canonicalSymbol = GetCanonicalOptionSymbol(symbol);
|
||||
IEnumerable<OptionUniverse> optionChain;
|
||||
|
||||
// TODO: Until future options are supported by OptionUniverse, we need to fall back to the OptionChainProvider for them
|
||||
if (canonicalSymbol.SecurityType != SecurityType.FutureOption)
|
||||
{
|
||||
var history = History<OptionUniverse>(canonicalSymbol, 1);
|
||||
optionChain = history?.SingleOrDefault()?.Data?.Cast<OptionUniverse>() ?? Enumerable.Empty<OptionUniverse>();
|
||||
}
|
||||
else
|
||||
{
|
||||
optionChain = OptionChainProvider.GetOptionContractList(canonicalSymbol, Time)
|
||||
.Select(contractSymbol => new OptionUniverse()
|
||||
{
|
||||
Symbol = contractSymbol,
|
||||
EndTime = Time.Date
|
||||
});
|
||||
}
|
||||
|
||||
return new DataHistory<OptionUniverse>(optionChain, new Lazy<PyObject>(() => PandasConverter.GetDataFrame(optionChain)));
|
||||
}
|
||||
|
||||
private static Symbol GetCanonicalOptionSymbol(Symbol symbol)
|
||||
{
|
||||
// We got the underlying
|
||||
if (symbol.SecurityType.HasOptions())
|
||||
{
|
||||
return QuantConnect.Symbol.CreateCanonicalOption(symbol);
|
||||
}
|
||||
|
||||
if (symbol.SecurityType.IsOption())
|
||||
{
|
||||
return symbol.Canonical;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"The symbol {symbol} is not an option or an underlying symbol.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the properties and exchange hours for a given key into our databases
|
||||
/// </summary>
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace QuantConnect.Brokerages
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// HashSet containing the order types supported by TradeStation.
|
||||
/// HashSet containing the order types supported by the <see cref="CanSubmitOrder"/> operation in TradeStation.
|
||||
/// </summary>
|
||||
private readonly HashSet<OrderType> _supportOrderTypes = new(
|
||||
new[]
|
||||
@@ -47,7 +47,9 @@ namespace QuantConnect.Brokerages
|
||||
OrderType.Market,
|
||||
OrderType.Limit,
|
||||
OrderType.StopMarket,
|
||||
OrderType.StopLimit
|
||||
OrderType.StopLimit,
|
||||
OrderType.ComboMarket,
|
||||
OrderType.ComboLimit,
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
@@ -94,9 +96,13 @@ namespace QuantConnect.Brokerages
|
||||
|
||||
if (!_supportOrderTypes.Contains(order.Type))
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
|
||||
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportOrderTypes));
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportOrderTypes));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BrokerageExtensions.OrderCrossesZero(security.Holdings.Quantity, order.Quantity) && IsComboOrderType(order.Type))
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedCrossZeroByOrderType(this, order.Type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,7 +121,7 @@ namespace QuantConnect.Brokerages
|
||||
{
|
||||
message = null;
|
||||
|
||||
if (BrokerageExtensions.OrderCrossesZero(security.Holdings.Quantity, order.Quantity)
|
||||
if (BrokerageExtensions.OrderCrossesZero(security.Holdings.Quantity, order.Quantity)
|
||||
&& request.Quantity != null && request.Quantity != order.Quantity)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
|
||||
@@ -123,7 +129,23 @@ namespace QuantConnect.Brokerages
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsComboOrderType(order.Type) && request.Quantity != null && request.Quantity != order.Quantity)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedUpdateQuantityOrder(this, order.Type));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the provided order type is a combo order.
|
||||
/// </summary>
|
||||
/// <param name="orderType">The order type to check.</param>
|
||||
/// <returns>True if the order type is a combo order; otherwise, false.</returns>
|
||||
private static bool IsComboOrderType(OrderType orderType)
|
||||
{
|
||||
return orderType == OrderType.ComboMarket || orderType == OrderType.ComboLimit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
101
Common/Data/Market/BaseGreeks.cs
Normal file
101
Common/Data/Market/BaseGreeks.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.Data.Market
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the greeks
|
||||
/// </summary>
|
||||
public abstract class BaseGreeks
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the delta.
|
||||
/// <para>
|
||||
/// Delta measures the rate of change of the option value with respect to changes in
|
||||
/// the underlying asset'sprice. (∂V/∂S)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Delta { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gamma.
|
||||
/// <para>
|
||||
/// Gamma measures the rate of change of Delta with respect to changes in
|
||||
/// the underlying asset'sprice. (∂²V/∂S²)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Gamma { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vega.
|
||||
/// <para>
|
||||
/// Vega measures the rate of change of the option value with respect to changes in
|
||||
/// the underlying's volatility. (∂V/∂σ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Vega { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the theta.
|
||||
/// <para>
|
||||
/// Theta measures the rate of change of the option value with respect to changes in
|
||||
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Theta { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rho.
|
||||
/// <para>
|
||||
/// Rho measures the rate of change of the option value with respect to changes in
|
||||
/// the risk free interest rate. (∂V/∂r)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Rho { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lambda.
|
||||
/// <para>
|
||||
/// Lambda is the percentage change in option value per percentage change in the
|
||||
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
|
||||
/// (∂V/∂S ✕ S/V)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract decimal Lambda { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lambda.
|
||||
/// <para>
|
||||
/// Lambda is the percentage change in option value per percentage change in the
|
||||
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
|
||||
/// (∂V/∂S ✕ S/V)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias for <see cref="Lambda"/> required for compatibility with Python when
|
||||
/// PEP8 API is used (lambda is a reserved keyword in Python).
|
||||
/// </remarks>
|
||||
public virtual decimal Lambda_ => Lambda;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the theta per day.
|
||||
/// <para>
|
||||
/// Theta measures the rate of change of the option value with respect to changes in
|
||||
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal ThetaPerDay => Theta / 365m;
|
||||
}
|
||||
}
|
||||
@@ -17,111 +17,6 @@ using System;
|
||||
|
||||
namespace QuantConnect.Data.Market
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the greeks
|
||||
/// </summary>
|
||||
public class BaseGreeks
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the delta.
|
||||
/// <para>
|
||||
/// Delta measures the rate of change of the option value with respect to changes in
|
||||
/// the underlying asset'sprice. (∂V/∂S)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Delta { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gamma.
|
||||
/// <para>
|
||||
/// Gamma measures the rate of change of Delta with respect to changes in
|
||||
/// the underlying asset'sprice. (∂²V/∂S²)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Gamma { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vega.
|
||||
/// <para>
|
||||
/// Vega measures the rate of change of the option value with respect to changes in
|
||||
/// the underlying's volatility. (∂V/∂σ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Vega { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the theta.
|
||||
/// <para>
|
||||
/// Theta measures the rate of change of the option value with respect to changes in
|
||||
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Theta { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rho.
|
||||
/// <para>
|
||||
/// Rho measures the rate of change of the option value with respect to changes in
|
||||
/// the risk free interest rate. (∂V/∂r)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Rho { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lambda.
|
||||
/// <para>
|
||||
/// Lambda is the percentage change in option value per percentage change in the
|
||||
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
|
||||
/// (∂V/∂S ✕ S/V)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual decimal Lambda { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lambda.
|
||||
/// <para>
|
||||
/// Lambda is the percentage change in option value per percentage change in the
|
||||
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
|
||||
/// (∂V/∂S ✕ S/V)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias for <see cref="Lambda"/> required for compatibility with Python when
|
||||
/// PEP8 API is used (lambda is a reserved keyword in Python).
|
||||
/// </remarks>
|
||||
public decimal Lambda_ => Lambda;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the theta per day.
|
||||
/// <para>
|
||||
/// Theta measures the rate of change of the option value with respect to changes in
|
||||
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public decimal ThetaPerDay => Theta / 365m;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new default instance of the <see cref="BaseGreeks"/> class
|
||||
/// </summary>
|
||||
public BaseGreeks()
|
||||
: this(0m, 0m, 0m, 0m, 0m, 0m)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseGreeks"/> class
|
||||
/// </summary>
|
||||
public BaseGreeks(decimal delta, decimal gamma, decimal vega, decimal theta, decimal rho, decimal lambda)
|
||||
{
|
||||
Delta = delta;
|
||||
Gamma = gamma;
|
||||
Vega = vega;
|
||||
Theta = theta;
|
||||
Rho = rho;
|
||||
Lambda = lambda;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the greeks
|
||||
/// </summary>
|
||||
@@ -219,7 +114,8 @@ namespace QuantConnect.Data.Market
|
||||
/// <summary>
|
||||
/// Initializes a new default instance of the <see cref="Greeks"/> class
|
||||
/// </summary>
|
||||
public Greeks() : base()
|
||||
public Greeks()
|
||||
: this(0m, 0m, 0m, 0m, 0m, 0m)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -111,12 +111,8 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
/// <param name="underlying">The associated underlying price data if any</param>
|
||||
/// <param name="filteredContracts">The contracts selected by the universe</param>
|
||||
public BaseDataCollection(DateTime time, DateTime endTime, Symbol symbol, List<BaseData> data, BaseData underlying, HashSet<Symbol> filteredContracts)
|
||||
: this(time, endTime, symbol, underlying, filteredContracts)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Time = time;
|
||||
_endTime = endTime;
|
||||
Underlying = underlying;
|
||||
FilteredContracts = filteredContracts;
|
||||
if (data != null && data.Count == 1 && data[0] is BaseDataCollection collection && collection.Data != null && collection.Data.Count > 0)
|
||||
{
|
||||
// we were given a base data collection, let's be nice and fetch it's data if it has any
|
||||
@@ -128,13 +124,26 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create an instance without setting the data list
|
||||
/// </summary>
|
||||
protected BaseDataCollection(DateTime time, DateTime endTime, Symbol symbol, BaseData underlying, HashSet<Symbol> filteredContracts)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Time = time;
|
||||
_endTime = endTime;
|
||||
Underlying = underlying;
|
||||
FilteredContracts = filteredContracts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor for <see cref="BaseDataCollection"/>
|
||||
/// </summary>
|
||||
/// <param name="other">The base data collection being copied</param>
|
||||
public BaseDataCollection(BaseDataCollection other)
|
||||
: this(other.Time, other.EndTime, other.Symbol, other.Data, other.Underlying, other.FilteredContracts)
|
||||
: this(other.Time, other.EndTime, other.Symbol, other.Underlying, other.FilteredContracts)
|
||||
{
|
||||
Data = other.Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -228,6 +237,11 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
lock (_symbolsCache)
|
||||
{
|
||||
// limit the cache size to help with memory usage
|
||||
if (_symbolsCache.Count >= 600000)
|
||||
{
|
||||
_symbolsCache.Clear();
|
||||
}
|
||||
_symbolsCache.TryAdd(ticker, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
/// </summary>
|
||||
public class OptionUniverse : BaseDataCollection, ISymbol
|
||||
{
|
||||
private bool _throwIfNotAnOption = true;
|
||||
private const int StartingGreeksCsvIndex = 7;
|
||||
|
||||
// We keep the properties as they are in the csv file to reduce memory usage (strings vs decimals)
|
||||
private char[] _csvLine;
|
||||
private readonly string _csvLine;
|
||||
|
||||
/// <summary>
|
||||
/// The security identifier of the option symbol
|
||||
@@ -49,7 +50,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
get
|
||||
{
|
||||
// Parse the values every time to avoid keeping them in memory
|
||||
return GetDecimalFromCsvLine(0);
|
||||
return _csvLine.GetDecimalFromCsv(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDecimalFromCsvLine(1);
|
||||
return _csvLine.GetDecimalFromCsv(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDecimalFromCsvLine(2);
|
||||
return _csvLine.GetDecimalFromCsv(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDecimalFromCsvLine(3);
|
||||
return _csvLine.GetDecimalFromCsv(3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetDecimalFromCsvLine(4);
|
||||
return _csvLine.GetDecimalFromCsv(4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
get
|
||||
{
|
||||
ThrowIfNotAnOption(nameof(OpenInterest));
|
||||
return GetDecimalFromCsvLine(5);
|
||||
return _csvLine.GetDecimalFromCsv(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
get
|
||||
{
|
||||
ThrowIfNotAnOption(nameof(ImpliedVolatility));
|
||||
return GetDecimalFromCsvLine(6);
|
||||
return _csvLine.GetDecimalFromCsv(6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +130,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
get
|
||||
{
|
||||
ThrowIfNotAnOption(nameof(Greeks));
|
||||
|
||||
return new BaseGreeks(
|
||||
GetDecimalFromCsvLine(7),
|
||||
GetDecimalFromCsvLine(8),
|
||||
GetDecimalFromCsvLine(9),
|
||||
GetDecimalFromCsvLine(10),
|
||||
GetDecimalFromCsvLine(11),
|
||||
decimal.Zero);
|
||||
return new PreCalculatedGreeks(_csvLine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,9 +154,9 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
/// Creates a new instance of the <see cref="OptionUniverse"/> class
|
||||
/// </summary>
|
||||
public OptionUniverse(DateTime date, Symbol symbol, string csv)
|
||||
: base(date, symbol)
|
||||
: base(date, date, symbol, null, null)
|
||||
{
|
||||
_csvLine = csv.ToCharArray();
|
||||
_csvLine = csv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -245,11 +239,7 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
CacheSymbol(key, symbol);
|
||||
}
|
||||
|
||||
var result = new OptionUniverse(date, symbol, remainingLine);
|
||||
// The data list will not be used for a single contract data instance, might as well save some memory
|
||||
result.Data = null;
|
||||
|
||||
return result;
|
||||
return new OptionUniverse(date, symbol, remainingLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -286,18 +276,24 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
return new OptionUniverse(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default resolution for this data and security type
|
||||
/// </summary>
|
||||
/// <remarks>This is a method and not a property so that python
|
||||
/// custom data types can override it</remarks>
|
||||
public override Resolution DefaultResolution()
|
||||
{
|
||||
return Resolution.Daily;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CSV string representation of this universe entry
|
||||
/// </summary>
|
||||
public string ToCsv()
|
||||
public static string ToCsv(Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, decimal? openInterest,
|
||||
decimal? impliedVolatility, BaseGreeks greeks)
|
||||
{
|
||||
_throwIfNotAnOption = false;
|
||||
// Single access to avoid parsing the csv multiple times
|
||||
var greeks = Greeks;
|
||||
var csv = $"{Symbol.ID},{Symbol.Value},{Open},{High},{Low},{Close},{Volume}," +
|
||||
$"{OpenInterest},{ImpliedVolatility},{greeks.Delta},{greeks.Gamma},{greeks.Vega},{greeks.Theta},{greeks.Rho}";
|
||||
_throwIfNotAnOption = true;
|
||||
return csv;
|
||||
return $"{symbol.ID},{symbol.Value},{open},{high},{low},{close},{volume},"
|
||||
+ $"{openInterest},{impliedVolatility},{greeks?.Delta},{greeks?.Gamma},{greeks?.Vega},{greeks?.Theta},{greeks?.Rho}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -323,39 +319,71 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
public static string CsvHeader => "symbol_id,symbol_value,open,high,low,close,volume,open_interest,implied_volatility,delta,gamma,vega,theta,rho";
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private decimal GetDecimalFromCsvLine(int index)
|
||||
{
|
||||
if (_csvLine.IsNullOrEmpty())
|
||||
{
|
||||
return decimal.Zero;
|
||||
}
|
||||
|
||||
var span = _csvLine.AsSpan();
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
var commaIndex = span.IndexOf(',');
|
||||
if (commaIndex == -1)
|
||||
{
|
||||
return decimal.Zero;
|
||||
}
|
||||
span = span.Slice(commaIndex + 1);
|
||||
}
|
||||
|
||||
var nextCommaIndex = span.IndexOf(',');
|
||||
if (nextCommaIndex == -1)
|
||||
{
|
||||
nextCommaIndex = span.Length;
|
||||
}
|
||||
|
||||
return span.Slice(0, nextCommaIndex).ToString().ToDecimal();
|
||||
}
|
||||
|
||||
private void ThrowIfNotAnOption(string propertyName)
|
||||
{
|
||||
if (_throwIfNotAnOption && !Symbol.SecurityType.IsOption())
|
||||
if (!Symbol.SecurityType.IsOption())
|
||||
{
|
||||
throw new InvalidOperationException($"{propertyName} is only available for options.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-calculated greeks lazily parsed from csv line.
|
||||
/// It parses the greeks values from the csv line only when they are requested to avoid holding decimals in memory.
|
||||
/// </summary>
|
||||
private class PreCalculatedGreeks : BaseGreeks
|
||||
{
|
||||
private readonly string _csvLine;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Delta
|
||||
{
|
||||
get => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex);
|
||||
protected set => throw new InvalidOperationException("Delta is read-only.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Gamma
|
||||
{
|
||||
get => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 1);
|
||||
protected set => throw new InvalidOperationException("Gamma is read-only.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Vega
|
||||
{
|
||||
get => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 2);
|
||||
protected set => throw new InvalidOperationException("Vega is read-only.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Theta
|
||||
{
|
||||
get => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 3);
|
||||
protected set => throw new InvalidOperationException("Theta is read-only.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Rho
|
||||
{
|
||||
get => _csvLine.GetDecimalFromCsv(StartingGreeksCsvIndex + 4);
|
||||
protected set => throw new InvalidOperationException("Rho is read-only.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override decimal Lambda
|
||||
{
|
||||
get => decimal.Zero;
|
||||
protected set => throw new InvalidOperationException("Lambda is read-only.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new default instance of the <see cref="PreCalculatedGreeks"/> class
|
||||
/// </summary>
|
||||
public PreCalculatedGreeks(string csvLine)
|
||||
{
|
||||
_csvLine = csvLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace QuantConnect.Exceptions
|
||||
public SanitizedException(string message, string stackTrace)
|
||||
{
|
||||
_message = message;
|
||||
_stackTrace = Extensions.ClearLeanPaths(stackTrace);
|
||||
_stackTrace = Logging.Log.ClearLeanPaths(stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace QuantConnect
|
||||
/// <summary>
|
||||
/// Unknown exchange value
|
||||
/// </summary>
|
||||
public static Exchange UNKNOWN { get; } = new (string.Empty, string.Empty, "UNKNOWN", string.Empty);
|
||||
public static Exchange UNKNOWN { get; } = new(string.Empty, string.Empty, "UNKNOWN", string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// The Members Exchange (MEMX) is an independently owned, technology-driven stock exchange
|
||||
@@ -47,6 +47,12 @@ namespace QuantConnect
|
||||
public static Exchange NASDAQ { get; }
|
||||
= new("NASDAQ", "Q", "National Association of Securities Dealers Automated Quotation", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
|
||||
/// <summary>
|
||||
/// The NASDAQ options market
|
||||
/// </summary>
|
||||
public static Exchange NASDAQ_Options { get; }
|
||||
= new("XNDQ", "XNDQ", "NASDAQ options market", QuantConnect.Market.USA, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// Bats Global Markets, Better Alternative Trading System
|
||||
/// </summary>
|
||||
@@ -59,6 +65,12 @@ namespace QuantConnect
|
||||
public static Exchange ARCA { get; }
|
||||
= new("ARCA", "P", "New York Stock Archipelago Exchange", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
|
||||
/// <summary>
|
||||
/// New York Stock Archipelago Exchange
|
||||
/// </summary>
|
||||
public static Exchange ARCA_Options { get; }
|
||||
= new("ARCX", "ARCX", "NYSE Arca Options", QuantConnect.Market.USA, SecurityType.Option);
|
||||
|
||||
/// <summary>
|
||||
/// New York Stock Exchange
|
||||
/// </summary>
|
||||
@@ -113,7 +125,13 @@ namespace QuantConnect
|
||||
/// The Chicago Board Options Exchange
|
||||
/// </summary>
|
||||
public static Exchange CBOE { get; }
|
||||
= new("CBOE", "W", "The Chicago Board Options Exchange", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
= new("CBOE", "W", "The Chicago Board Options Exchange", QuantConnect.Market.USA, SecurityType.Equity, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// CBOE Options Exchange
|
||||
/// </summary>
|
||||
public static Exchange C2 { get; }
|
||||
= new("C2", "W", "CBOE Options Exchange", QuantConnect.Market.USA, SecurityType.Option);
|
||||
|
||||
/// <summary>
|
||||
/// The American Options Exchange
|
||||
@@ -139,6 +157,12 @@ namespace QuantConnect
|
||||
public static Exchange EDGX { get; }
|
||||
= new("EDGX", "K", "CBOE EDGX U.S. equities Exchange", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
|
||||
/// <summary>
|
||||
/// CBOE EDGO U.S. option Exchange
|
||||
/// </summary>
|
||||
public static Exchange EDGO { get; }
|
||||
= new("EDGO", "EDGO", "CBOE EDGX OPTIONS EXCHANGE.", QuantConnect.Market.USA, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// National Association of Securities Dealers Automated Quotation PSX
|
||||
/// </summary>
|
||||
@@ -158,6 +182,12 @@ namespace QuantConnect
|
||||
public static Exchange BOSTON { get; }
|
||||
= new("BOSTON", "BB", "The Boston Stock Exchange", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
|
||||
/// <summary>
|
||||
/// The Boston Option Exchange
|
||||
/// </summary>
|
||||
public static Exchange BOX { get; }
|
||||
= new("BOX", "B", "The Boston Option Exchange", QuantConnect.Market.USA, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// The American Stock Exchange
|
||||
/// </summary>
|
||||
@@ -190,12 +220,6 @@ namespace QuantConnect
|
||||
public static Exchange OPRA { get; }
|
||||
= new("OPRA", "O", "The Options Price Reporting Authority", QuantConnect.Market.USA, SecurityType.Option);
|
||||
|
||||
/// <summary>
|
||||
/// CBOE Options Exchange
|
||||
/// </summary>
|
||||
public static Exchange C2 { get; }
|
||||
= new("C2", "W", "CBOE Options Exchange", QuantConnect.Market.USA, SecurityType.Option);
|
||||
|
||||
/// <summary>
|
||||
/// Miami International Securities Options Exchange
|
||||
/// </summary>
|
||||
@@ -214,6 +238,12 @@ namespace QuantConnect
|
||||
public static Exchange MIAX_EMERALD { get; }
|
||||
= new("MIAX_EMERALD", "ME", "MIAX EMERALD", QuantConnect.Market.USA, SecurityType.Option);
|
||||
|
||||
/// <summary>
|
||||
/// MIAX Sapphire: Electronic and floor trading for derivatives.
|
||||
/// </summary>
|
||||
public static Exchange MIAX_SAPPHIRE { get; }
|
||||
= new("MIAX_SAPPHIRE", "SPHR", "Miax Sapphire, LLC", QuantConnect.Market.USA, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// International Securities Options Exchange GEMINI
|
||||
/// </summary>
|
||||
@@ -232,6 +262,14 @@ namespace QuantConnect
|
||||
public static Exchange CME { get; }
|
||||
= new("CME", "CME", "Futures and Options Chicago Mercantile Exchange", QuantConnect.Market.CME, SecurityType.Future, SecurityType.FutureOption);
|
||||
|
||||
/// <summary>
|
||||
/// The European Derivatives Exchange (EUREX)
|
||||
/// </summary>
|
||||
public static Exchange EUREX { get; }
|
||||
= new("EUREX", "EUREX", "European Derivatives Exchange", QuantConnect.Market.EUREX, SecurityType.Future, SecurityType.Index);
|
||||
|
||||
/// <summary>
|
||||
|
||||
/// <summary>
|
||||
/// The Chicago Board of Trade (CBOT) is a commodity exchange
|
||||
/// </summary>
|
||||
@@ -268,6 +306,18 @@ namespace QuantConnect
|
||||
public static Exchange NYSELIFFE { get; }
|
||||
= new("NYSELIFFE", "NYSELIFFE", "London International Financial Futures and Options Exchange", QuantConnect.Market.NYSELIFFE, SecurityType.Future, SecurityType.FutureOption);
|
||||
|
||||
/// <summary>
|
||||
/// Credit Suisse First Boston (also known as CSFB and CS First Boston) is the investment banking affiliate of Credit Suisse headquartered in New York.
|
||||
/// </summary>
|
||||
public static Exchange CSFB { get; }
|
||||
= new("CSFB", "CSFB", "Credit Suisse First Boston", QuantConnect.Market.USA, SecurityType.Equity);
|
||||
|
||||
/// <summary>
|
||||
/// Philadelphia Stock Exchange (PHLX), now known as Nasdaq PHLX, is the first stock exchange established in the United States and the oldest stock exchange in the nation.
|
||||
/// </summary>
|
||||
public static Exchange PHLX { get; }
|
||||
= new("PHLX", "X", "NASDAQ OMX PHLX", QuantConnect.Market.USA, SecurityType.Option, SecurityType.IndexOption);
|
||||
|
||||
/// <summary>
|
||||
/// Exchange description
|
||||
/// </summary>
|
||||
@@ -306,7 +356,7 @@ namespace QuantConnect
|
||||
/// <summary>
|
||||
/// Creates a new exchange instance
|
||||
/// </summary>
|
||||
private Exchange(string name, string code, string description, string market, params SecurityType[] securityTypes)
|
||||
public Exchange(string name, string code, string description, string market, params SecurityType[] securityTypes)
|
||||
{
|
||||
Name = name;
|
||||
Market = market;
|
||||
|
||||
@@ -67,7 +67,6 @@ namespace QuantConnect
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
private static readonly Regex LeanPathRegex = new Regex("(?:\\S*?\\\\pythonnet\\\\)|(?:\\S*?\\\\Lean\\\\)|(?:\\S*?/Lean/)|(?:\\S*?/pythonnet/)", RegexOptions.Compiled);
|
||||
private static readonly Dictionary<string, bool> _emptyDirectories = new ();
|
||||
private static readonly HashSet<string> InvalidSecurityTypes = new HashSet<string>();
|
||||
private static readonly Regex DateCheck = new Regex(@"\d{8}", RegexOptions.Compiled);
|
||||
@@ -132,20 +131,6 @@ namespace QuantConnect
|
||||
return !DateCheck.IsMatch(fileName) && DateTime.Now - TimeSpan.FromDays(DataUpdatePeriod) > File.GetLastWriteTime(filepath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to clear undesired paths from stack traces
|
||||
/// </summary>
|
||||
/// <param name="error">The error to cleanup</param>
|
||||
/// <returns>The sanitized error</returns>
|
||||
public static string ClearLeanPaths(string error)
|
||||
{
|
||||
if (string.IsNullOrEmpty(error))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
return LeanPathRegex.Replace(error, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to check if a directory exists and is not empty
|
||||
/// </summary>
|
||||
@@ -646,6 +631,54 @@ namespace QuantConnect
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a python property by name
|
||||
/// </summary>
|
||||
/// <param name="instance">The object instance to search the property in</param>
|
||||
/// <param name="name">The name of the property</param>
|
||||
/// <returns>The python property or null if not defined or CSharp implemented</returns>
|
||||
public static dynamic GetPythonBoolProperty(this PyObject instance, string name)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var objectType = instance.GetPythonType();
|
||||
if (!objectType.HasAttr(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var property = instance.GetAttr(name);
|
||||
var pythonType = property.GetPythonType();
|
||||
var isPythonDefined = pythonType.Repr().Equals("<class \'bool\'>", StringComparison.Ordinal);
|
||||
|
||||
if (isPythonDefined)
|
||||
{
|
||||
return property;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a python property by name
|
||||
/// </summary>
|
||||
/// <param name="instance">The object instance to search the property in</param>
|
||||
/// <param name="name">The name of the method</param>
|
||||
/// <returns>The python property or null if not defined or CSharp implemented</returns>
|
||||
public static dynamic GetPythonBoolPropertyWithChecks(this PyObject instance, string name)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
if (!instance.HasAttr(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return instance.GetPythonBoolProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a python method by name
|
||||
/// </summary>
|
||||
@@ -1523,6 +1556,75 @@ namespace QuantConnect
|
||||
return csv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index from a CSV line.
|
||||
/// </summary>
|
||||
/// <param name="csvLine">The CSV line</param>
|
||||
/// <param name="index">The index of the value to be extracted from the CSV line</param>
|
||||
/// <param name="result">The value at the given index</param>
|
||||
/// <returns>Whether there was a value at the given index and could be extracted</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetFromCsv(this string csvLine, int index, out ReadOnlySpan<char> result)
|
||||
{
|
||||
result = ReadOnlySpan<char>.Empty;
|
||||
if (string.IsNullOrEmpty(csvLine) || index < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var span = csvLine.AsSpan();
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
var commaIndex = span.IndexOf(',');
|
||||
if (commaIndex == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
span = span.Slice(commaIndex + 1);
|
||||
}
|
||||
|
||||
var nextCommaIndex = span.IndexOf(',');
|
||||
if (nextCommaIndex == -1)
|
||||
{
|
||||
nextCommaIndex = span.Length;
|
||||
}
|
||||
|
||||
result = span.Slice(0, nextCommaIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index from a CSV line, converted into a decimal.
|
||||
/// </summary>
|
||||
/// <param name="csvLine">The CSV line</param>
|
||||
/// <param name="index">The index of the value to be extracted from the CSV line</param>
|
||||
/// <param name="value">The decimal value at the given index</param>
|
||||
/// <returns>Whether there was a value at the given index and could be extracted and converted into a decimal</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetDecimalFromCsv(this string csvLine, int index, out decimal value)
|
||||
{
|
||||
value = decimal.Zero;
|
||||
if (!csvLine.TryGetFromCsv(index, out var csvValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return decimal.TryParse(csvValue, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified index from a CSV line, converted into a decimal.
|
||||
/// </summary>
|
||||
/// <param name="csvLine">The CSV line</param>
|
||||
/// <param name="index">The index of the value to be extracted from the CSV line</param>
|
||||
/// <returns>The decimal value at the given index. If the index is invalid or conversion fails, it will return zero</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static decimal GetDecimalFromCsv(this string csvLine, int index)
|
||||
{
|
||||
csvLine.TryGetDecimalFromCsv(index, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a number is NaN or infinity
|
||||
/// </summary>
|
||||
|
||||
@@ -851,6 +851,7 @@ namespace QuantConnect
|
||||
case "Y":
|
||||
case "BATS Y":
|
||||
case "BATS_Y":
|
||||
case "BYX":
|
||||
return Exchange.BATS_Y;
|
||||
case "BB":
|
||||
case "BOSTON":
|
||||
@@ -873,6 +874,8 @@ namespace QuantConnect
|
||||
case "MM":
|
||||
case "MEMX":
|
||||
return Exchange.MEMX;
|
||||
case "CSFB":
|
||||
return Exchange.CSFB;
|
||||
}
|
||||
}
|
||||
else if (securityType == SecurityType.Option)
|
||||
@@ -910,6 +913,21 @@ namespace QuantConnect
|
||||
case "W":
|
||||
case "C2":
|
||||
return Exchange.C2;
|
||||
case "XNDQ":
|
||||
return Exchange.NASDAQ_Options;
|
||||
case "ARCX":
|
||||
return Exchange.ARCA_Options;
|
||||
case "EDGO":
|
||||
return Exchange.EDGO;
|
||||
case "BOX":
|
||||
case "B":
|
||||
return Exchange.BOX;
|
||||
case "PHLX":
|
||||
return Exchange.PHLX;
|
||||
case "SPHR":
|
||||
case "MIAX SAPPHIRE":
|
||||
case "MIAX_SAPPHIRE":
|
||||
return Exchange.MIAX_SAPPHIRE;
|
||||
default:
|
||||
return Exchange.UNKNOWN;
|
||||
}
|
||||
@@ -930,6 +948,10 @@ namespace QuantConnect
|
||||
return Exchange.CFE;
|
||||
case "COMEX":
|
||||
return Exchange.COMEX;
|
||||
case "NYSELIFFE":
|
||||
return Exchange.NYSELIFFE;
|
||||
case "EUREX":
|
||||
return Exchange.EUREX;
|
||||
default:
|
||||
return Exchange.UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace QuantConnect
|
||||
Tuple.Create(Bybit, 37),
|
||||
Tuple.Create(Coinbase, 38),
|
||||
Tuple.Create(InteractiveBrokers, 39),
|
||||
Tuple.Create(EUREX, 40)
|
||||
};
|
||||
|
||||
static Market()
|
||||
@@ -153,6 +154,11 @@ namespace QuantConnect
|
||||
/// </summary>
|
||||
public const string CME = "cme";
|
||||
|
||||
/// <summary>
|
||||
/// EUREX
|
||||
/// </summary>
|
||||
public const string EUREX = "eurex";
|
||||
|
||||
/// <summary>
|
||||
/// Singapore Exchange
|
||||
/// </summary>
|
||||
|
||||
@@ -128,6 +128,24 @@ namespace QuantConnect
|
||||
{
|
||||
return Invariant($"Invalid security type: {security.Type}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a message indicating that the specified order type is not supported for orders that cross the zero holdings threshold.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string UnsupportedCrossZeroByOrderType(IBrokerageModel brokerageModel, OrderType orderType)
|
||||
{
|
||||
return Invariant($"Order type '{orderType}' is not supported for orders that cross the zero holdings threshold in the {brokerageModel.GetType().Name}. This means you cannot change a position from positive to negative or vice versa using this order type. Please close the existing position first.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a message indicating that the specified order type cannot be updated quantity using the given brokerage model.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string UnsupportedUpdateQuantityOrder(IBrokerageModel brokerageModel, OrderType orderType)
|
||||
{
|
||||
return Invariant($"Order type '{orderType}' is not supported to update quantity in the {brokerageModel.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -112,6 +112,15 @@ namespace QuantConnect
|
||||
return Invariant($"InteractiveBrokersFeeModel.UnitedStatesFutureFees(): Unsupported security type: {security.Type}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string message saying the type of the given security was unsupported
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string EUREXFutureFeesUnsupportedSecurityType(Securities.Security security)
|
||||
{
|
||||
return Invariant($"InteractiveBrokersFeeModel.EUREXFutureFees(): Unsupported security type: {security.Type}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string message saying the quote currency of the given security was
|
||||
/// unexpected for Hong Kong futures exchange
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace QuantConnect.Orders.Fees
|
||||
new()
|
||||
{
|
||||
{ Market.USA, UnitedStatesFutureFees },
|
||||
{ Market.HKFE, HongKongFutureFees }
|
||||
{ Market.HKFE, HongKongFutureFees },
|
||||
{ Market.EUREX, EUREXFutureFees }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -377,6 +378,37 @@ namespace QuantConnect.Orders.Fees
|
||||
return new CashAmount(ibFeePerContract * 1.5m, security.QuoteCurrency.Symbol);
|
||||
}
|
||||
|
||||
private static CashAmount EUREXFutureFees(Security security)
|
||||
{
|
||||
IDictionary<string, decimal> fees, exchangeFees;
|
||||
decimal ibFeePerContract, exchangeFeePerContract;
|
||||
string symbol;
|
||||
|
||||
switch (security.Symbol.SecurityType)
|
||||
{
|
||||
case SecurityType.Future:
|
||||
fees = _eurexFuturesFees;
|
||||
exchangeFees = _eurexFuturesExchangeFees;
|
||||
symbol = security.Symbol.ID.Symbol;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException(Messages.InteractiveBrokersFeeModel.EUREXFutureFeesUnsupportedSecurityType(security));
|
||||
}
|
||||
|
||||
if (!fees.TryGetValue(symbol, out ibFeePerContract))
|
||||
{
|
||||
ibFeePerContract = 1.00m;
|
||||
}
|
||||
|
||||
if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
|
||||
{
|
||||
exchangeFeePerContract = 0.00m;
|
||||
}
|
||||
|
||||
// Add exchange fees + IBKR regulatory fee (0.02)
|
||||
return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.EUR);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference at https://www.interactivebrokers.com/en/pricing/commissions-futures.php?re=amer
|
||||
/// </summary>
|
||||
@@ -394,6 +426,15 @@ namespace QuantConnect.Orders.Fees
|
||||
{ "MIR", 0.15m }, { "M6C", 0.15m }, { "M6S", 0.15m }, { "MNH", 0.15m },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Reference at https://www.interactivebrokers.com/en/pricing/commissions-futures-europe.php?re=europe
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, decimal> _eurexFuturesFees = new()
|
||||
{
|
||||
// Futures
|
||||
{ "FESX", 1.00m },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, decimal> _usaFutureOptionsFees = new()
|
||||
{
|
||||
// Micro E-mini Future Options
|
||||
@@ -419,6 +460,12 @@ namespace QuantConnect.Orders.Fees
|
||||
{ "MIR", 0.24m }, { "M6C", 0.24m }, { "M6S", 0.24m }, { "MNH", 0.24m },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, decimal> _eurexFuturesExchangeFees = new()
|
||||
{
|
||||
// Futures
|
||||
{ "FESX", 0.00m },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, decimal> _usaFutureOptionsExchangeFees = new()
|
||||
{
|
||||
// E-mini Future Options
|
||||
|
||||
91
Common/Orders/GroupOrderCacheManager.cs
Normal file
91
Common/Orders/GroupOrderCacheManager.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace QuantConnect.Orders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a thread-safe service for caching and managing original orders when they are part of a group.
|
||||
/// </summary>
|
||||
public class GroupOrderCacheManager
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe dictionary that caches original orders when they are part of a group.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The dictionary uses the order ID as the key and stores the original <see cref="Order"/> objects as values.
|
||||
/// This allows for the modification of the original orders, such as setting the brokerage ID,
|
||||
/// without retrieving a cloned instance from the order provider.
|
||||
/// </remarks>
|
||||
private readonly ConcurrentDictionary<int, Order> _pendingGroupOrders = new();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve all the orders in the combo group from the cache.
|
||||
/// </summary>
|
||||
/// <param name="order">Target order, which can be any of the legs of the combo</param>
|
||||
/// <param name="orders">List of orders in the combo</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if all the orders in the combo group were successfully retrieved from the cache;
|
||||
/// otherwise, <c>false</c>. If the retrieval fails, the target order is cached for future retrieval.
|
||||
/// </returns>
|
||||
public bool TryGetGroupCachedOrders(Order order, out List<Order> orders)
|
||||
{
|
||||
if (!order.TryGetGroupOrders(TryGetOrder, out orders))
|
||||
{
|
||||
// some order of the group is missing but cache the new one
|
||||
CacheOrder(order);
|
||||
return false;
|
||||
}
|
||||
RemoveCachedOrders(orders);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve an original order from the cache using the specified order ID.
|
||||
/// </summary>
|
||||
/// <param name="orderId">The unique identifier of the order to retrieve.</param>
|
||||
/// <returns>
|
||||
/// The original <see cref="Order"/> if found; otherwise, <c>null</c>.
|
||||
/// </returns>
|
||||
private Order TryGetOrder(int orderId)
|
||||
{
|
||||
_pendingGroupOrders.TryGetValue(orderId, out var order);
|
||||
return order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caches an original order in the internal dictionary for future retrieval.
|
||||
/// </summary>
|
||||
/// <param name="order">The <see cref="Order"/> object to cache.</param>
|
||||
private void CacheOrder(Order order)
|
||||
{
|
||||
_pendingGroupOrders[order.Id] = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a list of orders from the internal cache.
|
||||
/// </summary>
|
||||
/// <param name="orders">The list of <see cref="Order"/> objects to remove from the cache.</param>
|
||||
private void RemoveCachedOrders(List<Order> orders)
|
||||
{
|
||||
for (var i = 0; i < orders.Count; i++)
|
||||
{
|
||||
_pendingGroupOrders.TryRemove(orders[i].Id, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace QuantConnect.Orders
|
||||
var order = orders[i];
|
||||
var security = securityProvider.GetSecurity(order.Symbol);
|
||||
|
||||
if(security == null)
|
||||
if (security == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace QuantConnect.Orders
|
||||
/// The unique order group Id
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public long Id { get; }
|
||||
public int Id { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The group order quantity
|
||||
@@ -89,19 +89,29 @@ namespace QuantConnect.Orders
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance
|
||||
/// Creates a new instance of <see cref="GroupOrderManager"/>
|
||||
/// </summary>
|
||||
/// <param name="id">This order group unique Id</param>
|
||||
/// <param name="legCount">The order leg count</param>
|
||||
/// <param name="quantity">The group order quantity</param>
|
||||
/// <param name="limitPrice">The limit price associated with this order group if any</param>
|
||||
public GroupOrderManager(int id, int legCount, decimal quantity, decimal limitPrice = 0)
|
||||
public GroupOrderManager(int id, int legCount, decimal quantity, decimal limitPrice = 0) : this(legCount, quantity, limitPrice)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GroupOrderManager"/>
|
||||
/// </summary>
|
||||
/// <param name="legCount">The order leg count</param>
|
||||
/// <param name="quantity">The group order quantity</param>
|
||||
/// <param name="limitPrice">The limit price associated with this order group if any</param>
|
||||
public GroupOrderManager(int legCount, decimal quantity, decimal limitPrice = 0)
|
||||
{
|
||||
Count = legCount;
|
||||
Quantity = quantity;
|
||||
LimitPrice = limitPrice;
|
||||
OrderIds = new (capacity: legCount);
|
||||
OrderIds = new(capacity: legCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,27 @@ namespace QuantConnect.Orders
|
||||
private volatile int _incrementalId;
|
||||
private decimal _quantity;
|
||||
private decimal _price;
|
||||
private int _id;
|
||||
|
||||
/// <summary>
|
||||
/// Order ID.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public int Id { get; internal set; }
|
||||
public int Id
|
||||
{
|
||||
get => _id;
|
||||
internal set
|
||||
{
|
||||
_id = value;
|
||||
if (_id != 0 && GroupOrderManager != null)
|
||||
{
|
||||
lock (GroupOrderManager.OrderIds)
|
||||
{
|
||||
GroupOrderManager.OrderIds.Add(_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order id to process before processing this order.
|
||||
@@ -462,13 +477,6 @@ namespace QuantConnect.Orders
|
||||
}
|
||||
order.Status = OrderStatus.New;
|
||||
order.Id = orderId;
|
||||
if (groupOrderManager != null)
|
||||
{
|
||||
lock (groupOrderManager.OrderIds)
|
||||
{
|
||||
groupOrderManager.OrderIds.Add(orderId);
|
||||
}
|
||||
}
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,14 @@ namespace QuantConnect.Orders
|
||||
/// Represents the properties of an order in TradeStation.
|
||||
/// </summary>
|
||||
public class TradeStationOrderProperties : OrderProperties
|
||||
{ }
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables the "All or None" feature for your order, ensuring it will only be filled completely or not at all.
|
||||
/// Set to true to activate this feature, or false to allow partial fills.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Applicable to Equities and Options.
|
||||
/// </remarks>
|
||||
public bool AllOrNone { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace QuantConnect.Python
|
||||
/// <summary>
|
||||
/// Set of helper methods to invoke Python methods with runtime checks for return values and out parameter's conversions.
|
||||
/// </summary>
|
||||
private class PythonRuntimeChecker
|
||||
public class PythonRuntimeChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes method <paramref name="method"/> and converts the returned value to type <typeparamref name="TResult"/>
|
||||
|
||||
@@ -153,6 +153,18 @@ namespace QuantConnect.Scheduling
|
||||
return new FuncTimeRule(name, applicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies an event should fire at market open +- <paramref name="minutesBeforeOpen"/>
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol whose market open we want an event for</param>
|
||||
/// <param name="minutesBeforeOpen">The minutes before market open that the event should fire</param>
|
||||
/// <param name="extendedMarketOpen">True to use extended market open, false to use regular market open</param>
|
||||
/// <returns>A time rule that fires the specified number of minutes before the symbol's market open</returns>
|
||||
public ITimeRule BeforeMarketOpen(Symbol symbol, double minutesBeforeOpen = 0, bool extendedMarketOpen = false)
|
||||
{
|
||||
return AfterMarketOpen(symbol, minutesBeforeOpen * (-1), extendedMarketOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies an event should fire at market open +- <paramref name="minutesAfterOpen"/>
|
||||
/// </summary>
|
||||
@@ -163,7 +175,8 @@ namespace QuantConnect.Scheduling
|
||||
public ITimeRule AfterMarketOpen(Symbol symbol, double minutesAfterOpen = 0, bool extendedMarketOpen = false)
|
||||
{
|
||||
var type = extendedMarketOpen ? "ExtendedMarketOpen" : "MarketOpen";
|
||||
var name = Invariant($"{symbol}: {minutesAfterOpen:0.##} min after {type}");
|
||||
var afterOrBefore = minutesAfterOpen > 0 ? "after" : "before";
|
||||
var name = Invariant($"{symbol}: {Math.Abs(minutesAfterOpen):0.##} min {afterOrBefore} {type}");
|
||||
var exchangeHours = GetSecurityExchangeHours(symbol);
|
||||
|
||||
var timeAfterOpen = TimeSpan.FromMinutes(minutesAfterOpen);
|
||||
@@ -179,6 +192,18 @@ namespace QuantConnect.Scheduling
|
||||
return new FuncTimeRule(name, applicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies an event should fire at the market close +- <paramref name="minutesAfterClose"/>
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol whose market close we want an event for</param>
|
||||
/// <param name="minutesAfterClose">The time after market close that the event should fire</param>
|
||||
/// <param name="extendedMarketClose">True to use extended market close, false to use regular market close</param>
|
||||
/// <returns>A time rule that fires the specified number of minutes after the symbol's market close</returns>
|
||||
public ITimeRule AfterMarketClose(Symbol symbol, double minutesAfterClose = 0, bool extendedMarketClose = false)
|
||||
{
|
||||
return BeforeMarketClose(symbol, minutesAfterClose * (-1), extendedMarketClose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies an event should fire at the market close +- <paramref name="minutesBeforeClose"/>
|
||||
/// </summary>
|
||||
@@ -189,7 +214,8 @@ namespace QuantConnect.Scheduling
|
||||
public ITimeRule BeforeMarketClose(Symbol symbol, double minutesBeforeClose = 0, bool extendedMarketClose = false)
|
||||
{
|
||||
var type = extendedMarketClose ? "ExtendedMarketClose" : "MarketClose";
|
||||
var name = Invariant($"{symbol}: {minutesBeforeClose:0.##} min before {type}");
|
||||
var afterOrBefore = minutesBeforeClose > 0 ? "before" : "after";
|
||||
var name = Invariant($"{symbol}: {Math.Abs(minutesBeforeClose):0.##} min {afterOrBefore} {type}");
|
||||
var exchangeHours = GetSecurityExchangeHours(symbol);
|
||||
|
||||
var timeBeforeClose = TimeSpan.FromMinutes(minutesBeforeClose);
|
||||
|
||||
@@ -282,6 +282,16 @@ namespace QuantConnect.Securities
|
||||
return BackMonths().FrontMonth();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the reference date used for expiration filtering. By default it just returns the same date.
|
||||
/// </summary>
|
||||
/// <param name="referenceDate">The reference date to be adjusted</param>
|
||||
/// <returns>The adjusted date</returns>
|
||||
protected virtual DateTime AdjustExpirationReferenceDate(DateTime referenceDate)
|
||||
{
|
||||
return referenceDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies filter selecting options contracts based on a range of expiration dates relative to the current day
|
||||
/// </summary>
|
||||
@@ -294,19 +304,21 @@ namespace QuantConnect.Securities
|
||||
{
|
||||
if (LocalTime == default)
|
||||
{
|
||||
return (T) this;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
if (maxExpiry > Time.MaxTimeSpan) maxExpiry = Time.MaxTimeSpan;
|
||||
|
||||
var minExpiryToDate = LocalTime.Date + minExpiry;
|
||||
var maxExpiryToDate = LocalTime.Date + maxExpiry;
|
||||
var referenceDate = AdjustExpirationReferenceDate(LocalTime.Date);
|
||||
|
||||
var minExpiryToDate = referenceDate + minExpiry;
|
||||
var maxExpiryToDate = referenceDate + maxExpiry;
|
||||
|
||||
Data = Data
|
||||
.Where(symbol => symbol.ID.Date.Date >= minExpiryToDate && symbol.ID.Date.Date <= maxExpiryToDate)
|
||||
.ToList();
|
||||
|
||||
return (T) this;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1596,6 +1596,12 @@ namespace QuantConnect.Securities
|
||||
/// MSCI USA Index Futures
|
||||
/// </summary>
|
||||
public const string MSCIUsaIndex = "MXUS";
|
||||
|
||||
/// <summary>
|
||||
/// Euro Stoxx 50 Index Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string EuroStoxx50 = "FESX";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace QuantConnect.Securities.Future
|
||||
// Monthly contracts
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
|
||||
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(lastBusinessDay, -1, holidays);
|
||||
|
||||
return lastBusinessDay;
|
||||
@@ -353,6 +353,20 @@ namespace QuantConnect.Securities.Future
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// EuroStoxx50 (FESX): https://www.xetra.com/resource/blob/63488/437afcd347fb020377873dd1ceac10ba/EURO-STOXX-50-Factsheet-data.pdf
|
||||
{Symbol.Create(Futures.Indices.EuroStoxx50, SecurityType.Future, Market.EUREX), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar/3, Jun/6 , Sep/9 , Dec/12) listed for 9 consecutive quarters and 3 additional December contract months.
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading can occur up to 9:30 a.m. Eastern Time (ET) on the 3rd Friday of the contract month
|
||||
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// NASDAQ100EMini (NQ): http://www.cmegroup.com/trading/equity-index/us-index/e-mini-nasdaq-100_contract_specifications.html
|
||||
{Symbol.Create(Futures.Indices.NASDAQ100EMini, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
@@ -465,7 +479,7 @@ namespace QuantConnect.Securities.Future
|
||||
//Contracts listed for the 2 nearest serial and 4 quarterly months.
|
||||
//Trading terminates on the second to last business day of the contract month at the end of trading on the Hong Kong Exchange Securities Market
|
||||
var secondLastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time,2, holidays);
|
||||
|
||||
|
||||
while (!FuturesExpiryUtilityFunctions.NotHoliday(secondLastBusinessDay, holidays))
|
||||
{
|
||||
secondLastBusinessDay = secondLastBusinessDay.AddDays(-1);
|
||||
@@ -1143,7 +1157,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Grains.BlackSeaCornFinanciallySettledPlatts;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 15 consecutive months.
|
||||
// Trading terminates on the last business day of the contract month which is also a Platts publication date for the price assessment.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1348,7 +1362,7 @@ namespace QuantConnect.Securities.Future
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday,-2, holidays);
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(secondBusinessDayPrecedingThirdWednesday, -1, holidays);
|
||||
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14,16,0));
|
||||
})
|
||||
},
|
||||
@@ -1363,7 +1377,7 @@ namespace QuantConnect.Securities.Future
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2, holidays);
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(secondBusinessDayPrecedingThirdWednesday, -1, holidays);
|
||||
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14,16,0));
|
||||
})
|
||||
},
|
||||
@@ -1481,7 +1495,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Currencies.StandardSizeUSDOffshoreRMBCNH;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 13 consecutive months and quarterly contracts (Mar, Jun, Sep, Dec) listed for the next 8 quarters.
|
||||
// Trading terminates on the second Hong Kong business day prior to the third Wednesday of the contract month at 11:00 a.m. Hong Kong local time.
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
@@ -1500,7 +1514,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Currencies.EuroFXEmini;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1521,7 +1535,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Currencies.EURAUD;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 6 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1542,7 +1556,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Currencies.EURCAD;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 6 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1563,7 +1577,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Currencies.EURSEK;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Six months in the March quarterly cycle (Mar, Jun, Sep, Dec)
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1605,7 +1619,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.Y30TreasuryBond;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 3 quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1624,7 +1638,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.Y10TreasuryNote;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 3 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1660,7 +1674,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.Y2TreasuryNote;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 3 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1678,7 +1692,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CME;
|
||||
var symbol = Futures.Financials.EuroDollar;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 40 consecutive quarters and the nearest 4 serial contract months.
|
||||
// List a new quarterly contract for trading on the last trading day of the nearby expiry.
|
||||
|
||||
@@ -1695,7 +1709,7 @@ namespace QuantConnect.Securities.Future
|
||||
{
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.FiveYearUSDMACSwap;
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1721,7 +1735,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.UltraUSTreasuryBond;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 3 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1740,7 +1754,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.CBOT;
|
||||
var symbol = Futures.Financials.UltraTenYearUSTreasuryNote;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 3 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
@@ -1770,7 +1784,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ArgusPropaneFarEastIndexBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for three cconsecutive months
|
||||
// Trading shall cease on the last business day of the contract month. Business days are based on the Singapore Public Holiday calendar.
|
||||
// TODO: Might need singapore calendar
|
||||
@@ -1783,7 +1797,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MiniEuropeanThreePointPercentFiveFuelOilBargesPlatts;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and the next 4 calendar years.
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1798,7 +1812,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MiniSingaporeFuelOil180CstPlatts;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and the next 5 calendar years.
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
// Special case exists where the last trade occurs on US holiday, but not an exchange holiday (markets closed)
|
||||
@@ -1841,7 +1855,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.PropaneNonLDHMontBelvieuOPIS;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 48 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1864,7 +1878,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.PremiumUnleadedGasoline10ppmFOBMEDPlatts;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// 48 consecutive months
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(lastBusinessDay, -1, holidays);
|
||||
@@ -1878,7 +1892,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ArgusPropaneFarEastIndex;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 48 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1890,7 +1904,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.GasolineEurobobOxyNWEBargesArgusCrackSpreadBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for 3 consecutive months
|
||||
// Trading ceases on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1902,7 +1916,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MontBelvieuNaturalGasolineOPIS;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 56 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1914,7 +1928,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MontBelvieuNormalButaneOPISBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for the current month and the following month listed 10 business days prior to the start of the contract month
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1926,7 +1940,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ConwayPropaneOPIS;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and the next 4 calendar years.
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1938,7 +1952,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MontBelvieuLDHPropaneOPISBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for the current month and the following month listed 10 business days prior to the start of the contract month
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1950,7 +1964,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ArgusPropaneFarEastIndexVsEuropeanPropaneCIFARAArgus;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 36 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -1965,7 +1979,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ArgusPropaneSaudiAramco;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for 48 consecutive months
|
||||
// Trading shall terminate on the last business day of the month prior to the contract month.
|
||||
// Business days are based on the Singapore Public Holiday Calendar.
|
||||
@@ -1998,7 +2012,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.GroupThreeSuboctaneGasolinePlattsVsRBOB;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// 36 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2010,7 +2024,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.SingaporeFuelOil180cstPlattsBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for 3 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2022,7 +2036,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.SingaporeFuelOil380cstPlattsBALMO;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly BALMO contracts listed for 3 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2034,7 +2048,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MontBelvieuEthaneOPIS;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and the next 4 calendar years.
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2046,7 +2060,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.MontBelvieuNormalButaneOPIS;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and next 4 calendar years.
|
||||
// Trading shall cease on the last business day of the contract month.
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2071,7 +2085,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.ArgusLLSvsWTIArgusTradeMonth;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Trading shall cease at the close of trading on the last business day that falls on or before the 25th calendar day of the month prior to the contract month. If the 25th calendar day is a weekend or holiday, trading shall cease on the first business day prior to the 25th calendar day.
|
||||
var previousMonth = time.AddMonths(-1);
|
||||
var twentyFifthDay = new DateTime(previousMonth.Year, previousMonth.Month, 25);
|
||||
@@ -2089,7 +2103,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.SingaporeGasoilPlattsVsLowSulphurGasoilFutures;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// Monthly contracts listed for the current year and the next 2 calendar years.
|
||||
// Trading ceases on the last business day of the contract month
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2104,7 +2118,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.NYMEX;
|
||||
var symbol = Futures.Energy.LosAngelesCARBOBGasolineOPISvsRBOBGasoline;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// 36 consecutive months
|
||||
// Trading shall cease on the last business day of the contract month
|
||||
return FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays);
|
||||
@@ -2321,7 +2335,7 @@ namespace QuantConnect.Securities.Future
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(twoMonthsPriorToContractMonth, 1, holidays);
|
||||
}
|
||||
|
||||
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(lastBusinessDay, -1, holidays);
|
||||
|
||||
return lastBusinessDay;
|
||||
@@ -2877,7 +2891,7 @@ namespace QuantConnect.Securities.Future
|
||||
var market = Market.ICE;
|
||||
var symbol = Futures.Softs.OrangeJuice;
|
||||
var holidays = FuturesExpiryUtilityFunctions.GetHolidays(market, symbol);
|
||||
|
||||
|
||||
// January, March, May, July, September, November.
|
||||
while (!FutureExpirationCycles.FHKNUX.Contains(time.Month))
|
||||
{
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace QuantConnect.Securities
|
||||
/// </summary>
|
||||
public class OptionFilterUniverse : ContractSecurityFilterUniverse<OptionFilterUniverse, OptionUniverse>
|
||||
{
|
||||
private Option.Option _option;
|
||||
|
||||
// Fields used in relative strikes filter
|
||||
private List<decimal> _uniqueStrikes;
|
||||
private bool _refreshUniqueStrikes;
|
||||
@@ -59,6 +61,7 @@ namespace QuantConnect.Securities
|
||||
/// <param name="option">The canonical option chain security</param>
|
||||
public OptionFilterUniverse(Option.Option option)
|
||||
{
|
||||
_option = option;
|
||||
_underlyingScaleFactor = option.SymbolProperties.StrikeMultiplier;
|
||||
}
|
||||
|
||||
@@ -66,9 +69,10 @@ namespace QuantConnect.Securities
|
||||
/// Constructs OptionFilterUniverse
|
||||
/// </summary>
|
||||
/// <remarks>Used for testing only</remarks>
|
||||
public OptionFilterUniverse(IEnumerable<OptionUniverse> allData, BaseData underlying, decimal underlyingScaleFactor = 1)
|
||||
public OptionFilterUniverse(Option.Option option, IEnumerable<OptionUniverse> allData, BaseData underlying, decimal underlyingScaleFactor = 1)
|
||||
: base(allData, underlying.EndTime)
|
||||
{
|
||||
_option = option;
|
||||
UnderlyingInternal = underlying;
|
||||
_refreshUniqueStrikes = true;
|
||||
_underlyingScaleFactor = underlyingScaleFactor;
|
||||
@@ -128,6 +132,24 @@ namespace QuantConnect.Securities
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the date to the next trading day if the current date is not a trading day, so that expiration filter is properly applied.
|
||||
/// e.g. Selection for Mondays happen on Friday midnight (Saturday start), so if the minimum time to expiration is, say 0,
|
||||
/// contracts expiring on Monday would be filtered out if the date is not properly adjusted to the next trading day (Monday).
|
||||
/// </summary>
|
||||
/// <param name="referenceDate">The date to be adjusted</param>
|
||||
/// <returns>The adjusted date</returns>
|
||||
protected override DateTime AdjustExpirationReferenceDate(DateTime referenceDate)
|
||||
{
|
||||
// Check whether the reference time is a tradable date:
|
||||
if (!_option.Exchange.Hours.IsDateOpen(referenceDate))
|
||||
{
|
||||
referenceDate = _option.Exchange.Hours.GetNextTradingDay(referenceDate);
|
||||
}
|
||||
|
||||
return referenceDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies filter selecting options contracts based on a range of strikes in relative terms
|
||||
/// </summary>
|
||||
|
||||
@@ -117,12 +117,19 @@ namespace QuantConnect.Securities
|
||||
|
||||
Cash baseCash = null;
|
||||
// we skip cfd because we don't need to add the base cash
|
||||
if (symbol.SecurityType != SecurityType.Cfd && CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrencySymbol, out _))
|
||||
if (symbol.SecurityType != SecurityType.Cfd)
|
||||
{
|
||||
if (!_cashBook.TryGetValue(baseCurrencySymbol, out baseCash))
|
||||
if (CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrencySymbol, out _))
|
||||
{
|
||||
// since we have none it's safe to say the conversion is zero
|
||||
baseCash = _cashBook.Add(baseCurrencySymbol, 0, 0);
|
||||
if (!_cashBook.TryGetValue(baseCurrencySymbol, out baseCash))
|
||||
{
|
||||
// since we have none it's safe to say the conversion is zero
|
||||
baseCash = _cashBook.Add(baseCurrencySymbol, 0, 0);
|
||||
}
|
||||
}
|
||||
else if (CurrencyPairUtil.IsValidSecurityType(symbol.SecurityType, false))
|
||||
{
|
||||
throw new ArgumentException($"Failed to resolve base currency for '{symbol.ID.Symbol}', it might be missing from the Symbol database or market '{symbol.ID.Market}' could be wrong");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ using QuantConnect.Interfaces;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Securities.Future;
|
||||
using QuantConnect.Util;
|
||||
using static QuantConnect.Messages;
|
||||
|
||||
namespace QuantConnect
|
||||
{
|
||||
@@ -220,8 +221,8 @@ namespace QuantConnect
|
||||
public SecurityType SecurityType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the option strike price. This only applies to SecurityType.Option
|
||||
/// and will thrown anexception if accessed otherwise.
|
||||
/// Gets the option strike price. This only applies if SecurityType is Option,
|
||||
/// IndexOption or FutureOption and will thrown anexception if accessed otherwise.
|
||||
/// </summary>
|
||||
public decimal StrikePrice
|
||||
{
|
||||
@@ -258,8 +259,8 @@ namespace QuantConnect
|
||||
|
||||
/// <summary>
|
||||
/// Gets the option type component of this security identifier. This
|
||||
/// only applies to SecurityType.Open and will throw an exception if
|
||||
/// accessed otherwise.
|
||||
/// only applies if SecurityType is Option, IndexOption or FutureOption
|
||||
/// and will throw an exception if accessed otherwise.
|
||||
/// </summary>
|
||||
public OptionRight OptionRight
|
||||
{
|
||||
@@ -281,8 +282,8 @@ namespace QuantConnect
|
||||
|
||||
/// <summary>
|
||||
/// Gets the option style component of this security identifier. This
|
||||
/// only applies to SecurityType.Open and will throw an exception if
|
||||
/// accessed otherwise.
|
||||
/// only applies if SecurityType is Option, IndexOption or FutureOption
|
||||
/// and will throw an exception if accessed otherwise.
|
||||
/// </summary>
|
||||
public OptionStyle OptionStyle
|
||||
{
|
||||
@@ -843,6 +844,20 @@ namespace QuantConnect
|
||||
|
||||
private static readonly char[] SplitSpace = {' '};
|
||||
|
||||
private static void CacheSid(string key, SecurityIdentifier identifier)
|
||||
{
|
||||
lock (SecurityIdentifierCache)
|
||||
{
|
||||
// limit the cache size to help with memory usage
|
||||
if (SecurityIdentifierCache.Count >= 600000)
|
||||
{
|
||||
SecurityIdentifierCache.Clear();
|
||||
}
|
||||
|
||||
SecurityIdentifierCache[key] = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string into its component ulong pieces
|
||||
/// </summary>
|
||||
@@ -867,7 +882,8 @@ namespace QuantConnect
|
||||
if (string.IsNullOrWhiteSpace(value) || value == " 0")
|
||||
{
|
||||
// we know it's not null already let's cache it
|
||||
SecurityIdentifierCache[value] = identifier = Empty;
|
||||
identifier = Empty;
|
||||
CacheSid(value, identifier);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -900,7 +916,7 @@ namespace QuantConnect
|
||||
GetMarketIdentifier(identifier.Market);
|
||||
|
||||
var key = i < sids.Length - 1 ? $"{current}|{sids[i + 1]}" : current;
|
||||
SecurityIdentifierCache[key] = identifier;
|
||||
CacheSid(key, identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -914,7 +930,7 @@ namespace QuantConnect
|
||||
exception = error;
|
||||
Log.Error($@"SecurityIdentifier.TryParseProperties(): {
|
||||
Messages.SecurityIdentifier.ErrorParsingSecurityIdentifier(value, exception)}");
|
||||
SecurityIdentifierCache[value] = null;
|
||||
CacheSid(value, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user