Compare commits

..

22 Commits
16607 ... 16632

Author SHA1 Message Date
Martin-Molinero
6351773a01 Option universe resolution improvements (#8324)
* Option universe improvements

- Improvement for resolution handling of option universes, affecting
  performance in live mode. Adding regression algorithm

* Minor fix for research test
2024-09-16 10:22:58 -03:00
Jhonathan Abreu
bf4b08e202 Fix: adjust option expiry reference date (#8322)
* Fix: adjust option expiry reference date

* Add universe files

* Update data and other minor changes

* Minor changes

* Add regression algorithm summary
2024-09-13 18:22:26 -04:00
Jhonathan Abreu
fa9ff399cc Euro Stoxx 50 Index and Index Futures support (#8278)
* EUREX data

EUREX data model and sample data

* Add EUREX futures expiry function and sample algorithms

* Add EuroStoxx50 futures map and factor files

* Reduce eurex data for repo

* Map eurex market to primary exchange

* Update Euro Stoxx 50 (FESX) map and factol files

* Update Euro Stoxx 50 (FESX) minute data

* Added EURSD data

* Added 2 basic FESX futures algorithms in CSharp and Python (#2)

* Add regression algorithms

* Update regression algorithms and data

* Minor change

* Cleanup

---------

Co-authored-by: paulius-an <118921953+paulius-an@users.noreply.github.com>
2024-09-12 10:00:16 -04:00
Jhonathan Abreu
16c4259342 Add QCAlgorithm.OptionChain() method to fetch option chains (#8316)
* Add new QCAlgorithm.OptionChain method to get full data option chain

* Add extension method to get canonical symbol

* Support future options in new OptionChain method

* Replace option chain provider with OptionChain method in some regression algorithms

* Add new regression algorithms for OptionChain method

* Replace option chain provider with OptionChain method in some regression algorithms

* Minor

* Cleanup

* Minor changes in regression algorithms

* Minor adjustments
2024-09-11 15:15:51 -04:00
Ricardo Andrés Marino Rojas
724d0b06a5 Add extra argument in QuantBook.UniverseHistory() for using an IDateRule (#8301)
* First draft of the solution

* Handle end date better

* Improve unit tests

* Add extra argument in missing method

* Nit change

* Nit change

* Nit change

* Undo changes to C# generic UniverseHistory()

* Address suggestions

* Improve unit test

* Add null checks

* Minor adjustment

---------

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2024-09-11 13:41:57 -03:00
Ricardo Andrés Marino Rojas
ba7fe05574 Add BeforeMarketOpen() and AfterMarketClose() date rules (#8311)
* First draft of the solution

* Add unit tests

* Improve unit tests

* Nit changes
2024-09-10 15:56:31 -03:00
Ricardo Andrés Marino Rojas
50437946e2 Add missing end_date in python regression algorithm (#8315) 2024-09-10 14:33:44 -03:00
Ricardo Andrés Marino Rojas
418970bb48 Add TravisExclude category to failing CI unit test (#8313) 2024-09-10 13:25:41 -03:00
Roman Yavnikov
bef045a360 Fix: Get GroupOrders ByBrokerageId (#8310)
* fix: Get GroupOrders ByBrokerageId

* test:feat: return all combo orders with the same brokerageId
2024-09-10 13:25:22 -03:00
Jhonathan Abreu
49bf436aa2 Remove Lean path info from runtime exceptions (#8309)
* Strip lean path info from runtime exceptions

* Minor fix
2024-09-09 17:54:29 -04:00
Jhonathan Abreu
e29bb2c5e0 File-based options universe (#8212)
* Initial options universe with greeks implementation

* Options universe improvements

* Address peer review

* File based options universe fixes and improvements.

- Adjust OptionUniverse start-end times and period.
- Adapt unit tests and some algorithms to pass with new options universe selection.

* Updated options regression algorithms stats for new universe data

* Updated options regression algorithms stats for new universe data

* Updated options regression algorithms stats for new universe data

* Updated options regression algorithms stats for new universe data

* Updated options regression algorithms stats for new universe data

* Option chain provider with new options universe

* Allow canonical option history requests

* Address peer review

* Address peer review

* Fix symbols parsing in OptionUniverse

* Fix universe selection subscriptions start time to not include extended market hours

* Minor changes

* Minor changes

* Peer recommended changes and fixes

* Update regression algorithm stats

* Update regression algorithms stats and minor fixes

* Fix option chain provider history request

* Round option indicators values

* Added option universe csv header property

* Update regression algorithms stats

* Update regression algorithms stats

* Data fixes and regression algos stats update

* Unit test fixes

* Minor changes

* Option chain handling in live trading data feed

* Minor changes

* Added processed data provider

* Fix thread-safety violation in Slice class

* Minor change

* Update options filter universe API to use OptionUniverse data

Add new filter methods for greeks, IV and open interest

* Option filter universe api updates

* Add OptionUniverse history regression algorithms

* Add regression algorithms for new options filter universe api methods

* Added options greeks data and updated regression algorithms

* Address peer review

* Address peer review

* Add more assertions to new options filter api regression algorithms

* Minor performance improvement.

Reduce greeks binomial model steps to 140

* Minor tests updates

* Greeks numerical models performance improvements

* Greeks numerical models performance improvements

* Revert array pool change for option pricing numerical models

* Update default dividend yield provider depending on option type

* [TEST]

* Add helper method con calculate time till expiration

* Use double in price option numerical models

* Implied volatility calculation improvements

- Adjust root finding method accuracy as a factor of the option price
- Use BSM to get a first guess

* Cleanup

* Some regression algorithms and unit tests cleanup

* Regression tests updates after rebasing from master

* Add universe files

* Self review and cleanup

* Minor regression tests updates after rebase

* Fix: set data time zone to same as exchange tz for options universes

* Minor change

* Minor change

* Fix for live trading options universe selection

* Keep underlying when aggregating collections in BaseDataCollectionAggregatorEnumerator

* Update index options regression algorithms stats

* Minor change

* Address peer review

* Memory usage improvements

* Minor build fix

* Minor changes and test fixes

* Cache symbols in OptionUniverse

* Cleanup

* Fix index option creation in OptionUniverse

* Use cached underlying SID when parsing from string

* Abstract symbols cache to BaseDataCollection

* Return actual underlying symbol when mapping decomposing ICO ticker

* Address peer review

* Minor performance improvements reduce garbage

* Limit Symbols and SIDs cache size to help with memory usage

* Minor fix in symbols and sid cache cleanup

* Build fix

* Lazily parse greeks on individual access

* Cleanup and tests

* Address peer review

* Minor greeks fix

---------

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2024-09-09 12:39:31 -03:00
Dennis Yemelyanov
8fcc9f7d4e fix some comments for option security types (#8304) 2024-09-09 11:37:26 -03:00
Andy Geach
5209332074 Update readme.md to remove references to Visual Studio for Mac, which has been discontinued (#8298) 2024-09-06 11:59:28 -03:00
Roman Yavnikov
adfad475cc Feature:TradeStationBrokerage: support ComboMarket && ComboLimit (#8290)
* refactor: adding OrderId in GroupOrderManger

* feat: new support of OrderTypes in TradeStationBrokerageModel
feat: unSupported OrderTypes in CanUpdateOrder's TradeStationBrokerageModel

* feat: AllOrNone property in TradeStationOrderProperties

* feat: unsupported SubmitCrossZero of Combo Order in TSBrokerageModel

* test:feat: submit / update CrossZero Combo Orders

* feat: new Message Brokerage error message
refactor: use new Message in TradeStationBrokerageModel

* feat: setter of Id in GroupOrderManager

* feat: new constructor of GroupOrderManager

* feat: develop GroupOrderCacheManager service

* fix: groupOrderManger.Id in OrderProvider

* fix: incrementOrderGroupOrderManagerID in BrokerageTransactionHandler
feat: add _groupOrderManagerId in OrderProvider

* remove: extra semicolon

* refactor: prevent increment GroupOrderID

* feat: add new Exchanges

* feat: Try Get Group Combo Orders extension

* refactor: ComboORderType in TSBrokerageModel

* remove: implementing of prop ID in GroupOrderManager

* refactor: UnsupportedCrossZeroByOrderType message

* fix: warning of UnsupportedCrossZeroByOrderType

* fix: several exchanges code based on tradier docs
https://documentation.tradier.com/brokerage-api/reference/exchanges

* feat: add missed Exchange in Global class

* refactor: possible update LimitPrice in TSBrokerageModel
test:feat: validate upddate LimitPrice of ComboLimit Order

* refactor: GroupOrderCacheManager
remove: TryGetGroupCachedOrders from extension

* refactor: exchange SPHR to MIAX_SAPPHIRE

* remove: Exchange BYX cuz It is BATS_Y

* refactor: change position of Exchange C2

* refactor: change constructor's access modifier in class Exchange
2024-09-06 10:31:14 -03:00
Martin-Molinero
21fcadf0f8 Ignore failing live future option unit test (#8300) 2024-09-05 16:42:42 -03:00
Ricardo Andrés Marino Rojas
e893e67e9b Throw exception when base currency not found (#8289)
* First draft of the solution

* The same check for crytpo is done before

* Fix failing unit tests

* Remove non perpetual crypto futures from SPDB

* Address requests

* Fix failing unit tests
2024-09-04 12:14:53 -03:00
Alexandre Catarino
3b588d04fb Adds Overload to AddIndexOption (#8291)
* Adds Overload to AddIndexOption

Simplify usage. We don't need to create/add the underlying explicitly, see AddIndexOption(string, Resolution, string, bool) overload.

* Addresses Peer-Review

- Default market is `null` instead of `Market.USA` allowing for `BrokerageModel` setup.
- `AddIndexOption` and `AddIndexOptionContract` methods now return `indexOption` objects.

* Fixes Logic Bug
2024-09-04 10:30:05 -03:00
Ricardo Andrés Marino Rojas
e81bcbb987 Improve PythonIndicator.IsReady implementation (#8287)
* First draft of the solution

* nit change

* Add improvements

* Fix failing unit tests

* Add improvements
2024-09-04 10:07:13 -03:00
Roman Yavnikov
96e91b446d Fix: #8226, update spdb binance/binanceus (#8293)
* feature: update binance spdb data

* feature: update binanceus spdb data

* test:fix: EURUSDC stable coins conversation
2024-09-04 10:05:07 -03:00
Ricardo Andrés Marino Rojas
403f0348bd Update Bybit SPDB (#8294)
* Update Bybit SPDB

* Remove trailing comma
2024-09-03 17:55:12 -03:00
Ricardo Andrés Marino Rojas
eeeb310438 Update minimum price variation of some entries of CME futures in SPDB (#8288)
* Update some entries of CME futures in SPDB

* Adjust 6B minimum price variation
2024-08-28 19:59:40 -03:00
oussamanahdi
271f0bb08e Update readme.md (#8286) 2024-08-26 19:11:04 -03:00
143 changed files with 4706 additions and 914 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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)

View 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"}
};
}
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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[]

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View 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"}
};
}
}

View File

@@ -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

View File

@@ -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)

View 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"}
};
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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");
}
}

View File

@@ -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)

View 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"}
};
}
}

View File

@@ -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

View File

@@ -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

View 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}")

View File

@@ -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

View File

@@ -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
]

View File

@@ -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))

View File

@@ -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

View File

@@ -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]

View File

@@ -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))

View File

@@ -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.

View File

@@ -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
)

View File

@@ -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

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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]}")

View File

@@ -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}")

View File

@@ -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")

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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()

View File

@@ -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);

View File

@@ -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>

View File

@@ -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;
}
}
}

View 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;
}
}

View File

@@ -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)
{
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -86,7 +86,7 @@ namespace QuantConnect.Exceptions
public SanitizedException(string message, string stackTrace)
{
_message = message;
_stackTrace = Extensions.ClearLeanPaths(stackTrace);
_stackTrace = Logging.Log.ClearLeanPaths(stackTrace);
}
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View 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 _);
}
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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; }
}
}

View File

@@ -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"/>

View File

@@ -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);

View File

@@ -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>

View File

@@ -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>

View File

@@ -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))
{

View File

@@ -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>

View File

@@ -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");
}
}

View File

@@ -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