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