Overall Statistics |
Total Trades 76 Average Win 1.04% Average Loss -0.44% Compounding Annual Return 2.844% Drawdown 6.400% Expectancy 0.216 Net Profit 7.798% Sharpe Ratio 0.605 Loss Rate 64% Win Rate 36% Profit-Loss Ratio 2.38 Alpha 0.03 Beta -0.003 Annual Standard Deviation 0.048 Annual Variance 0.002 Information Ratio -1.147 Tracking Error 0.123 Treynor Ratio -11.629 Total Fees $76.14 |
namespace QuantConnect { // // Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all // files use "public partial class" if you want to split up your algorithm namespace into multiple files. // /// <summary> /// This indicator computes Average Directional Index which measures trend strength without regard to trend direction. /// Firstly, it calculates the Directional Movement and the True Range value, and then the values are accumulated and smoothed /// using a custom smoothing method proposed by Wilder. For an n period smoothing, 1/n of each period's value is added to the total period. /// From these accumulated values we are therefore able to derived the 'Positive Directional Index' (+DI) and 'Negative Directional Index' (-DI) /// which is used to calculate the Average Directional Index. /// </summary> public class AverageDirectionalIndex : IndicatorBase<TradeBar> { private TradeBar _previousInput; private readonly int _period; private IndicatorBase<TradeBar> TrueRange { get; set; } private IndicatorBase<TradeBar> DirectionalMovementPlus { get; set; } private IndicatorBase<TradeBar> DirectionalMovementMinus { get; set; } private IndicatorBase<IndicatorDataPoint> SmoothedDirectionalMovementPlus { get; set; } private IndicatorBase<IndicatorDataPoint> SmoothedDirectionalMovementMinus { get; set; } private IndicatorBase<IndicatorDataPoint> SmoothedTrueRange { get; set; } /// <summary> /// Gets or sets the index of the Plus Directional Indicator /// </summary> /// <value> /// The index of the Plus Directional Indicator. /// </value> public IndicatorBase<IndicatorDataPoint> PositiveDirectionalIndex { get; private set; } /// <summary> /// Gets or sets the index of the Minus Directional Indicator /// </summary> /// <value> /// The index of the Minus Directional Indicator. /// </value> public IndicatorBase<IndicatorDataPoint> NegativeDirectionalIndex { get; private set; } /// <summary> /// Initializes a new instance of the <see cref="AverageDirectionalIndex"/> class. /// </summary> /// <param name="name">The name.</param> /// <param name="period">The period.</param> public AverageDirectionalIndex(string name, int period) : base(name) { _period = period; TrueRange = new FunctionalIndicator<TradeBar>(name + "_TrueRange", currentBar => { var value = ComputeTrueRange(currentBar); return value; }, isReady => _previousInput != null ); DirectionalMovementPlus = new FunctionalIndicator<TradeBar>(name + "_PositiveDirectionalMovement", currentBar => { var value = ComputePositiveDirectionalMovement(currentBar); return value; }, isReady => _previousInput != null ); DirectionalMovementMinus = new FunctionalIndicator<TradeBar>(name + "_NegativeDirectionalMovement", currentBar => { var value = ComputeNegativeDirectionalMovement(currentBar); return value; }, isReady => _previousInput != null ); PositiveDirectionalIndex = new FunctionalIndicator<IndicatorDataPoint>(name + "_PositiveDirectionalIndex", input => ComputePositiveDirectionalIndex(), positiveDirectionalIndex => DirectionalMovementPlus.IsReady && TrueRange.IsReady, () => { DirectionalMovementPlus.Reset(); TrueRange.Reset(); } ); NegativeDirectionalIndex = new FunctionalIndicator<IndicatorDataPoint>(name + "_NegativeDirectionalIndex", input => ComputeNegativeDirectionalIndex(), negativeDirectionalIndex => DirectionalMovementMinus.IsReady && TrueRange.IsReady, () => { DirectionalMovementMinus.Reset(); TrueRange.Reset(); } ); SmoothedTrueRange = new FunctionalIndicator<IndicatorDataPoint>(name + "_SmoothedTrueRange", currentBar => ComputeSmoothedTrueRange(period), isReady => _previousInput != null ); SmoothedDirectionalMovementPlus = new FunctionalIndicator<IndicatorDataPoint>(name + "_SmoothedDirectionalMovementPlus", currentBar => ComputeSmoothedDirectionalMovementPlus(period), isReady => _previousInput != null ); SmoothedDirectionalMovementMinus = new FunctionalIndicator<IndicatorDataPoint>(name + "_SmoothedDirectionalMovementMinus", currentBar => ComputeSmoothedDirectionalMovementMinus(period), isReady => _previousInput != null ); } /// <summary> /// Computes the Smoothed Directional Movement Plus value. /// </summary> /// <param name="period">The period.</param> /// <returns></returns> private decimal ComputeSmoothedDirectionalMovementPlus(int period) { decimal value; if (Samples < period) { value = SmoothedDirectionalMovementPlus.Current + DirectionalMovementPlus.Current; } else { value = SmoothedDirectionalMovementPlus.Current - (SmoothedDirectionalMovementPlus.Current / period) + DirectionalMovementPlus.Current; } return value; } /// <summary> /// Computes the Smoothed Directional Movement Minus value. /// </summary> /// <param name="period">The period.</param> /// <returns></returns> private decimal ComputeSmoothedDirectionalMovementMinus(int period) { decimal value; if (Samples < period) { value = SmoothedDirectionalMovementMinus.Current + DirectionalMovementMinus.Current; } else { value = SmoothedDirectionalMovementMinus.Current - (SmoothedDirectionalMovementMinus.Current / 14) + DirectionalMovementMinus.Current; } return value; } /// <summary> /// Computes the Smoothed True Range value. /// </summary> /// <param name="period">The period.</param> /// <returns></returns> private decimal ComputeSmoothedTrueRange(int period) { decimal value; if (Samples < period) { value = SmoothedTrueRange.Current + TrueRange.Current; } else { value = SmoothedTrueRange.Current - (SmoothedTrueRange.Current / period) + TrueRange.Current; } return value; } /// <summary> /// Gets a flag indicating when this indicator is ready and fully initialized /// </summary> public override bool IsReady { get { return Samples >= _period; } } /// <summary> /// Computes the True Range value. /// </summary> /// <param name="input">The input.</param> /// <returns></returns> private decimal ComputeTrueRange(TradeBar input) { var trueRange = new decimal(0.0); if (_previousInput == null) return trueRange; trueRange = (Math.Max(Math.Abs(input.Low - _previousInput.Close), Math.Max(TrueRange.Current, Math.Abs(input.High - _previousInput.Close)))); return trueRange; } /// <summary> /// Computes the positive directional movement. /// </summary> /// <param name="input">The input.</param> /// <returns></returns> private decimal ComputePositiveDirectionalMovement(TradeBar input) { var postiveDirectionalMovement = new decimal(0.0); if (_previousInput == null) return postiveDirectionalMovement; if ((input.High - _previousInput.High) >= (_previousInput.Low - input.Low)) { if ((input.High - _previousInput.High) > 0) { postiveDirectionalMovement = input.High - _previousInput.High; } } return postiveDirectionalMovement; } /// <summary> /// Computes the negative directional movement. /// </summary> /// <param name="input">The input.</param> /// <returns></returns> private decimal ComputeNegativeDirectionalMovement(TradeBar input) { var negativeDirectionalMovement = new decimal(0.0); if (_previousInput == null) return negativeDirectionalMovement; if ((_previousInput.Low - input.Low) > (input.High - _previousInput.High)) { if ((_previousInput.Low - input.Low) > 0) { negativeDirectionalMovement = _previousInput.Low - input.Low; } } return negativeDirectionalMovement; } /// <summary> /// Computes the next value of this indicator from the given state /// </summary> /// <param name="input">The input given to the indicator</param> /// <returns>A new value for this indicator</returns> protected override decimal ComputeNextValue(TradeBar input) { TrueRange.Update(input); DirectionalMovementPlus.Update(input); DirectionalMovementMinus.Update(input); SmoothedTrueRange.Update(Current); SmoothedDirectionalMovementMinus.Update(Current); SmoothedDirectionalMovementPlus.Update(Current); if (_previousInput != null) { PositiveDirectionalIndex.Update(Current); NegativeDirectionalIndex.Update(Current); } var diff = Math.Abs(PositiveDirectionalIndex - NegativeDirectionalIndex); var sum = PositiveDirectionalIndex + NegativeDirectionalIndex; var value = sum == 0 ? 50 : ((_period - 1) * Current.Value + 100 * diff / sum ) / _period; _previousInput = input; return value; } /// <summary> /// Computes the Plus Directional Indicator (+DI period). /// </summary> /// <returns></returns> private decimal ComputePositiveDirectionalIndex() { if (SmoothedTrueRange == 0) return new decimal(0.0); var positiveDirectionalIndex = (SmoothedDirectionalMovementPlus.Current.Value / SmoothedTrueRange.Current.Value) * 100; return positiveDirectionalIndex; } /// <summary> /// Computes the Minus Directional Indicator (-DI period). /// </summary> /// <returns></returns> private decimal ComputeNegativeDirectionalIndex() { if (SmoothedTrueRange == 0) return new decimal(0.0); var negativeDirectionalIndex = (SmoothedDirectionalMovementMinus.Current.Value / SmoothedTrueRange.Current.Value) * 100; return negativeDirectionalIndex; } /// <summary> /// Resets this indicator to its initial state /// </summary> public override void Reset() { base.Reset(); TrueRange.Reset(); DirectionalMovementPlus.Reset(); DirectionalMovementMinus.Reset(); SmoothedTrueRange.Reset(); SmoothedDirectionalMovementMinus.Reset(); SmoothedDirectionalMovementPlus.Reset(); PositiveDirectionalIndex.Reset(); NegativeDirectionalIndex.Reset(); } } }
namespace QuantConnect { /* * QuantConnect University: Full Basic Template: * * The underlying QCAlgorithm class is full of helper methods which enable you to use QuantConnect. * We have explained some of these here, but the full algorithm can be found at: * https://github.com/QuantConnect/QCAlgorithm/blob/master/QuantConnect.Algorithm/QCAlgorithm.cs */ public class BasicTemplateAlgorithm : QCAlgorithm { private string symbol = "SPY"; private ParabolicSAR psar14; private ParabolicSAR psar30; //Initialize the data and resolution you require for your strategy: public override void Initialize() { //Start and End Date range for the backtest: SetStartDate(2013, 1, 1); SetEndDate(DateTime.Now.Date.AddDays(-1)); //Cash allocation SetCash(25000); //Add as many securities as you like. All the data will be passed into the event handler: AddSecurity(SecurityType.Equity, symbol, Resolution.Daily); psar14 = new ParabolicSAR("PSAR", 1); psar30 = new ParabolicSAR("PSAR", 30); } //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) { // "TradeBars" object holds many "TradeBar" objects: it is a dictionary indexed by the symbol: // // e.g. data["MSFT"] data["GOOG"] // update Indicators decimal price = data[symbol].Close; IndicatorDataPoint datum = new IndicatorDataPoint(data[symbol].Time, price); psar14.Update(data[symbol]); //Debug("Current PSAR: " + psar14.Current.Value); // if decimal cash = Portfolio.Cash; int holdings = Portfolio[symbol].Quantity; var quantity = Convert.ToInt32((cash * 0.5m) / price); if (holdings > 0 || holdings == 0) { if (!psar14._trend) { Order(symbol, -(holdings + quantity)); } } else if (holdings < 0 || holdings == 0) { if (psar14._trend) { Order(symbol, Math.Abs(holdings) + quantity); } } Plot(symbol, psar14); } } }
namespace QuantConnect { // // Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all // files use "public partial class" if you want to split up your algorithm namespace into multiple files. // /// <summary> /// /// </summary> public class ParabolicSAR : WindowIndicator<TradeBar> { private TradeBar _previousInput; public IndicatorBase<TradeBar> _psarInitial { get; private set; } public IndicatorBase<TradeBar> _psar { get; private set; } public IndicatorBase<TradeBar> _extremePoint { get; private set; } public IndicatorBase<TradeBar> _psarEpAcc { get; private set; } public IndicatorBase<TradeBar> _previousPsar { get; private set; } public IndicatorBase<TradeBar> _previousExtremePoint { get; private set; } public IndicatorBase<TradeBar> _acceleration { get; private set; } private decimal highPoint2; private decimal lowPoint2; private readonly decimal _accelerationMax; private readonly decimal _accelerationMin; private static bool _previousTrend; public bool _trend; /// <summary> /// Initializes a new instance of the LogReturn class with the specified name and period /// </summary> /// <param name="name">The name of this indicator</param> /// <param name="period">The period of the LOGR</param> public ParabolicSAR(string name, int period, decimal accelerationLow = 0.02m, decimal accelerationHigh = 0.2m, bool initialTrend = false) : base(name, period) { _accelerationMax = accelerationHigh; _accelerationMin = accelerationLow; _acceleration = new FunctionalIndicator<TradeBar>(name + "_acceleration", currentBar => { var value = ComputeAcceleration(currentBar); return value; }, isReady => _previousInput != null ); ; _trend = initialTrend; _previousTrend = initialTrend; // Psar Initial Indicator _psarInitial = new FunctionalIndicator<TradeBar>(name + "_psar_initial", currentBar => { var value = ComputePsarInitial(currentBar); return value; }, isReady => _previousInput != null ); // Psar Indicator _psar = new FunctionalIndicator<TradeBar>(name + "_psar", currentBar => { var value = ComputePsar(currentBar); return value; }, isReady => _previousInput != null ); // Extreme Point Indicator _extremePoint = new FunctionalIndicator<TradeBar>(name + "_extreme_point", currentBar => { var value = ComputeExtremePoint(currentBar); return value; }, isReady => _previousInput != null ); // (Psar - Exteme Point) * Acceleration Indicator _psarEpAcc = new FunctionalIndicator<TradeBar>(name + "_psar_ep_acc", currentBar => { var value = ComputePsarEpAcc(currentBar); return value; }, isReady => _previousInput != null ); // Previous Psar Indcator _previousPsar = new FunctionalIndicator<TradeBar>(name + "_psar_previous", currentBar => { var value = ComputePsarPrevious(currentBar); return value; }, isReady => _previousInput != null ); _previousExtremePoint = new FunctionalIndicator<TradeBar>(name + "_previous_extreme_point", currentBar => { var value = ComputePreviousExtremePoint(currentBar); return value; }, isReady => _previousInput != null ); } /// <summary> /// Initializes a new instance of the LogReturn class with the default name and period /// </summary> /// <param name="period">The period of the SMA</param> public ParabolicSAR(int period, decimal accelerationLow = 0.02m, decimal accelerationHigh = 0.2m, bool initialTrend = false) : base("PSAR" + period, period) { string name = "PSAR"; _accelerationMax = accelerationHigh; _accelerationMin = accelerationLow; _acceleration = new FunctionalIndicator<TradeBar>(name + "_acceleration", currentBar => { var value = ComputeAcceleration(currentBar); return value; }, isReady => _previousInput != null ); ; _trend = initialTrend; _previousTrend = initialTrend; // Psar Initial Indicator _psarInitial = new FunctionalIndicator<TradeBar>(name + "_psar_initial", currentBar => { var value = ComputePsarInitial(currentBar); return value; }, isReady => _previousInput != null ); // Psar Indicator _psar = new FunctionalIndicator<TradeBar>(name + "_psar", currentBar => { var value = ComputePsar(currentBar); return value; }, isReady => _previousInput != null ); // Extreme Point Indicator _extremePoint = new FunctionalIndicator<TradeBar>(name + "_extreme_point", currentBar => { var value = ComputeExtremePoint(currentBar); return value; }, isReady => _previousInput != null ); // (Psar - Exteme Point) * Acceleration Indicator _psarEpAcc = new FunctionalIndicator<TradeBar>(name + "_psar_ep_acc", currentBar => { var value = ComputePsarEpAcc(currentBar); return value; }, isReady => _previousInput != null ); // Previous Psar Indcator _previousPsar = new FunctionalIndicator<TradeBar>(name + "_psar_previous", currentBar => { var value = ComputePsarPrevious(currentBar); return value; }, isReady => _previousInput != null ); _previousExtremePoint = new FunctionalIndicator<TradeBar>(name + "_previous_extreme_point", currentBar => { var value = ComputePreviousExtremePoint(currentBar); return value; }, isReady => _previousInput != null ); } protected override decimal ComputeNextValue(IReadOnlyWindow<TradeBar> window, TradeBar input) { ////////////////////////////////////////////////////////////////////////////////////////// if (this.Samples == 1) { _previousInput = input; _extremePoint.Update(input); _psar.Update(input); //trend is set _acceleration.Update(input); _psarEpAcc.Update(input); _psarInitial.Update(input); _previousExtremePoint.Update(input); // set psar - ep * acc decimal psar = ComputePsar(input); _previousPsar.Current.Value = psar; highPoint2 = input.High; lowPoint2 = input.Low; return psar; } //_psar.Current. // update initial psar _psarInitial.Update(input); // update psar _psar.Update(input); // set previous trend _previousTrend = _trend; // update trend _trend = _psar.Current.Value <= _previousInput.Close; // update extreme point _extremePoint.Update(input); // update acceleration _acceleration.Update(input); // update (psar - ep) * ac _psarEpAcc.Update(input); // set highPoint2, lowPoint2, and previous input. highPoint2 = _previousInput.High; lowPoint2 = _previousInput.Low; _previousInput = new TradeBar(input); // set previous psar _previousPsar.Current.Value = _psar.Current.Value; // update previous extreme point _previousExtremePoint.Update(input); return _psar.Current.Value; } private decimal ComputeAcceleration(TradeBar input) { if (Samples == 1) { return _accelerationMin; } decimal acceleration = _acceleration.Current.Value; // acceleration can not be higher than the price if (_trend == true && _previousTrend == true) { // if (_extremePoint.Current.Value > _previousExtremePoint.Current.Value && _acceleration <= _accelerationMax) { acceleration += _accelerationMin; } else if (_extremePoint.Current.Value == _previousExtremePoint.Current.Value) { } else { acceleration = _accelerationMax; } } // when the trend changes, reset _acceleration to _accelerationMin else if (_trend == false && _previousTrend == false) { if (_extremePoint.Current.Value < _previousExtremePoint.Current.Value && _acceleration <= _accelerationMax) { acceleration += _accelerationMin; } else if (_extremePoint.Current.Value == _previousExtremePoint.Current.Value) { } else { acceleration = _accelerationMax; } } else if (_trend == true && _previousTrend == false) { acceleration = _accelerationMin; } if (_previousTrend != _trend) { acceleration = _accelerationMin; } return acceleration; } private decimal ComputePsar(TradeBar input) { if (Samples == 1) { return !_trend ? input.High : input.Low; } //IF(AND(L5=â€fallingâ€,C6<J6),J6,IF(AND(L5=â€risingâ€,D6>J6),J6,IF(AND(L5=â€fallingâ€,C6>=J6),G5,IF(AND(L5=â€risingâ€,D6<=J6),G5,â€â€)))) // If the trend is falling and the high price is less than the psar if (!_trend && input.High < _psarInitial.Current.Value) { return _psarInitial.Current.Value; } else if (_trend && input.Low > _psarInitial.Current.Value) { return _psarInitial.Current.Value; } else if (!_trend && input.High >= _psarInitial.Current.Value) { return _previousExtremePoint.Current.Value; } else if (_trend && input.Low <= _psarInitial.Current.Value) { return _previousExtremePoint.Current.Value; } return 0; } private decimal ComputePsarInitial(TradeBar input) { // If the previous trend is falling, the initial psar is the max value // of the previous psar - the previous psar - ep * ac, or the 2 previous high prices // If the previous trend is rising, the initial psar is the min value // of the previous psar - the previous psar - ep * ac, or the 2 previous low prices // //IF(L5=â€fallingâ€,MAX(K5-I5,C5,C4),IF(L5=â€risingâ€,MIN(K5-I5,D5,D4),â€â€)) decimal value = 0m; if (Samples == 1) { return value; } if (!_trend) { value = Math.Max(_previousPsar.Current.Value - _psarEpAcc.Current.Value, _previousInput.High); if (Samples > 1) { value = Math.Max(value, highPoint2); } } else if (_trend) { value = Math.Min(_previousPsar.Current.Value - _psarEpAcc.Current.Value, _previousInput.Low); if (Samples > 2) { value = Math.Min(value, lowPoint2); } } return value; } private decimal ComputePsarEpAcc(TradeBar input) { // (K6-G6)*H6 // (psar - ep) * acceleration if (Samples == 1) { return (_psar.Current.Value - _extremePoint.Current.Value) * _acceleration.Current.Value; } decimal psarEpAcc = (_psar.Current.Value - _extremePoint.Current.Value) * _acceleration.Current.Value; return psarEpAcc; } private decimal ComputePsarPrevious(TradeBar input) { if (Samples == 1) { return !_trend ? input.Low : input.High; } decimal psarPrev = _psar.Current.Value; return psarPrev; } private decimal ComputeExtremePoint(TradeBar input) { // If trending decimal ep; if (Samples == 1) { ep = !_trend ? input.Low : input.High; return ep; } ep = _trend ? Math.Max(_previousExtremePoint.Current.Value, input.High) : Math.Min(_previousExtremePoint.Current.Value, input.Low); return ep; } private decimal ComputePreviousExtremePoint(TradeBar input) { decimal pep; if (Samples == 1) { pep = !_trend ? input.Low : input.High; return pep; } pep = _extremePoint.Current.Value; return pep; } } }