Files
quantconnect--lean/Algorithm.Framework/Alphas/AlphaStreamAlphaModule.cs
T
Martin-Molinero ad865e2a53
Regression Tests / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Add new CustomWeight, AlphaModel for AlphaStreams (#6052)
- Add new CustomWeight PCM for alpha streams
- Add new AlphaStreams AlphaModule that will handle security additions
  and removals, removing this logic from AlphaStreamsBasicTemplateAlgo
2021-11-15 14:33:16 -03:00

121 lines
5.0 KiB
C#

/*
* 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.Linq;
using QuantConnect.Data;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Data.Custom.AlphaStreams;
namespace QuantConnect.Algorithm.Framework.Alphas
{
/// <summary>
/// Alpha model that will handle adding and removing securities from the algorithm based on the current portfolio of the different alphas
/// </summary>
public sealed class AlphaStreamAlphaModule : AlphaModel
{
private Dictionary<Symbol, HashSet<Symbol>> _symbolsPerAlpha = new Dictionary<Symbol, HashSet<Symbol>>();
/// <summary>
/// Initialize new <see cref="AlphaStreamAlphaModule"/>
/// </summary>
public AlphaStreamAlphaModule(string name = null)
{
Name = name ?? "AlphaStreamAlphaModule";
}
/// <summary>
/// Updates this alpha model with the latest data from the algorithm.
/// This is called each time the algorithm receives data for subscribed securities
/// </summary>
/// <param name="algorithm">The algorithm instance</param>
/// <param name="data">The new data available</param>
/// <returns>The new insights generated</returns>
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
foreach (var portfolioState in data.Get<AlphaStreamsPortfolioState>().Values)
{
ProcessPortfolioState(algorithm, portfolioState);
}
return Enumerable.Empty<Insight>();
}
/// <summary>
/// Event fired each time the we add/remove securities from the data feed
/// </summary>
/// <param name="algorithm">The algorithm instance that experienced the change in securities</param>
/// <param name="changes">The security additions and removals from the algorithm</param>
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
changes.FilterCustomSecurities = false;
foreach (var addedSecurity in changes.AddedSecurities)
{
if (addedSecurity.Symbol.IsCustomDataType<AlphaStreamsPortfolioState>())
{
if (!_symbolsPerAlpha.ContainsKey(addedSecurity.Symbol))
{
_symbolsPerAlpha[addedSecurity.Symbol] = new HashSet<Symbol>();
}
// warmup alpha state, adding target securities
ProcessPortfolioState(algorithm, addedSecurity.Cache.GetData<AlphaStreamsPortfolioState>());
}
}
algorithm.Log($"OnSecuritiesChanged: {changes}");
}
/// <summary>
/// Will handle adding and removing securities from the algorithm based on the current portfolio of the different alphas
/// </summary>
private void ProcessPortfolioState(QCAlgorithm algorithm, AlphaStreamsPortfolioState portfolioState)
{
if (portfolioState == null)
{
return;
}
var alphaId = portfolioState.Symbol;
if (!_symbolsPerAlpha.TryGetValue(alphaId, out var currentSymbols))
{
_symbolsPerAlpha[alphaId] = currentSymbols = new HashSet<Symbol>();
}
var newSymbols = new HashSet<Symbol>(currentSymbols.Count);
foreach (var symbol in portfolioState.PositionGroups?.SelectMany(positionGroup => positionGroup.Positions).Select(state => state.Symbol) ?? Enumerable.Empty<Symbol>())
{
// only add it if it's not used by any alpha (already added check)
if (newSymbols.Add(symbol) && !UsedBySomeAlpha(symbol))
{
algorithm.AddSecurity(symbol,
resolution: algorithm.UniverseSettings.Resolution,
extendedMarketHours: algorithm.UniverseSettings.ExtendedMarketHours);
}
}
_symbolsPerAlpha[alphaId] = newSymbols;
foreach (var symbol in currentSymbols.Where(symbol => !UsedBySomeAlpha(symbol)))
{
algorithm.RemoveSecurity(symbol);
}
}
private bool UsedBySomeAlpha(Symbol asset)
{
return _symbolsPerAlpha.Any(pair => pair.Value.Contains(asset));
}
}
}