5 EMA 戦略
5 EMA戦略は、5期間EMAの完全に下または上でクローズするローソク足をマークします。価格がブロックウィンドウ外で3バー以内にそのシグナルローソク足の極値をブレイクした場合、ブレイクアウト方向にエントリーします。ターゲットはユーザー定義のリスク・リワード比に基づき、特定の時刻に強制的に取引を閉じることができます。
詳細
- エントリー条件:
- ローソク足の終値と高値がEMAより下 → ロング準備;3バー以内にシグナルの高値を上抜けたら買い。
- ローソク足の終値と安値がEMAより上 → ショート準備;3バー以内にシグナルの安値を下抜けたら売り。
- ロング/ショート: 両方。
- エグジット条件:
- シグナルローソク足の反対側の極値にストップ。
TargetRR× リスクのターゲット。- カスタム時刻でのオプション出口 (
ExitHour,ExitMinute)。
- ストップ: はい。
- デフォルト値:
EmaLength= 5TargetRR= 3.0ExitHour= 15,ExitMinute= 30BlockStartHour= 15,BlockStartMinute= 0BlockEndHour= 15,BlockEndMinute= 30
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: ロング/ショート
- インジケーター: EMA
- ストップ: はい
- 複雑さ: 低
- 時間軸: 5分
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// 5 EMA strategy.
/// A candle whose close and high are below the EMA marks a long setup, one whose close and low are above it marks a short setup.
/// If price breaks the signal candle's high (long) or low (short) within the next three candles and outside the block window, the
/// strategy enters in that direction with the stop at the signal candle's opposite extreme and the target at TargetRR times the risk.
/// Open positions are closed at ExitHour:ExitMinute.
/// </summary>
public class FiveEmaStrategy : Strategy
{
private const int _signalBars = 3;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _targetRR;
private readonly StrategyParam<int> _exitHour;
private readonly StrategyParam<int> _exitMinute;
private readonly StrategyParam<int> _blockStartHour;
private readonly StrategyParam<int> _blockStartMinute;
private readonly StrategyParam<int> _blockEndHour;
private readonly StrategyParam<int> _blockEndMinute;
private readonly StrategyParam<DataType> _candleType;
private decimal? _signalHigh;
private decimal? _signalLow;
private bool _signalIsLong;
private int _barsSinceSignal;
private decimal? _stopPrice;
private decimal? _targetPrice;
/// <summary>
/// EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Reward to risk ratio of the target.
/// </summary>
public decimal TargetRR
{
get => _targetRR.Value;
set => _targetRR.Value = value;
}
/// <summary>
/// Hour of the forced exit.
/// </summary>
public int ExitHour
{
get => _exitHour.Value;
set => _exitHour.Value = value;
}
/// <summary>
/// Minute of the forced exit.
/// </summary>
public int ExitMinute
{
get => _exitMinute.Value;
set => _exitMinute.Value = value;
}
/// <summary>
/// Hour the entry block starts.
/// </summary>
public int BlockStartHour
{
get => _blockStartHour.Value;
set => _blockStartHour.Value = value;
}
/// <summary>
/// Minute the entry block starts.
/// </summary>
public int BlockStartMinute
{
get => _blockStartMinute.Value;
set => _blockStartMinute.Value = value;
}
/// <summary>
/// Hour the entry block ends.
/// </summary>
public int BlockEndHour
{
get => _blockEndHour.Value;
set => _blockEndHour.Value = value;
}
/// <summary>
/// Minute the entry block ends.
/// </summary>
public int BlockEndMinute
{
get => _blockEndMinute.Value;
set => _blockEndMinute.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FiveEmaStrategy()
{
_emaLength = Param(nameof(EmaLength), 5)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "EMA");
_targetRR = Param(nameof(TargetRR), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Target R:R", "Reward to risk ratio of the target", "Risk");
_exitHour = Param(nameof(ExitHour), 15)
.SetRange(0, 23)
.SetDisplay("Exit Hour", "Hour of the forced exit", "Time");
_exitMinute = Param(nameof(ExitMinute), 30)
.SetRange(0, 59)
.SetDisplay("Exit Minute", "Minute of the forced exit", "Time");
_blockStartHour = Param(nameof(BlockStartHour), 15)
.SetRange(0, 23)
.SetDisplay("Block Start Hour", "Hour the entry block starts", "Time");
_blockStartMinute = Param(nameof(BlockStartMinute), 0)
.SetRange(0, 59)
.SetDisplay("Block Start Minute", "Minute the entry block starts", "Time");
_blockEndHour = Param(nameof(BlockEndHour), 15)
.SetRange(0, 23)
.SetDisplay("Block End Hour", "Hour the entry block ends", "Time");
_blockEndMinute = Param(nameof(BlockEndMinute), 30)
.SetRange(0, 59)
.SetDisplay("Block End Minute", "Minute the entry block ends", "Time");
_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();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ResetState()
{
_signalHigh = null;
_signalLow = null;
_signalIsLong = false;
_barsSinceSignal = 0;
_stopPrice = null;
_targetPrice = null;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!emaValue.IsFormed)
return;
var ema = emaValue.ToDecimal();
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
if (_signalHigh != null)
{
_barsSinceSignal++;
if (_barsSinceSignal > _signalBars)
_signalHigh = _signalLow = null;
}
var signalHigh = _signalHigh;
var signalLow = _signalLow;
var signalIsLong = _signalIsLong;
// A new signal candle replaces the pending one and can only be broken by later candles.
var newSignal = true;
if (close < ema && high < ema)
SetSignal(high, low, true);
else if (close > ema && low > ema)
SetSignal(high, low, false);
else
newSignal = false;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var tod = candle.OpenTime.TimeOfDay;
var exitTime = new TimeSpan(ExitHour, ExitMinute, 0);
var frame = CandleType.Arg is TimeSpan tf ? tf : TimeSpan.Zero;
if (Position != 0 && tod <= exitTime && exitTime < tod + frame)
{
ClosePosition();
return;
}
if (Position > 0 && _stopPrice is decimal longStop && _targetPrice is decimal longTarget)
{
if (low <= longStop || high >= longTarget)
{
ClosePosition();
return;
}
}
else if (Position < 0 && _stopPrice is decimal shortStop && _targetPrice is decimal shortTarget)
{
if (high >= shortStop || low <= shortTarget)
{
ClosePosition();
return;
}
}
if (signalHigh is not decimal sHigh || signalLow is not decimal sLow)
return;
var blockStart = new TimeSpan(BlockStartHour, BlockStartMinute, 0);
var blockEnd = new TimeSpan(BlockEndHour, BlockEndMinute, 0);
if (tod >= blockStart && tod < blockEnd)
return;
var risk = sHigh - sLow;
if (risk <= 0)
return;
if (signalIsLong && high > sHigh && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = sLow;
_targetPrice = sHigh + risk * TargetRR;
if (!newSignal)
_signalHigh = _signalLow = null;
}
else if (!signalIsLong && low < sLow && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = sHigh;
_targetPrice = sLow - risk * TargetRR;
if (!newSignal)
_signalHigh = _signalLow = null;
}
}
private void SetSignal(decimal high, decimal low, bool isLong)
{
_signalHigh = high;
_signalLow = low;
_signalIsLong = isLong;
_barsSinceSignal = 0;
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_stopPrice = null;
_targetPrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
SIGNAL_BARS = 3
class five_ema_strategy(Strategy):
"""
5 EMA strategy.
A candle whose close and high are below the EMA marks a long setup, one whose close and low are above it marks a short setup.
If price breaks the signal candle's high (long) or low (short) within the next three candles and outside the block window, the
strategy enters in that direction with the stop at the signal candle's opposite extreme and the target at TargetRR times the risk.
Open positions are closed at ExitHour:ExitMinute.
"""
def __init__(self):
super(five_ema_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 5).SetGreaterThanZero().SetDisplay("EMA Length", "EMA period", "EMA")
self._target_rr = self.Param("TargetRR", 3.0).SetGreaterThanZero().SetDisplay("Target R:R", "Reward to risk ratio of the target", "Risk")
self._exit_hour = self.Param("ExitHour", 15).SetRange(0, 23).SetDisplay("Exit Hour", "Hour of the forced exit", "Time")
self._exit_minute = self.Param("ExitMinute", 30).SetRange(0, 59).SetDisplay("Exit Minute", "Minute of the forced exit", "Time")
self._block_start_hour = self.Param("BlockStartHour", 15).SetRange(0, 23).SetDisplay("Block Start Hour", "Hour the entry block starts", "Time")
self._block_start_minute = self.Param("BlockStartMinute", 0).SetRange(0, 59).SetDisplay("Block Start Minute", "Minute the entry block starts", "Time")
self._block_end_hour = self.Param("BlockEndHour", 15).SetRange(0, 23).SetDisplay("Block End Hour", "Hour the entry block ends", "Time")
self._block_end_minute = self.Param("BlockEndMinute", 30).SetRange(0, 59).SetDisplay("Block End Minute", "Minute the entry block ends", "Time")
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._signal_high = None
self._signal_low = None
self._signal_is_long = False
self._bars_since_signal = 0
self._stop_price = None
self._target_price = None
def OnReseted(self):
super(five_ema_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(five_ema_strategy, self).OnStarted2(time)
self._reset_state()
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not ema_value.IsFormed:
return
ema = ema_value.GetValue[Decimal](None)
high = candle.HighPrice
low = candle.LowPrice
close = candle.ClosePrice
if self._signal_high is not None:
self._bars_since_signal += 1
if self._bars_since_signal > SIGNAL_BARS:
self._signal_high = None
self._signal_low = None
signal_high = self._signal_high
signal_low = self._signal_low
signal_is_long = self._signal_is_long
# A new signal candle replaces the pending one and can only be broken by later candles.
new_signal = True
if close < ema and high < ema:
self._set_signal(high, low, True)
elif close > ema and low > ema:
self._set_signal(high, low, False)
else:
new_signal = False
if not self.IsFormedAndOnlineAndAllowTrading():
return
tod = candle.OpenTime.TimeOfDay
exit_time = TimeSpan(self._exit_hour.Value, self._exit_minute.Value, 0)
frame = self.candle_type.Arg if isinstance(self.candle_type.Arg, TimeSpan) else TimeSpan.Zero
if self.Position != 0 and tod <= exit_time and exit_time < tod + frame:
self._close_position()
return
if self.Position > 0 and self._stop_price is not None and self._target_price is not None:
if low <= self._stop_price or high >= self._target_price:
self._close_position()
return
elif self.Position < 0 and self._stop_price is not None and self._target_price is not None:
if high >= self._stop_price or low <= self._target_price:
self._close_position()
return
if signal_high is None or signal_low is None:
return
block_start = TimeSpan(self._block_start_hour.Value, self._block_start_minute.Value, 0)
block_end = TimeSpan(self._block_end_hour.Value, self._block_end_minute.Value, 0)
if tod >= block_start and tod < block_end:
return
risk = signal_high - signal_low
if risk <= 0:
return
target_rr = Decimal(self._target_rr.Value)
if signal_is_long and high > signal_high and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = signal_low
self._target_price = signal_high + risk * target_rr
if not new_signal:
self._signal_high = None
self._signal_low = None
elif not signal_is_long and low < signal_low and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = signal_high
self._target_price = signal_low - risk * target_rr
if not new_signal:
self._signal_high = None
self._signal_low = None
def _set_signal(self, high, low, is_long):
self._signal_high = high
self._signal_low = low
self._signal_is_long = is_long
self._bars_since_signal = 0
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
self._stop_price = None
self._target_price = None
def CreateClone(self):
return five_ema_strategy()