Overall Statistics |
Total Trades 331 Average Win 5.08% Average Loss -34.48% Compounding Annual Return 80.505% Drawdown 88.500% Expectancy 0.133 Net Profit 114241.053% Sharpe Ratio 1.262 Probabilistic Sharpe Ratio 17.268% Loss Rate 1% Win Rate 99% Profit-Loss Ratio 0.15 Alpha 0.753 Beta 5.304 Annual Standard Deviation 0.995 Annual Variance 0.99 Information Ratio 1.293 Tracking Error 0.898 Treynor Ratio 0.237 Total Fees $0.00 Estimated Strategy Capacity $190000.00 Lowest Capacity Asset SPX500USD 8I |
using System.Linq; using System.Collections.Generic; using MathNet.Numerics.Statistics; using System.Collections.Concurrent; namespace QuantConnect.CSharp.Algorithms { public class ScalpingAlgorithm : QCAlgorithm { private SimpleMovingAverage ema; private SimpleMovingAverage fma; //private RateOfChange roc; private RateOfChange rocvix; private string symbol = "SPX500USD"; private Delay delayrocvix; decimal lastPrice; decimal lastQuantity=0m; decimal tppp; //$ delta for taking profit based on KDE DateTime vixtime; decimal maxProfit; MyRollingWindow<double> ws; Symbol _vix; decimal vix; decimal currentKDE; const double sigma = 0.03d; bool printt = false; private List<Symbol> symbols; private ArmsIndex t; public override void Initialize() { SetStartDate(2010, 1, 1); //SetEndDate(2010, 6, 1); //SetEndDate(DateTime.Now); SetCash(10000); AddSecurity(SecurityType.Cfd, symbol, Resolution.Minute, Market.Oanda, false, 100m, true); _vix = AddData<Fred>(Fred.CBOE.VIX).Symbol; SetBenchmark(symbol); fma = new SimpleMovingAverage(9); ema = new SimpleMovingAverage(20); ws = new MyRollingWindow<double>(4*60); rocvix = new RateOfChange(Int32.Parse(GetParameter("rocvix"))); delayrocvix = new Delay(1).Of(rocvix); RegisterIndicator(symbol,fma,Resolution.Daily); RegisterIndicator(symbol,ema,Resolution.Daily);//, data => data.Value); SetWarmup(60*100); tppp=decimal.Parse(GetParameter("tppp")); foreach(var b in History<QuoteBar>(symbol, 201, Resolution.Daily).ToList()) { fma.Update(new IndicatorDataPoint(b.Time, b.Close)); ema.Update(new IndicatorDataPoint(b.Time, b.Close)); } foreach(var b in History<QuantConnect.DataSource.Fred>(_vix, 21, Resolution.Daily).ToList()) { rocvix.Update(new IndicatorDataPoint(b.Time, b.Value)); vix = b.Value; vixtime=b.Time; } foreach(var b in History<QuoteBar>(symbol, 4*60+1, Resolution.Minute).ToList()) { lastPrice=b.Price; ws.Add((double)lastPrice);//-llastPrice)); } Log("vix "+vix); Log("vixtime "+vixtime); Log("rocvix "+rocvix.Current.Value); Log("rocvix-delay "+delayrocvix.Current.Value); Log("fma" + fma); Log("ema" + ema); //SetBrokerageModel(new MyBrokerageModel(AccountType.Margin)/*BrokerageName.OandaBrokerage, AccountType.Margin*/); SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin); UniverseSettings.Resolution = Resolution.Daily; AddUniverse(CoarseSelectionFunction,SelectFine); } private int _lastMonth = -1; // sort the data by daily dollar volume and take the top 'NumberOfSymbols' public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { //if (_lastMonth!=-1) if ( Time.Year == _lastMonth) { return Universe.Unchanged; } var top = (from x in coarse where x.HasFundamentalData && x.Volume > 0 && x.Price > 5 orderby x.DollarVolume descending select x).Take(_numberOfSymbolsCoarse).ToList(); // we need to return only the symbol objects symbols = top.Select(x => x.Symbol).ToList<Symbol>(); _dollarVolumeBySymbol.Clear(); foreach (var x in top) { _dollarVolumeBySymbol[x.Symbol] = x.DollarVolume; } // If no security has met the QC500 criteria, the universe is unchanged. // A new selection will be attempted on the next trading day as _lastMonth is not updated if (_dollarVolumeBySymbol.Count == 0) { return Universe.Unchanged; } return _dollarVolumeBySymbol.Keys; } private readonly Dictionary<Symbol, decimal> _dollarVolumeBySymbol = new Dictionary<Symbol, decimal>(); private const int _numberOfSymbolsCoarse = 1000; private const int _numberOfSymbolsFine = 100; /// <summary> /// Performs fine selection for the QC500 constituents /// The company's headquarter must in the U.S. /// The stock must be traded on either the NYSE or NASDAQ /// At least half a year since its initial public offering /// The stock's market cap must be greater than 500 million /// </summary> public IEnumerable<Symbol> SelectFine(IEnumerable<FineFundamental> fine) { var filteredFine = (from x in fine where x.CompanyReference.CountryId == "USA" && (x.CompanyReference.PrimaryExchangeID == "NYS" || x.CompanyReference.PrimaryExchangeID == "NAS") && (Time - x.SecurityReference.IPODate).Days > 180 && x.MarketCap > 500000000m select x).ToList(); var count = filteredFine.Count; // If no security has met the QC500 criteria, the universe is unchanged. // A new selection will be attempted on the next trading day as _lastMonth is not updated if (count == 0) { return Universe.Unchanged; } // Update _lastMonth after all QC500 criteria checks passed _lastMonth = Time.Year; var percent = _numberOfSymbolsFine / (double)count; // select stocks with top dollar volume in every single sector var topFineBySector = (from x in filteredFine // Group by sector group x by x.CompanyReference.IndustryTemplateCode into g let y = from item in g orderby _dollarVolumeBySymbol[item.Symbol] descending select item let c = (int)Math.Ceiling(y.Count() * percent) select new { g.Key, Value = y.Take(c) } ).ToDictionary(x => x.Key, x => x.Value); return topFineBySector.SelectMany(x => x.Value) .OrderByDescending(x => _dollarVolumeBySymbol[x.Symbol]) .Take(_numberOfSymbolsFine) .Select(x => x.Symbol); } public override void OnData(Slice data) { if(data.ContainsKey(_vix)) { vix=data[_vix].Value; vixtime=data[_vix].Time; rocvix.Update(new IndicatorDataPoint(data[_vix].Time, vix)); if(IsWarmingUp) return; Log("vix "+vix); Log("vixtime "+vixtime); } } public override void OnSecuritiesChanged(SecurityChanges changes) { var s = changes.AddedSecurities.Where(x=> x.Symbol.Value!="SPX500USD").Select(x => x.Symbol); if (changes.AddedSecurities.Count > 0 && s.ToList().Count>0) { t = TRIN(s,Resolution.Daily); foreach(var b in History<TradeBar>(s, 3, Resolution.Daily).ToList()) { foreach(var c in b.Values) { t.Update(c); } } } } public void OnData(QuoteBars data) { if(IsWarmingUp) return; if(!data.ContainsKey(symbol)) return; lastPrice=data[symbol].Price; ws.Add((double)lastPrice); if (!ws.IsReady) return; if(!printt) { Log("vix "+vix); Log("vixtime "+vixtime); printt=true; Log("I'm ready "); } if ( IsMarketOpen(symbol) ) { if ( Portfolio[symbol].Invested && lastQuantity>0 ) { var profitPercent = Securities[symbol].Holdings.UnrealizedProfitPercent; maxProfit = Math.Max(profitPercent,maxProfit); if ( (lastPrice>currentKDE && profitPercent < maxProfit*decimal.Parse(GetParameter("tl")) && maxProfit>decimal.Parse(GetParameter("tp"))) ) { MarketOrder(symbol,-lastQuantity); lastQuantity=0; maxProfit=0; } else if( highvolatity()) { //STOP loss, market is changing direction MarketOrder(symbol,-lastQuantity); lastQuantity=0; maxProfit=0; } } if ( fma>ema && lastQuantity==0 && t<1.0m) { // if trending up if(!highvolatity()) { // if volatility is high int quantity = maxquantity(); currentKDE=getKDE(); if(data[symbol].Bid.Close<currentKDE-1.0m) { MarketOrder(symbol,quantity); lastQuantity=lastQuantity+quantity; } } } } } private bool highvolatity() { return (delayrocvix.Current.Value > decimal.Parse(GetParameter("rocstop")) && delayrocvix.Current.Value<rocvix.Current.Value); } private int maxquantity() { var totalPortfolioValue = Portfolio.TotalPortfolioValue; var margin = Portfolio.GetMarginRemaining(totalPortfolioValue); var freemargin = margin / 2; var marginperunit = (lastPrice*0.05m); //margin utilized per unit var unitsavailable = margin/marginperunit; //margin utilized per unit var maxquantity = Math.Round(unitsavailable*0.22m); return (int)maxquantity; } private decimal getKDE() { double[,] k = KernelDensityEstimation(ws.ToArray(),sigma,ws.ToArray().Length); var m = 0d; var pp=0m; for (var ii=0; ii<k.Length/2-1; ii++) { if(k[ii,1]>m) { m = k[ii,1]; pp=(decimal)k[ii,0]; } } return pp; } public double[,] KernelDensityEstimation(double[] data, double sigma, int nsteps) { // probability density function (PDF) signal analysis // Works like ksdensity in mathlab. // KDE performs kernel density estimation (KDE)on one - dimensional data // http://en.wikipedia.org/wiki/Kernel_density_estimation // Input: -data: input data, one-dimensional // -sigma: bandwidth(sometimes called "h") // -nsteps: optional number of abscis points.If nsteps is an // array, the abscis points will be taken directly from it. (default 100) // Output: -x: equispaced abscis points // -y: estimates of p(x) // This function is part of the Kernel Methods Toolbox(KMBOX) for MATLAB. // http://sourceforge.net/p/kmbox // Converted to C# code by ksandric double[,] result = new double[nsteps, 2]; double[] x = new double[nsteps], y = new double[nsteps]; double MAX = Double.MinValue, MIN = Double.MaxValue; int N = data.Length; // number of data points // Find MIN MAX values in data for (int i = 0; i < N; i++) { if (MAX < data[i]) { MAX = data[i]; } if (MIN > data[i]) { MIN = data[i]; } } // Like MATLAB linspace(MIN, MAX, nsteps); x[0] = MIN; for (int i = 1; i < nsteps; i++) { x[i] = x[i - 1] + ((MAX - MIN) / nsteps); } // kernel density estimation double c = 1.0 / (Math.Sqrt(2 * Math.PI * sigma * sigma)); for (int i = 0; i < N; i++) { for (int j = 0; j < nsteps; j++) { y[j] = y[j] + 1.0 / N * c * Math.Exp(-(data[i] - x[j]) * (data[i] - x[j]) / (2 * sigma * sigma)); } } // compilation of the X,Y to result. Good for creating plot(x, y) for (int i = 0; i < nsteps; i++) { result[i, 0] = x[i]; result[i, 1] = y[i]; } return result; } } }
/* * 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; using System.Collections.Generic; using System.Threading; using static QuantConnect.StringExtensions; namespace QuantConnect.Indicators { /// <summary> /// This is a window that allows for list access semantics, /// where this[0] refers to the most recent item in the /// window and this[Count-1] refers to the last item in the window /// </summary> /// <typeparam name="T">The type of data in the window</typeparam> public class MyRollingWindow<T> : IReadOnlyWindow<T> { // the backing list object used to hold the data private readonly List<T> _list; // read-write lock used for controlling access to the underlying list data structure private readonly ReaderWriterLockSlim _listLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); // the most recently removed item from the window (fell off the back) private T _mostRecentlyRemoved; // the total number of samples taken by this indicator private decimal _samples; // used to locate the last item in the window as an indexer into the _list private int _tail; /// <summary> /// Initializes a new instance of the RollwingWindow class with the specified window size. /// </summary> /// <param name="size">The number of items to hold in the window</param> public MyRollingWindow(int size) { if (size < 1) { throw new ArgumentException("RollingWindow must have size of at least 1.", nameof(size)); } _list = new List<T>(size); Size = size; } /// <summary> /// Gets the size of this window /// </summary> public int Size { get; } /// <summary> /// Gets the current number of elements in this window /// </summary> public int Count { get { try { _listLock.EnterReadLock(); return _list.Count; } finally { _listLock.ExitReadLock(); } } } /// <summary> /// Gets the number of samples that have been added to this window over its lifetime /// </summary> public decimal Samples { get { try { _listLock.EnterReadLock(); return _samples; } finally { _listLock.ExitReadLock(); } } } /// <summary> /// Gets the most recently removed item from the window. This is the /// piece of data that just 'fell off' as a result of the most recent /// add. If no items have been removed, this will throw an exception. /// </summary> public T MostRecentlyRemoved { get { try { _listLock.EnterReadLock(); if (Samples <= Size) { throw new InvalidOperationException("No items have been removed yet!"); } return _mostRecentlyRemoved; } finally { _listLock.ExitReadLock(); } } } /// <summary> /// Indexes into this window, where index 0 is the most recently /// entered value /// </summary> /// <param name="i">the index, i</param> /// <returns>the ith most recent entry</returns> public T[] ToArray() { return _list.ToArray(); } /// <summary> /// Indexes into this window, where index 0 is the most recently /// entered value /// </summary> /// <param name="i">the index, i</param> /// <returns>the ith most recent entry</returns> public T this [int i] { get { try { _listLock.EnterReadLock(); if (Count == 0) { throw new ArgumentOutOfRangeException(nameof(i), "Rolling window is empty"); } else if (i > Size - 1 || i < 0) { throw new ArgumentOutOfRangeException(nameof(i), i, Invariant($"Index must be between 0 and {Size - 1} (rolling window is of size {Size})") ); } else if (i > Count - 1) { throw new ArgumentOutOfRangeException(nameof(i), i, Invariant($"Index must be between 0 and {Count - 1} (entry {i} does not exist yet)") ); } return _list[(Count + _tail - i - 1) % Count]; } finally { _listLock.ExitReadLock(); } } set { try { _listLock.EnterWriteLock(); if (i < 0 || i > Count - 1) { throw new ArgumentOutOfRangeException(nameof(i), i, Invariant($"Must be between 0 and {Count - 1}")); } _list[(Count + _tail - i - 1) % Count] = value; } finally { _listLock.ExitWriteLock(); } } } /// <summary> /// Gets a value indicating whether or not this window is ready, i.e, /// it has been filled to its capacity /// </summary> public bool IsReady { get { try { _listLock.EnterReadLock(); return Samples >= Size; } finally { _listLock.ExitReadLock(); } } } /// <summary> /// Returns an enumerator that iterates through the collection. /// </summary> /// <returns> /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection. /// </returns> /// <filterpriority>1</filterpriority> public IEnumerator<T> GetEnumerator() { // we make a copy on purpose so the enumerator isn't tied // to a mutable object, well it is still mutable but out of scope var temp = new List<T>(Count); try { _listLock.EnterReadLock(); for (int i = 0; i < Count; i++) { temp.Add(this[i]); } return temp.GetEnumerator(); } finally { _listLock.ExitReadLock(); } } /// <summary> /// Returns an enumerator that iterates through a collection. /// </summary> /// <returns> /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection. /// </returns> /// <filterpriority>2</filterpriority> IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// <summary> /// Adds an item to this window and shifts all other elements /// </summary> /// <param name="item">The item to be added</param> public void Add(T item) { try { _listLock.EnterWriteLock(); _samples++; if (Size == Count) { // keep track of what's the last element // so we can reindex on this[ int ] _mostRecentlyRemoved = _list[_tail]; _list[_tail] = item; _tail = (_tail + 1) % Size; } else { _list.Add(item); } } finally { _listLock.ExitWriteLock(); } } /// <summary> /// Clears this window of all data /// </summary> public void Reset() { try { _listLock.EnterWriteLock(); _samples = 0; _list.Clear(); _tail = 0; } finally { _listLock.ExitWriteLock(); } } } }