| 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();
}
}
}
}