chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,120 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Python.Runtime;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Util;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that combines multiple universe
/// selection models into a single model.
/// </summary>
public class CompositeUniverseSelectionModel : UniverseSelectionModel
{
private readonly List<IUniverseSelectionModel> _universeSelectionModels = new List<IUniverseSelectionModel>();
private bool _alreadyCalledCreateUniverses;
/// <summary>
/// Initializes a new instance of the <see cref="CompositeUniverseSelectionModel"/> class
/// </summary>
/// <param name="universeSelectionModels">The individual universe selection models defining this composite model</param>
public CompositeUniverseSelectionModel(params IUniverseSelectionModel[] universeSelectionModels)
{
if (universeSelectionModels.IsNullOrEmpty())
{
throw new ArgumentException("Must specify at least 1 universe selection model for the CompositeUniverseSelectionModel");
}
_universeSelectionModels.AddRange(universeSelectionModels);
}
/// <summary>
/// Initializes a new instance of the <see cref="CompositeUniverseSelectionModel"/> class
/// </summary>
/// <param name="universeSelectionModels">The individual universe selection models defining this composite model</param>
public CompositeUniverseSelectionModel(params PyObject[] universeSelectionModels)
{
if (universeSelectionModels.IsNullOrEmpty())
{
throw new ArgumentException("Must specify at least 1 universe selection model for the CompositeUniverseSelectionModel");
}
foreach (var pyUniverseSelectionModel in universeSelectionModels)
{
AddUniverseSelection(pyUniverseSelectionModel);
}
}
/// <summary>
/// Adds a new <see cref="IUniverseSelectionModel"/>
/// </summary>
/// <param name="universeSelectionModel">The universe selection model to add</param>
public void AddUniverseSelection(IUniverseSelectionModel universeSelectionModel)
{
_universeSelectionModels.Add(universeSelectionModel);
}
/// <summary>
/// Adds a new <see cref="IUniverseSelectionModel"/>
/// </summary>
/// <param name="pyUniverseSelectionModel">The universe selection model to add</param>
public void AddUniverseSelection(PyObject pyUniverseSelectionModel)
{
IUniverseSelectionModel selectionModel;
if (!pyUniverseSelectionModel.TryConvert(out selectionModel))
{
selectionModel = new UniverseSelectionModelPythonWrapper(pyUniverseSelectionModel);
}
_universeSelectionModels.Add(selectionModel);
}
/// <summary>
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
/// </summary>
public override DateTime GetNextRefreshTimeUtc()
{
return _universeSelectionModels.Min(model => model.GetNextRefreshTimeUtc());
}
/// <summary>
/// Creates the universes for this algorithm.
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <returns>The universes to be used by the algorithm</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
foreach (var universeSelectionModel in _universeSelectionModels)
{
var selectionRefreshTime = universeSelectionModel.GetNextRefreshTimeUtc();
var refreshTime = algorithm.UtcTime >= selectionRefreshTime;
if (!_alreadyCalledCreateUniverses // first initial call
|| refreshTime
|| selectionRefreshTime == DateTime.MaxValue)
{
foreach (var universe in universeSelectionModel.CreateUniverses(algorithm))
{
yield return universe;
}
}
}
_alreadyCalledCreateUniverses = true;
}
}
}
+69
View File
@@ -0,0 +1,69 @@
/*
* 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.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Defines a universe as a set of dynamically set symbols.
/// </summary>
public class CustomUniverse : UserDefinedUniverse
{
/// <summary>
/// Creates a new instance of the <see cref="CustomUniverse"/>
/// </summary>
public CustomUniverse(SubscriptionDataConfig configuration,
UniverseSettings universeSettings,
TimeSpan interval,
Func<DateTime, IEnumerable<string>> selector)
: base(configuration, universeSettings, interval, selector)
{
}
/// <summary>
/// Gets the subscription requests to be added for the specified security
/// </summary>
/// <param name="security">The security to get subscriptions for</param>
/// <param name="currentTimeUtc">The current time in utc. This is the frontier time of the algorithm</param>
/// <param name="maximumEndTimeUtc">The max end time</param>
/// <param name="subscriptionService">Instance which implements <see cref="ISubscriptionDataConfigService"/> interface</param>
/// <returns>All subscriptions required by this security</returns>
public override IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security, DateTime currentTimeUtc, DateTime maximumEndTimeUtc,
ISubscriptionDataConfigService subscriptionService)
{
// CustomUniverse will return any existing SDC for the symbol, else will create new, using universe settings.
var existingSubscriptionDataConfigs = subscriptionService.GetSubscriptionDataConfigs(security.Symbol);
if (existingSubscriptionDataConfigs.Any())
{
return existingSubscriptionDataConfigs.Select(
config => new SubscriptionRequest(isUniverseSubscription: false,
universe: this,
security: security,
configuration: config,
startTimeUtc: currentTimeUtc,
endTimeUtc: maximumEndTimeUtc));
}
return base.GetSubscriptionRequests(security, currentTimeUtc, maximumEndTimeUtc, subscriptionService);
}
}
}
@@ -0,0 +1,150 @@
/*
* 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 Python.Runtime;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that simply
/// subscribes to the specified set of symbols
/// </summary>
public class CustomUniverseSelectionModel : UniverseSelectionModel
{
private static readonly MarketHoursDatabase MarketHours = MarketHoursDatabase.FromDataFolder();
private readonly Symbol _symbol;
private readonly Func<DateTime, IEnumerable<string>> _selector;
private readonly UniverseSettings _universeSettings;
private readonly TimeSpan _interval;
/// <summary>
/// Initializes a new instance of the <see cref="CustomUniverseSelectionModel"/> class
/// for <see cref="Market.USA"/> and <see cref="SecurityType.Equity"/>
/// using the algorithm's universe settings
/// </summary>
/// <param name="name">A unique name for this universe</param>
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
public CustomUniverseSelectionModel(string name, Func<DateTime, IEnumerable<string>> selector)
: this(SecurityType.Equity, name, Market.USA, selector, null, Time.OneDay)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomUniverseSelectionModel"/> class
/// for <see cref="Market.USA"/> and <see cref="SecurityType.Equity"/>
/// using the algorithm's universe settings
/// </summary>
/// <param name="name">A unique name for this universe</param>
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
public CustomUniverseSelectionModel(string name, PyObject selector)
: this(SecurityType.Equity, name, Market.USA, selector, null, Time.OneDay)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomUniverseSelectionModel"/> class
/// </summary>
/// <param name="securityType">The security type of the universe</param>
/// <param name="name">A unique name for this universe</param>
/// <param name="market">The market of the universe</param>
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
/// <param name="universeSettings">The settings used when adding symbols to the algorithm, specify null to use algorithm.UniverseSettings</param>
/// <param name="interval">The interval at which selection should be performed</param>
public CustomUniverseSelectionModel(SecurityType securityType, string name, string market, Func<DateTime, IEnumerable<string>> selector, UniverseSettings universeSettings, TimeSpan interval)
{
_interval = interval;
_selector = selector;
_universeSettings = universeSettings;
_symbol = Symbol.Create($"{name}-{securityType}-{market}", securityType, market);
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomUniverseSelectionModel"/> class
/// </summary>
/// <param name="securityType">The security type of the universe</param>
/// <param name="name">A unique name for this universe</param>
/// <param name="market">The market of the universe</param>
/// <param name="selector">Function delegate that accepts a DateTime and returns a collection of string symbols</param>
/// <param name="universeSettings">The settings used when adding symbols to the algorithm, specify null to use algorithm.UniverseSettings</param>
/// <param name="interval">The interval at which selection should be performed</param>
public CustomUniverseSelectionModel(SecurityType securityType, string name, string market, PyObject selector, UniverseSettings universeSettings, TimeSpan interval)
: this(
securityType,
name,
market,
selector.SafeAs<Func<DateTime, object>>().ConvertToUniverseSelectionStringDelegate(),
universeSettings,
interval
)
{
}
/// <summary>
/// Creates the universes for this algorithm. Called at algorithm start.
/// </summary>
/// <returns>The universes defined by this model</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
var universeSettings = _universeSettings ?? algorithm.UniverseSettings;
var entry = MarketHours.GetEntry(_symbol.ID.Market, (string)null, _symbol.SecurityType);
var config = new SubscriptionDataConfig(
universeSettings.Resolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar),
_symbol,
universeSettings.Resolution,
entry.DataTimeZone,
entry.ExchangeHours.TimeZone,
universeSettings.FillForward,
universeSettings.ExtendedMarketHours,
true
);
yield return new CustomUniverse(config, universeSettings, _interval, dt => Select(algorithm, dt));
}
/// <summary>
///
/// </summary>
/// <param name="algorithm"></param>
/// <param name="date"></param>
/// <returns></returns>
public virtual IEnumerable<string> Select(QCAlgorithm algorithm, DateTime date)
{
// Check if this method was overridden in Python
if (TryInvokePythonOverride(nameof(Select), out IEnumerable<string> result, algorithm, date))
{
return result;
}
if (_selector == null)
{
throw new ArgumentNullException(nameof(_selector));
}
return _selector(date);
}
/// <summary>
/// Returns a string that represents the current object
/// </summary>
public override string ToString() => _symbol.Value;
}
}
@@ -0,0 +1,40 @@
/*
* 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.UniverseSelection;
using QuantConnect.Interfaces;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Algorithm framework model that defines the universes to be used by an algorithm
/// </summary>
public interface IUniverseSelectionModel
{
/// <summary>
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
/// </summary>
DateTime GetNextRefreshTimeUtc();
/// <summary>
/// Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <returns>The universes to be used by the algorithm</returns>
IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm);
}
}
+85
View File
@@ -0,0 +1,85 @@
/*
* 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.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Defines a universe as a set of manually set symbols. This differs from <see cref="UserDefinedUniverse"/>
/// in that these securities were not added via AddSecurity.
/// </summary>
/// <remarks>Incompatible with multiple <see cref="Universe"/> selecting the same <see cref="Symbol"/>.
/// with different <see cref="SubscriptionDataConfig"/>. More information <see cref="GetSubscriptionRequests"/></remarks>
public class ManualUniverse : UserDefinedUniverse
{
/// <summary>
/// Creates a new instance of the <see cref="ManualUniverse"/>
/// </summary>
public ManualUniverse(SubscriptionDataConfig configuration,
UniverseSettings universeSettings,
IEnumerable<Symbol> symbols)
: base(configuration, universeSettings, Time.MaxTimeSpan, symbols)
{
}
/// <summary>
/// Creates a new instance of the <see cref="ManualUniverse"/>
/// </summary>
public ManualUniverse(SubscriptionDataConfig configuration,
UniverseSettings universeSettings,
Symbol[] symbols)
: base(configuration, universeSettings, Time.MaxTimeSpan, symbols)
{
}
/// <summary>
/// Gets the subscription requests to be added for the specified security
/// </summary>
/// <param name="security">The security to get subscriptions for</param>
/// <param name="currentTimeUtc">The current time in utc. This is the frontier time of the algorithm</param>
/// <param name="maximumEndTimeUtc">The max end time</param>
/// <param name="subscriptionService">Instance which implements <see cref="ISubscriptionDataConfigService"/> interface</param>
/// <returns>All subscriptions required by this security</returns>
public override IEnumerable<SubscriptionRequest> GetSubscriptionRequests(Security security, DateTime currentTimeUtc, DateTime maximumEndTimeUtc,
ISubscriptionDataConfigService subscriptionService)
{
// ManualUniverse will return any existing SDC for the symbol, else will create new, using universe settings.
// This is for maintaining existing behavior and preventing breaking changes: Specifically motivated
// by usages of Algorithm.Securities.Keys as constructor parameter of the ManualUniverseSelectionModel.
// Symbols at Algorithm.Securities.Keys added by Addxxx() calls will already be added by the UserDefinedUniverse.
var existingSubscriptionDataConfigs = subscriptionService.GetSubscriptionDataConfigs(security.Symbol);
if (existingSubscriptionDataConfigs.Any())
{
return existingSubscriptionDataConfigs.Select(
config => new SubscriptionRequest(isUniverseSubscription: false,
universe: this,
security: security,
configuration: config,
startTimeUtc: currentTimeUtc,
endTimeUtc: maximumEndTimeUtc));
}
return base.GetSubscriptionRequests(security, currentTimeUtc, maximumEndTimeUtc, subscriptionService);
}
}
}
@@ -0,0 +1,133 @@
/*
* 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.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that simply
/// subscribes to the specified set of symbols
/// </summary>
public class ManualUniverseSelectionModel : UniverseSelectionModel
{
private static readonly MarketHoursDatabase MarketHours = MarketHoursDatabase.FromDataFolder();
private readonly IReadOnlyList<Symbol> _symbols;
private readonly UniverseSettings _universeSettings;
/// <summary>
/// Initializes a new instance of the <see cref="ManualUniverseSelectionModel"/> class using the algorithm's
/// security initializer and universe settings
/// </summary>
public ManualUniverseSelectionModel()
: this(Enumerable.Empty<Symbol>())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ManualUniverseSelectionModel"/> class using the algorithm's
/// security initializer and universe settings
/// </summary>
/// <param name="symbols">The symbols to subscribe to.
/// Should not send in symbols at <see cref="QCAlgorithm.Securities"/> since those will be managed by the <see cref="UserDefinedUniverse"/></param>
public ManualUniverseSelectionModel(IEnumerable<Symbol> symbols)
: this(symbols.ToArray())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ManualUniverseSelectionModel"/> class using the algorithm's
/// security initializer and universe settings
/// </summary>
/// <param name="symbols">The symbols to subscribe to
/// Should not send in symbols at <see cref="QCAlgorithm.Securities"/> since those will be managed by the <see cref="UserDefinedUniverse"/></param>
public ManualUniverseSelectionModel(params Symbol[] symbols)
: this (symbols?.AsEnumerable(), null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ManualUniverseSelectionModel"/> class
/// </summary>
/// <param name="symbols">The symbols to subscribe to
/// Should not send in symbols at <see cref="QCAlgorithm.Securities"/> since those will be managed by the <see cref="UserDefinedUniverse"/></param>
/// <param name="universeSettings">The settings used when adding symbols to the algorithm, specify null to use algorithm.UniverseSettings</param>
public ManualUniverseSelectionModel(IEnumerable<Symbol> symbols, UniverseSettings universeSettings)
{
if (symbols == null)
{
throw new ArgumentNullException(nameof(symbols));
}
_symbols = symbols.Where(s => !s.IsCanonical()).ToList();
_universeSettings = universeSettings;
foreach (var symbol in _symbols)
{
SymbolCache.Set(symbol.Value, symbol);
}
}
/// <summary>
/// Creates the universes for this algorithm.
/// Called at algorithm start.
/// </summary>
/// <returns>The universes defined by this model</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
var universeSettings = _universeSettings ?? algorithm.UniverseSettings;
var resolution = universeSettings.Resolution;
var type = resolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar);
// universe per security type/market
foreach (var grp in _symbols.GroupBy(s => new { s.ID.Market, s.SecurityType }))
{
MarketHoursDatabase.Entry entry;
var market = grp.Key.Market;
var securityType = grp.Key.SecurityType;
var hashCode = 1;
foreach (var symbol in grp)
{
hashCode = hashCode * 31 + symbol.GetHashCode();
}
var universeSymbol = Symbol.Create($"manual-universe-selection-model-{securityType}-{market}-{hashCode}", securityType, market);
if (securityType == SecurityType.Base)
{
// add an entry for this custom universe symbol -- we don't really know the time zone for sure,
// but we set it to TimeZones.NewYork in AddData, also, since this is a manual universe, the time
// zone doesn't actually matter since this universe specifically doesn't do anything with data.
var symbolString = MarketHoursDatabase.GetDatabaseSymbolKey(universeSymbol);
var alwaysOpen = SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork);
entry = MarketHours.SetEntry(market, symbolString, securityType, alwaysOpen, TimeZones.NewYork);
}
else
{
entry = MarketHours.GetEntry(market, (string) null, securityType);
}
var config = new SubscriptionDataConfig(type, universeSymbol, resolution, entry.DataTimeZone, entry.ExchangeHours.TimeZone, false, false, true);
yield return new ManualUniverse(config, universeSettings, grp);
}
}
}
}
@@ -0,0 +1,66 @@
# 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 *
from clr import GetClrType as typeof
from Selection.UniverseSelectionModel import UniverseSelectionModel
from itertools import groupby
class ManualUniverseSelectionModel(UniverseSelectionModel):
'''Provides an implementation of IUniverseSelectionModel that simply subscribes to the specified set of symbols'''
def __init__(self, symbols = list(), universe_settings = None):
self.marketHours = MarketHoursDatabase.from_data_folder()
self.symbols = symbols
self.universe_settings = universe_settings
for symbol in symbols:
SymbolCache.set(symbol.Value, symbol)
def create_universes(self, algorithm: QCAlgorithm) -> list[Universe]:
'''Creates the universes for this algorithm. Called once after IAlgorithm.Initialize
Args:
algorithm: The algorithm instance to create universes for</param>
Returns:
The universes to be used by the algorithm'''
universe_settings = self.universe_settings \
if self.universe_settings is not None else algorithm.universe_settings
resolution = universe_settings.resolution
type = typeof(Tick) if resolution == Resolution.TICK else typeof(TradeBar)
universes = list()
# universe per security type/market
self.symbols = sorted(self.symbols, key=lambda s: (s.id.market, s.security_type))
for key, grp in groupby(self.symbols, lambda s: (s.id.market, s.security_type)):
market = key[0]
security_type = key[1]
universe_symbol = Symbol.create(f"manual-universe-selection-model-{security_type}-{market}", security_type, market)
if security_type == SecurityType.BASE:
# add an entry for this custom universe symbol -- we don't really know the time zone for sure,
# but we set it to TimeZones.NewYork in AddData, also, since this is a manual universe, the time
# zone doesn't actually matter since this universe specifically doesn't do anything with data.
symbol_string = MarketHoursDatabase.get_database_symbol_key(universe_symbol)
always_open = SecurityExchangeHours.always_open(TimeZones.NEW_YORK)
entry = self.marketHours.set_entry(market, symbol_string, security_type, always_open, TimeZones.NEW_YORK)
else:
entry = self.marketHours.get_entry(market, None, security_type)
config = SubscriptionDataConfig(type, universe_symbol, resolution, entry.data_time_zone, entry.exchange_hours.time_zone, False, False, True)
universes.append( ManualUniverse(config, universe_settings, list(grp)))
return universes
@@ -0,0 +1,36 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides a null implementation of <see cref="IUniverseSelectionModel"/>
/// </summary>
public class NullUniverseSelectionModel : UniverseSelectionModel
{
/// <summary>
/// Creates the universes for this algorithm.
/// Called at algorithm start.
/// </summary>
/// <returns>The universes defined by this model</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
yield break;
}
}
}
@@ -0,0 +1,114 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Securities.Future;
using Python.Runtime;
namespace QuantConnect.Algorithm.Selection
{
/// <summary>
/// This universe selection model will chain to the security changes of a given <see cref="Universe"/> selection
/// output and create a new <see cref="OptionChainUniverse"/> for each of them
/// </summary>
public class OptionChainedUniverseSelectionModel : UniverseSelectionModel
{
private DateTime _nextRefreshTimeUtc;
private IEnumerable<Symbol> _currentSymbols;
private readonly UniverseSettings _universeSettings;
private readonly Func<OptionFilterUniverse, OptionFilterUniverse> _optionFilter;
/// <summary>
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
/// </summary>
public override DateTime GetNextRefreshTimeUtc() => _nextRefreshTimeUtc;
/// <summary>
/// Creates a new instance of <see cref="OptionChainedUniverseSelectionModel"/>
/// </summary>
/// <param name="universe">The universe we want to chain to</param>
/// <param name="optionFilter">The option filter universe to use</param>
/// <param name="universeSettings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
public OptionChainedUniverseSelectionModel(Universe universe,
Func<OptionFilterUniverse, OptionFilterUniverse> optionFilter,
UniverseSettings universeSettings = null)
{
_optionFilter = optionFilter;
_universeSettings = universeSettings;
_nextRefreshTimeUtc = DateTime.MaxValue;
_currentSymbols = Enumerable.Empty<Symbol>();
universe.SelectionChanged += (sender, args) =>
{
// the universe we were watching changed, this will trigger a call to CreateUniverses
_nextRefreshTimeUtc = DateTime.MinValue;
// We must create the new option Symbol using the CreateOption(Symbol, ...) overload.
// Otherwise, we'll end up loading equity data for the selected Symbol, which won't
// work whenever we're loading options data for any non-equity underlying asset class.
_currentSymbols = ((Universe.SelectionEventArgs)args).CurrentSelection
.Select(symbol => Symbol.CreateOption(
symbol,
symbol.ID.Market,
symbol.SecurityType.DefaultOptionStyle(),
default(OptionRight),
0m,
SecurityIdentifier.DefaultDate))
.ToList();
};
}
/// <summary>
/// Creates a new instance of <see cref="OptionChainedUniverseSelectionModel"/>
/// </summary>
/// <param name="universe">The universe we want to chain to</param>
/// <param name="optionFilter">The python option filter universe to use</param>
/// <param name="universeSettings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
public OptionChainedUniverseSelectionModel(Universe universe,
PyObject optionFilter,
UniverseSettings universeSettings = null): this(universe, ConvertOptionFilter(optionFilter), universeSettings)
{
}
/// <summary>
/// Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <returns>The universes to be used by the algorithm</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
_nextRefreshTimeUtc = DateTime.MaxValue;
foreach (var optionSymbol in _currentSymbols)
{
yield return algorithm.CreateOptionChain(optionSymbol, _optionFilter, _universeSettings);
}
}
private static Func<OptionFilterUniverse, OptionFilterUniverse> ConvertOptionFilter(PyObject optionFilter)
{
using (Py.GIL())
{
return optionFilter.SafeAs<Func<OptionFilterUniverse, OptionFilterUniverse>>();
}
}
}
}
@@ -0,0 +1,107 @@
/*
* 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 System.Collections.Generic;
using System.Collections.Specialized;
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Algorithm.Selection
{
/// <summary>
/// This universe will hold single option contracts and their underlying, managing removals and additions
/// </summary>
public class OptionContractUniverse : UserDefinedUniverse
{
private readonly HashSet<Symbol> _symbols;
/// <summary>
/// Creates a new empty instance
/// </summary>
/// <param name="configuration">The universe configuration to use</param>
/// <param name="universeSettings">The universe settings to use</param>
public OptionContractUniverse(SubscriptionDataConfig configuration, UniverseSettings universeSettings)
: base(AdjustUniverseConfiguration(configuration), universeSettings, Time.EndOfTimeTimeSpan,
// Argument isn't used since we override 'SelectSymbols'
Enumerable.Empty<Symbol>())
{
_symbols = new HashSet<Symbol>();
}
/// <summary>
/// Returns the symbols defined by the user for this universe
/// </summary>
/// <param name="utcTime">The current utc time</param>
/// <param name="data">The symbols to remain in the universe</param>
/// <returns>The data that passes the filter</returns>
public override IEnumerable<Symbol> SelectSymbols(DateTime utcTime, BaseDataCollection data)
{
return _symbols;
}
/// <summary>
/// Event invocator for the <see cref="UserDefinedUniverse.CollectionChanged"/> event
/// </summary>
/// <param name="e">The notify collection changed event arguments</param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
var removedSymbol = (Symbol)e.OldItems[0];
_symbols.Remove(removedSymbol);
// the option has been removed! This can happen when the user manually removed the option contract we remove the underlying
// but only if there isn't any other option selected using the same underlying!
if (removedSymbol.SecurityType.IsOption()
&& !_symbols.Any(symbol => symbol.SecurityType.IsOption() && symbol.Underlying == removedSymbol.Underlying))
{
Remove(removedSymbol.Underlying);
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
// QCAlgorithm.AddOptionContract will add both underlying and option contract
_symbols.Add((Symbol)e.NewItems[0]);
}
base.OnCollectionChanged(e);
}
/// <summary>
/// Creates a user defined universe symbol
/// </summary>
/// <param name="market">The market</param>
/// <param name="securityType">The underlying option security type</param>
/// <returns>A symbol for user defined universe of the specified security type and market</returns>
public static Symbol CreateSymbol(string market, SecurityType securityType)
{
var ticker = $"qc-universe-optioncontract-{securityType.SecurityTypeToLower()}-{market.ToLowerInvariant()}";
var underlying = Symbol.Create(ticker, securityType, market);
var sid = SecurityIdentifier.GenerateOption(SecurityIdentifier.DefaultDate, underlying.ID, market, 0, 0, 0);
return new Symbol(sid, ticker);
}
/// <summary>
/// Make sure the configuration of the universe is what we want
/// </summary>
private static SubscriptionDataConfig AdjustUniverseConfiguration(SubscriptionDataConfig input)
{
return new SubscriptionDataConfig(input, fillForward: false);
}
}
}
@@ -0,0 +1,55 @@
/*
* 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.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Python;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides a base class for universe selection models.
/// </summary>
public class UniverseSelectionModel : BasePythonWrapper<UniverseSelectionModel>, IUniverseSelectionModel
{
/// <summary>
/// Initializes a new instance of the <see cref="UniverseSelectionModel"/> class.
/// </summary>
public UniverseSelectionModel()
{
}
/// <summary>
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
/// </summary>
public virtual DateTime GetNextRefreshTimeUtc()
{
return DateTime.MaxValue;
}
/// <summary>
/// Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <returns>The universes to be used by the algorithm</returns>
public virtual IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
throw new NotImplementedException("Types deriving from 'UniverseSelectionModel' must implement the 'IEnumerable<Universe> CreateUniverses(QCAlgorithm) method.");
}
}
}
@@ -0,0 +1,33 @@
# 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 UniverseSelectionModel:
'''Provides a base class for universe selection models.'''
def get_next_refresh_time_utc(self) -> datetime:
'''Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.'''
if hasattr(self, "GetNextRefreshTimeUtc") and callable(self.GetNextRefreshTimeUtc):
return self.GetNextRefreshTimeUtc()
return datetime.max
def create_universes(self, algorithm: QCAlgorithm) -> list[Universe]:
'''Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
Args:
algorithm: The algorithm instance to create universes for</param>
Returns:
The universes to be used by the algorithm'''
if hasattr(self, "CreateUniverses") and callable(self.CreateUniverses):
return self.CreateUniverses(algorithm)
raise NotImplementedError("Types deriving from 'UniverseSelectionModel' must implement the 'def CreateUniverses(QCAlgorithm) method.")
@@ -0,0 +1,82 @@
/*
* 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 Python.Runtime;
using QuantConnect.Data.UniverseSelection;
using System;
using System.Collections.Generic;
using QuantConnect.Interfaces;
using QuantConnect.Python;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that wraps a <see cref="PyObject"/> object
/// </summary>
public class UniverseSelectionModelPythonWrapper : UniverseSelectionModel
{
private readonly bool _modelHasGetNextRefreshTime;
/// <summary>
/// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.
/// </summary>
public override DateTime GetNextRefreshTimeUtc()
{
if (!_modelHasGetNextRefreshTime)
{
return DateTime.MaxValue;
}
return InvokeMethod<DateTime>(nameof(GetNextRefreshTimeUtc));
}
/// <summary>
/// Constructor for initialising the <see cref="IUniverseSelectionModel"/> class with wrapped <see cref="PyObject"/> object
/// </summary>
/// <param name="model">Model defining universes for the algorithm</param>
public UniverseSelectionModelPythonWrapper(PyObject model)
{
SetPythonInstance(model, false);
using (Py.GIL())
{
_modelHasGetNextRefreshTime = HasAttr(nameof(IUniverseSelectionModel.GetNextRefreshTimeUtc));
foreach (var attributeName in new[] { "CreateUniverses" })
{
if (!HasAttr(attributeName))
{
throw new NotImplementedException($"UniverseSelectionModel.{attributeName} must be implemented. Please implement this missing method on {model.GetPythonType()}");
}
}
var methodName = nameof(SetPythonInstance);
if (HasAttr(methodName))
{
InvokeMethod(methodName, model);
}
}
}
/// <summary>
/// Creates the universes for this algorithm. Called once after <see cref="IAlgorithm.Initialize"/>
/// </summary>
/// <param name="algorithm">The algorithm instance to create universes for</param>
/// <returns>The universes to be used by the algorithm</returns>
public override IEnumerable<Universe> CreateUniverses(QCAlgorithm algorithm)
{
return InvokeMethodAndEnumerate<Universe>(nameof(CreateUniverses), algorithm);
}
}
}