| Overall Statistics |
|
Total Trades 7923 Average Win 0.58% Average Loss -0.24% Compounding Annual Return 5.774% Drawdown 61.600% Expectancy 0.178 Net Profit 216.330% Sharpe Ratio 0.318 Probabilistic Sharpe Ratio 0.009% Loss Rate 65% Win Rate 35% Profit-Loss Ratio 2.39 Alpha 0.062 Beta -0.005 Annual Standard Deviation 0.192 Annual Variance 0.037 Information Ratio -0.503 Tracking Error 0.399 Treynor Ratio -12.955 Total Fees $0.00 |
using System;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Algorithm.CSharp;
namespace QuantConnect.Indicators
{
public class HoltIndicator : BarIndicator
{
public readonly RollingWindow<decimal> Observed;
public readonly RollingWindow<decimal> Level;
public readonly RollingWindow<decimal> Slope;
public readonly RollingWindow<decimal> Forecast;
decimal alpha = 0;
decimal beta = 0;
public double SquareError = 0;
public HoltIndicator(string name, int length = 100, decimal alpha = 0.9m, decimal beta = 0.008m)
: base(name)
{
Observed = new RollingWindow<decimal>(length);
Level = new RollingWindow<decimal>(length);
Slope = new RollingWindow<decimal>(length);
Forecast = new RollingWindow<decimal>(length);
this.alpha = alpha;
this.beta = beta;
}
/// <summary>
/// Computes the average value
/// </summary>
/// <param name="input">The data for the calculation</param>
/// <returns>The average value</returns>
protected override decimal ComputeNextValue(IBaseDataBar data)
{
Observed.Add(data.Close);
if(Level.Samples==0)
{
Level.Add(data.Close);
Forecast.Add(0);
return 0m;
}
else if(Level.Samples==1)
{
//init slope
Slope.Add(Observed[1]-Observed[0]);
//calculate level = current observed * (previous level+previous slope)
Level.Add((alpha*Observed[0])+(1-alpha)*(Level[0]+Slope[0]));
//calculate slope = (current level-previous level) * previous slope
Slope.Add(beta*(Level[0]-Level[1])+(1-beta)*Slope[0]);
Forecast.Add(Level[1]+Slope[1]);
return 0;
}
//calculate level = current observed * (previous level+previous slope)
Level.Add((alpha*Observed[0])+(1-alpha)*(Level[0]+Slope[0]));
//calculate slope = (current level-previous level) * previous slope
Slope.Add(beta*(Level[0]-Level[1])+(1-beta)*Slope[0]);
Forecast.Add(Level[1]+Slope[1]);
SquareError = Math.Pow((double)Observed[0]-(double)Forecast[1],2);
return (decimal)Forecast[0];
}
/// <summary>
/// Returns whether the indicator will return valid results
/// </summary>
public override bool IsReady
{
get { return Forecast.Samples>2; }
}
/// <summary>
/// Resets the average to its initial state
/// </summary>
public override void Reset()
{
base.Reset();
}
}
}/*
* 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 EMA cross to create insights
/// </summary>
public class AlphaSkew : AlphaModel
{
private readonly int _fastPeriod;
private readonly int _slowPeriod;
private readonly int _minutes;
private readonly Resolution _resolution;
private readonly int _predictionInterval;
private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol;
/// <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(
int fastPeriod = 12,
int slowPeriod = 26,
Resolution resolution = Resolution.Hour,
int minutes = 10
)
{
_fastPeriod = fastPeriod;
_slowPeriod = slowPeriod;
_resolution = resolution;
_minutes = minutes;
_predictionInterval = minutes;
_symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
Name = $"{nameof(EmaCrossAlphaModel)}({fastPeriod},{slowPeriod},{resolution})";
}
int count = 0;
private List<Insight> lastinsights = new List<Insight>();
private DateTime lastInsightDate;
/// <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(algorithm.Time.Minute/10!=0) return insights;
var vix = ((ResistanceModulatedSplitter)algorithm).vix;
var vixROC = ((ResistanceModulatedSplitter)algorithm).vixROC*100;
var insightPeriod = _resolution.ToTimeSpan().Multiply(_predictionInterval).Multiply(1440).Add(new TimeSpan(0,10,0));
if(securitiesChanging) return insights;
/*
if(vix (vixROC>15 && vix>15) || vix>20 )
{
foreach (var symbolData in _symbolDataBySymbol.Values)
{
if(algorithm.IsMarketOpen(symbolData.Symbol))
{
if(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);
}
}
}
return insights;
}
*/
if(lastInsightDate==null || algorithm.Time.Subtract(lastInsightDate).TotalDays > 2)
{
lastInsightDate = algorithm.Time;
Dictionary<SymbolData, decimal> skews = new Dictionary<SymbolData, decimal>();
foreach (var symbolData in _symbolDataBySymbol.Values)
{
if(symbolData.Skew.IsReady && algorithm.Securities[symbolData.Symbol].Price>0)
skews[symbolData] = symbolData.Skew;
}
var ascskews = skews.Where(x=> x.Value>1m).OrderByDescending(pair => pair.Value).Take(2).ToDictionary(pair => pair.Key, pair => pair.Value).Keys;
var desskews = skews.Where(x=> x.Value<-1m).OrderBy(pair => pair.Value).Take(2).ToDictionary(pair => pair.Key, pair => pair.Value).Keys;
// var ascskews = skews.Where(x=> x.Value>1).OrderByDescending(pair => pair.Value).Take((skews.Count/3)).ToDictionary(pair => pair.Key, pair => pair.Value).Keys;
// var desskews = skews.Where(x=> x.Value<-1).OrderBy(pair => pair.Value).Take((skews.Count/3)).ToDictionary(pair => pair.Key, pair => pair.Value).Keys;
/*
foreach (var symbolData in _symbolDataBySymbol.Values)
{
if (symbolData == null) continue;
var symbol = symbolData.Ticker;
if(algorithm.IsMarketOpen(symbol))
{
if(IsDateOK(algorithm.Time,symbolData.insightDate))
{
if(symbolData.CurrentMeanInsightDirection()==InsightDirection.Up && ascskews.Where(x=> x.Ticker==symbol).Select(x=>x).Count()>0)
{
insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, symbolData.CurrentMeanInsightDirection(), Math.Abs(symbolData.ROCValue()), null));
symbolData.insightDate = algorithm.Time.Add(insightPeriod);
symbolData.TradePrice = algorithm.Securities[symbolData.Symbol].Price;
}
if(symbolData.CurrentMeanInsightDirection()==InsightDirection.Down && desskews.Where(x=> x.Ticker==symbol).Select(x=>x).Count()>0)
{
//insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, symbolData.CurrentMeanInsightDirection(), Math.Abs(symbolData.ROCValue()), null));
//symbolData.insightDate = algorithm.Time.Add(insightPeriod);
//symbolData.TradePrice = algorithm.Securities[symbolData.Symbol].Price;
}
}
}
}
lastinsights = insights;
return insights;
*/
foreach (var symbolData in ascskews)
{
if (symbolData == null) continue;
var symbol = symbolData.Ticker;
if(algorithm.IsMarketOpen(symbol))
{
if(IsDateOK(algorithm.Time,symbolData.insightDate) )
{
//if(symbolData.CurrentMeanInsightDirection()==InsightDirection.Up)
//if(symbolData.ema1>symbolData.ema2) /*&& symbolData.holt.Slope[0]>0*/ /*&& symbolData.CurrentMeanInsightDirection()==InsightDirection.Up)*/
{
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;
}
/*
else
{
insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Flat, Math.Abs(symbolData.ROCValue()), null));
symbolData.insightDate = algorithm.Time.Add(insightPeriod);
}
*/
}
}
}
foreach (var symbolData in desskews)
{
if (symbolData == null) continue;
var symbol = symbolData.Ticker;
if(algorithm.IsMarketOpen(symbol))
{
if(IsDateOK(algorithm.Time,symbolData.insightDate) )
{
//if(symbolData.CurrentMeanInsightDirection()==InsightDirection.Down)
//if( symbolData.ema1<symbolData.ema2 )
/*&& symbolData.holt.Slope[0]>0*/ /*&& symbolData.CurrentMeanInsightDirection()==InsightDirection.Up)*/
{
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;
}
/*
else
{
insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Flat, Math.Abs(symbolData.ROCValue()), null));
symbolData.insightDate = algorithm.Time.Add(insightPeriod);
}
*/
}
}
}
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
{
//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<0.9m)
|| (i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].MaxPrice/algorithm.Securities[i.Symbol].Price<0.9m)))
// 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 && algorithm.Securities[i.Symbol].Price/_symbolDataBySymbol[i.Symbol].TradePrice>0.98m)
|| (i.Direction==InsightDirection.Down && _symbolDataBySymbol[i.Symbol].TradePrice/algorithm.Securities[i.Symbol].Price>0.98m)))
{
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;
}
}
bool securitiesChanging = false;
/// <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>();
securitiesChanging=true;
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);
security.SetLeverage(100);
symbolData = new SymbolData(algorithm, _resolution, security);
symbolData.insightDate = algorithm.Time;
symbolData.Security = added;
symbolData.ROC = new RateOfChange(ticker, 20);
symbolData.ema1 = algorithm.EMA(ticker,50);
symbolData.ema2 = algorithm.EMA(ticker,200);
/*
var bars = algorithm.History(added.Symbol, _resolution.ToTimeSpan().Multiply(20), _resolution);
foreach(var bar in bars)
{
symbolData.ROC.Update(new IndicatorDataPoint(bar.EndTime,bar.Close));
//symbolData.MEAN.Update(bar);
symbolData.Update(bar.Close);
}
*/
algorithm.RegisterIndicator(ticker, symbolData.Skew, _resolution.ToTimeSpan().Multiply(_minutes).Multiply(1440));
algorithm.RegisterIndicator(ticker, symbolData.ROC, _resolution.ToTimeSpan().Multiply(_minutes).Multiply(1440));//_resolution);
//algorithm.RegisterIndicator(ticker, symbolData.MEAN, _resolution.ToTimeSpan().Multiply(_minutes));
//algorithm.RegisterIndicator(ticker, symbolData.MOM, _resolution.ToTimeSpan().Multiply(_minutes));
//algorithm.RegisterIndicator(ticker, symbolData.ema1, _resolution.ToTimeSpan().Multiply(_minutes));
//algorithm.RegisterIndicator(ticker, symbolData.ema2, _resolution.ToTimeSpan().Multiply(_minutes));
//algorithm.RegisterIndicator(ticker, symbolData.holt, _resolution.ToTimeSpan().Multiply(_minutes));
_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, _resolution)
.PushThrough(bar =>
{
SymbolData symbolData;
if (_symbolDataBySymbol.TryGetValue(bar.Symbol, out symbolData))
{
symbolData.ROC.Update(bar.EndTime, bar.Value);
symbolData.Update(bar.Value);
symbolData.MEAN.Update(bar);
symbolData.holt.Update(bar);
symbolData.MOM.Update(new IndicatorDataPoint(bar.EndTime, bar.Value));
try
{
symbolData.ema1.Update(new IndicatorDataPoint(bar.EndTime, bar.Value));
symbolData.ema2.Update(new IndicatorDataPoint(bar.EndTime, bar.Value));
}
catch(Exception) {/*will happen if EMA already exists.. ERROR] FATAL UNHANDLED EXCEPTION:This is a forward only indicator: EMA(20, MO_min) Input: 2005-03-04 00:00:00Z Previous: 2005-03-04 09:31:00Z.*/ }
// symbolData.MEAN.Update(new IndicatorDataPoint(bar.EndTime, bar.Value));
}
});
}
securitiesChanging=false;
}
private bool IsDateOK(DateTime time, DateTime time2)
{
return time.Subtract(time2).TotalMinutes>=-30;
}
class SymbolData
{
public RateOfChange ROC;
public Security Security { get; set; }
public Symbol Symbol => Security.Symbol;
public SwingSRIndicator MEAN;
public SkewIndicator Skew;
public Momentum MOM;
public string Ticker;
public long previous = 0;
public int Day = 0;
public ExponentialMovingAverage ema1;
public ExponentialMovingAverage ema2;
public RollingWindow<IndicatorDataPoint> ema;
public MovingAverageConvergenceDivergence MACD;
public RelativeStrengthIndex RSI;
public HoltIndicator holt;
public readonly IDataConsolidator Consolidator;
public SymbolData(QCAlgorithm algorithm, Resolution resolution, Security security)
{
Ticker = security.Symbol;
MEAN = new SwingSRIndicator(Ticker,50);
Skew = new SkewIndicator(Ticker,100);//251);
MOM = new Momentum(200);
holt = new HoltIndicator(Ticker,100,0.9m,0.008m);
Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution);
algorithm.SubscriptionManager.AddConsolidator(security.Symbol, Consolidator);
}
public decimal _insight_price = 1;
public decimal _price = 0;
public InsightDirection _insight_dir = InsightDirection.Flat;
public decimal pps = 0;
public DateTime insightDate;
bool emit = false;
public decimal TradePrice = 0;
public decimal MaxPrice = 0;
public InsightDirection CurrentMeanInsightDirection()
{
return ((int)MEAN.Current.Value == 1)? InsightDirection.Up : InsightDirection.Down;
}
public bool IsReady()
{
if (!Skew.IsReady) return false;
return true;
}
public void Reset()
{
}
public void Update(decimal value)
{
_price = value;
emit = true;
}
public double ROCValue()
{
return double.IsNaN((double)ROC.Current.Value)?(double)0d: (double)ROC.Current.Value;
}
public bool CanEmit()
{
if (!MEAN.Updated()) return false;
return ROC.IsReady;
}
}
}
}/*
* 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, ReturnsSymbolData> _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, ReturnsSymbolData>();
}
/// <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;
}
ReturnsSymbolData 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)
{
double weight = 0;
if(sidx<w.Length) weight = w[sidx];
if(double.IsNaN(weight))
{
weight = 0;
}
// don't trust the optimizer
else if (_portfolioBias != PortfolioBias.LongShort
&& Math.Sign(weight) != (int)_portfolioBias)
{
weight = 0;
}
var insightSimbol = activeInsights.First(insight => insight.Symbol == symbol);
targets[insightSimbol] = (int)insightSimbol.Direction * Math.Abs(weight) * 2;
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)
{
ReturnsSymbolData 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 ReturnsSymbolData(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 =>
{
ReturnsSymbolData symbolData;
if (_symbolDataDict.TryGetValue(bar.Symbol, out symbolData))
{
symbolData.Update(bar.EndTime, bar.Value);
}
});
}
}
}using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
/* code found on internet Hardkjarni, adapted to find lows and highs from clusters */
namespace QuantConnect.Algorithm.CSharp
{
public class KMeansStats {
public KMeansStats(double[] _low, double[] _mean, double[] _high, double[] _length)
{
lows = _low;
means = _mean;
highs = _high;
lengths = _length;
}
public void Reset()
{
for (int i = 0; i < means.Length; i++)
{
means[i] = 0;
}
for (int i = 0; i < lows.Length; i++)
{
lows[i] = 0;
}
for (int i = 0; i < highs.Length; i++)
{
highs[i] = 0;
}
for (int i = 0; i < lengths.Length; i++)
{
lengths[i] = 0;
}
}
public double[] lows { get; private set; }
public double[] means { get; private set; }
public double[] highs { get; private set; }
public double[] lengths { get; private set; }
}
/// <summary>
/// Defines a property or field as an attribute to use for the k-means clustering
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class KMeansValueAttribute : Attribute { }
/// <summary>
/// Delegate that can be passed in to the <see cref="KMeans.Cluster{T}"/> function that allows the caller to provide their own distance calculation function
/// for a point to a centroid.
/// </summary>
/// <param name="point">the point being calculated</param>
/// <param name="centroid">the centroid that is being calculated against</param>
/// <returns>the distance value between the point and the centroid</returns>
public delegate double KMeansCalculateDistanceDelegate(double[] point, double[] centroid);
/// <summary>
/// Provides a simple implementation of the k-Means algorithm. This solution is quite simple and does not support any parallel execution as of yet.
/// </summary>
public static class KMeans
{
private static double[][] ConvertEntities<T>(T[] items)
{
var type = typeof(T);
var data = new List<double[]>();
// If the type is an array type
if (type.IsArray && type.IsAssignableFrom(typeof(double[])))
{
foreach (var item in items)
{
var val = item as double[];
data.Add(val);
}
return data.ToArray();
}
var getters = new List<MethodInfo>();
// Iterate over the type and extract all the properties that have the KMeansValueAttribute set and use them as attributes
var attribType = typeof(KMeansValueAttribute);
foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var attribs = property.GetCustomAttributes(attribType, false).OfType<KMeansValueAttribute>().ToArray();
if (attribs.Length <= 0)
continue;
var getter = property.GetGetMethod();
if (getter == null)
throw new InvalidOperationException("No public getter for property '" + property.Name + "'. All properties marked with the KMeansValueAttribute must have a public getter");
if (!property.PropertyType.IsAssignableFrom(typeof(double)) &&
!property.PropertyType.IsAssignableFrom(typeof(int)) &&
!property.PropertyType.IsAssignableFrom(typeof(float)) &&
!property.PropertyType.IsAssignableFrom(typeof(long)) &&
!property.PropertyType.IsAssignableFrom(typeof(decimal)) &&
!property.PropertyType.IsAssignableFrom(typeof(short)))
throw new InvalidOperationException("Property type '" + property.PropertyType.Name + "' for property '" + property.Name + "' cannot be assigned to System.Double. ");
getters.Add(getter);
}
foreach (var item in items)
{
List<double> values = new List<double>(getters.Count);
foreach (var getter in getters)
values.Add(Convert.ToDouble(getter.Invoke(item, null)));
data.Add(values.ToArray());
}
return data.ToArray();
}
/// <summary>
/// Clusters the given item set into the desired number of clusters.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">the list of data items that should be processed, this can be an array of primitive values such as <see cref="System.Double[]"/>
/// or a class struct that exposes properties using the <see cref="KMeansValueAttribute"/></param>
/// <param name="clusterCount">the desired number of clusters</param>
/// <param name="maxIterations">the maximum number of iterations to perform</param>
/// <param name="calculateDistanceFunction">optional, custom distance function, if omitted then the euclidean distance will be used as default</param>
/// <param name="randomSeed">optional, a seed for the random generator that initially arranges the clustering of the nodes (specify the same value to ensure that the start ordering will be the same)</param>
/// <param name="initialCentroidIndices">optional, the initial centroid configuration (as indicies into the <see cref="items"/> array). When this is used the <see cref="randomSeed"/> has no effect.
/// Experiment with this as the initial arrangements of the centroids has a huge impact on the final cluster arrangement.</param>
/// <returns>a result containing the items arranged into clusters as well as the centroids converged on and the total distance value for the cluster nodes.</returns>
public static KMeansResults<T> Cluster<T>(T[] items, int clusterCount, int maxIterations, KMeansCalculateDistanceDelegate calculateDistanceFunction = null, int randomSeed = 0, int[] initialCentroidIndices = null)
{
double[][] data = ConvertEntities(items);
// Use the built in Euclidean distance calculation if no custom one is specified
if (calculateDistanceFunction == null)
calculateDistanceFunction = CalculateDistance;
bool hasChanges = true;
int iteration = 0;
double totalDistance = 0;
int numData = data.Length;
int numAttributes = data[0].Length;
// Create a random initial clustering assignment
int[] clustering = InitializeClustering(numData, clusterCount, randomSeed);
// Create cluster means and centroids
KMeansStats[] stats = CreateMatrixCluster(clusterCount, numAttributes);
int[] centroidIdx = new int[clusterCount];
int[] clusterItemCount = new int[clusterCount];
// If we specify initial centroid indices then let's assign clustering based on those immediately
if (initialCentroidIndices != null && initialCentroidIndices.Length == clusterCount)
{
centroidIdx = initialCentroidIndices;
AssignClustering(data, clustering, centroidIdx, clusterCount, calculateDistanceFunction);
// Debug.WriteLine("Pre-Seeded Centroids resulted in initial clustering: " + string.Join(",", clustering.Select(x => x.ToString()).ToArray()));
}
// Perform the clustering
while (hasChanges && iteration < maxIterations)
{
clusterItemCount = new int[clusterCount];
totalDistance = CalculateClusteringInformation(data, clustering, ref stats, ref centroidIdx, clusterCount, ref clusterItemCount, calculateDistanceFunction);
// Debug.WriteLine("------------- Iter: " + iteration);
// Debug.WriteLine("Clustering: " + string.Join(",", clustering.Select(x => x.ToString()).ToArray()));
// Debug.WriteLine("Means: " + string.Join(",", means.Select(x => "[" + string.Join(",", x.Select(y => y.ToString("#0.0")).ToArray()) + "]").ToArray()));
// Debug.WriteLine("Centroids: " + string.Join(",", centroidIdx.Select(x => x.ToString()).ToArray()));
// Debug.WriteLine("Cluster Counts: " + string.Join(",", clusterItemCount.Select(x => x.ToString()).ToArray()));
hasChanges = AssignClustering(data, clustering, centroidIdx, clusterCount, calculateDistanceFunction);
++iteration;
}
// Create the final clusters
T[][] clusters = new T[clusterCount][];
try
{
for (int k = 0; k < clusters.Length; k++)
clusters[k] = new T[clusterItemCount[k]];
int[] clustersCurIdx = new int[clusterCount];
for (int i = 0; i < clustering.Length; i++)
{
clusters[clustering[i]][clustersCurIdx[clustering[i]]] = items[i];
++clustersCurIdx[clustering[i]];
}
}
catch(Exception e) { }
// Return the results
return new KMeansResults<T>(clusters, stats, centroidIdx, totalDistance);
}
private static int[] InitializeClustering(int numData, int clusterCount, int seed)
{
var rnd = new Random(seed);
var clustering = new int[numData];
for (int i = 0; i < numData; ++i)
clustering[i] = rnd.Next(0, clusterCount);
return clustering;
}
private static double[][] CreateMatrix(int rows, int columns)
{
var matrix = new double[rows][];
for (int i = 0; i < matrix.Length; i++)
matrix[i] = new double[columns];
return matrix;
}
private static KMeansStats[] CreateMatrixCluster(int rows, int columns)
{
var matrix = new KMeansStats[rows];
for (int i = 0; i < matrix.Length; i++)
{
var low = new double[columns];
var mean = new double[columns];
var high = new double[columns];
var length = new double[columns];
matrix[i] = new KMeansStats(low,mean,high,length);
}
return matrix;
}
private static double CalculateClusteringInformation(double[][] data, int[] clustering, ref KMeansStats[] stats, ref int[] centroidIdx,
int clusterCount, ref int[] clusterItemCount, KMeansCalculateDistanceDelegate calculateDistanceFunction)
{
for (int i = 0; i < stats.Length; i++)
{
stats[i].Reset();
}
// Calculate the means for each cluster
// Do this in two phases, first sum them all up and then divide by the count in each cluster
for (int i = 0; i < data.Length; i++)
{
// Sum up the means
var row = data[i];
var clusterIdx = clustering[i]; // What cluster is data i assigned to
++clusterItemCount[clusterIdx]; // Increment the count of the cluster that row i is assigned to
for (int j = 0; j < row.Length; j++)
{
stats[clusterIdx].means[j] += row[j];
stats[clusterIdx].lows[j] = stats[clusterIdx].lows[j] == 0 ? row[j] : Math.Min(stats[clusterIdx].lows[j], row[j]);
stats[clusterIdx].highs[j]= Math.Max(stats[clusterIdx].highs[j], row[j]);
stats[clusterIdx].lengths[j]= clusterItemCount[clusterIdx];
}
}
// Now divide to get the average
for (int k = 0; k < stats.Length; k++)
{
for (int a = 0; a < stats[k].means.Length; a++)
{
int itemCount = clusterItemCount[k];
stats[k].means[a] /= itemCount > 0 ? itemCount : 1;
}
}
double totalDistance = 0;
// Calc the centroids
double[] minDistances = new double[clusterCount].Select(x => double.MaxValue).ToArray();
for (int i = 0; i < data.Length; i++)
{
var clusterIdx = clustering[i]; // What cluster is data i assigned to
//var distance = CalculateDistance(data[i], means[clusterIdx]);
var distance = calculateDistanceFunction(data[i], stats[clusterIdx].means);
totalDistance += distance;
if (distance < minDistances[clusterIdx])
{
minDistances[clusterIdx] = distance;
centroidIdx[clusterIdx] = i;
}
}
//double totalCentroidDistance = minDistances.Sum();
return totalDistance;
}
/// <summary>
/// Calculates the distance for each point in <see cref="data"/> from each of the centroid in <see cref="centroidIdx"/> and
/// assigns the data item to the cluster with the minimum distance.
/// </summary>
/// <returns>true if any clustering arrangement has changed, false if clustering did not change.</returns>
private static bool AssignClustering(double[][] data, int[] clustering, int[] centroidIdx, int clusterCount, KMeansCalculateDistanceDelegate calculateDistanceFunction)
{
bool changed = false;
for (int i = 0; i < data.Length; i++)
{
double minDistance = double.MaxValue;
int minClusterIndex = -1;
for (int k = 0; k < clusterCount; k++)
{
double distance = calculateDistanceFunction(data[i], data[centroidIdx[k]]);
if (distance < minDistance)
{
minDistance = distance;
minClusterIndex = k;
}
// todo: track outliers here as well and maintain an average and std calculation for the distances!
}
// Re-arrange the clustering for datapoint if needed
if (minClusterIndex != -1 && clustering[i] != minClusterIndex)
{
changed = true;
clustering[i] = minClusterIndex;
}
}
return changed;
}
/// <summary>
/// Calculates the eculidean distance from the <see cref="point"/> to the <see cref="centroid"/>
/// </summary>
private static double CalculateDistance(double[] point, double[] centroid)
{
// For each attribute calculate the squared difference between the centroid and the point
double sum = 0;
for (int i = 0; i < point.Length; i++)
sum += Math.Pow(centroid[i] - point[i], 2);
return Math.Sqrt(sum);
//return Math.Sqrt(point.Select((t, i) => Math.Pow(centroid[i] - t, 2)).Sum()); // LINQ is slower than doing the for-loop!
}
}
/// <summary>
/// Represents a single result from the <see cref="KMeans"/> algorithm.
/// Contains the original items arranged into the clusters converged on as well as the centroids chosen and the total distance of the converged solution.
/// </summary>
/// <typeparam name="T"></typeparam>
public class KMeansResults<T>
{
/// <summary>
/// The original items arranged into the clusters converged on
/// </summary>
public T[][] Clusters { get; private set; }
/// <summary>
/// The final low, high, mean, length values used for the clusters. Mostly for debugging purposes.
/// </summary>
public KMeansStats[] Stats { get; private set; }
/// <summary>
/// The list of centroids used in the final solution. These are indicies into the original data.
/// </summary>
public int[] Centroids { get; private set; }
/// <summary>
/// The total distance between all the nodes and their centroids in the final solution.
/// This can be used as a reference point on how "good" the solution is when the algorithm is run repeatedly with different starting configuration.
/// Lower is "usually" better.
/// </summary>
public double TotalDistance { get; private set; }
public KMeansResults(T[][] clusters, KMeansStats[] stats, int[] centroids, double totalDistance)
{
Clusters = clusters;
Stats = stats;
Centroids = centroids;
TotalDistance = totalDistance;
}
}
}using System;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Algorithm.CSharp;
namespace QuantConnect.Indicators
{
public class DataK { public DataK(double _price) { this.price = _price; } [KMeansValue] public double price { get; set; } }
public class SwingSRIndicator : BarIndicator
{
public readonly RollingWindow<double> Prices;
int _trend_dir = 0;
bool changed = false;
bool updated = false;
public SwingSRIndicator(string name, int length = 20)
: base(name)
{
Prices = new RollingWindow<double>(length);
}
/// <summary>
/// Computes the average value
/// </summary>
/// <param name="input">The data for the calculation</param>
/// <returns>The average value</returns>
protected override decimal ComputeNextValue(IBaseDataBar data)
{
if( (Prices.IsReady))
{
List<DataK> d = new List<DataK>();
foreach (var bar in Prices)
{
d.Add(new DataK(bar));
}
int clustersize = 6;
var clusters = KMeans.Cluster(d.ToArray(), clustersize, 30);
var _lowlow = 1000000d;
var _lowhigh = 1000000d;
var _lowmean = 1000000d;
var _highlow = 0d;
var _highhigh = 0d;
var _highmean = 0d;
int j=0;
int[] excludes =new int[clusters.Stats.Length];
double[] weight =new double[clusters.Stats.Length];
foreach(var stats in clusters.Stats)
{
excludes[j] = (int)stats.lengths[0];
weight[j++] = stats.lengths[0]/d.Count();
}
var price = data.Close;
int minIndex = Array.IndexOf(excludes, excludes.Min());
int maxIndex = Array.IndexOf(excludes, excludes.Max());
j=0;
double max=0;
double _high = (double)0;
foreach(var stats in clusters.Stats)
{
if( weight[j]>=1/clustersize)
{
if(stats.lows[0]<_lowlow)
{
_lowlow = stats.lows[0];
_lowhigh = stats.highs[0];
_lowmean = stats.means[0];
}
}
if(weight[j]>=1/clustersize)
{
if(stats.highs[0]>_highhigh)
{
_highlow = stats.lows[0];
_highhigh = stats.highs[0];
_highmean = stats.means[0];
}
}
j++;
}
if (price<(decimal)_lowmean)
{
if(_trend_dir!=1) {changed=true; }
_trend_dir = 1;
}
else if (price>(decimal)_highmean)
{
if(_trend_dir!=-1) changed=true;
_trend_dir = -1;
}
}
Prices.Add((double)data.Close);
if (!Prices.IsReady) return 0;
updated = true;
return (decimal)_trend_dir;
}
public bool Updated()
{
if(updated) { updated = false; return true; }
return false;
}
public bool ChangedDirection()
{
return changed;
}
/// <summary>
/// Returns whether the indicator will return valid results
/// </summary>
public override bool IsReady
{
get { return Prices.IsReady; }
}
/// <summary>
/// Resets the average to its initial state
/// </summary>
public override void Reset()
{
Prices.Reset();
base.Reset();
}
}
}/*
* 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 ResistanceModulatedSplitter : QCAlgorithm
{
public Symbol cboeVix;
public decimal vix;
public IEnumerable<Symbol> symbols;
public RateOfChange vixROC;
Resolution resolution = Resolution.Minute;
int periods =1;
public override void Initialize()
{
//Start and End Date range for the backtest:
SetStartDate(2000, 1, 1);
SetEndDate(2020, 7, 1);
SetCash(10000);
SetBenchmark("AAPL");
// RegisterIndicator
// SetWarmUp(60);
UniverseSettings.Leverage = 100;
cboeVix = AddData<CBOE>("VIX", Resolution.Daily).Symbol;
SetAlpha(new AlphaSkew(1, 1, resolution, periods));
vixROC = new RateOfChange(cboeVix, 9);
RegisterIndicator(cboeVix, vixROC, Resolution.Daily);
SetExecution(new ImmediateExecutionModel2());//VolumeWeightedAveragePriceExecutionModel());
//SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel2(new TimeSpan(1,0,0)));
//SetExecution(new ImmediateExecutionModel2());//VolumeWeightedAveragePriceExecutionModel());
SetPortfolioConstruction(new MeanVarianceOptimizationPortfolioConstructionModel2( timeSpan:new TimeSpan(1,0,0), portfolioBias: PortfolioBias.LongShort,optimizer:new MaximumSharpeRatioPortfolioOptimizer()));
//SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
// SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
// AddUniverseSelection(new ManualUniverseSelectionModel(
// QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA)));
AddUniverseSelection(new ManualUniverseSelectionModel(
QuantConnect.Symbol.Create("BCOUSD", SecurityType.Cfd, Market.Oanda),
//uantConnect.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)
));
SetBrokerageModel(QuantConnect.Brokerages.BrokerageName.OandaBrokerage);
AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity());
AddRiskManagement(new MaximumDrawdownPercentPerSecurity());
//SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
}
/// 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)
{
if (data.ContainsKey(cboeVix))
{
vix = data.Get<CBOE>(cboeVix).Close;
if(vix>1)
vixROC.Update(new IndicatorDataPoint(data.Get<CBOE>(cboeVix).Time, data.Get<CBOE>(cboeVix).Close));
}
}
int lastMonth=-1;
int lastYear=-1;
// sort the data by daily dollar volume and take the top 'NumberOfSymbols'
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
if (symbols !=null && lastMonth==Time.Month)// && Time.Month % 2 != 0 )//&& Time.DayOfWeek != DayOfWeek.Friday)
{
return symbols;
}
lastMonth=Time.Month;
lastYear=Time.Year;
var numberOfSymbolsCoarse = 500;
// select only symbols with fundamental data and sort descending by daily dollar volume
var sortedByDollarVolume = coarse
.Where(x => x.HasFundamentalData)
.Where(x => x.Price>5)
.OrderByDescending(x => x.DollarVolume);
// take the top entries from our sorted collection
var top5 = sortedByDollarVolume.Take(numberOfSymbolsCoarse);
// we need to return only the symbol objects
return top5.Select(x => x.Symbol);
}
int finelastMonth=-1;
int finelastYear=-1;
// sort the data by P/E ratio and take the top 'numberOfSymbolsFine'
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
if (symbols !=null && finelastMonth==Time.Month)// && Time.Month % 2 != 0 )//&& Time.Month % 2 != 0 && Time.DayOfWeek != DayOfWeek.Friday){
{
return symbols;
}
finelastMonth=Time.Month;
finelastYear=Time.Year;
List <FineFundamental> s = new List<FineFundamental>();
var numberOfSymbolsFine = 8;
fine.OrderByDescending(x => x.OperationRatios.ROE.Value);
// we need to return only the symbol objects
symbols = Momemtum(fine.Take(50)).Take(10).Select(x => x.Symbol);
return symbols;
}
public static IEnumerable<FineFundamental> EVEBITDA(IEnumerable<FineFundamental> fine)
{
return fine
.Where(x => x.ValuationRatios.EVToEBITDA > 0)
.Where(x => x.EarningReports.BasicAverageShares.ThreeMonths > 0)
.Where(x => x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths*x.ValuationRatios.PERatio) > 5000000000);
}
public static IEnumerable<FineFundamental> Industry(IEnumerable<FineFundamental> fine, string industry, int numberOfSymbolsFine)
{
var I = fine
.Where(x => x.CompanyReference.IndustryTemplateCode == industry)
.OrderBy(x => x.ValuationRatios.EVToEBITDA);
// take the top entries from our sorted collection
return I.Take(numberOfSymbolsFine);
}
public static decimal ZScore(decimal totalassets, decimal totalliabilities,decimal workingcapital, decimal retainedearnings, decimal ebit, decimal totalrevenue,decimal shares, decimal price)
{
if(totalassets==0 || totalliabilities==0) return 0;
var X1 = 1.2m * (workingcapital / totalassets);
var X2 = 1.4m * (retainedearnings / totalassets);
var X3 = 3.3m * (ebit / totalassets);
var X4 = 0.6m * ((shares * price) / totalliabilities);
var X5 = 1.0m * (totalrevenue / totalassets);
return X1 + X2 + X3 + X4 + X5;
}
public static IEnumerable<FineFundamental> NCAV(IEnumerable<FineFundamental> fine)
{
List <FineFundamental> f = new List <FineFundamental>();
foreach (var x in fine)
{
if(x.EarningReports.BasicAverageShares.Value==0) continue;
var total_liabilities = x.FinancialStatements.BalanceSheet.CurrentLiabilities.Value+
x.FinancialStatements.BalanceSheet.TotalNonCurrentLiabilitiesNetMinorityInterest.Value;
var ncav = (x.FinancialStatements.BalanceSheet.CurrentAssets.Value - total_liabilities)/x.EarningReports.BasicAverageShares.Value;
if(ncav > 1.5m)
{
f.Add(x);
}
}
return f;
}
public static IEnumerable<FineFundamental> ZScore2(IEnumerable<FineFundamental> fine)
{
List <FineFundamental> f = new List <FineFundamental>();
foreach (var x in fine)
{
if( ResistanceModulatedSplitter.ZScore(x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths,
x.FinancialStatements.BalanceSheet.CurrentLiabilities.Value,
x.FinancialStatements.BalanceSheet.WorkingCapital.TwelveMonths,
x.FinancialStatements.BalanceSheet.RetainedEarnings.TwelveMonths,
x.FinancialStatements.IncomeStatement.EBIT.TwelveMonths,
x.FinancialStatements.IncomeStatement.TotalRevenue.TwelveMonths,
x.EarningReports.BasicAverageShares.TwelveMonths,
x.Price) > 1.81m)
{
f.Add(x);
}
}
return f;
}
public IEnumerable<FineFundamental> Momemtum(IEnumerable<FineFundamental> fine)
{
List <FineFundamental> f = new List <FineFundamental>();
List<MomentumSelection> s = new List<MomentumSelection>();
foreach (var x in fine)
{
var history = History(x.Symbol,126,Resolution.Daily);
if(history.Count()>0)
{
var m = new MomentumSelection();
m.symbol = x.Symbol;
m.fine = x;
m.rate = history.First().Close/history.Last().Close;
s.Add(m);
}
}
if(s.Count()<1) return f;
s.OrderByDescending(x => x.rate);
return s.Select(x=> x.fine);
}
}
/*
.Where(x => x.SecurityReference.SecurityType == "ST00000001")
.Where(x => x.SecurityReference.IsPrimaryShare)
.Where(x => x.ValuationRatios.EVToEBITDA > 0)
.Where(x => x.EarningReports.BasicAverageShares.ThreeMonths > 0)
.Where(x =>
{
var averageShares = x.EarningReports.BasicAverageShares.ThreeMonths;
var history = History(x.Symbol, 1, Resolution.Daily);
var close = history.FirstOrDefault()?.Close;
// If history is empty, close will be null
// In this case, we will not consider the security
if (close == null)
{
return false;
}
return averageShares * close > 2 * 1000 * 1000 * 1000;
})
.OrderByDescending(x => x.ValuationRatios.EVToEBITDA)
.Select(x => x.Symbol);
*/
public class MomentumSelection
{
public Symbol symbol;
public decimal rate;
public FineFundamental fine;
}
}/*
* 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.Algorithm.Framework.Alphas;
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Algorithm.Framework.Portfolio
{
/// <summary>
/// Provides an implementation of <see cref="IPortfolioConstructionModel"/> that gives equal weighting to all
/// securities. The target percent holdings of each security is 1/N where N is the number of securities. For
/// insights of direction <see cref="InsightDirection.Up"/>, long targets are returned and for insights of direction
/// <see cref="InsightDirection.Down"/>, short targets are returned.
/// </summary>
public class EqualWeightingPortfolioConstructionModel2 : PortfolioConstructionModel
{
private DateTime _rebalancingTime;
private readonly TimeSpan _rebalancingPeriod;
private List<Symbol> _removedSymbols;
private readonly InsightCollection _insightCollection = new InsightCollection();
private DateTime? _nextExpiryTime;
/// <summary>
/// Initialize a new instance of <see cref="EqualWeightingPortfolioConstructionModel"/>
/// </summary>
/// <param name="resolution">Rebalancing frequency</param>
public EqualWeightingPortfolioConstructionModel2(Resolution resolution = Resolution.Daily)
{
_rebalancingPeriod = resolution.ToTimeSpan();
}
public EqualWeightingPortfolioConstructionModel2(TimeSpan timespan)
{
_rebalancingPeriod = timespan;
}
/// <summary>
/// Create portfolio targets from the specified insights
/// </summary>
/// <param name="algorithm">The algorithm instance</param>
/// <param name="insights">The insights to create portfolio targets from</param>
/// <returns>An enumerable of portfolio targets to be sent to the execution model</returns>
public override IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
{
var targets = new List<IPortfolioTarget>();
if (algorithm.UtcTime <= _nextExpiryTime &&
algorithm.UtcTime <= _rebalancingTime &&
insights.Length == 0 &&
_removedSymbols == null)
{
return targets;
}
_insightCollection.AddRange(insights);
// Create flatten target for each security that was removed from the universe
if (_removedSymbols != null)
{
var universeDeselectionTargets = _removedSymbols.Select(symbol => new PortfolioTarget(symbol, 0));
targets.AddRange(universeDeselectionTargets);
_removedSymbols = null;
}
// Get insight that haven't expired of each symbol that is still in the universe
var activeInsights = _insightCollection.GetActiveInsights(algorithm.UtcTime);
// 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();
// give equal weighting to each security
var count = lastActiveInsights.Count(x => x.Direction != InsightDirection.Flat);
var percent = count == 0 ? 0 : 1m / count;
//percent = 0.1m;
// var percent = Math.Min(count == 0 ? 0 : 1m / count,0.1m);
// percent = 0.5m;
var errorSymbols = new HashSet<Symbol>();
foreach (var insight in lastActiveInsights)
{
var target = PortfolioTarget.Percent(algorithm, insight.Symbol, (int) insight.Direction * percent);
if (target != null)
{
targets.Add(target);
}
else
{
errorSymbols.Add(insight.Symbol);
}
}
// Get expired insights and create flatten targets for each symbol
var expiredInsights = _insightCollection.RemoveExpiredInsights(algorithm.UtcTime);
var expiredTargets = from insight in expiredInsights
group insight.Symbol by insight.Symbol into g
where !_insightCollection.HasActiveInsights(g.Key, algorithm.UtcTime) && !errorSymbols.Contains(g.Key)
select new PortfolioTarget(g.Key, 0);
targets.AddRange(expiredTargets);
_nextExpiryTime = _insightCollection.GetNextExpiryTime();
_rebalancingTime = algorithm.UtcTime.Add(_rebalancingPeriod);
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)
{
// Get removed symbol and invalidate them in the insight collection
_removedSymbols = changes.RemovedSecurities.Select(x => x.Symbol).ToList();
_insightCollection.Clear(_removedSymbols.ToArray());
}
}
}