G-Channel と EMA 戦略
G-ChannelのチャネルロジックとトレンドフィルターとしてのEMAを組み合わせた戦略です。
最後のクロスが下方向かつ価格がEMAを下回っているときに買い。最後のクロスが上方向かつ価格がEMAを上回っているときに売り。
詳細
- エントリー条件: EMAフィルター付きのG-Channel状態。
- ロング/ショート: 両方向。
- エグジット条件: 反対シグナル。
- ストップ: いいえ。
- デフォルト値:
ChannelLength= 100EmaLength= 200CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: G-Channel, EMA
- ストップ: いいえ
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// G-Channel with EMA strategy.
/// The G-Channel upper band is max(close, upper) minus (upper - lower) / ChannelLength and the lower band min(close, lower) plus the
/// same amount. As in the original G-Channel, an upward cross is the close moving from above the lower band to below it and a
/// downward cross the close moving from above the upper band to below it. With the last cross downward and the close below the EMA
/// the strategy goes long; with the last cross upward and the close above the EMA it goes short. An opposite signal reverses the position.
/// </summary>
public class GChannelEmaStrategy : Strategy
{
private readonly StrategyParam<int> _channelLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _upper;
private decimal? _lower;
private decimal? _prevClose;
private int _lastCross;
/// <summary>
/// G-Channel length.
/// </summary>
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
/// <summary>
/// EMA length.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GChannelEmaStrategy()
{
_channelLength = Param(nameof(ChannelLength), 100)
.SetGreaterThanZero()
.SetDisplay("Channel Length", "G-Channel length", "Indicators");
_emaLength = Param(nameof(EmaLength), 200)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA length", "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()
{
_upper = null;
_lower = null;
_prevClose = null;
_lastCross = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_upper is not decimal prevUpper || _lower is not decimal prevLower || _prevClose is not decimal prevClose)
{
_upper = close;
_lower = close;
_prevClose = close;
return;
}
var step = (prevUpper - prevLower) / ChannelLength;
var upper = Math.Max(close, prevUpper) - step;
var lower = Math.Min(close, prevLower) + step;
if (prevLower < prevClose && lower > close)
_lastCross = 1;
else if (prevUpper < prevClose && upper > close)
_lastCross = -1;
_upper = upper;
_lower = lower;
_prevClose = close;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_lastCross < 0 && close < ema && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (_lastCross > 0 && close > ema && Position >= 0)
SellMarket(Volume + Math.Abs(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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class g_channel_ema_strategy(Strategy):
"""
G-Channel with EMA strategy.
The G-Channel upper band is max(close, upper) minus (upper - lower) / ChannelLength and the lower band min(close, lower) plus the
same amount. As in the original G-Channel, an upward cross is the close moving from above the lower band to below it and a
downward cross the close moving from above the upper band to below it. With the last cross downward and the close below the EMA
the strategy goes long; with the last cross upward and the close above the EMA it goes short. An opposite signal reverses the position.
"""
def __init__(self):
super(g_channel_ema_strategy, self).__init__()
self._channel_length = self.Param("ChannelLength", 100).SetGreaterThanZero().SetDisplay("Channel Length", "G-Channel length", "Indicators")
self._ema_length = self.Param("EmaLength", 200).SetGreaterThanZero().SetDisplay("EMA Length", "EMA length", "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._upper = None
self._lower = None
self._prev_close = None
self._last_cross = 0
def OnReseted(self):
super(g_channel_ema_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(g_channel_ema_strategy, self).OnStarted2(time)
self._reset_state()
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(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):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._upper is None or self._lower is None or self._prev_close is None:
self._upper = close
self._lower = close
self._prev_close = close
return
prev_upper = self._upper
prev_lower = self._lower
prev_close = self._prev_close
step = (prev_upper - prev_lower) / Decimal(self._channel_length.Value)
upper = max(close, prev_upper) - step
lower = min(close, prev_lower) + step
if prev_lower < prev_close and lower > close:
self._last_cross = 1
elif prev_upper < prev_close and upper > close:
self._last_cross = -1
self._upper = upper
self._lower = lower
self._prev_close = close
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._last_cross < 0 and close < ema and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self._last_cross > 0 and close > ema and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return g_channel_ema_strategy()