Compare commits

..

64 Commits
16632 ... 16583

Author SHA1 Message Date
Jhonathan Abreu
201aa70c87 Minor change 2024-08-21 17:27:44 -04:00
Jhonathan Abreu
8633714bd0 Update index options regression algorithms stats 2024-08-21 17:19:00 -04:00
Jhonathan Abreu
baaef45704 Keep underlying when aggregating collections in BaseDataCollectionAggregatorEnumerator 2024-08-21 17:16:16 -04:00
Jhonathan Abreu
a229df86b7 Fix for live trading options universe selection 2024-08-21 16:43:14 -04:00
Jhonathan Abreu
385330a476 Minor change 2024-08-20 17:46:29 -04:00
Jhonathan Abreu
6d272c3481 Minor change 2024-08-20 17:33:17 -04:00
Jhonathan Abreu
25a49f48f5 Fix: set data time zone to same as exchange tz for options universes 2024-08-20 16:43:06 -04:00
Jhonathan Abreu
3f716ba61a Minor regression tests updates after rebase 2024-08-19 11:31:55 -04:00
Jhonathan Abreu
d5069b5b46 Self review and cleanup 2024-08-19 09:58:43 -04:00
Jhonathan Abreu
ff1f4d6cfa Add universe files 2024-08-19 09:58:43 -04:00
Jhonathan Abreu
f4e826fb59 Regression tests updates after rebasing from master 2024-08-19 09:58:43 -04:00
Jhonathan Abreu
f996df7348 Some regression algorithms and unit tests cleanup 2024-08-19 09:58:41 -04:00
Jhonathan Abreu
cece3b50eb Cleanup 2024-08-19 09:58:32 -04:00
Jhonathan Abreu
b109427542 Implied volatility calculation improvements
- Adjust root finding method accuracy as a factor of the option price
- Use BSM to get a first guess
2024-08-19 09:58:32 -04:00
Jhonathan Abreu
e70df95b7c Use double in price option numerical models 2024-08-19 09:58:32 -04:00
Jhonathan Abreu
8102dc3de3 Add helper method con calculate time till expiration 2024-08-19 09:58:32 -04:00
Jhonathan Abreu
aed5a4cea0 [TEST] 2024-08-19 09:58:32 -04:00
Jhonathan Abreu
887822f76f Update default dividend yield provider depending on option type 2024-08-19 09:58:29 -04:00
Jhonathan Abreu
9658286820 Revert array pool change for option pricing numerical models 2024-08-19 09:58:05 -04:00
Jhonathan Abreu
a311732f47 Greeks numerical models performance improvements 2024-08-19 09:58:05 -04:00
Jhonathan Abreu
e30800357d Greeks numerical models performance improvements 2024-08-19 09:58:05 -04:00
Jhonathan Abreu
ad3c2aada7 Minor tests updates 2024-08-19 09:58:02 -04:00
Jhonathan Abreu
6b425ead2c Minor performance improvement.
Reduce greeks binomial model steps to 140
2024-08-19 09:56:40 -04:00
Jhonathan Abreu
27ab68100d Add more assertions to new options filter api regression algorithms 2024-08-19 09:56:40 -04:00
Jhonathan Abreu
f930c11a93 Address peer review 2024-08-19 09:56:40 -04:00
Jhonathan Abreu
a17be3ccba Address peer review 2024-08-19 09:56:40 -04:00
Jhonathan Abreu
b81c13ac4a Added options greeks data and updated regression algorithms 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
1e6713ccf0 Add regression algorithms for new options filter universe api methods 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
e149940fb9 Add OptionUniverse history regression algorithms 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
f0908e3e75 Option filter universe api updates 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
c9638871ad Update options filter universe API to use OptionUniverse data
Add new filter methods for greeks, IV and open interest
2024-08-19 09:56:39 -04:00
Jhonathan Abreu
07a90c7455 Minor change 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
69963d493a Fix thread-safety violation in Slice class 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
32bd8c248f Added processed data provider 2024-08-19 09:56:39 -04:00
Jhonathan Abreu
64f1b87b01 Minor changes 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
1ad5916968 Option chain handling in live trading data feed 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
128adba745 Minor changes 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
02e88c0370 Unit test fixes 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
5e0f1b3742 Data fixes and regression algos stats update 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
0fdd3759b2 Update regression algorithms stats 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
bdb5ec9521 Update regression algorithms stats 2024-08-19 09:56:38 -04:00
Jhonathan Abreu
993c3b036d Added option universe csv header property 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
29968de84c Round option indicators values 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
3a9303ecc6 Fix option chain provider history request 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
78a381c569 Update regression algorithms stats and minor fixes 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
dfd4f54e3f Update regression algorithm stats 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
b9f4cb87e3 Peer recommended changes and fixes 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
aad49fecbf Minor changes 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
47eedf6ccd Minor changes 2024-08-19 09:56:37 -04:00
Jhonathan Abreu
3ef8e0d16f Fix universe selection subscriptions start time to not include extended market hours 2024-08-19 09:56:36 -04:00
Jhonathan Abreu
e58dab6c5e Fix symbols parsing in OptionUniverse 2024-08-19 09:56:36 -04:00
Jhonathan Abreu
39496b10a4 Address peer review 2024-08-19 09:56:36 -04:00
Jhonathan Abreu
afa3c23450 Address peer review 2024-08-19 09:56:36 -04:00
Jhonathan Abreu
5fff7cc399 Allow canonical option history requests 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
388dd25827 Option chain provider with new options universe 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
4ec92a93a4 Updated options regression algorithms stats for new universe data 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
96ccc567f7 Updated options regression algorithms stats for new universe data 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
068e09e897 Updated options regression algorithms stats for new universe data 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
4edf4e2e63 Updated options regression algorithms stats for new universe data 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
078a977eb6 Updated options regression algorithms stats for new universe data 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
bd5f4e47be 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.
2024-08-19 09:56:35 -04:00
Jhonathan Abreu
457b8f3839 Address peer review 2024-08-19 09:56:35 -04:00
Jhonathan Abreu
10d467fb72 Options universe improvements 2024-08-19 09:56:34 -04:00
Jhonathan Abreu
5d63f27688 Initial options universe with greeks implementation 2024-08-19 09:56:34 -04:00
183 changed files with 1941 additions and 6901 deletions

View File

