| Overall Statistics |
|
Total Trades 444 Average Win 0.99% Average Loss -1.27% Compounding Annual Return 24.237% Drawdown 11.400% Expectancy 0.166 Net Profit 60.904% Sharpe Ratio 0.884 Loss Rate 34% Win Rate 66% Profit-Loss Ratio 0.78 Alpha 0.529 Beta -19.822 Annual Standard Deviation 0.229 Annual Variance 0.053 Information Ratio 0.812 Tracking Error 0.229 Treynor Ratio -0.01 Total Fees $603.04 |
namespace QuantConnect.Algorithm.CSharp
{
public class MeowChickyIronmanFramework : QCAlgorithmFramework
{
public override void Initialize()
{
SetStartDate(2017, 1, 1); //Set Start Date
SetEndDate(DateTime.Now.AddDays(-1)); //Set End Date
SetCash(2000); //Set Strategy Cash
/// Available Indicators: { "ema","rsi" }
string[] indicators = { "rsi" };
UniverseSettings.Resolution = Resolution.Minute;
SetWarmup(TimeSpan.FromDays(50));
SetUniverseSelection(new MeowChickyUniverseSelectionModel());
SetAlpha(new MeowChickyAlphaModel(indicators, resolution: UniverseSettings.Resolution));
//SetAlpha(new EmaCrossAlphaModel(10,50,resolution: UniverseSettings.Resolution));
//SetAlpha(new CompositeAlphaModel(
//new MeowChickyAlphaModel(indicators,10,50, resolution: UniverseSettings.Resolution),
//new EmaCrossAlphaModel(10,50,resolution: UniverseSettings.Resolution)
//));
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new StandardDeviationExecutionModel(60, 2, UniverseSettings.Resolution));
//SetExecution(new ImmediateExecutionModel());
SetRiskManagement(new CompositeRiskManagementModel(
//new MaximumSectorExposureRiskManagementModel(.45m),
//new MaximumUnrealizedProfitPercentPerSecurity(.15m),
new MaximumDrawdownPercentPerSecurity(.01m),
new TrailingStopRiskManagementModel(.02m)
));
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status.IsFill())
{
// Debug($"Purchased Stock: {orderEvent.Symbol}");
}
}
}
}/*
* 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.Fundamental;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Selection
{
/// <summary>
/// MeowChicky Universe Selection Model
/// </summary>
public class MeowChickyUniverseSelectionModel : FundamentalUniverseSelectionModel
{
private const int NumberOfSymbolsCoarse = 500;
private const int NumberOfSymbolsFine = 100;
// rebalances at the start of each month
private int _lastMonth = -1;
private readonly Dictionary<Symbol, decimal> _dollarVolumeBySymbol = new Dictionary<Symbol, decimal>();
/// <summary>
/// Initializes a new default instance of the <see cref="MeowChickyUniverseSelectionModel"/>
/// </summary>
public MeowChickyUniverseSelectionModel()
: base(true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MeowChickyUniverseSelectionModel"/>
/// </summary>
/// <param name="universeSettings">Universe settings defines what subscription properties will be applied to selected securities</param>
/// <param name="securityInitializer">Security initializer initializes newly selected securities</param>
public MeowChickyUniverseSelectionModel(UniverseSettings universeSettings, ISecurityInitializer securityInitializer)
: base(true, universeSettings, securityInitializer)
{
}
/// <summary>
/// The stocks must have fundamental data
/// The stock must have positive previous-day close price
/// The stock must have positive volume on the previous trading day
/// </summary>
public override IEnumerable<Symbol> SelectCoarse(QCAlgorithmFramework algorithm, IEnumerable<CoarseFundamental> coarse)
{
// materialize to collection if not already materialized
coarse = coarse as ICollection<CoarseFundamental> ?? coarse.ToList();
if (!coarse.Any())
{
return Universe.Unchanged;
}
var month = coarse.First().EndTime.Month;
if (month == _lastMonth)
{
return Universe.Unchanged;
}
_lastMonth = month;
// The stocks must have fundamental data
// The stock must have positive previous-day close price
// The stock must have positive volume on the previous trading day
var sortedByDollarVolume = from x in coarse
where x.HasFundamentalData && x.Volume > 0 && x.Price > 1m && x.Price < 20m
// Leave these nasdaq test symbols commented out for the time being.
//where x.Symbol != "ZWZZT SJURTIR7CJFP" ||
//x.Symbol != "ZXZZT SJTSDFE19S9X" || x.Symbol != "ZVZZT SJTSDFE19S9X" || x.Symbol != "ZWZZT" ||
//x.Symbol != "ZXZZT" || x.Symbol != "ZVZZT" || x.Symbol != "ECOM"
where x.Symbol != "ECOM"
orderby x.DollarVolume descending
select x;
var top = sortedByDollarVolume.Take(NumberOfSymbolsCoarse).ToList();
foreach (var i in top)
{
_dollarVolumeBySymbol[i.Symbol] = i.DollarVolume;
}
return top.Select(x => x.Symbol);
}
/// <summary>
/// The company's headquarter must in the U.S.
/// The stock must be traded on either the NYSE or NASDAQ
/// At least half a year since its initial public offering
/// The stock's market cap must be greater than 500 million
/// </summary>
public override IEnumerable<Symbol> SelectFine(QCAlgorithmFramework algorithm, IEnumerable<FineFundamental> fine)
{
// The company's headquarter must in the U.S.
// The stock must be traded on either the NYSE or NASDAQ
// At least half a year since its initial public offering
// The stock's market cap must be greater than 500 million
var filteredFine = (from x in fine
where
x.CompanyReference.CountryId == "USA" &&
(x.CompanyReference.PrimaryExchangeID == "NYS" || x.CompanyReference.PrimaryExchangeID == "NAS") &&
(algorithm.Time - x.SecurityReference.IPODate).Days > 180 &&
x.EarningReports.BasicAverageShares.ThreeMonths *
x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 500000000m &&
x.ValuationRatios.PSRatio < 1m &&
x.ValuationRatios.PricetoCashRatio < 15 &&
x.ValuationRatios.EVToEBITDA < 10 &&
x.ValuationRatios.PEGRatio < 1
select x).ToList();
// select stocks with top dollar volume in every single sector
//var myDict = new[] { "B", "I", "M", "N", "T", "U" }.ToDictionary(x => x, y => new List<FineFundamental>());
//var percent = (float)NumberOfSymbolsFine / filteredFine.Count;
//foreach (var key in myDict.Keys.ToList())
//{
var value = (from x in filteredFine
//where x.CompanyReference.IndustryTemplateCode == key
orderby _dollarVolumeBySymbol[x.Symbol] descending
select x).ToList();
//myDict[key] = value.Take((int)Math.Ceiling(value.Count * percent)).ToList();
//}
//var topFine = myDict.Values.ToList().SelectMany(x => x).Take(NumberOfSymbolsFine).ToList();
return value.Select(x => x.Symbol).Take(NumberOfSymbolsFine);
}
}
}/*
* 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.Consolidators;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Alphas
{
/// <summary>
/// Defines a custom alpha model that uses MACD crossovers. The MACD signal line is
/// used to generate up/down insights if it's stronger than the bounce threshold.
/// If the MACD signal is within the bounce threshold then a flat price insight is returned.
/// </summary>
public class MeowChickyAdxAlphaModel3 : AlphaModel
{
private readonly int _period;
private readonly Resolution _resolution;
//private const decimal BounceThresholdPercent = 0.01m;
private readonly Dictionary<Symbol, SymbolData> _symbolData;
/// <summary>
/// Initializes a new instance of the <see cref="MacdAlphaModel"/> class
/// </summary>
/// <param name="period">The ADX period</param>
/// <param name="resolution">The resolution of data sent into the MACD indicator</param>
public MeowChickyAdxAlphaModel3(
int period = 14,
Resolution resolution = Resolution.Daily
)
{
_period = period;
_resolution = resolution;
_symbolData = new Dictionary<Symbol, SymbolData>();
Name = $"{nameof(MeowChickyAdxAlphaModel3)}({period},{resolution})";
}
/// <summary>
/// Determines an insight for each security based on it's current Adx signal
/// </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(QCAlgorithmFramework algorithm, Slice data)
{
foreach (var sd in _symbolData.Values)
{
if (sd.Security.Price == 0) continue;
var direction = InsightDirection.Flat;
decimal magnitude = 0;
if (sd.ADX > 25 && sd.ADX != 0)
{
if (sd.ADX.PositiveDirectionalIndex > sd.ADX && sd.ADX.NegativeDirectionalIndex < sd.ADX)
{
direction = InsightDirection.Up;
}
}
else if (sd.ADX > 25 && sd.ADX != 0)
{
if (sd.ADX.NegativeDirectionalIndex > sd.ADX && sd.ADX.PositiveDirectionalIndex < sd.ADX)
{
direction = InsightDirection.Down;
}
}
// ignore signal for same direction as previous signal
if (direction == sd.PreviousDirection)
{
continue;
}
if (sd.ADX >25 && direction == InsightDirection.Up)
{
magnitude = ((sd.ADX.PositiveDirectionalIndex - sd.ADX.NegativeDirectionalIndex)/100)*(sd.ADX.NegativeDirectionalIndex);
}
else if (sd.ADX > 25 && direction == InsightDirection.Down)
{
magnitude = ((sd.ADX.NegativeDirectionalIndex - sd.ADX.PositiveDirectionalIndex)/100)*(sd.ADX.PositiveDirectionalIndex);
}
if (sd.ADX != 0)
{
var insightPeriod = _resolution.ToTimeSpan().Multiply(_period);
var insight = Insight.Price(sd.Security.Symbol, insightPeriod, direction, magnitude: Convert.ToDouble(magnitude));
sd.PreviousDirection = insight.Direction;
yield return insight;
}
}
}
/// <summary>
/// Event fired each time the we add/remove securities from the data feed.
/// This initializes the MACD for each added security and cleans up the indicator for each removed security.
/// </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(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
foreach (var added in changes.AddedSecurities)
{
if (_symbolData.ContainsKey(added.Symbol))
{
continue;
}
_symbolData.Add(added.Symbol, new SymbolData(algorithm, added, _period, _resolution));
}
foreach (var removed in changes.RemovedSecurities)
{
SymbolData data;
if (_symbolData.TryGetValue(removed.Symbol, out data))
{
// clean up our consolidator
algorithm.SubscriptionManager.RemoveConsolidator(data.Security.Symbol, data.Consolidator);
_symbolData.Remove(removed.Symbol);
}
}
}
class SymbolData
{
public InsightDirection? PreviousDirection { get; set; }
public readonly Security Security;
public readonly IDataConsolidator Consolidator;
public readonly AverageDirectionalIndex ADX;
public SymbolData(QCAlgorithmFramework algorithm, Security security, int period, Resolution resolution)
{
Security = security;
Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution);
algorithm.SubscriptionManager.AddConsolidator(security.Symbol, Consolidator);
ADX = new AverageDirectionalIndex("ADX", period);
TimeSpan timePeriod = TimeSpan.FromDays(period);
if (resolution == Resolution.Hour)
{
timePeriod = TimeSpan.FromHours(period);
}
else if (resolution == Resolution.Minute)
{
timePeriod = TimeSpan.FromMinutes(period);
}
else if (resolution == Resolution.Second)
{
timePeriod = TimeSpan.FromSeconds(period);
}
var history = algorithm.History(security.Symbol, timePeriod);
foreach (var bar in history)
{
ADX.Update(bar);
}
algorithm.RegisterIndicator(security.Symbol, ADX, Consolidator);
}
}
}
}/*
* 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.Indicators;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.Framework.Alphas
{
/// <summary>
/// Alpha model that uses an EMA cross to create insights
/// </summary>
public class MeowChickyAlphaModel : AlphaModel
{
// Alpha Model - Ema Properties
private readonly int _emaFastPeriod;
private readonly int _emaSlowPeriod;
private readonly int _emaPredictionInterval;
// Alpha Model - Roc Properties
private readonly int _rocPeriod;
// Alpha Model - Rsi Properties
private readonly int _rsiPeriod;
// Alpha Model Properties
private readonly string[] _indicators;
private readonly Resolution _resolution;
private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol;
private QCAlgorithm _algorithm;
/// <summary>
/// Initializes a new instance of the <see cref="MeowChickyAlphaModel"/> class
/// </summary>
/// <param name="indicators">The list of indicators to be used in this alpha model</param>
/// <param name="emaFastPeriod">The fast EMA period</param>
/// <param name="emaSlowPeriod">The slow EMA period</param>
/// <param name="rocPeriod">The Roc period</param>
/// <param name="rsiPeriod">The Rsi period</param>
/// <param name="resolution">The resolution of data sent into the EMA indicators</param>
public MeowChickyAlphaModel(
string[] indicators,
int emaFastPeriod = 10,
int emaSlowPeriod = 50,
int rocPeriod = 10,
int rsiPeriod = 14,
Resolution resolution = Resolution.Minute
)
{
_indicators = indicators;
_emaFastPeriod = emaFastPeriod;
_emaSlowPeriod = emaSlowPeriod;
_emaPredictionInterval = emaFastPeriod;
_rocPeriod = rocPeriod;
_rsiPeriod = rsiPeriod;
_resolution = resolution;
_symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
Name = $"{nameof(MeowChickyAlphaModel)}({indicators},{emaFastPeriod},{emaSlowPeriod},{rocPeriod},{rsiPeriod},{resolution})";
}
/// <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(QCAlgorithmFramework algorithm, Slice data)
{
_algorithm = algorithm;
var insights = new List<Insight>();
foreach (var symbolData in _symbolDataBySymbol.Values)
{
foreach (var indicator in _indicators)
{
switch (indicator.ToLower())
{
case "ema":
{
insights = EmaAlphaModel(symbolData, insights);
break;
}
case "rsi":
{
insights = RsiAlphaModel(symbolData, insights);
break;
}
}
}
}
return insights;
}
private List<Insight> EmaAlphaModel(SymbolData symbolData, List<Insight> insights)
{
if (symbolData.EmaFast.IsReady && symbolData.EmaSlow.IsReady)
{
var insightPeriod = _resolution.ToTimeSpan().Multiply(_emaPredictionInterval);
if (symbolData.FastIsOverSlow)
{
if (symbolData.EmaSlow > symbolData.EmaFast)
{
insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Down));
}
}
else if (symbolData.SlowIsOverFast)
{
if (symbolData.EmaFast > symbolData.EmaSlow)
{
insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Up));
}
}
}
symbolData.FastIsOverSlow = symbolData.EmaFast > symbolData.EmaSlow;
return insights;
}
private List<Insight> RsiAlphaModel(SymbolData symbolData, List<Insight> insights)
{
if (symbolData.Rsi.IsReady && symbolData.Roc.IsReady)
{
InsightDirection insightDirection = symbolData.RsiPreviousInsightDirection;
var insightPeriod = _resolution.ToTimeSpan().Multiply(_rsiPeriod);
if (_algorithm.Portfolio[symbolData.Symbol].Invested == false)
{
if (symbolData.Rsi < symbolData.RsiBuyThreshold)
{
if (symbolData.RsiPreviousInsightDirection == InsightDirection.Down || symbolData.RsiPreviousInsightDirection == InsightDirection.Flat)
{
insights.Add(Insight.Price(symbolData.Symbol, _resolution, _rsiPeriod, InsightDirection.Up, magnitude: Convert.ToDouble(symbolData.Roc)));
symbolData.RsiPreviousInsightDirection = InsightDirection.Up;
}
}
}
if (_algorithm.Portfolio[symbolData.Symbol].Invested == true)
{
if (symbolData.Rsi > symbolData.RsiSellThreshold)
{
if (symbolData.RsiPreviousInsightDirection == InsightDirection.Up || symbolData.RsiPreviousInsightDirection == InsightDirection.Flat)
{
insights.Add(Insight.Price(symbolData.Symbol, _resolution, _rsiPeriod, InsightDirection.Down, magnitude: Convert.ToDouble(symbolData.Roc)));
symbolData.RsiPreviousInsightDirection = InsightDirection.Down;
}
}
}
}
return insights;
}
/// <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(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
foreach (var added in changes.AddedSecurities)
{
SymbolData symbolData;
if (!_symbolDataBySymbol.TryGetValue(added.Symbol, out symbolData))
{
// create fast/slow EMAs
var emaFast = algorithm.EMA(added.Symbol, _emaFastPeriod, _resolution);
var emaSlow = algorithm.EMA(added.Symbol, _emaSlowPeriod, _resolution);
// Create Roc indicator
var roc = algorithm.ROC(added.Symbol, _rocPeriod, _resolution);
// Create RSIs
var rsi = algorithm.RSI(added.Symbol, _rsiPeriod, resolution: _resolution);
_symbolDataBySymbol[added.Symbol] = new SymbolData
{
EmaFast = emaFast,
EmaSlow = emaSlow,
Roc = roc,
Rsi = rsi,
Security = added
};
}
else
{
// a security that was already initialized was re-added, reset the indicators
symbolData.EmaFast.Reset();
symbolData.EmaSlow.Reset();
symbolData.Roc.Reset();
symbolData.Rsi.Reset();
}
}
}
/// <summary>
/// Contains data specific to a symbol required by this model
/// </summary>
private class SymbolData
{
public Security Security { get; set; }
public Symbol Symbol => Security.Symbol;
public ExponentialMovingAverage EmaFast { get; set; }
public ExponentialMovingAverage EmaSlow { get; set; }
public RateOfChange Roc { get; set; }
public RelativeStrengthIndex Rsi { get; set; }
public InsightDirection RsiPreviousInsightDirection { get; set; } = InsightDirection.Flat;
public int RsiBuyThreshold { get; set; } = 25;
public int RsiSellThreshold { get; set; } = 70;
/// <summary>
/// True if the fast is above the slow, otherwise false.
/// This is used to prevent emitting the same signal repeatedly
/// </summary>
public bool FastIsOverSlow { get; set; }
public bool SlowIsOverFast => !FastIsOverSlow;
}
}
}/*
* 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.Indicators;
using QuantConnect.Util;
namespace QuantConnect.Algorithm.Framework.Alphas
{
/// <summary>
/// Uses Roc Percentage to create insights.
/// </summary>
public class MeowChickyRocpAlphaModel : AlphaModel
{
private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
private readonly int _period;
private readonly Resolution _resolution;
/// <summary>
/// Initializes a new instance of the <see cref="MeowChickyRocpAlphaModel"/> class
/// </summary>
/// <param name="period">The Rocp indicator period</param>
/// <param name="resolution">The resolution of data sent into the RSI indicator</param>
public MeowChickyRocpAlphaModel(
int period = 14,
Resolution resolution = Resolution.Daily
)
{
_period = period;
_resolution = resolution;
Name = $"{nameof(MeowChickyRocpAlphaModel)}({_period},{_resolution})";
}
/// <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(QCAlgorithmFramework algorithm, Slice data)
{
var insights = new List<Insight>();
foreach (var kvp in _symbolDataBySymbol)
{
var symbol = kvp.Key;
var rocp = kvp.Value.ROCP;
var previousDirection = kvp.Value.PreviousDirection;
if (rocp.IsReady)
{
var insightPeriod = _resolution.ToTimeSpan().Multiply(_period);
var direction = InsightDirection.Flat;
if (rocp > 0)
{
direction = InsightDirection.Up;
}
else if(rocp < 0)
{
direction = InsightDirection.Down;
}
// ignore signal for same direction as previous signal
if (direction == previousDirection)
{
continue;
}
var insight = Insight.Price(kvp.Value.Symbol, _resolution, _period, direction, magnitude: Convert.ToDouble(rocp));
kvp.Value.PreviousDirection = insight.Direction;
yield return insight;
}
}
}
/// <summary>
/// Cleans out old security data and initializes the Rocp for any newly added securities.
/// This functional also seeds any new indicators using a history request.
/// </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(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
// clean up data for removed securities
if (changes.RemovedSecurities.Count > 0)
{
var removed = changes.RemovedSecurities.ToHashSet(x => x.Symbol);
foreach (var subscription in algorithm.SubscriptionManager.Subscriptions)
{
if (removed.Contains(subscription.Symbol))
{
_symbolDataBySymbol.Remove(subscription.Symbol);
subscription.Consolidators.Clear();
}
}
}
// initialize data for added securities
var addedSymbols = new List<Symbol>();
foreach (var added in changes.AddedSecurities)
{
if (!_symbolDataBySymbol.ContainsKey(added.Symbol))
{
var rocp = algorithm.ROCP(added.Symbol, _period, _resolution);
var symbolData = new SymbolData(added.Symbol, rocp);
_symbolDataBySymbol[added.Symbol] = symbolData;
addedSymbols.Add(symbolData.Symbol);
}
}
if (addedSymbols.Count > 0)
{
// warmup our indicators by pushing history through the consolidators
algorithm.History(addedSymbols, _period, _resolution)
.PushThrough(data =>
{
SymbolData symbolData;
if (_symbolDataBySymbol.TryGetValue(data.Symbol, out symbolData))
{
symbolData.ROCP.Update(data.EndTime, data.Value);
}
});
}
}
/// <summary>
/// Contains data specific to a symbol required by this model
/// </summary>
private class SymbolData
{
public Symbol Symbol { get; }
public RateOfChangePercent ROCP { get; }
public InsightDirection PreviousDirection { get; set; }
public SymbolData(Symbol symbol, RateOfChangePercent rocp)
{
Symbol = symbol;
ROCP = rocp;
}
}
}
}/*
* 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.Indicators;
using QuantConnect.Util;
namespace QuantConnect.Algorithm.Framework.Alphas
{
/// <summary>
/// Uses ROC to create insights. Crossing from pos to neg will create a direction insight and rate of change is magnitude
/// </summary>
public class MeowChickyRocAlphaModel : AlphaModel
{
private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
private readonly int _period;
private readonly Resolution _resolution;
/// <summary>
/// Initializes a new instance of the <see cref="MeowChickyRocAlphaModel"/> class
/// </summary>
/// <param name="period">The Roc indicator period</param>
/// <param name="resolution">The resolution of data sent into the Roc indicator</param>
public MeowChickyRocAlphaModel(
int period = 14,
Resolution resolution = Resolution.Daily
)
{
_period = period;
_resolution = resolution;
Name = $"{nameof(MeowChickyRocAlphaModel)}({_period},{_resolution})";
}
/// <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(QCAlgorithmFramework algorithm, Slice data)
{
var insights = new List<Insight>();
foreach (var kvp in _symbolDataBySymbol)
{
var symbol = kvp.Key;
var roc = kvp.Value.ROC;
var previousDirection = kvp.Value.PreviousDirection;
if (roc.IsReady)
{
var direction = InsightDirection.Flat;
if (roc > 0)
{
direction = InsightDirection.Up;
}
else if(roc < 0)
{
direction = InsightDirection.Down;
}
// ignore signal for same direction as previous signal
if (direction == previousDirection)
{
continue;
}
double magnitude = 0;
if(roc < 1)
{
magnitude = Convert.ToDouble(roc.Current.Value);
}
else
{
magnitude = 1;
}
var insight = Insight.Price(kvp.Value.Symbol, _resolution, _period, direction, magnitude: magnitude);
kvp.Value.PreviousDirection = insight.Direction;
yield return insight;
}
}
}
/// <summary>
/// Cleans out old security data and initializes the Roc for any newly added securities.
/// This functional also seeds any new indicators using a history request.
/// </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(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
// clean up data for removed securities
if (changes.RemovedSecurities.Count > 0)
{
var removed = changes.RemovedSecurities.ToHashSet(x => x.Symbol);
foreach (var subscription in algorithm.SubscriptionManager.Subscriptions)
{
if (removed.Contains(subscription.Symbol))
{
_symbolDataBySymbol.Remove(subscription.Symbol);
subscription.Consolidators.Clear();
}
}
}
// initialize data for added securities
var addedSymbols = new List<Symbol>();
foreach (var added in changes.AddedSecurities)
{
if (!_symbolDataBySymbol.ContainsKey(added.Symbol))
{
var roc = algorithm.ROC(added.Symbol, _period, _resolution);
var symbolData = new SymbolData(added.Symbol, roc);
_symbolDataBySymbol[added.Symbol] = symbolData;
addedSymbols.Add(symbolData.Symbol);
}
}
if (addedSymbols.Count > 0)
{
// warmup our indicators by pushing history through the consolidators
algorithm.History(addedSymbols, _period, _resolution)
.PushThrough(data =>
{
SymbolData symbolData;
if (_symbolDataBySymbol.TryGetValue(data.Symbol, out symbolData))
{
symbolData.ROC.Update(data.EndTime, data.Value);
}
});
}
}
/// <summary>
/// Contains data specific to a symbol required by this model
/// </summary>
private class SymbolData
{
public Symbol Symbol { get; }
public RateOfChange ROC { get; }
public InsightDirection PreviousDirection { get; set; }
public SymbolData(Symbol symbol, RateOfChange roc)
{
Symbol = symbol;
ROC = roc;
}
}
}
}