Parabolic SAR と MACD 確認戦略
この戦略はParabolic SARインジケーターとMACDの確認を組み合わせます。MACDが支持する方向にPrice がSARをクロスしたときにポジションを建て、トレンド転換を捉えることを目的とします。
詳細
- エントリー条件: 価格がSARをクロスし、MACDラインがシグナルラインの同じ側にある。
- ロング/ショート: 両方向。
- エグジット条件: 価格/SARまたはMACDの逆クロス。
- ストップ: なし。
- デフォルト値:
SarStart= 0.02mSarIncrement= 0.02mSarMax= 0.2mMacdFast= 12MacdSlow= 26MacdSignal= 9CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Parabolic SAR, MACD
- ストップ: なし
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: なし
- ニューラルネットワーク: なし
- ダイバージェンス: なし
- リスクレベル: 中
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>
/// Parabolic SAR with MACD confirmation.
/// A close crossing above the Parabolic SAR while the MACD line is above its signal line goes long, a close crossing below the SAR
/// while MACD is below its signal goes short, reversing an opposite position. A position is closed on the opposite price/SAR
/// crossover or on the opposite MACD/signal crossover.
/// </summary>
public class ParabolicSarMacdTrendZoneStrategy : Strategy
{
private readonly StrategyParam<decimal> _sarStart;
private readonly StrategyParam<decimal> _sarIncrement;
private readonly StrategyParam<decimal> _sarMax;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal? _prevSar;
private decimal? _prevMacd;
private decimal? _prevSignal;
/// <summary>
/// Initial SAR acceleration factor.
/// </summary>
public decimal SarStart
{
get => _sarStart.Value;
set => _sarStart.Value = value;
}
/// <summary>
/// SAR acceleration factor increment.
/// </summary>
public decimal SarIncrement
{
get => _sarIncrement.Value;
set => _sarIncrement.Value = value;
}
/// <summary>
/// Maximum SAR acceleration factor.
/// </summary>
public decimal SarMax
{
get => _sarMax.Value;
set => _sarMax.Value = value;
}
/// <summary>
/// Fast EMA period of MACD.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// Slow EMA period of MACD.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// Signal line period of MACD.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ParabolicSarMacdTrendZoneStrategy()
{
_sarStart = Param(nameof(SarStart), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Start", "Initial SAR acceleration factor", "Indicators");
_sarIncrement = Param(nameof(SarIncrement), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Increment", "SAR acceleration factor increment", "Indicators");
_sarMax = Param(nameof(SarMax), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Max", "Maximum SAR acceleration factor", "Indicators");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA period of MACD", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA period of MACD", "Indicators");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal line period of MACD", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevClose = null;
_prevSar = null;
_prevMacd = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var sar = new ParabolicSar
{
Acceleration = SarStart,
AccelerationStep = SarIncrement,
AccelerationMax = SarMax
};
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sar, macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, macd);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue sarValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!sarValue.IsFormed || !macdValue.IsFormed)
return;
if (macdValue is not IMovingAverageConvergenceDivergenceSignalValue { Macd: decimal macdLine, Signal: decimal signalLine })
return;
var sar = sarValue.ToDecimal();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevSar = _prevSar;
var prevMacd = _prevMacd;
var prevSignal = _prevSignal;
_prevClose = close;
_prevSar = sar;
_prevMacd = macdLine;
_prevSignal = signalLine;
if (prevClose is not decimal pc || prevSar is not decimal ps || prevMacd is not decimal pm || prevSignal is not decimal pSig)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var crossUp = pc <= ps && close > sar;
var crossDown = pc >= ps && close < sar;
var macdCrossUp = pm <= pSig && macdLine > signalLine;
var macdCrossDown = pm >= pSig && macdLine < signalLine;
if (crossUp && macdLine > signalLine && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && macdLine < signalLine && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && (crossDown || macdCrossDown))
SellMarket(Position);
else if (Position < 0 && (crossUp || macdCrossUp))
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ParabolicSar, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_macd_trend_zone_strategy(Strategy):
"""
Parabolic SAR with MACD confirmation.
A close crossing above the Parabolic SAR while the MACD line is above its signal line goes long, a close crossing below the SAR
while MACD is below its signal goes short, reversing an opposite position. A position is closed on the opposite price/SAR
crossover or on the opposite MACD/signal crossover.
"""
def __init__(self):
super(parabolic_sar_macd_trend_zone_strategy, self).__init__()
self._sar_start = self.Param("SarStart", 0.02).SetGreaterThanZero().SetDisplay("SAR Start", "Initial SAR acceleration factor", "Indicators")
self._sar_increment = self.Param("SarIncrement", 0.02).SetGreaterThanZero().SetDisplay("SAR Increment", "SAR acceleration factor increment", "Indicators")
self._sar_max = self.Param("SarMax", 0.2).SetGreaterThanZero().SetDisplay("SAR Max", "Maximum SAR acceleration factor", "Indicators")
self._macd_fast = self.Param("MacdFast", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "Fast EMA period of MACD", "Indicators")
self._macd_slow = self.Param("MacdSlow", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "Slow EMA period of MACD", "Indicators")
self._macd_signal = self.Param("MacdSignal", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "Signal line period of MACD", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_close = None
self._prev_sar = None
self._prev_macd = None
self._prev_signal = None
def OnReseted(self):
super(parabolic_sar_macd_trend_zone_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(parabolic_sar_macd_trend_zone_strategy, self).OnStarted2(time)
self._reset_state()
sar = ParabolicSar()
sar.Acceleration = Decimal(self._sar_start.Value)
sar.AccelerationStep = Decimal(self._sar_increment.Value)
sar.AccelerationMax = Decimal(self._sar_max.Value)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sar, macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, macd)
def _process_candle(self, candle, sar_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not sar_value.IsFormed or not macd_value.IsFormed:
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
sar = sar_value.GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_sar = self._prev_sar
prev_macd = self._prev_macd
prev_signal = self._prev_signal
self._prev_close = close
self._prev_sar = sar
self._prev_macd = macd_line
self._prev_signal = signal_line
if prev_close is None or prev_sar is None or prev_macd is None or prev_signal is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cross_up = prev_close <= prev_sar and close > sar
cross_down = prev_close >= prev_sar and close < sar
macd_cross_up = prev_macd <= prev_signal and macd_line > signal_line
macd_cross_down = prev_macd >= prev_signal and macd_line < signal_line
if cross_up and macd_line > signal_line and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and macd_line < signal_line and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and (cross_down or macd_cross_down):
self.SellMarket(self.Position)
elif self.Position < 0 and (cross_up or macd_cross_up):
self.BuyMarket(-self.Position)
def CreateClone(self):
return parabolic_sar_macd_trend_zone_strategy()