@@ -40,8 +40,8 @@ namespace QuantConnect.Algorithm.CSharp
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
_contract = OptionChain(aapl)
.OrderBy(x => x.ID.Symbol)
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
.OrderBy(symbol => symbol.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 = OptionChain(aapl)
.OrderBy(x => x.ID.Symbol)
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
.OrderBy(symbol => symbol.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 = OptionChain(contract.Symbol).ToList();
var option_contract_symbols = OptionChainProvider.GetOptionContractList(contract.Symbol, Time).ToList();
if(option_contract_symbols.Count == 0)
{
continue;

View File

@@ -46,8 +46,8 @@ namespace QuantConnect.Algorithm.CSharp
{
if (_option == null)
{
var option = OptionChain(_twx)
.OrderBy(x => x.ID.Symbol)
var option = OptionChainProvider.GetOptionContractList(_twx, Time)
.OrderBy(symbol => symbol.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 QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.CSharp
{
@@ -110,14 +110,14 @@ namespace QuantConnect.Algorithm.CSharp
foreach (var addedSecurity in changes.AddedSecurities)
{
var option = OptionChain(addedSecurity.Symbol)
.OrderBy(contractData => contractData.ID.Symbol)
var option = OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, Time)
.OrderBy(symbol => symbol.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.Symbol, option.Underlying.Symbol })
foreach (var symbol in new[] { option, option.Underlying })
{
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 = OptionChain(aapl)
.OrderBy(x => x.ID.StrikePrice)
_contract = OptionChainProvider.GetOptionContractList(aapl, Time)
.OrderBy(symbol => symbol.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 = OptionChain(aapl)
.OrderBy(x => x.ID.StrikePrice)
var contracts = OptionChainProvider.GetOptionContractList(aapl, Time)
.OrderBy(symbol => symbol.ID.StrikePrice)
.Where(optionContract => optionContract.ID.OptionRight == OptionRight.Call
&& optionContract.ID.OptionStyle == OptionStyle.American)
.Take(2)

View File

@@ -1,239 +0,0 @@
/*
* 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,12 +43,14 @@ 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

@@ -53,6 +53,7 @@ namespace QuantConnect.Algorithm.CSharp
// - 6th: AAPL is selected, TWX is removed but subscriptions are not removed because the securities are invested.
// - TWX and its options are liquidated.
// - 7th: Since options universe selection is daily now, TWX subscriptions are removed the next day (7th)
//SetEndDate(2014, 06, 06);
SetEndDate(2014, 06, 07);
var selectionUniverse = AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },

View File

@@ -37,9 +37,9 @@ namespace QuantConnect.Algorithm.CSharp
var equity = AddEquity("GOOG");
_optionSymbol = OptionChain(equity.Symbol)
.OrderBy(x => x.ID.StrikePrice)
.ThenByDescending(x => x.ID.Date)
_optionSymbol = OptionChainProvider.GetOptionContractList(equity.Symbol, Time)
.OrderBy(symbol => symbol.ID.StrikePrice)
.ThenByDescending(symbol => symbol.ID.Date)
.First(optionContract => optionContract.ID.OptionRight == OptionRight.Call);
var option = AddOptionContract(_optionSymbol);

View File

@@ -51,7 +51,10 @@ namespace QuantConnect.Algorithm.CSharp
if (_addOption)
{
var contracts = OptionChain(_spx).Where(x => x.ID.OptionRight == OptionRight.Put && x.ID.Date.Date == new DateTime(2021, 1, 15));
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 option = AddIndexOptionContract(contracts.First(), Resolution.Minute);
_optionExpiry = option.Expiry;

View File

@@ -52,7 +52,7 @@ namespace QuantConnect.Algorithm.CSharp
var _twxOption = AddOption("TWX", Resolution.Minute);
_exchange = _twxOption.Exchange;
_twxOption.SetFilter((x) => x
.Contracts(c => c.Where(s => _contracts.Contains(s.Symbol.Value))));
.Contracts(c => c.Select(x => x.Symbol).Where(s => _contracts.Contains(s.Value))));
SetBenchmark(t => 1);
}

View File

@@ -136,10 +136,10 @@ namespace QuantConnect.Algorithm.CSharp
{"Tracking Error", "0.456"},
{"Treynor Ratio", "0"},
{"Total Fees", "$2650.41"},
{"Estimated Strategy Capacity", "$29000.00"},
{"Estimated Strategy Capacity", "$30000.00"},
{"Lowest Capacity Asset", "BTCUSD 2XR"},
{"Portfolio Turnover", "46.79%"},
{"OrderListHash", "70610cb67cc63d197e22ca71180b2df2"}
{"OrderListHash", "864a3590199bfde14bed81bfbb8fcf70"}
};
}
}

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 = OptionChain(es20m20)
.Concat(OptionChain(es20h20))
.Where(contractData => contractData.ID.StrikePrice == 3200m && contractData.ID.OptionRight == OptionRight.Call)
.Select(contractData => AddFutureOptionContract(contractData, Resolution.Minute).Symbol)
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)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20.Symbol)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20.Symbol, new DateTime(2020, 1, 5))
.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(OptionChain(_es19m20)
.Where(contractData => contractData.ID.StrikePrice >= 3300m && contractData.ID.OptionRight == OptionRight.Call)
.OrderBy(contractData => contractData.ID.StrikePrice)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
.OrderBy(x => x.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 = OptionChain(dc)
DcOption = OptionChainProvider.GetOptionContractList(dc, Time)
.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(OptionChain(underlying)
var option = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(underlying, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.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 = OptionChain(spx)
var spxOptions = OptionChainProvider.GetOptionContractList(spx, Time)
.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(OptionChain(_spx)
_spxOption = 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)
.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(OptionChain(_spx)
_spxOption = 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)
.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(OptionChain(_spx)
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
.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(OptionChain(_spx)
_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)
.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(OptionChain(_spx)
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
.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(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)
_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)
.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(OptionChain(_spx)
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
.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(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)
_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)
.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(OptionChain(_spx)
_spxOption = AddIndexOptionContract(OptionChainProvider.GetOptionContractList(_spx, Time)
.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

@@ -105,7 +105,7 @@ namespace QuantConnect.Algorithm.CSharp
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public int AlgorithmHistoryDataPoints => 69;
public int AlgorithmHistoryDataPoints => 70;
/// <summary>
/// Final status of the algorithm

View File

@@ -128,6 +128,7 @@ namespace QuantConnect.Algorithm.CSharp
if (_futureContract == null)
{
_futureContract = futureChain.TradeBars.Values.FirstOrDefault().Symbol;
return;
}
if (futureChain.TradeBars.TryGetValue(_futureContract, out var value))
@@ -152,24 +153,23 @@ namespace QuantConnect.Algorithm.CSharp
throw new RegressionTestException("At least one trade bar should have been found, but none was found");
}
var backtestDays = (EndDate - StartDate).Days;
var futureIndicator = new Identity("");
var futureVolumeHistory = IndicatorHistory(futureIndicator, _futureContract, backtestDays, Resolution.Daily, Field.Volume);
var futureVolumeHistory = IndicatorHistory(futureIndicator, _futureContract, 200, Resolution.Daily, Field.Volume);
if (Math.Abs(futureVolumeHistory.Current.Select(x => x.Value).Where(x => x != 0).Average() - _futurePoints.Average()) > 0.001m)
{
throw new RegressionTestException($"No history indicator future data point was found using Field.Volume selector for {_futureContract}!");
throw new Exception("No history indicator future data point was found using Field.Volume selector!");
}
var volumeHistory = IndicatorHistory(_tradebarIndicatorHistory, _aapl, 109, Resolution.Daily, Field.Volume);
if (Math.Abs(volumeHistory.Current.Select(x => x.Value).Average() - _aaplPoints.Average()) > 0.001m)
{
throw new RegressionTestException($"No history indicator data point was found using Field.Volume selector for {_aapl}!");
throw new Exception("No history indicator data point was found using Field.Volume selector!");
}
var bidCloseHistory = IndicatorHistory(_quotebarIndicatorHistory, _eurusd, 132, Resolution.Daily, Field.BidClose);
if (Math.Abs(bidCloseHistory.Current.Select(x => x.Value).Average() - _eurusdPoints.Average()) > 0.001m)
{
throw new RegressionTestException($"No history indicator data point was found using Field.BidClose selector for {_eurusd}!");
throw new Exception("No history indicator data point was found using Field.BidClose selector!");
}
}
@@ -191,7 +191,7 @@ namespace QuantConnect.Algorithm.CSharp
/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public int AlgorithmHistoryDataPoints => 351;
public int AlgorithmHistoryDataPoints => 354;
/// <summary>
/// Final status of the algorithm

View File

@@ -44,7 +44,7 @@ namespace QuantConnect.Algorithm.CSharp
_stock = AddEquity("GOOG").Symbol;
var contracts = OptionChain(_stock).ToList();
var contracts = OptionChainProvider.GetOptionContractList(_stock, UtcTime).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 => 7;
public int AlgorithmHistoryDataPoints => 6;
/// <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 = OptionChain(Stock.Symbol).ToList();
var contracts = OptionChainProvider.GetOptionContractList(Stock.Symbol, UtcTime).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 = OptionChain(_goog.Symbol).ToList();
var contracts = OptionChainProvider.GetOptionContractList(_goog.Symbol, UtcTime).ToList();
_googCall600Symbol = contracts
.Where(c => c.ID.OptionRight == OptionRight.Call)

View File

@@ -1,127 +0,0 @@
/*
* 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

@@ -96,7 +96,7 @@ namespace QuantConnect.Algorithm.CSharp
protected override OptionFilterUniverse Filter(OptionFilterUniverse filter)
{
return filter.BackMonth().Contracts(contracts => contracts.Take(15));
return filter.BackMonth().Contracts(filter.Take(15).Select(x => x.Symbol));
}
}

View File

@@ -63,7 +63,7 @@ namespace QuantConnect.Algorithm.CSharp
}
return universe.IncludeWeeklys()
.BackMonth() // back month so that they don't get removed because of being delisted
.Contracts(contracts => contracts.Take(5));
.Contracts(universe.Take(5).Select(x => x.Symbol));
});
}

View File

@@ -95,7 +95,7 @@ namespace QuantConnect.Algorithm.CSharp
/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public long DataPoints => 19701;
public long DataPoints => 19700;
/// <summary>
/// Data Points count of the algorithm history

View File

@@ -44,11 +44,11 @@ namespace QuantConnect.Algorithm.CSharp
var option = AddOption("AAPL", Resolution.Minute);
_option = option.Symbol;
option.SetFilter(universe => from contract in universe
option.SetFilter(universe => from symbol in universe
.WeeklysOnly()
.Strikes(-5, +5)
.Expiration(TimeSpan.Zero, TimeSpan.FromDays(29))
select contract.Symbol);
select symbol.Symbol);
}
public override void OnOrderEvent(OrderEvent orderEvent)

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(OptionChain(_es19m20)
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
.OrderBy(x => x.ID.StrikePrice)
.Take(1)

View File

@@ -1,137 +0,0 @@
/*
* 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 = OptionChain(equitySymbol).ToList();
var contracts = OptionChainProvider.GetOptionContractList(equitySymbol, UtcTime).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 = OptionChain(_symbol)
var contractSymbol = OptionChainProvider.GetOptionContractList(_symbol, Time)
.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 => 787;
public int AlgorithmHistoryDataPoints => 3;
/// <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.Daily))
.Any(config => config.Resolution != Resolution.Minute))
{
throw new RegressionTestException("Was expecting resolution to be set to Daily");
throw new RegressionTestException("Was expecting resolution to be set to Minute");
}
}

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 = OptionChain(_equitySymbol).ToList();
var contracts = OptionChainProvider.GetOptionContractList(_equitySymbol, UtcTime).ToList();
var callOptionSymbol = contracts
.Where(c => c.ID.OptionRight == OptionRight.Call)

View File

@@ -1,147 +0,0 @@
/*
* 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(self._twx)
options = self.option_chain_provider.get_option_contract_list(self._twx, self.time)
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(addedSecurity.symbol)
options = self.option_chain_provider.get_option_contract_list(addedSecurity.symbol, self.time)
options = sorted(options, key=lambda x: x.id.symbol)
option = next((option

View File

@@ -1,120 +0,0 @@
# 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,12 +25,14 @@ 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("SPX")
self.spx_options = self.add_index_option(self.spx)
self.spx_options.set_filter(lambda u: (u.strikes(0, 1).expiration(0, 30)))
# weekly option SPX contracts
spxw = self.add_index_option("SPX", "SPXW")
spxw = self.add_index_option(self.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

@@ -28,6 +28,7 @@ class CoarseFineOptionUniverseChainRegressionAlgorithm(QCAlgorithm):
# - 6th: AAPL is selected, TWX is removed but subscriptions are not removed because the securities are invested.
# - TWX and its options are liquidated.
# - 7th: Since options universe selection is daily now, TWX subscriptions are removed the next day (7th)
#self.set_end_date(2014,6,6)
self.set_end_date(2014,6,7)
self.universe_settings.resolution = Resolution.MINUTE

View File

@@ -52,7 +52,8 @@ 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 (list(self.option_chain(self.es19m20)) + list(self.option_chain(self.es20h20)))
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))
if i.id.strike_price == 3200.0 and i.id.option_right == OptionRight.CALL
]

View File

@@ -41,8 +41,7 @@ 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(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)
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)
)[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,8 +45,7 @@ class FutureOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
self.es_option = self.add_future_option_contract(
list(
sorted(
[x for x in self.option_chain(self.es19m20)
if x.id.strike_price >= 3300.0 and x.id.option_right == OptionRight.CALL],
[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],
key=lambda x: x.id.strike_price
)
)[0], Resolution.MINUTE).symbol

View File

@@ -34,9 +34,7 @@ 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(self.dc)
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_provider.get_option_contract_list(self.dc, self.time)) if x.id.strike_price == 17 and x.id.option_right == OptionRight.CALL
]
self.dc_option = dc_options[0]

View File

@@ -40,8 +40,7 @@ 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(self.es19m20) 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_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)
)[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(self.es19m20) if x.id.strike_price <= 3150.0 and x.id.option_right == OptionRight.PUT],
[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],
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(self.es19m20) if x.id.strike_price <= 3100.0 and x.id.option_right == OptionRight.CALL],
[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],
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(self.es19m20) if x.id.strike_price >= 3400.0 and x.id.option_right == OptionRight.CALL],
[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],
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(self.es19m20) if x.id.strike_price <= 3400.0 and x.id.option_right == OptionRight.PUT],
[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],
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(self.es19m20) if x.id.strike_price <= 3000.0 and x.id.option_right == OptionRight.PUT],
[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],
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(spx)\
for i in self.option_chain_provider.get_option_contract_list(spx, self.time)\
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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(self.spx))
self.spx_option = list(self.option_chain_provider.get_option_contract_list(self.spx, self.time))
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

@@ -1,89 +0,0 @@
# 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

@@ -112,6 +112,7 @@ class IndicatorSelectorWorksWithDifferentOptions(QCAlgorithm):
if (self.future in slice.futures_chains.keys()):
if self.future_contract == None:
self.future_contract = slice.future_chains[self.future].trade_bars.values()[0].symbol
return
if self.future_contract in slice.future_chains[self.future].trade_bars:
value = slice.future_chains[self.future].trade_bars[self.future_contract]
if value.volume != 0:
@@ -125,12 +126,11 @@ class IndicatorSelectorWorksWithDifferentOptions(QCAlgorithm):
raise Exception("At least one trade bar should have been found, but none was found")
future_indicator = Identity("")
backtest_days = (self.end_date - self.start_date).days
future_volume_history = self.indicator_history(future_indicator, self.future_contract, backtest_days, Resolution.DAILY, Field.VOLUME).current
future_volume_history = self.indicator_history(future_indicator, self.future_contract, 50, Resolution.DAILY, Field.VOLUME).current
future_volume_history_values = list(map(lambda x: x.value, future_volume_history))
future_volume_history_values = list(filter(lambda x: x != 0, future_volume_history_values))
if abs(sum(future_volume_history_values)/len(future_volume_history_values) - sum(self.future_points)/len(self.future_points)) > 0.001:
raise Exception(f"No history indicator future data point was found using Field.Volume selector! {self.future_points}")
raise Exception("No history indicator future data point was found using Field.Volume selector!")
volume_history = self.indicator_history(self.tradebar_history_indicator, self.aapl, 109, Resolution.DAILY, Field.VOLUME).current
volume_history_values = list(map(lambda x: x.value, volume_history))

View File

@@ -1,46 +0,0 @@
# 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

@@ -25,22 +25,22 @@ class OptionUniverseHistoryRegressionAlgorithm(QCAlgorithm):
option = self.add_option("GOOG").symbol
historical_options_data_df = self.history(option, 3, Resolution.DAILY)
#historical_options_data = self.history(OptionUniverse, option, 3, Resolution.DAILY)
historical_options_data = list(self.history[OptionUniverse](option, 3, Resolution.DAILY))
if historical_options_data_df.shape[0] != 3:
raise RegressionTestException(f"Expected 3 option chains from history request, but got {historical_options_data_df.shape[0]}")
if len(historical_options_data) != 3:
raise RegressionTestException(f"Expected 3 option chains from history request, but got {historical_options_data.Count}")
for index, row in historical_options_data_df.iterrows():
data = row.data
date = index[4]
for history_option_universe in historical_options_data:
date = history_option_universe.end_time
chain = list(self.option_chain_provider.get_option_contract_list(option, date))
if len(chain) == 0:
raise RegressionTestException(f"No options in chain on {date}")
if len(chain) != len(data):
raise RegressionTestException(f"Expected {len(chain)} options in chain on {date}, but got {len(data)}")
if len(chain) != len(history_option_universe.data):
raise RegressionTestException(f"Expected {len(chain)} options in chain on {date}, but got {len(history_option_universe.data.data)}")
for i in range(len(chain)):
if data[i].symbol != chain[i]:
if history_option_universe.data[i].symbol != chain[i]:
raise RegressionTestException(f"Missing option contract {chain[i]} on {date}")

View File

@@ -75,7 +75,17 @@ namespace QuantConnect.Algorithm.Framework.Alphas
/// <returns>The new insights generated</returns>
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
return _model.InvokeMethodAndEnumerate<Insight>(nameof(Update), algorithm, data);
using (Py.GIL())
{
var insights = _model.InvokeMethod(nameof(Update), algorithm, new PythonSlice(data));
var iterator = insights.GetIterator();
foreach (PyObject insight in iterator)
{
yield return insight.GetAndDispose<Insight>();
}
iterator.Dispose();
insights.Dispose();
}
}
/// <summary>
@@ -85,7 +95,7 @@ namespace QuantConnect.Algorithm.Framework.Alphas
/// <param name="changes">The security additions and removals from the algorithm</param>
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
_model.InvokeVoidMethod(nameof(OnSecuritiesChanged), algorithm, changes);
_model.InvokeMethod(nameof(OnSecuritiesChanged), algorithm, changes).Dispose();
}
}
}

View File

@@ -77,7 +77,7 @@ namespace QuantConnect.Algorithm.Framework.Portfolio
}
}
_model.InvokeVoidMethod(nameof(SetPythonWrapper), this);
_model.InvokeMethod(nameof(SetPythonWrapper), this).Dispose();
_implementsDetermineTargetPercent = model.GetPythonMethod("DetermineTargetPercent") != null;
}
@@ -91,7 +91,7 @@ namespace QuantConnect.Algorithm.Framework.Portfolio
/// <returns>An enumerable of portfolio targets to be sent to the execution model</returns>
public override IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
{
return _model.InvokeMethodAndEnumerate<IPortfolioTarget>(nameof(CreateTargets), algorithm, insights);
return _model.InvokeMethod<IEnumerable<IPortfolioTarget>>(nameof(CreateTargets), algorithm, insights);
}
/// <summary>
@@ -101,7 +101,7 @@ namespace QuantConnect.Algorithm.Framework.Portfolio
/// <param name="changes">The security additions and removals from the algorithm</param>
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
_model.InvokeVoidMethod(nameof(OnSecuritiesChanged), algorithm, changes);
_model.InvokeMethod(nameof(OnSecuritiesChanged), algorithm, changes).Dispose();
}
/// <summary>
@@ -144,13 +144,32 @@ namespace QuantConnect.Algorithm.Framework.Portfolio
/// <returns>A target percent for each insight</returns>
protected override Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights)
{
if (!_implementsDetermineTargetPercent)
using (Py.GIL())
{
// the implementation is in C#
return _model.InvokeMethod<Dictionary<Insight, double>>(nameof(DetermineTargetPercent), activeInsights);
}
if (!_implementsDetermineTargetPercent)
{
// the implementation is in C#
return _model.InvokeMethod<Dictionary<Insight, double>>(nameof(DetermineTargetPercent), activeInsights);
}
return _model.InvokeMethodAndGetDictionary<Insight, double>(nameof(DetermineTargetPercent), activeInsights);
Dictionary<Insight, double> dic;
var result = _model.InvokeMethod(nameof(DetermineTargetPercent), activeInsights) as dynamic;
if ((result as PyObject).TryConvert(out dic))
{
// this is required if the python implementation is actually returning a C# dic, not common,
// but could happen if its actually calling a base C# implementation
return dic;
}
dic = new Dictionary<Insight, double>();
foreach (var pyInsight in result)
{
var insight = (pyInsight as PyObject).As<Insight>();
dic[insight] = result[pyInsight];
}
return dic;
}
}
}
}

View File

@@ -948,8 +948,8 @@ namespace QuantConnect.Algorithm
Resolution? resolution = null, bool? fillForward = null, bool? extendedMarketHours = null, DataMappingMode? dataMappingMode = null,
DataNormalizationMode? dataNormalizationMode = null, int? contractDepthOffset = null)
{
var arrayOfSymbols = symbols.ToArray();
return CreateDateRangeHistoryRequests(symbols, Extensions.GetCustomDataTypeFromSymbols(arrayOfSymbols) ?? typeof(BaseData), startAlgoTz, endAlgoTz, resolution, fillForward, extendedMarketHours,
symbols = symbols.ToArray();
return CreateDateRangeHistoryRequests(symbols, GetCustomDataTypeFromSymbols((Symbol[])symbols) ?? typeof(BaseData), startAlgoTz, endAlgoTz, resolution, fillForward, extendedMarketHours,
dataMappingMode, dataNormalizationMode, contractDepthOffset);
}
@@ -1084,7 +1084,7 @@ namespace QuantConnect.Algorithm
if (!LeanData.IsCommonLeanDataType(type) && !type.IsAbstract)
{
// we already know it's not a common lean data type
var isCustom = Extensions.IsCustomDataType(symbol, type);
var isCustom = !type.Namespace.StartsWith("QuantConnect.Data", StringComparison.InvariantCultureIgnoreCase);
// we were giving a specific type let's fetch it
return new[] { new SubscriptionDataConfig(

View File

@@ -3094,7 +3094,7 @@ namespace QuantConnect.Algorithm
private IEnumerable<Slice> GetIndicatorWarmUpHistory(IEnumerable<Symbol> symbols, IIndicator indicator, TimeSpan timeSpan, out bool identityConsolidator)
{
identityConsolidator = false;
if (!AssertIndicatorHasWarmupPeriod(indicator))
if (AssertIndicatorHasWarmupPeriod(indicator))
{
return Enumerable.Empty<Slice>();
}
@@ -3143,17 +3143,16 @@ namespace QuantConnect.Algorithm
where T : class, IBaseData
{
IDataConsolidator consolidator;
if (identityConsolidator)
{
period = TimeSpan.Zero;
}
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).Count > 0)
{
consolidator = Consolidate(symbol, period, handler);
}
else
{
if (identityConsolidator)
{
period = TimeSpan.Zero;
}
var providedType = typeof(T);
if (providedType.IsAbstract)
{
@@ -3475,10 +3474,6 @@ namespace QuantConnect.Algorithm
public IndicatorHistory IndicatorHistory(IndicatorBase<IndicatorDataPoint> indicator, IEnumerable<Symbol> symbols, int period, Resolution? resolution = null, Func<IBaseData, decimal> selector = null)
{
var warmupPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod ?? 0;
if (warmupPeriod > 0 && period > 0)
{
warmupPeriod -= 1;
}
var history = History(symbols, period + warmupPeriod, resolution, dataNormalizationMode: GetIndicatorHistoryDataNormalizationMode(indicator));
return IndicatorHistory(indicator, history, selector);
}
@@ -3513,10 +3508,6 @@ namespace QuantConnect.Algorithm
where T : IBaseData
{
var warmupPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod ?? 0;
if (warmupPeriod > 0 && period > 0)
{
warmupPeriod -= 1;
}
var history = History(symbols, period + warmupPeriod, resolution, dataNormalizationMode: GetIndicatorHistoryDataNormalizationMode(indicator));
return IndicatorHistory(indicator, history, selector);
}
@@ -3902,17 +3893,13 @@ namespace QuantConnect.Algorithm
var warmupPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod ?? 0;
if (warmupPeriod != 0)
{
warmupPeriod -= 1;
if (warmupPeriod > 0)
foreach (var request in CreateDateRangeHistoryRequests(symbols, start, end, resolution))
{
foreach (var request in CreateDateRangeHistoryRequests(symbols, start, end, resolution))
var adjustedStart = _historyRequestFactory.GetStartTimeAlgoTz(request.StartTimeUtc, request.Symbol, warmupPeriod, request.Resolution,
request.ExchangeHours, request.DataTimeZone, request.DataType, request.IncludeExtendedMarketHours);
if (adjustedStart < start)
{
var adjustedStart = _historyRequestFactory.GetStartTimeAlgoTz(request.StartTimeUtc, request.Symbol, warmupPeriod, request.Resolution,
request.ExchangeHours, request.DataTimeZone, request.DataType, request.IncludeExtendedMarketHours);
if (adjustedStart < start)
{
start = adjustedStart;
}
start = adjustedStart;
}
}
}
@@ -3945,15 +3932,13 @@ namespace QuantConnect.Algorithm
.ToList();
var indicatorsDataPointsByTime = new List<IndicatorDataPoints>();
var lastConsumedTime = DateTime.MinValue;
IndicatorDataPoint lastPoint = null;
void consumeLastPoint(IndicatorDataPoint newInputPoint)
{
if (newInputPoint == null || lastConsumedTime == newInputPoint.EndTime)
if (lastPoint == null)
{
return;
}
lastConsumedTime = newInputPoint.EndTime;
var IndicatorDataPoints = new IndicatorDataPoints { Time = newInputPoint.Time, EndTime = newInputPoint.EndTime };
indicatorsDataPointsByTime.Add(IndicatorDataPoints);
@@ -3962,6 +3947,7 @@ namespace QuantConnect.Algorithm
var newPoint = indicatorsDataPointPerProperty[i].UpdateValue();
IndicatorDataPoints.SetProperty(indicatorsDataPointPerProperty[i].Name, newPoint);
}
lastPoint = null;
}
IndicatorUpdatedHandler callback = (object _, IndicatorDataPoint newInputPoint) =>
@@ -3971,15 +3957,16 @@ namespace QuantConnect.Algorithm
return;
}
if (lastPoint == null || lastPoint.Time != newInputPoint.Time)
if (lastPoint != null && lastPoint.Time != newInputPoint.Time)
{
// if null, it's the first point, we transitions from not ready to ready
// else when the time changes we fetch the indicators values, some indicators which consume data from multiple symbols might trigger the Updated event
// when the time changes we let through the previous point, some indicators which consume data from multiple symbols might trigger the Updated event
// even if their value has not changed yet
consumeLastPoint(newInputPoint);
}
lastPoint = newInputPoint;
};
// flush the last point
consumeLastPoint(lastPoint);
// register the callback, update the indicator and unregister finally
indicator.Updated += callback;
@@ -3999,8 +3986,6 @@ namespace QuantConnect.Algorithm
}
}
}
// flush the last point, this will be useful for indicator consuming time from multiple symbols
consumeLastPoint(lastPoint);
indicator.Updated -= callback;
return new IndicatorHistory(indicatorsDataPointsByTime, indicatorsDataPointPerProperty,

View File

@@ -715,36 +715,6 @@ namespace QuantConnect.Algorithm
WarmUpIndicator(symbol, WrapPythonIndicator(indicator), resolution, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
}
/// <summary>
/// Warms up a given indicator with historical data
/// </summary>
/// <param name="symbol">The symbol whose indicator we want</param>
/// <param name="indicator">The indicator we want to warm up</param>
/// <param name="period">The necessary period to warm up the indicator</param>
/// <param name="selector">Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)</param>
[DocumentationAttribute(Indicators)]
[DocumentationAttribute(HistoricalData)]
public void WarmUpIndicator(Symbol symbol, PyObject indicator, TimeSpan period, PyObject selector = null)
{
if (indicator.TryConvert(out IndicatorBase<IndicatorDataPoint> indicatorDataPoint))
{
WarmUpIndicator(symbol, indicatorDataPoint, period, selector?.ConvertToDelegate<Func<IBaseData, decimal>>());
return;
}
if (indicator.TryConvert(out IndicatorBase<IBaseDataBar> indicatorDataBar))
{
WarmUpIndicator(symbol, indicatorDataBar, period, selector?.ConvertToDelegate<Func<IBaseData, IBaseDataBar>>());
return;
}
if (indicator.TryConvert(out IndicatorBase<TradeBar> indicatorTradeBar))
{
WarmUpIndicator(symbol, indicatorTradeBar, period, selector?.ConvertToDelegate<Func<IBaseData, TradeBar>>());
return;
}
WarmUpIndicator(symbol, WrapPythonIndicator(indicator), period, selector?.ConvertToDelegate<Func<IBaseData, IBaseData>>());
}
/// <summary>
/// Plot a chart using string series name, with value.
/// </summary>
@@ -936,7 +906,7 @@ namespace QuantConnect.Algorithm
}
var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols);
var dataType = GetCustomDataTypeFromSymbols(symbols);
return GetDataFrame(History(symbols, periods, resolution, fillForward, extendedMarketHours, dataMappingMode, dataNormalizationMode,
contractDepthOffset), dataType);
@@ -999,7 +969,7 @@ namespace QuantConnect.Algorithm
}
var symbols = tickers.ConvertToSymbolEnumerable().ToArray();
var dataType = Extensions.GetCustomDataTypeFromSymbols(symbols);
var dataType = GetCustomDataTypeFromSymbols(symbols);
return GetDataFrame(History(symbols, start, end, resolution, fillForward, extendedMarketHours, dataMappingMode,
dataNormalizationMode, contractDepthOffset), dataType);
@@ -1749,5 +1719,20 @@ namespace QuantConnect.Algorithm
}
return history;
}
private Type GetCustomDataTypeFromSymbols(Symbol[] symbols)
{
if (symbols.Any())
{
if (!SecurityIdentifier.TryGetCustomDataTypeInstance(symbols[0].ID.Symbol, out var dataType)
|| symbols.Any(x => !SecurityIdentifier.TryGetCustomDataTypeInstance(x.ID.Symbol, out var customDataType) || customDataType != dataType))
{
return null;
}
return dataType;
}
return null;
}
}
}

View File

@@ -92,15 +92,7 @@ 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))
@@ -109,7 +101,7 @@ namespace QuantConnect.Algorithm
underlyingSymbol.Value,
resolution,
underlyingSymbol.ID.Market,
isFillForward,
configs.IsFillForward(),
Security.NullLeverage,
configs.IsExtendedMarketHours(),
dataNormalizationMode: DataNormalizationMode.Raw);

View File

@@ -33,7 +33,6 @@ 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;
@@ -53,7 +52,6 @@ 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
{
@@ -468,9 +466,6 @@ 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>
@@ -1939,15 +1934,6 @@ 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,
@@ -1955,8 +1941,8 @@ namespace QuantConnect.Algorithm
if (dataNormalizationMode.HasValue)
{
configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol,
securityResolution,
securityFillForward,
resolution,
fillForward,
extendedMarketHours,
isFilteredSubscription,
dataNormalizationMode: dataNormalizationMode.Value,
@@ -1965,8 +1951,8 @@ namespace QuantConnect.Algorithm
else
{
configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol,
securityResolution,
securityFillForward,
resolution,
fillForward,
extendedMarketHours,
isFilteredSubscription,
contractDepthOffset: (uint)contractDepthOffset);
@@ -1984,16 +1970,10 @@ namespace QuantConnect.Algorithm
if (!UniverseManager.ContainsKey(symbol))
{
var canonicalConfig = configs.First();
var universeSettingsResolution = canonicalConfig.Resolution;
if (symbol.SecurityType.IsOption())
{
universeSettingsResolution = resolution ?? UniverseSettings.Resolution;
}
var settings = new UniverseSettings(universeSettingsResolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse)
var settings = new UniverseSettings(canonicalConfig.Resolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse)
{
Asynchronous = UniverseSettings.Asynchronous
};
if (symbol.SecurityType.IsOption())
{
universe = new OptionChainUniverse((Option)security, settings);
@@ -2241,15 +2221,18 @@ namespace QuantConnect.Algorithm
/// <summary>
/// Creates and adds index options to the algorithm.
/// </summary>
/// <param name="underlying">The underlying ticker of the Index Option</param>
/// <param name="ticker">The 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">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="market">Market of the index option. If no market is provided, we default to <see cref="Market.USA"/> </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, Resolution? resolution = null, string market = null, bool fillForward = true)
public Option AddIndexOption(string ticker, Resolution? resolution = null, string market = Market.USA, bool fillForward = true)
{
return AddIndexOption(underlying, null, resolution, market, fillForward);
return AddIndexOption(
QuantConnect.Symbol.Create(ticker, SecurityType.Index, market),
resolution,
fillForward);
}
/// <summary>
@@ -2260,7 +2243,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 IndexOption AddIndexOption(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
public Option AddIndexOption(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
{
return AddIndexOption(symbol, null, resolution, fillForward);
}
@@ -2274,36 +2257,14 @@ 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 IndexOption AddIndexOption(Symbol symbol, string targetOption, Resolution? resolution = null, bool fillForward = true)
public Option 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 (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);
return AddOption(symbol, targetOption, resolution, symbol.ID.Market, fillForward);
}
/// <summary>
@@ -2315,14 +2276,14 @@ namespace QuantConnect.Algorithm
/// <returns>Index Option Contract</returns>
/// <exception cref="ArgumentException">The provided Symbol is not an Index Option</exception>
[DocumentationAttribute(AddingData)]
public IndexOption AddIndexOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
public Option AddIndexOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true)
{
if (symbol.SecurityType != SecurityType.IndexOption || symbol.IsCanonical())
if (symbol.SecurityType != SecurityType.IndexOption)
{
throw new ArgumentException("Symbol provided must be non-canonical and of type SecurityType.IndexOption");
throw new ArgumentException("Symbol provided must be of type SecurityType.IndexOption");
}
return (IndexOption)AddOptionContract(symbol, resolution, fillForward);
return AddOptionContract(symbol, resolution, fillForward);
}
/// <summary>
@@ -3344,63 +3305,6 @@ 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

@@ -44,7 +44,17 @@ namespace QuantConnect.Algorithm.Framework.Risk
/// <param name="targets">The current portfolio targets to be assessed for risk</param>
public override IEnumerable<IPortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] targets)
{
return _model.InvokeMethodAndEnumerate<IPortfolioTarget>(nameof(ManageRisk), algorithm, targets);
using (Py.GIL())
{
var riskTargetOverrides = _model.InvokeMethod("ManageRisk", algorithm, targets);
var iterator = riskTargetOverrides.GetIterator();
foreach (PyObject target in iterator)
{
yield return target.GetAndDispose<IPortfolioTarget>();
}
iterator.Dispose();
riskTargetOverrides.Dispose();
}
}
/// <summary>

View File

@@ -71,7 +71,17 @@ namespace QuantConnect.Algorithm.Framework.Selection
/// <returns>The universes to be used by the algorithm</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
return _model.InvokeMethodAndEnumerate<Universe>(nameof(CreateUniverses), algorithm);
using (Py.GIL())
{
var universes = _model.InvokeMethod(nameof(CreateUniverses), algorithm);
var iterator = universes.GetIterator();
foreach (PyObject universe in iterator)
{
yield return universe.GetAndDispose<Universe>();
}
iterator.Dispose();
universes.Dispose();
}
}
}
}

View File

@@ -872,7 +872,8 @@ namespace QuantConnect.AlgorithmFactory.Python.Wrappers
{
using (Py.GIL())
{
var result = InvokeMethod(nameof(OnMarginCall), requests);
var method = GetMethod(nameof(OnMarginCall));
var result = method.Invoke<PyObject>(requests);
if (_onMarginCall != null)
{

View File

@@ -39,7 +39,7 @@ namespace QuantConnect.Brokerages
});
/// <summary>
/// HashSet containing the order types supported by the <see cref="CanSubmitOrder"/> operation in TradeStation.
/// HashSet containing the order types supported by TradeStation.
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(
new[]
@@ -47,9 +47,7 @@ namespace QuantConnect.Brokerages
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.StopLimit
});
/// <summary>
@@ -96,13 +94,9 @@ namespace QuantConnect.Brokerages
if (!_supportOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportOrderTypes));
return false;
}
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportOrderTypes));
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;
}
@@ -121,7 +115,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",
@@ -129,23 +123,7 @@ 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

@@ -1,101 +0,0 @@
/*
* 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

@@ -20,7 +20,7 @@ namespace QuantConnect.Data.Market
/// <summary>
/// Defines the greeks
/// </summary>
public class Greeks : BaseGreeks
public class Greeks
{
private Lazy<decimal> _delta;
private Lazy<decimal> _gamma;
@@ -33,84 +33,144 @@ namespace QuantConnect.Data.Market
// for optimization purposes (approximation of delta and gamma is very similar)
private Lazy<Tuple<decimal, decimal>> _deltaGamma;
/// <inheritdoc />
public override decimal Delta
/// <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 decimal Delta
{
get
{
return _delta != null ? _delta.Value : _deltaGamma.Value.Item1;
}
protected set
private set
{
_delta = new Lazy<decimal>(() => value);
}
}
/// <inheritdoc />
public override decimal Gamma
/// <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 decimal Gamma
{
get
{
return _gamma != null ? _gamma.Value : _deltaGamma.Value.Item2;
}
protected set
private set
{
_gamma = new Lazy<decimal>(() => value);
}
}
/// <inheritdoc />
public override decimal Vega
/// <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 decimal Vega
{
get
{
return _vega.Value;
}
protected set
private set
{
_vega = new Lazy<decimal>(() => value);
}
}
/// <inheritdoc />
public override decimal Theta
/// <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 decimal Theta
{
get
{
return _theta.Value;
}
protected set
private set
{
_theta = new Lazy<decimal>(() => value);
}
}
/// <inheritdoc />
public override decimal Rho
/// <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 decimal Rho
{
get
{
return _rho.Value;
}
protected set
private set
{
_rho = new Lazy<decimal>(() => value);
}
}
/// <inheritdoc />
public override decimal Lambda
/// <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 decimal Lambda
{
get
{
return _lambda.Value;
}
protected set
private set
{
_lambda = new Lazy<decimal>(() => value);
}
}
/// <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="Greeks"/> class
/// </summary>

View File

@@ -33,8 +33,6 @@ namespace QuantConnect.Data
{
private readonly PriorityQueue<ConsolidatorWrapper, ConsolidatorScanPriority> _consolidatorsSortedByScanTime;
private readonly Dictionary<IDataConsolidator, ConsolidatorWrapper> _consolidators;
private List<Tuple<ConsolidatorWrapper, ConsolidatorScanPriority>> _consolidatorsToAdd;
private readonly object _threadSafeCollectionLock;
private readonly ITimeKeeper _timeKeeper;
private IAlgorithmSubscriptionManager _subscriptionManager;
@@ -67,7 +65,6 @@ namespace QuantConnect.Data
_consolidators = new();
_timeKeeper = timeKeeper;
_consolidatorsSortedByScanTime = new(1000);
_threadSafeCollectionLock = new object();
}
/// <summary>
@@ -184,11 +181,7 @@ namespace QuantConnect.Data
var wrapper = _consolidators[consolidator] =
new ConsolidatorWrapper(consolidator, subscription.Increment, _timeKeeper, _timeKeeper.GetLocalTimeKeeper(subscription.ExchangeTimeZone));
lock (_threadSafeCollectionLock)
{
_consolidatorsToAdd ??= new();
_consolidatorsToAdd.Add(new(wrapper, wrapper.Priority));
}
_consolidatorsSortedByScanTime.Enqueue(wrapper, wrapper.Priority);
return;
}
}
@@ -267,15 +260,6 @@ namespace QuantConnect.Data
/// <param name="algorithm">The algorithm instance</param>
public void ScanPastConsolidators(DateTime newUtcTime, IAlgorithm algorithm)
{
if (_consolidatorsToAdd != null)
{
lock (_threadSafeCollectionLock)
{
_consolidatorsToAdd.DoForEach(x => _consolidatorsSortedByScanTime.Enqueue(x.Item1, x.Item2));
_consolidatorsToAdd = null;
}
}
while (_consolidatorsSortedByScanTime.TryPeek(out _, out var priority) && priority.UtcScanTime < newUtcTime)
{
var consolidatorToScan = _consolidatorsSortedByScanTime.Dequeue();

View File

@@ -26,11 +26,6 @@ namespace QuantConnect.Data.UniverseSelection
/// </summary>
public class BaseDataCollection : BaseData, IEnumerable<BaseData>
{
/// <summary>
/// Cache for the symbols to avoid creating them multiple times
/// </summary>
private static readonly Dictionary<string, Symbol> _symbolsCache = new();
private DateTime _endTime;
/// <summary>
@@ -111,9 +106,13 @@ 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)
{
if (data != null && data.Count == 1 && data[0] is BaseDataCollection collection && collection.Data != null && collection.Data.Count > 0)
Symbol = symbol;
Time = time;
_endTime = endTime;
Underlying = underlying;
FilteredContracts = filteredContracts;
if (data != null && data.Count == 1 && data[0] is BaseDataCollection collection && collection.Data.Count > 0)
{
// we were given a base data collection, let's be nice and fetch it's data if it has any
Data = collection.Data;
@@ -124,26 +123,13 @@ 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.Underlying, other.FilteredContracts)
: this(other.Time, other.EndTime, other.Symbol, other.Data, other.Underlying, other.FilteredContracts)
{
Data = other.Data;
}
/// <summary>
@@ -163,7 +149,7 @@ namespace QuantConnect.Data.UniverseSelection
/// <returns>Whether this contains data that should be stored in the security cache</returns>
public override bool ShouldCacheToSecurity()
{
if (Data == null || Data.Count == 0)
if (Data.Count == 0)
{
return true;
}
@@ -218,32 +204,5 @@ namespace QuantConnect.Data.UniverseSelection
{
return GetEnumerator();
}
/// <summary>
/// Tries to get a symbol from the cache
/// </summary>
protected static bool TryGetCachedSymbol(string ticker, out Symbol symbol)
{
lock (_symbolsCache)
{
return _symbolsCache.TryGetValue(ticker, out symbol);
}
}
/// <summary>
/// Caches a symbol
/// </summary>
protected static void CacheSymbol(string ticker, Symbol symbol)
{
lock (_symbolsCache)
{
// limit the cache size to help with memory usage
if (_symbolsCache.Count >= 600000)
{
_symbolsCache.Clear();
}
_symbolsCache.TryAdd(ticker, symbol);
}
}
}
}

View File

@@ -27,10 +27,16 @@ namespace QuantConnect.Data.UniverseSelection
/// </summary>
public class OptionUniverse : BaseDataCollection, ISymbol
{
private const int StartingGreeksCsvIndex = 7;
private string[] _csvLine;
// We keep the properties as they are in the csv file to reduce memory usage (strings vs decimals)
private readonly string _csvLine;
private decimal? _open;
private decimal? _high;
private decimal? _low;
private decimal? _close;
private decimal? _volume;
private decimal? _openInterest;
private decimal? _impliedVolatility;
private Greeks _greeks;
/// <summary>
/// The security identifier of the option symbol
@@ -49,8 +55,12 @@ namespace QuantConnect.Data.UniverseSelection
{
get
{
// Parse the values every time to avoid keeping them in memory
return _csvLine.GetDecimalFromCsv(0);
if (!_open.HasValue)
{
_open = GetDecimalFromCsvLine(2);
}
return _open.Value;
}
}
@@ -61,7 +71,12 @@ namespace QuantConnect.Data.UniverseSelection
{
get
{
return _csvLine.GetDecimalFromCsv(1);
if (!_high.HasValue)
{
_high = GetDecimalFromCsvLine(3);
}
return _high.Value;
}
}
@@ -72,7 +87,12 @@ namespace QuantConnect.Data.UniverseSelection
{
get
{
return _csvLine.GetDecimalFromCsv(2);
if (!_low.HasValue)
{
_low = GetDecimalFromCsvLine(4);
}
return _low.Value;
}
}
@@ -83,7 +103,12 @@ namespace QuantConnect.Data.UniverseSelection
{
get
{
return _csvLine.GetDecimalFromCsv(3);
if (!_close.HasValue)
{
_close = GetDecimalFromCsvLine(5);
}
return _close.Value;
}
}
@@ -94,53 +119,90 @@ namespace QuantConnect.Data.UniverseSelection
{
get
{
return _csvLine.GetDecimalFromCsv(4);
if (!_volume.HasValue)
{
_volume = GetDecimalFromCsvLine(6);
}
return _volume.Value;
}
}
/// <summary>
/// Open interest value of the option
/// </summary>
public decimal OpenInterest
public decimal? OpenInterest
{
get
{
ThrowIfNotAnOption(nameof(OpenInterest));
return _csvLine.GetDecimalFromCsv(5);
if (!_openInterest.HasValue)
{
_openInterest = GetDecimalFromCsvLine(7);
}
return _openInterest.Value;
}
}
/// <summary>
/// Implied volatility value of the option
/// </summary>
public decimal ImpliedVolatility
public decimal? ImpliedVolatility
{
get
{
ThrowIfNotAnOption(nameof(ImpliedVolatility));
return _csvLine.GetDecimalFromCsv(6);
if (!_impliedVolatility.HasValue)
{
_impliedVolatility = GetDecimalFromCsvLine(8);
}
return _impliedVolatility.Value;
}
}
/// <summary>
/// Greeks values of the option
/// </summary>
public BaseGreeks Greeks
public Greeks Greeks
{
get
{
ThrowIfNotAnOption(nameof(Greeks));
return new PreCalculatedGreeks(_csvLine);
if (_greeks == null)
{
_greeks = new Greeks(GetDecimalFromCsvLine(9),
GetDecimalFromCsvLine(10),
GetDecimalFromCsvLine(11),
GetDecimalFromCsvLine(12),
GetDecimalFromCsvLine(13),
0m);
}
return _greeks;
}
private set
{
_greeks = value;
}
}
/// <summary>
/// Period of the data
/// </summary>
public TimeSpan Period { get; } = TimeSpan.FromDays(1);
/// <summary>
/// Time that the data became available to use
/// </summary>
public override DateTime EndTime
{
get { return Time + QuantConnect.Time.OneDay; }
set { Time = value - QuantConnect.Time.OneDay; }
get { return Time + Period; }
set { Time = value - Period; }
}
/// <summary>
@@ -153,12 +215,35 @@ namespace QuantConnect.Data.UniverseSelection
/// <summary>
/// Creates a new instance of the <see cref="OptionUniverse"/> class
/// </summary>
public OptionUniverse(DateTime date, Symbol symbol, string csv)
: base(date, date, symbol, null, null)
public OptionUniverse(DateTime date, Symbol symbol, string[] csv)
: base(date, symbol)
{
_csvLine = csv;
}
/// <summary>
/// Creates a new instance of the <see cref="OptionUniverse"/> class
/// </summary>
public OptionUniverse(DateTime date, Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume,
decimal? openInterest, decimal? impliedVolatility, Greeks greeks)
: base(date, symbol)
{
Initialize(open, high, low, close, volume, openInterest, impliedVolatility, greeks);
}
private void Initialize(decimal? open, decimal? high, decimal? low, decimal? close, decimal? volume, decimal? openInterest,
decimal? impliedVolatility, Greeks greeks)
{
_open = open;
_high = high;
_low = low;
_close = close;
_volume = volume;
_openInterest = openInterest;
_impliedVolatility = impliedVolatility;
_greeks = greeks;
}
/// <summary>
/// Creates a new instance of the <see cref="OptionUniverse"/> class as a copy of the given instance
/// </summary>
@@ -166,6 +251,7 @@ namespace QuantConnect.Data.UniverseSelection
: base(other)
{
_csvLine = other._csvLine;
Initialize(other._open, other._high, other._low, other._close, other._volume, other._openInterest, other._impliedVolatility, other._greeks);
}
/// <summary>
@@ -188,58 +274,32 @@ namespace QuantConnect.Data.UniverseSelection
/// each time it is called.
/// </summary>
/// <param name="config">Subscription data config setup object</param>
/// <param name="stream">Stream reader of the source document</param>
/// <param name="line">Line of the source document</param>
/// <param name="date">Date of the requested data</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
if (stream == null || stream.EndOfStream)
if (string.IsNullOrEmpty(line) || line.StartsWith("#", StringComparison.InvariantCulture))
{
return null;
}
var sidStr = stream.GetString();
var csv = line.Split(',');
var sid = SecurityIdentifier.Parse(csv[0]);
var symbolValue = csv[1];
if (sidStr.StartsWith("#", StringComparison.InvariantCulture))
Symbol symbol;
if (sid.HasUnderlying)
{
stream.ReadLine();
return null;
symbol = Symbol.CreateOption(sid, symbolValue);
}
else
{
symbol = new Symbol(sid, symbolValue);
}
var symbolValue = stream.GetString();
var remainingLine = stream.ReadLine();
var key = $"{sidStr}:{symbolValue}";
if (!TryGetCachedSymbol(key, out var symbol))
{
var sid = SecurityIdentifier.Parse(sidStr);
if (sid.HasUnderlying)
{
// Let's try to get the underlying symbol from the cache
SymbolRepresentation.TryDecomposeOptionTickerOSI(symbolValue, sid.SecurityType,
out var _, out var underlyingValue, out var _, out var _, out var _);
var underlyingKey = $"{sid.Underlying}:{underlyingValue}";
var underlyingWasCached = TryGetCachedSymbol(underlyingKey, out var underlyingSymbol);
symbol = Symbol.CreateOption(sid, symbolValue, underlyingSymbol);
if (!underlyingWasCached)
{
CacheSymbol(underlyingKey, symbol.Underlying);
}
}
else
{
symbol = new Symbol(sid, symbolValue);
}
CacheSymbol(key, symbol);
}
return new OptionUniverse(date, symbol, remainingLine);
return new OptionUniverse(date, symbol, csv);
}
/// <summary>
@@ -276,24 +336,13 @@ 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 static string ToCsv(Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, decimal? openInterest,
decimal? impliedVolatility, BaseGreeks greeks)
public string ToCsv()
{
return $"{symbol.ID},{symbol.Value},{open},{high},{low},{close},{volume},"
+ $"{openInterest},{impliedVolatility},{greeks?.Delta},{greeks?.Gamma},{greeks?.Vega},{greeks?.Theta},{greeks?.Rho}";
return $"{Symbol.ID},{Symbol.Value},{Open},{High},{Low},{Close},{Volume}," +
$"{_openInterest},{_impliedVolatility},{_greeks?.Delta},{_greeks?.Gamma},{_greeks?.Vega},{_greeks?.Theta},{_greeks?.Rho}";
}
/// <summary>
@@ -319,6 +368,11 @@ 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)
{
return !_csvLine.IsNullOrEmpty() ? _csvLine[index].ToDecimal() : decimal.Zero;
}
private void ThrowIfNotAnOption(string propertyName)
{
if (!Symbol.SecurityType.IsOption())
@@ -326,64 +380,5 @@ namespace QuantConnect.Data.UniverseSelection
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

@@ -97,7 +97,17 @@ namespace QuantConnect.Data.UniverseSelection
/// <returns>The data that passes the filter</returns>
public override IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data)
{
return _model.InvokeMethodAndEnumerate<Symbol>(nameof(SelectSymbols), utcTime, data);
using (Py.GIL())
{
var symbols = _model.InvokeMethod(nameof(SelectSymbols), utcTime, data);
var iterator = symbols.GetIterator();
foreach (PyObject symbol in iterator)
{
yield return symbol.GetAndDispose<Symbol>();
}
iterator.Dispose();
symbols.Dispose();
}
}
/// <summary>
@@ -111,11 +121,18 @@ namespace QuantConnect.Data.UniverseSelection
public override IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security, DateTime currentTimeUtc, DateTime maximumEndTimeUtc,
ISubscriptionDataConfigService subscriptionService)
{
var requests = _model.InvokeMethodAndEnumerate<SubscriptionRequest>(nameof(GetSubscriptionRequests), security, currentTimeUtc,
maximumEndTimeUtc, subscriptionService);
foreach (var subscriptionRequest in requests)
using (Py.GIL())
{
yield return new SubscriptionRequest(subscriptionRequest, universe: this);
var subscriptionRequests = _model.InvokeMethod(nameof(GetSubscriptionRequests), security, currentTimeUtc,
maximumEndTimeUtc, subscriptionService);
var iterator = subscriptionRequests.GetIterator();
foreach (PyObject request in iterator)
{
var subscriptionRequest = request.GetAndDispose<SubscriptionRequest>();
yield return new SubscriptionRequest(subscriptionRequest, universe:this);
}
iterator.Dispose();
subscriptionRequests.Dispose();
}
}
}

Some files were not shown because too many files have changed in this diff Show More