MA SAR ADX Strategy
Strategy combining Moving Average, Parabolic SAR and Average Directional Index (ADX). Buys when the price is above both the moving average and SAR while +DI is above -DI. Sells when the price is below both the moving average and SAR while +DI is below -DI. Positions are closed when price crosses the SAR.
Details
- Entry Criteria:
- Long:
Close > MA && +DI >= -DI && Close > SAR - Short:
Close < MA && +DI <= -DI && Close < SAR
- Long:
- Long/Short: Both
- Exit Criteria: Price crosses Parabolic SAR
- Stops: No
- Default Values:
MaPeriod= 100AdxPeriod= 14SarStep= 0.02mSarMax= 0.1mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: SMA, Parabolic SAR, ADX
- Stops: No
- Complexity: Basic
- Timeframe: Short-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining Moving Average, Parabolic SAR and momentum confirmation.
/// </summary>
public class MaSarAdxStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMax;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _rsiLongLevel;
private readonly StrategyParam<decimal> _rsiShortLevel;
private int _cooldownRemaining;
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal SarStep { get => _sarStep.Value; set => _sarStep.Value = value; }
public decimal SarMax { get => _sarMax.Value; set => _sarMax.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public decimal RsiLongLevel { get => _rsiLongLevel.Value; set => _rsiLongLevel.Value = value; }
public decimal RsiShortLevel { get => _rsiShortLevel.Value; set => _rsiShortLevel.Value = value; }
public MaSarAdxStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Simple moving average period", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Momentum confirmation period", "Indicators");
_sarStep = Param(nameof(SarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Step", "Initial acceleration factor", "Indicators");
_sarMax = Param(nameof(SarMax), 0.1m)
.SetGreaterThanZero()
.SetDisplay("SAR Max", "Maximum acceleration factor", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
_cooldownBars = Param(nameof(CooldownBars), 3)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
_rsiLongLevel = Param(nameof(RsiLongLevel), 52m)
.SetDisplay("RSI Long", "Minimum RSI level for long entries", "Filters");
_rsiShortLevel = Param(nameof(RsiShortLevel), 48m)
.SetDisplay("RSI Short", "Maximum RSI level for short entries", "Filters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var sma = new SimpleMovingAverage { Length = MaPeriod };
var sar = new ParabolicSar
{
Acceleration = SarStep,
AccelerationMax = SarMax
};
var rsi = new RelativeStrengthIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, sar, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue maValue, IIndicatorValue sarValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished || !maValue.IsFinal || !sarValue.IsFinal || !rsiValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var price = candle.ClosePrice;
var ma = maValue.ToDecimal();
var sar = sarValue.ToDecimal();
var rsi = rsiValue.ToDecimal();
var longSignal = price > ma && price > sar && rsi >= RsiLongLevel;
var shortSignal = price < ma && price < sar && rsi <= RsiShortLevel;
var longExit = price < sar || price < ma;
var shortExit = price > sar || price > ma;
if (Position == 0 && _cooldownRemaining == 0)
{
if (longSignal)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (shortSignal)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (Position > 0 && longExit)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && shortExit)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, ParabolicSar, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ma_sar_adx_strategy(Strategy):
"""
SMA + Parabolic SAR + RSI strategy.
Buys when price > SMA and > SAR and RSI >= long level.
"""
def __init__(self):
super(ma_sar_adx_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 100).SetDisplay("MA Period", "SMA period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetDisplay("RSI Period", "RSI period", "Indicators")
self._sar_step = self.Param("SarStep", 0.02).SetDisplay("SAR Step", "SAR acceleration", "Indicators")
self._sar_max = self.Param("SarMax", 0.1).SetDisplay("SAR Max", "SAR max acceleration", "Indicators")
self._rsi_long = self.Param("RsiLongLevel", 52.0).SetDisplay("RSI Long", "Min RSI for long", "Filters")
self._rsi_short = self.Param("RsiShortLevel", 48.0).SetDisplay("RSI Short", "Max RSI for short", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 3).SetDisplay("Cooldown", "Bars after position change", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Timeframe", "General")
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_sar_adx_strategy, self).OnReseted()
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ma_sar_adx_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
sar = ParabolicSar()
sar.Acceleration = self._sar_step.Value
sar.AccelerationMax = self._sar_max.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, sar, rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_val, sar_val, rsi_val):
if candle.State != CandleStates.Finished:
return
if not ma_val.IsFinal or not sar_val.IsFinal or not rsi_val.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
price = float(candle.ClosePrice)
ma = float(ma_val)
sar = float(sar_val)
rsi = float(rsi_val)
long_signal = price > ma and price > sar and rsi >= self._rsi_long.Value
short_signal = price < ma and price < sar and rsi <= self._rsi_short.Value
long_exit = price < sar or price < ma
short_exit = price > sar or price > ma
if self.Position == 0 and self._cooldown_remaining == 0:
if long_signal:
self.BuyMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif short_signal:
self.SellMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif self.Position > 0 and long_exit:
self.SellMarket()
self._cooldown_remaining = self._cooldown_bars.Value
elif self.Position < 0 and short_exit:
self.BuyMarket()
self._cooldown_remaining = self._cooldown_bars.Value
def CreateClone(self):
return ma_sar_adx_strategy()