Overall Statistics |
Total Trades 1 Average Win 0.62% Average Loss 0% Compounding Annual Return 69.263% Drawdown 0.500% Expectancy 0 Net Profit 0.618% Sharpe Ratio 6.047 Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha -0.22 Beta 0.921 Annual Standard Deviation 0.052 Annual Variance 0.003 Information Ratio -13.116 Tracking Error 0.02 Treynor Ratio 0.338 |
using System; using System.Collections; using System.Collections.Generic; using QuantConnect.Securities; using QuantConnect.Models; namespace QuantConnect { // Name your algorithm class anything, as long as it inherits QCAlgorithm public class BasicTemplateAlgorithm : QCAlgorithm { AverageDirectionalIndex adx = new AverageDirectionalIndex(3); //Initialize the data and resolution you require for your strategy: public override void Initialize() { SetStartDate(2014, 12, 01); SetEndDate(DateTime.Now.Date.AddDays(-1)); SetCash(25000); AddSecurity(SecurityType.Equity, "SPY", Resolution.Minute); } //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol. public void OnData(TradeBars data) { TradeBar SPY = data["SPY"]; if (!Portfolio.Invested) Order("SPY", 100); //Build indicator: adx.AddSample(SPY); Log("READY? " + adx.Ready + " VALUE: " + Math.Round(adx.ADX,2).ToString()); Plot("ADX", "adx", Math.Round(adx.ADX,2)); } } }
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Concurrent; using QuantConnect.Securities; using QuantConnect.Models; using System.Linq; namespace QuantConnect { /* ADX Indicator - Average Directional Index is a complicated but powerful indicator. * * 1. Calculate the Directional Movements: +DM_t, -DM_t. * 2. Find the Average Directional Movement: ADM_t * 3. Calculate the Average True Range: ATR_t * 4. Calculate the Directional Indexes: DI+_t, DI-_t, * Directional Movement Index: DX_t, * then Average Directional Movement Index! ADX_t * * Requires day to be divided into segments, periods and a running record kept of the previous value so the averages can be made. */ public class AverageDirectionalIndex { //Public Result Access: Primary ADX Indicator public decimal ADX { get; private set; } //Public Result Access: DMI Positive: public decimal DMI_Pos { get; private set; } //Public Result Access: DMI Negative: public decimal DMI_Neg { get; private set; } //Public Result Access: Direction of the ADX Indicator: public decimal Decision { get; private set; } //Public Result Access: When indicator has sufficient data flags as true. public bool Ready { get; private set; } //Initialize private ADXCacheItem indicatorMemory = new ADXCacheItem(); private decimal _expConst = 0; private int _samplePeriods = 0; private Candle _superCandle = new Candle(); private FixedLengthQueue<TradeBar> _rollingQueue = new FixedLengthQueue<TradeBar>(0); // Constructor: Set the sample period: public AverageDirectionalIndex(int samplePeriods) { _expConst = (2m / (decimal)(samplePeriods + 1)); _samplePeriods = samplePeriods; _rollingQueue = new FixedLengthQueue<TradeBar>(samplePeriods); Ready = false; } /// <summary> /// Calculate the ADX figure, return an indicator result. /// </summary> public decimal AddSample(TradeBar bar) { //0. Save these samples in running OHLC candle until requested samplePeriod reached: _rollingQueue.Enqueue(bar); if (_rollingQueue.Count < _samplePeriods) return Decision; //Create the bar on this rolling window: var bars = _rollingQueue.ToList(); foreach (var queueBar in bars) { _superCandle.Update(bar); } //0. Define a result storage for this session. ADXCacheItem current = new ADXCacheItem(); current.OHLC = _superCandle; //0. If this is the first candle skip it and come back for second: to calc directional index can't be relative to 0. if (!indicatorMemory.Set) { current.Set = true; indicatorMemory = current; _superCandle = new Candle(); return Decision; } //1. Calculate the Directional Movements: store results back into current-variable class cache GetDirectionalMovements(ref current); //2. Find the Average Directional Movement. GetAverageDirectionalMovements(ref current); //3. Get the Average True Range: GetAverageTrueRange(ref current); //4. Get the Average Directional Movement Index ADX-t, and DI+, DI- GetADX(ref current); //Strong Trend is Present, and have at least X-min data Decision = 0; if (current.Adx > 40) { //NOW! We have an ADX result, interpret it.. if (current.DmiPos > 40) { Decision = 1; } else if (current.DmiNeg > 40) { Decision = -1; } } //Save the results to publicly accessible properties. ADX = current.Adx; DMI_Neg = current.DmiNeg; DMI_Pos = current.DmiPos; //Update the indicator cache - store previous result between calls. current.Set = true; Ready = true; indicatorMemory = current; _superCandle = new Candle(); return Decision; } /// <summary> /// 1. Get the pure directional movements, in DM+, DM- Form. /// </summary> /// <param name="current">ADX Cache class for easy storing for next analysis session.</param> private void GetDirectionalMovements(ref ADXCacheItem current) { //Change from the previous period to now. decimal deltaHigh = current.OHLC.High - indicatorMemory.OHLC.High; decimal deltaLow = indicatorMemory.OHLC.Low - current.OHLC.Low; //Allocate the Delta Movement. if ((deltaHigh < 0 && deltaLow < 0) || (deltaHigh == deltaLow)) { current.Dm_plus = 0; current.Dm_neg = 0; } else if (deltaHigh > deltaLow) { current.Dm_plus = deltaHigh; current.Dm_neg = 0; } else if (deltaHigh < deltaLow) { current.Dm_plus = 0; current.Dm_neg = deltaLow; } } /// <summary> /// 2. Get the Exp Average of the directional movement indexs /// </summary> private void GetAverageDirectionalMovements(ref ADXCacheItem current) { if (!Ready) { //If this is the first run, current.Adm_plus = current.Dm_plus; current.Adm_neg = current.Dm_neg; } else { //This is not our first sample current.Adm_plus = (current.Dm_plus * _expConst) + (indicatorMemory.Adm_plus * (1 - _expConst)); current.Adm_neg = (current.Dm_neg * _expConst) + (indicatorMemory.Adm_neg * (1 - _expConst)); ; } } /// <summary> /// 3. Get the true range of the price. /// </summary> private void GetAverageTrueRange(ref ADXCacheItem current) { decimal yesterdayClose = indicatorMemory.OHLC.Close; decimal trueRange = System.Math.Max(Math.Abs(current.OHLC.High - current.OHLC.Low), System.Math.Max(Math.Abs(current.OHLC.High - yesterdayClose), Math.Abs(yesterdayClose - current.OHLC.Low))); //Get the current true range: if (indicatorMemory.Atr == 0) { current.Atr = trueRange; } else { current.Atr = (trueRange * _expConst) + ((1 - _expConst) * indicatorMemory.Atr); } } /// <summary> /// 4. Get the Directional Movement Index /// </summary> private void GetADX(ref ADXCacheItem current) { decimal dmi_plus = 0; decimal dmi_neg = 0; if (current.Atr > 0) { dmi_plus = (current.Adm_plus / current.Atr) * 100; dmi_neg = (current.Adm_neg / current.Atr) * 100; } if ((dmi_plus + dmi_neg) != 0) { current.Dx = (Math.Abs(dmi_plus - dmi_neg) / (dmi_plus + dmi_neg)) * 100; } else { current.Dx = indicatorMemory.Dx; } //Save the results. current.DmiPos = dmi_plus; current.DmiNeg = dmi_neg; current.Adx = current.Dx * _expConst + (1 - _expConst) * indicatorMemory.Adx; } /// <summary> /// Provide a structure for caching the previous values of the ADX /// </summary> public class ADXCacheItem { public Candle OHLC = new Candle(); public bool Set = false; public decimal Atr = 0; public decimal Dm_plus = 0; public decimal Dm_neg = 0; public decimal Adm_plus = 0; public decimal Adm_neg = 0; public decimal Dx = 0; public decimal DmiPos = 0; public decimal DmiNeg = 0; public decimal Adx = 0; } /// <summary> /// Simple online "super-tradebar" generator for making an OHLC from multiple bars. /// </summary> public class Candle { public Candle() { } public decimal Open = 0; public decimal High = Decimal.MinValue; public decimal Low = Decimal.MaxValue; public decimal Close = 0; public int Samples = 0; public void Update(TradeBar bar) { if (Open == 0) Open = bar.Open; if (High < bar.High) High = bar.High; if (Low > bar.Low) Low = bar.Low; Close = bar.Close; Samples++; } } /// <summary> /// Cap the queue length /// </summary> public class FixedLengthQueue<T> : ConcurrentQueue<T> { private readonly object syncObject = new object(); public int Size { get; private set; } public FixedLengthQueue(int size) { Size = size; } public new void Enqueue(T obj) { base.Enqueue(obj); lock (syncObject) { while (base.Count > Size) { T outObj; base.TryDequeue(out outObj); } } } } } // End ADX Indicator Class } // End Namespace