Overall Statistics |
Total Trades 126 Average Win 0.10% Average Loss -0.13% Compounding Annual Return -8.209% Drawdown 3.000% Expectancy 0.013 Net Profit -0.164% Sharpe Ratio 0.132 Probabilistic Sharpe Ratio 45.110% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 0.79 Alpha -2.15 Beta 1.3 Annual Standard Deviation 0.223 Annual Variance 0.05 Information Ratio -8.766 Tracking Error 0.188 Treynor Ratio 0.023 Total Fees $0.00 |
/* * 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.Market; namespace QuantConnect.Indicators { /// <summary> /// Represents the traditional simple moving average indicator (SMA) /// </summary> public class SkewIndicator : BarIndicator { public RollingWindow<decimal> bars; /// <summary> /// Gets a flag indicating when this indicator is ready and fully initialized /// </summary> public override bool IsReady => bars.IsReady; int Period; /// <summary> /// Resets this indicator to its initial state /// </summary> public override void Reset() { bars.Reset(); base.Reset(); } /// <summary> /// Initializes a new instance of the SimpleMovingAverage class with the specified name and period /// </summary> /// <param name="name">The name of this indicator</param> /// <param name="period">The period of the SMA</param> public SkewIndicator(string name, int period) : base(name) { bars = new RollingWindow<decimal>(period); Period = period; } /// <summary> /// Initializes a new instance of the SimpleMovingAverage class with the default name and period /// </summary> /// <param name="period">The period of the SMA</param> public SkewIndicator(int period) : this("Skew" + period, period) { } /// <summary> /// Computes the next value for this indicator from the given state. /// </summary> /// <param name="window">The window of data held in this indicator</param> /// <param name="input">The input value to this indicator on this time step</param> /// <returns>A new value for this indicator</returns> protected override decimal ComputeNextValue(IBaseDataBar input) { bars.Add(input.Close); if (!bars.IsReady) return 0; return Skewness(ToIEnumerable()); } public decimal Skewness(IEnumerable<double> list) { try { var d = MathNet.Numerics.Statistics.Statistics.Skewness(list); if (d >= (double)Decimal.MaxValue) return Decimal.MaxValue; if (d <= (double)Decimal.MinValue) return Decimal.MinValue; return Convert.ToDecimal(d); } catch(OverflowException) { return 0; } } private IEnumerable<double> ToIEnumerable() { var e = bars.GetEnumerator(); while (e.MoveNext()) { yield return (double)e.Current; } } } }
using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Securities; using QuantConnect.Algorithm.CSharp; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// Alpha model that uses an Skewness cross to create insights /// </summary> public class AlphaSkew : AlphaModel { private readonly int _periods; private readonly Resolution _resolution; private readonly int _predictionPeriods; private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol; private List<Insight> lastinsights = new List<Insight>(); private DateTime lastInsightDate; /// <summary> /// Initializes a new instance of the <see cref="EmaCrossAlphaModel"/> class /// </summary> /// <param name="fastPeriod">The fast EMA period</param> /// <param name="slowPeriod">The slow EMA period</param> /// <param name="resolution">The resolution of data sent into the EMA indicators</param> public AlphaSkew( Resolution resolution = Resolution.Minute, int periods = 1440, int predictionperiods = 1 ) { _resolution = resolution; _periods = periods; _predictionPeriods = predictionperiods; _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>(); Name = $"{nameof(AlphaSkew)}({periods},{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(QCAlgorithm algorithm, Slice data) { var insights = new List<Insight>(); if(data==null) return insights; if(lastInsightDate==null || algorithm.Time.Subtract(lastInsightDate).TotalMinutes > _resolution.ToTimeSpan().Multiply(_periods* SkewnessCommoditiesAlgo.alpharollingperiods).TotalMinutes ) { var insightPeriod = _resolution.ToTimeSpan().Multiply(_periods*_predictionPeriods).Add(new TimeSpan(0,5,0)); lastInsightDate = algorithm.Time; Dictionary<SymbolData, decimal> skews = new Dictionary<SymbolData, decimal>(); foreach (var symbolData in _symbolDataBySymbol.Values) { if(symbolData.Skew.IsReady && symbolData.Skew2.IsReady && algorithm.Securities[symbolData.Symbol].Price>0) skews[symbolData] = symbolData.Skew+symbolData.Skew2; } var ascskews = skews.Where(x=> x.Value>SkewnessCommoditiesAlgo.longSkew).OrderByDescending(pair => pair.Value).Take(SkewnessCommoditiesAlgo.longInstruments).ToDictionary(pair => pair.Key, pair => pair.Value).Keys; var desskews = skews.Where(x=> x.Value<SkewnessCommoditiesAlgo.shortSkew).OrderBy(pair => pair.Value).Take(SkewnessCommoditiesAlgo.shortInstruments).ToDictionary(pair => pair.Key, pair => pair.Value).Keys; foreach (var symbolData in ascskews) { if (symbolData == null) continue; var symbol = symbolData.Ticker; if(algorithm.IsMarketOpen(symbol)) { if(IsDateOK(algorithm.Time,symbolData.insightDate) && algorithm.Securities[symbolData.Symbol].Price>symbolData.EMAfast)//>symbolData.EMAslow) { insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Up, Math.Abs(symbolData.ROCValue()), null)); symbolData.insightDate = algorithm.Time.Add(insightPeriod); symbolData.TradePrice = algorithm.Securities[symbolData.Symbol].Price; } } } foreach (var symbolData in desskews) { if (symbolData == null) continue; var symbol = symbolData.Ticker; if(algorithm.IsMarketOpen(symbol)) { if(IsDateOK(algorithm.Time,symbolData.insightDate) && algorithm.Securities[symbolData.Symbol].Price<symbolData.EMAfast)//<symbolData.EMAslow) { insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Down, Math.Abs(symbolData.ROCValue()), null)); symbolData.insightDate = algorithm.Time.Add(insightPeriod); symbolData.TradePrice = algorithm.Securities[symbolData.Symbol].Price; } } } /* var toFlat = _symbolDataBySymbol.Values.ToList().Except(ascskews).ToList().Except(desskews).ToList(); foreach (var symbolData in toFlat) { if(algorithm.IsMarketOpen(symbolData.Symbol)) { if(IsDateOK(algorithm.Time,symbolData.insightDate) && algorithm.Securities[symbolData.Symbol].Price>0) { insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Flat, Math.Abs(symbolData.ROCValue()), null)); symbolData.insightDate = algorithm.Time.Add(insightPeriod); symbolData.TradePrice = 0; symbolData.MaxPrice = 0; } } }*/ lastinsights = insights; return insights; } else { return RollOrCutOffAlpha(algorithm); } } private IEnumerable<Insight> RollOrCutOffAlpha(QCAlgorithm algorithm) { var insights = new List<Insight>(); var insightPeriod = _resolution.ToTimeSpan().Multiply(_periods*_predictionPeriods).Add(new TimeSpan(0,5,0)); //push insights again foreach (var i in lastinsights) { var symbolData = _symbolDataBySymbol[i.Symbol]; if(i.Direction==InsightDirection.Down) symbolData.MaxPrice = symbolData.MaxPrice==0?algorithm.Securities[i.Symbol].Price:Math.Min(symbolData.MaxPrice,algorithm.Securities[i.Symbol].Price); else if(i.Direction==InsightDirection.Up) symbolData.MaxPrice = Math.Max(symbolData.MaxPrice,algorithm.Securities[i.Symbol].Price); if(_symbolDataBySymbol[i.Symbol].TradePrice>0 && ((i.Direction==InsightDirection.Up && algorithm.Securities[i.Symbol].Price/_symbolDataBySymbol[i.Symbol].MaxPrice<SkewnessCommoditiesAlgo.stopprofit) || (i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].MaxPrice/algorithm.Securities[i.Symbol].Price<SkewnessCommoditiesAlgo.stopprofit))) // if(algorithm.Securities[i.Symbol].Invested && algorithm.Securities[i.Symbol].Holdings.UnrealizedProfitPercent<-0.1m) { insights.Add(Insight.Price(i.Symbol, insightPeriod, InsightDirection.Flat, i.Magnitude, null)); _symbolDataBySymbol[i.Symbol].insightDate = algorithm.Time.Add(insightPeriod); _symbolDataBySymbol[i.Symbol].TradePrice=0; _symbolDataBySymbol[i.Symbol].MaxPrice=0; } else if(_symbolDataBySymbol[i.Symbol].TradePrice>0 && ((i.Direction==InsightDirection.Up && _symbolDataBySymbol[i.Symbol].Skew>SkewnessCommoditiesAlgo.stoplongSkew) || (i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].Skew<SkewnessCommoditiesAlgo.stopshortSkew))) { insights.Add(Insight.Price(i.Symbol, insightPeriod, InsightDirection.Flat, i.Magnitude, null)); _symbolDataBySymbol[i.Symbol].insightDate = algorithm.Time.Add(insightPeriod); _symbolDataBySymbol[i.Symbol].TradePrice=0; _symbolDataBySymbol[i.Symbol].MaxPrice=0; } else if(_symbolDataBySymbol[i.Symbol].TradePrice>0 && ((i.Direction==InsightDirection.Up && algorithm.Securities[i.Symbol].Price/_symbolDataBySymbol[i.Symbol].TradePrice>SkewnessCommoditiesAlgo.stoploss) || (i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].TradePrice/algorithm.Securities[i.Symbol].Price>SkewnessCommoditiesAlgo.stoploss))) { if(IsDateOK(algorithm.Time,_symbolDataBySymbol[i.Symbol].insightDate) ) insights.Add(Insight.Price(i.Symbol, insightPeriod, i.Direction, i.Magnitude, null)); } else if (i.Direction!=InsightDirection.Flat )//&& ((i.Direction==InsightDirection.Up && _symbolDataBySymbol[i.Symbol].Skew.Current.Value<1)||(i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].Skew.Current.Value>-1))) { insights.Add(Insight.Price(i.Symbol, insightPeriod, InsightDirection.Flat, i.Magnitude, null)); _symbolDataBySymbol[i.Symbol].insightDate = algorithm.Time.Add(insightPeriod); _symbolDataBySymbol[i.Symbol].TradePrice=0; _symbolDataBySymbol[i.Symbol].MaxPrice=0; } } lastinsights = insights; return insights; } private IEnumerable<Insight> RollAlpha(QCAlgorithm algorithm) { var insights = new List<Insight>(); var insightPeriod = _resolution.ToTimeSpan().Multiply(_periods*_predictionPeriods).Add(new TimeSpan(0,10,0)); //push insights again foreach (var i in lastinsights) { var symbolData = _symbolDataBySymbol[i.Symbol]; if(i.Direction==InsightDirection.Down) symbolData.MaxPrice = symbolData.MaxPrice==0?algorithm.Securities[i.Symbol].Price:Math.Min(symbolData.MaxPrice,algorithm.Securities[i.Symbol].Price); else if(i.Direction==InsightDirection.Up) symbolData.MaxPrice = Math.Max(symbolData.MaxPrice,algorithm.Securities[i.Symbol].Price); if(IsDateOK(algorithm.Time,_symbolDataBySymbol[i.Symbol].insightDate) ) insights.Add(Insight.Price(i.Symbol, insightPeriod, i.Direction, i.Magnitude, null)); } lastinsights = insights; 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(QCAlgorithm algorithm, SecurityChanges changes) { var addedSymbols = new List<Symbol>(); foreach (var added in changes.AddedSecurities) { SymbolData symbolData; if (!_symbolDataBySymbol.TryGetValue(added.Symbol, out symbolData)) { var ticker = added.Symbol; var security = algorithm.AddSecurity(added.Symbol.SecurityType, ticker, _resolution); algorithm.RegisterIndicator(security.Symbol, new Identity(security.Symbol), _resolution); security.SetLeverage(SkewnessCommoditiesAlgo.leverage); symbolData = new SymbolData(algorithm, _resolution, security, _periods); symbolData.insightDate = algorithm.Time; symbolData.Security = added; _symbolDataBySymbol[added.Symbol] = symbolData; addedSymbols.Add(added.Symbol); } } foreach (var removed in changes.RemovedSecurities) { SymbolData symbolData; if (!_symbolDataBySymbol.TryGetValue(removed.Symbol, out symbolData)) { _symbolDataBySymbol.Remove(removed.Symbol); algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, symbolData.Consolidator); } //algorithm.RemoveSecurity(removed.Symbol); } if (addedSymbols.Count > 0) { // warmup our indicators by pushing history through the consolidators algorithm.History(addedSymbols, 100*1440, _resolution) .PushThrough(bar => { SymbolData symbolData; if (_symbolDataBySymbol.TryGetValue(bar.Symbol, out symbolData)) { symbolData.ROC.Update(bar.EndTime, bar.Value); symbolData.Skew.Update(bar); symbolData.Skew2.Update(bar); symbolData.EMAfast.Update(bar.EndTime, bar.Value); symbolData.EMAslow.Update(bar.EndTime, bar.Value); } }); algorithm.History(addedSymbols, 200*SkewnessCommoditiesAlgo.periods, _resolution) .PushThrough(bar => { SymbolData symbolData; if (_symbolDataBySymbol.TryGetValue(bar.Symbol, out symbolData)) { symbolData.EMAfast.Update(bar.EndTime, bar.Value); symbolData.EMAslow.Update(bar.EndTime, bar.Value); } }); } } private bool IsDateOK(DateTime time, DateTime time2) { return time.Subtract(time2).TotalMinutes>=-10; } class SymbolData { public RateOfChange ROC; public RollingWindow<RateOfChange> RollingROC; public SimpleMovingAverage EMAfast; public SimpleMovingAverage EMAslow; public Security Security { get; set; } public Symbol Symbol => Security.Symbol; public SkewIndicator Skew; public SkewIndicator Skew2; public string Ticker; public readonly IDataConsolidator Consolidator; public SymbolData(QCAlgorithm algorithm, Resolution resolution, Security security, int _periods) { Ticker = security.Symbol; Skew = new SkewIndicator(Ticker,SkewnessCommoditiesAlgo.skewperiods);//251); Skew2 = new SkewIndicator(Ticker,SkewnessCommoditiesAlgo.skew2periods);//251); ROC = new RateOfChange(Ticker, SkewnessCommoditiesAlgo.alphaperiods); RollingROC = new RollingWindow<RateOfChange>(24/SkewnessCommoditiesAlgo.alphaperiods+1); EMAfast = new SimpleMovingAverage(Ticker, 50); EMAslow = new SimpleMovingAverage(Ticker, 200); //Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution); //algorithm.SubscriptionManager.AddConsolidator(security.Symbol, Consolidator); algorithm.RegisterIndicator(security.Symbol, Skew, resolution.ToTimeSpan().Multiply(_periods)); algorithm.RegisterIndicator(security.Symbol, Skew2, resolution.ToTimeSpan().Multiply(_periods)); algorithm.RegisterIndicator(security.Symbol, ROC, resolution.ToTimeSpan().Multiply(_periods*SkewnessCommoditiesAlgo.alphaperiods));//_resolution); algorithm.RegisterIndicator(security.Symbol, EMAfast, resolution.ToTimeSpan().Multiply(_periods));//resolution.ToTimeSpan().Multiply(_periods));//_resolution); algorithm.RegisterIndicator(security.Symbol, EMAslow, resolution.ToTimeSpan().Multiply(_periods));//resolution.ToTimeSpan().Multiply(_periods));//_resolution); ROC.Updated += (sender, updated) => { RollingROC.Add(ROC); }; } public InsightDirection _insight_dir = InsightDirection.Flat; public DateTime insightDate; public decimal TradePrice = 0; public decimal MaxPrice = 0; public bool IsReady() { if (!Skew.IsReady) return false; return true; } public void Reset() { } public double ROCValue() { if(!RollingROC.IsReady) return 0; return double.IsNaN((double)RollingROC[(24/SkewnessCommoditiesAlgo.alphaperiods)-1].Current.Value)?(double)0d: (double)RollingROC[(24/SkewnessCommoditiesAlgo.alphaperiods)-1].Current.Value; } } } }
/* * 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 Accord.Math; using Python.Runtime; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Scheduling; namespace QuantConnect.Algorithm.Framework.Portfolio { /// <summary> /// Provides an implementation of Mean-Variance portfolio optimization based on modern portfolio theory. /// The interval of weights in optimization method can be changed based on the long-short algorithm. /// The default model uses the last three months daily price to calculate the optimal weight /// with the weight range from -1 to 1 and minimize the portfolio variance with a target return of 2% /// </summary> public class MeanVarianceOptimizationPortfolioConstructionModel2 : PortfolioConstructionModel { private readonly int _lookback; private readonly int _period; private readonly Resolution _resolution; private readonly PortfolioBias _portfolioBias; private readonly IPortfolioOptimizer _optimizer; private readonly Dictionary<Symbol, ReturnsSymbolData2> _symbolDataDict; /// <summary> /// Initialize the model /// </summary> /// <param name="rebalancingDateRules">The date rules used to define the next expected rebalance time /// in UTC</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> public MeanVarianceOptimizationPortfolioConstructionModel2(IDateRule rebalancingDateRules, PortfolioBias portfolioBias = PortfolioBias.Long, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : this(rebalancingDateRules.ToFunc(), portfolioBias, lookback, period, resolution, targetReturn, optimizer) { } /// <summary> /// Initialize the model /// </summary> /// <param name="rebalanceResolution">Rebalancing frequency</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> public MeanVarianceOptimizationPortfolioConstructionModel2(Resolution rebalanceResolution = Resolution.Daily, PortfolioBias portfolioBias = PortfolioBias.LongShort, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : this(rebalanceResolution.ToTimeSpan(), portfolioBias, lookback, period, resolution, targetReturn, optimizer) { } /// <summary> /// Initialize the model /// </summary> /// <param name="timeSpan">Rebalancing frequency</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> public MeanVarianceOptimizationPortfolioConstructionModel2(TimeSpan timeSpan, PortfolioBias portfolioBias = PortfolioBias.LongShort, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : this(dt => dt.Add(timeSpan), portfolioBias, lookback, period, resolution, targetReturn, optimizer) { } /// <summary> /// Initialize the model /// </summary> /// <param name="rebalance">Rebalancing func or if a date rule, timedelta will be converted into func. /// For a given algorithm UTC DateTime the func returns the next expected rebalance time /// or null if unknown, in which case the function will be called again in the next loop. Returning current time /// will trigger rebalance. If null will be ignored</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> /// <remarks>This is required since python net can not convert python methods into func nor resolve the correct /// constructor for the date rules parameter. /// For performance we prefer python algorithms using the C# implementation</remarks> public MeanVarianceOptimizationPortfolioConstructionModel2(PyObject rebalance, PortfolioBias portfolioBias = PortfolioBias.LongShort, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : this((Func<DateTime, DateTime?>)null, portfolioBias, lookback, period, resolution, targetReturn, optimizer) { SetRebalancingFunc(rebalance); } /// <summary> /// Initialize the model /// </summary> /// <param name="rebalancingFunc">For a given algorithm UTC DateTime returns the next expected rebalance UTC time. /// Returning current time will trigger rebalance. If null will be ignored</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> public MeanVarianceOptimizationPortfolioConstructionModel2(Func<DateTime, DateTime> rebalancingFunc, PortfolioBias portfolioBias = PortfolioBias.LongShort, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : this(rebalancingFunc != null ? (Func<DateTime, DateTime?>)(timeUtc => rebalancingFunc(timeUtc)) : null, portfolioBias, lookback, period, resolution, targetReturn, optimizer) { } /// <summary> /// Initialize the model /// </summary> /// <param name="rebalancingFunc">For a given algorithm UTC DateTime returns the next expected rebalance time /// or null if unknown, in which case the function will be called again in the next loop. Returning current time /// will trigger rebalance.</param> /// <param name="portfolioBias">Specifies the bias of the portfolio (Short, Long/Short, Long)</param> /// <param name="lookback">Historical return lookback period</param> /// <param name="period">The time interval of history price to calculate the weight</param> /// <param name="resolution">The resolution of the history price</param> /// <param name="targetReturn">The target portfolio return</param> /// <param name="optimizer">The portfolio optimization algorithm. If the algorithm is not provided then the default will be mean-variance optimization.</param> public MeanVarianceOptimizationPortfolioConstructionModel2(Func<DateTime, DateTime?> rebalancingFunc, PortfolioBias portfolioBias = PortfolioBias.LongShort, int lookback = 1, int period = 63, Resolution resolution = Resolution.Daily, double targetReturn = 0.02, IPortfolioOptimizer optimizer = null) : base(rebalancingFunc) { _lookback = lookback; _period = period; _resolution = resolution; _portfolioBias = portfolioBias; var lower = portfolioBias == PortfolioBias.Long ? 0 : -1; var upper = portfolioBias == PortfolioBias.Short ? 0 : 1; _optimizer = optimizer ?? new MinimumVariancePortfolioOptimizer(lower, upper, targetReturn); _symbolDataDict = new Dictionary<Symbol, ReturnsSymbolData2>(); } /// <summary> /// Method that will determine if the portfolio construction model should create a /// target for this insight /// </summary> /// <param name="insight">The insight to create a target for</param> /// <returns>True if the portfolio should create a target for the insight</returns> protected override bool ShouldCreateTargetForInsight(Insight insight) { var filteredInsight = FilterInvalidInsightMagnitude(Algorithm, new[] { insight }).FirstOrDefault(); if (filteredInsight == null) { return false; } ReturnsSymbolData2 data; if (_symbolDataDict.TryGetValue(insight.Symbol, out data)) { if (!insight.Magnitude.HasValue) { Algorithm.SetRunTimeError( new ArgumentNullException( insight.Symbol.Value, "MeanVarianceOptimizationPortfolioConstructionModel does not accept 'null' as Insight.Magnitude. " + "Please checkout the selected Alpha Model specifications: " + insight.SourceModel)); return false; } data.Add(Algorithm.Time, insight.Magnitude.Value.SafeDecimalCast()); } return true; } /// <summary> /// Will determine the target percent for each insight /// </summary> /// <param name="activeInsights">The active insights to generate a target for</param> /// <returns>A target percent for each insight</returns> protected override Dictionary<Insight, double> DetermineTargetPercent(List<Insight> activeInsights) { var targets = new Dictionary<Insight, double>(); // Get the last generated active insight for each symbol var lastActiveInsights = from insight in activeInsights group insight by insight.Symbol into g select g.OrderBy(x => x.GeneratedTimeUtc).Last(); var symbols = lastActiveInsights.Where(x=> x.Direction !=InsightDirection.Flat).Select(x => x.Symbol).ToList(); // Get symbols' returns var returns = _symbolDataDict.FormReturnsMatrix(symbols); // Calculate rate of returns var rreturns = returns.Apply(e => Math.Pow(1.0 + e, 252.0) - 1.0); // The optimization method processes the data frame var w = _optimizer.Optimize(rreturns); // process results if (w.Length > 0) { var sidx = 0; foreach (var symbol in symbols) { var insightSimbol = activeInsights.First(insight => insight.Symbol == symbol); double weight = 0; if(sidx<w.Length) weight = w[sidx]; if(double.IsNaN(weight)) { weight = 0; } else if(insightSimbol.Direction==InsightDirection.Up && Math.Sign(weight) != 1) { weight = weight/2; } else if(insightSimbol.Direction==InsightDirection.Down && Math.Sign(weight) != -1) { weight = weight/2; } // don't trust the optimizer else if (_portfolioBias != PortfolioBias.LongShort && Math.Sign(weight) != (int)_portfolioBias) { weight = 0; } targets[insightSimbol] = (int)insightSimbol.Direction * Math.Abs(weight) * (int)((insightSimbol.Direction==InsightDirection.Up)? QuantConnect.Algorithm.CSharp.SkewnessCommoditiesAlgo.WeightMultiplierLong: QuantConnect.Algorithm.CSharp.SkewnessCommoditiesAlgo.WeightMultiplierShort); sidx++; } } symbols = lastActiveInsights.Where(x=> x.Direction ==InsightDirection.Flat).Select(x => x.Symbol).ToList(); foreach (var symbol in symbols) { targets[activeInsights.First(insight => insight.Symbol == symbol)] = 0; } return targets; } /// <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) { base.OnSecuritiesChanged(algorithm, changes); // clean up data for removed securities foreach (var removed in changes.RemovedSecurities) { ReturnsSymbolData2 data; if (_symbolDataDict.TryGetValue(removed.Symbol, out data)) { _symbolDataDict.Remove(removed.Symbol); } } if (changes.AddedSecurities.Count == 0) return; // initialize data for added securities foreach (var added in changes.AddedSecurities) { if (!_symbolDataDict.ContainsKey(added.Symbol)) { var symbolData = new ReturnsSymbolData2(added.Symbol, _lookback, _period); _symbolDataDict[added.Symbol] = symbolData; } } // warmup our indicators by pushing history through the consolidators algorithm.History(changes.AddedSecurities.Select(security => security.Symbol), _lookback * _period, _resolution) .PushThrough(bar => { ReturnsSymbolData2 symbolData; if (_symbolDataDict.TryGetValue(bar.Symbol, out symbolData)) { symbolData.Update(bar.EndTime, bar.Value); } }); } } }
/* * 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.Algorithm.Framework.Portfolio; using QuantConnect.Data.UniverseSelection; namespace QuantConnect.Algorithm.Framework.Execution { /// <summary> /// Provides an implementation of <see cref="IExecutionModel"/> that immediately submits /// market orders to achieve the desired portfolio targets /// </summary> public class ImmediateExecutionModel2 : ExecutionModel { private readonly PortfolioTargetCollection _targetsCollection = new PortfolioTargetCollection(); /// <summary> /// Immediately submits orders for the specified portfolio targets. /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="targets">The portfolio targets to be ordered</param> public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets) { _targetsCollection.AddRange(targets); foreach (var target in _targetsCollection.OrderByMarginImpact(algorithm)) { // calculate remaining quantity to be ordered var quantity = OrderSizing.GetUnorderedQuantity(algorithm, target); /* var existing = algorithm.Securities[target.Symbol].Holdings.Quantity + algorithm.Transactions.GetOpenOrders(target.Symbol) .Aggregate(0m, (d, order) => d + order.Quantity); var quantity = target.Quantity - existing; */ var lastData = algorithm.Securities[target.Symbol].GetLastData(); if (quantity != 0 && Math.Abs(algorithm.Time.Subtract ( lastData.EndTime ).TotalMinutes)<5 && algorithm.IsMarketOpen(target.Symbol)) { algorithm.MarketOrder(target.Symbol, quantity); } } _targetsCollection.ClearFulfilled(algorithm); } /// <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) { } } }
using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Data; using QuantConnect.Data.Fundamental; using QuantConnect.Data.UniverseSelection; using QuantConnect.Data.Custom.CBOE; using System.Collections.Concurrent; namespace QuantConnect.Algorithm.CSharp { public class SkewnessCommoditiesAlgo : QCAlgorithm { public static double WeightMultiplierLong = 1; //use 1 if no leverage public static double WeightMultiplierShort = 1; //use 1 if no leverage public IEnumerable<Symbol> symbols; Resolution resolution = Resolution.Minute; public static int periods =60; public static int alphaperiods =4; public static int alpharollingperiods =4; public static int skewperiods =4; public static int skew2periods =12; public static int longInstruments = 4; public static int shortInstruments = 4; public static decimal longSkew = 1.0m; public static Decimal shortSkew = -1.0m; public static decimal stoplongSkew = 2.0m; public static Decimal stopshortSkew = -2.0m; public static decimal stoploss = 0.8m; public static decimal stopprofit = 0.8m; public static int leverage = 100; public static IEnumerable<DateTime> EachHourMostLiquidity(DateTime from, DateTime thru) { for (var day = from; day <= thru; ) { day = day.AddHours(1); if(day.Hour >=9 && day.Hour <21) yield return day; } } public override void Initialize() { //Start and End Date range for the backtest: SetStartDate(2020, 7, 28); SetEndDate(2020, 8, 4); SetCash(1500); SetBenchmark("SPY"); SetAlpha(new AlphaSkew(resolution, periods,alphaperiods)); SetExecution(new ImmediateExecutionModel2()); SetPortfolioConstruction(new MeanVarianceOptimizationPortfolioConstructionModel2( //timeSpan:new TimeSpan(1,0,0), rebalancingDateRules:new FuncDateRule("EachHourMostLiquidity", EachHourMostLiquidity), portfolioBias: PortfolioBias.LongShort, //optimizer:new MaximumSharpeRatioPortfolioOptimizer(), period:63*24, lookback:24, targetReturn:0.1d)); AddUniverseSelection(new ManualUniverseSelectionModel( QuantConnect.Symbol.Create("BCOUSD", SecurityType.Cfd, Market.Oanda), //QuantConnect.Symbol.Create("WTIUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("XAGUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("XAUUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("XCUUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("SOYBNUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("WHEATUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("CORNUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("XPTUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("CORNUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("NATGASUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("XPDUSD", SecurityType.Cfd, Market.Oanda), QuantConnect.Symbol.Create("SUGARUSD", SecurityType.Cfd, Market.Oanda)/*, QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda), QuantConnect.Symbol.Create("USDJPY", SecurityType.Forex, Market.Oanda), QuantConnect.Symbol.Create("GBPUSD", SecurityType.Forex, Market.Oanda), QuantConnect.Symbol.Create("USDMXN", SecurityType.Forex, Market.Oanda), QuantConnect.Symbol.Create("AUDUSD", SecurityType.Forex, Market.Oanda)*/ )); SetBrokerageModel(QuantConnect.Brokerages.BrokerageName.OandaBrokerage); //either use this risk or the rollover alphas managed by alphaskew //AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity(0.02m)); //AddRiskManagement(new MaximumDrawdownPercentPerSecurity(0.01m)); } /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// Slice object keyed by symbol containing the stock data public override void OnData(Slice data) { } } }
/* * 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.Indicators; using QuantConnect.Util; namespace QuantConnect.Algorithm.Framework.Portfolio { /// <summary> /// Contains returns specific to a symbol required for optimization model /// </summary> public class ReturnsSymbolData2 { private readonly Symbol _symbol; private readonly RateOfChange _roc; private readonly RollingWindow<IndicatorDataPoint> _window; /// <summary> /// Initializes a new instance of the <see cref="ReturnsSymbolData"/> class /// </summary> /// <param name="symbol">The symbol of the data that updates the indicators</param> /// <param name="lookback">Look-back period for the RateOfChange indicator</param> /// <param name="period">Size of rolling window that contains historical RateOfChange</param> public ReturnsSymbolData2(Symbol symbol, int lookback, int period) { _symbol = symbol; _roc = new RateOfChange($"{_symbol}.ROC({lookback})", lookback); _window = new RollingWindow<IndicatorDataPoint>(period); _roc.Updated += OnRateOfChangeUpdated; } /// <summary> /// Historical returns /// </summary> public Dictionary<DateTime, double> Returns => _window.ToDictionary(x => x.EndTime, x => (double) x.Value); /// <summary> /// Adds an item to this window and shifts all other elements /// </summary> /// <param name="time">The time associated with the value</param> /// <param name="value">The value to use to update this window</param> public void Add(DateTime time, decimal value) { var item = new IndicatorDataPoint(_symbol, time, value); AddToWindow(item); } /// <summary> /// Updates the state of the RateOfChange with the given value and returns true /// if this indicator is ready, false otherwise /// </summary> /// <param name="time">The time associated with the value</param> /// <param name="value">The value to use to update this indicator</param> /// <returns>True if this indicator is ready, false otherwise</returns> public bool Update(DateTime time, decimal value) { return _roc.Update(time, value); } /// <summary> /// Resets all indicators of this object to its initial state /// </summary> public void Reset() { _roc.Updated -= OnRateOfChangeUpdated; _roc.Reset(); _window.Reset(); } /// <summary> /// When the RateOfChange is updated, adds the new value to the RollingWindow /// </summary> /// <param name="roc"></param> /// <param name="updated"></param> private void OnRateOfChangeUpdated(object roc, IndicatorDataPoint updated) { if (_roc.IsReady) { AddToWindow(updated); } } private void AddToWindow(IndicatorDataPoint updated) { if (_window.Samples > 0 && _window[0].EndTime >= updated.EndTime) { // this could happen with fill forward bars in the history request return; } _window.Add(updated); } } /// <summary> /// Extension methods for <see cref="ReturnsSymbolData"/> /// </summary> public static class ReturnsSymbolDataExtensions { /// <summary> /// Converts a dictionary of <see cref="ReturnsSymbolData"/> keyed by <see cref="Symbol"/> into a matrix /// </summary> /// <param name="symbolData">Dictionary of <see cref="ReturnsSymbolData"/> keyed by <see cref="Symbol"/> to be converted into a matrix</param> /// <param name="symbols">List of <see cref="Symbol"/> to be included in the matrix</param> public static double[,] FormReturnsMatrix(this Dictionary<Symbol, ReturnsSymbolData2> symbolData, IEnumerable<Symbol> symbols) { var returnsByDate = (from s in symbols join sd in symbolData on s equals sd.Key select sd.Value.Returns).ToList(); // Consolidate by date var alldates = returnsByDate.SelectMany(r => r.Keys).Distinct().ToList(); var max = symbolData.Count == 0 ? 0 : symbolData.Max(kvp => kvp.Value.Returns.Count); double obj; // Perfect match between the dates in the ReturnsSymbolData objects if (max == alldates.Count) { return Accord.Math.Matrix.Create(alldates // if a return date isn't found for a symbol we use 'double.NaN' .Select(d => returnsByDate.Select(s => s.TryGetValue(d, out obj) ? obj : double.NaN).ToArray()) .Where(r => !r.Select(Math.Abs).Sum().IsNaNOrZero()) // remove empty rows .ToArray()); } // If it is not a match, we assume that each index correspond to the same point in time var returnsByIndex = returnsByDate.Select((doubles, i) => doubles.Values.ToArray()); return Accord.Math.Matrix.Create(Enumerable.Range(0, max) // there is no guarantee that all symbols have the same amount of returns so we need to check range and use 'double.NaN' if required as above .Select(d => returnsByIndex.Select(s => s.Length < (d + 1) ? double.NaN : s[d]).ToArray()) .Where(r => !r.Select(Math.Abs).Sum().IsNaNOrZero()) // remove empty rows .ToArray()); } } }