Exp MAラウンディングチャネル戦略
この戦略は移動平均を固定のティックステップに丸め、その周囲にATRベースのチャネルを構築します。前のローソク足が上限バンドを上回って終値を付けた場合、ロングポジションを開きます。前のローソク足が下限バンドを下回って終値を付けた場合、ショートポジションを開きます。逆方向のシグナルで既存のポジションを決済します。ストップロスとテイクプロフィットはティックで定義され、自動的に管理されます。
詳細
- エントリー条件:
- ロング: 前のローソク足の終値が上限バンドを上回っている。
- ショート: 前のローソク足の終値が下限バンドを下回っている。
- エグジット条件:
- ロング: 前のローソク足の終値が下限バンドを下回っている。
- ショート: 前のローソク足の終値が上限バンドを上回っている。
- インジケーター:
- 指数移動平均。
- チャネル幅のためのAverage True Range。
- ストップ: あり、ティック単位の固定ストップロスとテイクプロフィット。
- デフォルト値:
MA period= 12.ATR period= 12.ATR factor= 1.MA round= 500 ティック.Stop loss= 1000 ティック.Take profit= 2000 ティック.Timeframe= 4時間.
フィルター
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: 複数
- ストップ: あり
- 複雑さ: 中程度
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中程度
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>
/// Moving average rounding channel strategy.
/// Opens long positions when price closes above the rounded upper channel
/// and opens short positions when price closes below the rounded lower channel.
/// </summary>
public class ExpMaRoundingChannelStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrFactor;
private readonly StrategyParam<decimal> _roundStep;
private readonly StrategyParam<DataType> _candleType;
private AverageTrueRange _atr;
private decimal _prevUpper;
private decimal _prevLower;
private decimal _prevClose;
public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrFactor { get => _atrFactor.Value; set => _atrFactor.Value = value; }
public decimal RoundStep { get => _roundStep.Value; set => _roundStep.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ExpMaRoundingChannelStrategy()
{
_maLength = Param(nameof(MaLength), 12)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Length of moving average", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for channel width", "Indicator");
_atrFactor = Param(nameof(AtrFactor), 2m)
.SetDisplay("ATR Factor", "Multiplier for ATR channel", "Indicator");
_roundStep = Param(nameof(RoundStep), 50m)
.SetDisplay("Round Step", "Rounding step for the moving average", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevUpper = 0;
_prevLower = 0;
var ema = new ExponentialMovingAverage { Length = MaLength };
_atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(3, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr = default;
_prevUpper = default;
_prevLower = default;
_prevClose = default;
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
var atrResult = _atr.Process(candle);
if (!atrResult.IsFormed)
return;
var atrValue = atrResult.ToDecimal();
// Round the MA value
var step = RoundStep;
var roundedMa = step > 0 ? Math.Round(maValue / step) * step : maValue;
var upper = roundedMa + atrValue * AtrFactor;
var lower = roundedMa - atrValue * AtrFactor;
if (_prevClose != 0m)
{
var breakUp = _prevClose <= _prevUpper && candle.ClosePrice > upper;
var breakDown = _prevClose >= _prevLower && candle.ClosePrice < lower;
if (breakUp && Position == 0)
BuyMarket();
else if (breakDown && Position == 0)
SellMarket();
}
_prevUpper = upper;
_prevLower = lower;
_prevClose = candle.ClosePrice;
}
}
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, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class exp_ma_rounding_channel_strategy(Strategy):
def __init__(self):
super(exp_ma_rounding_channel_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 12) \
.SetDisplay("MA Period", "Length of moving average", "Indicator")
self._atr_period = self.Param("AtrPeriod", 12) \
.SetDisplay("ATR Period", "ATR period for channel width", "Indicator")
self._atr_factor = self.Param("AtrFactor", 2.0) \
.SetDisplay("ATR Factor", "Multiplier for ATR channel", "Indicator")
self._round_step = self.Param("RoundStep", 50.0) \
.SetDisplay("Round Step", "Rounding step for the moving average", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe for calculation", "General")
self._atr = None
self._prev_upper = 0.0
self._prev_lower = 0.0
self._prev_close = 0.0
@property
def MaLength(self):
return self._ma_length.Value
@MaLength.setter
def MaLength(self, value):
self._ma_length.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrFactor(self):
return self._atr_factor.Value
@AtrFactor.setter
def AtrFactor(self, value):
self._atr_factor.Value = value
@property
def RoundStep(self):
return self._round_step.Value
@RoundStep.setter
def RoundStep(self, value):
self._round_step.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(exp_ma_rounding_channel_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
ema = ExponentialMovingAverage()
ema.Length = self.MaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ema, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(3, UnitTypes.Percent),
stopLoss=Unit(2, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
atr_result = self._atr.Process(CandleIndicatorValue(self._atr, candle))
if not atr_result.IsFormed:
return
atr_value = float(atr_result)
step = float(self.RoundStep)
ma_val = float(ma_value)
if step > 0:
rounded_ma = round(ma_val / step) * step
else:
rounded_ma = ma_val
upper = rounded_ma + atr_value * float(self.AtrFactor)
lower = rounded_ma - atr_value * float(self.AtrFactor)
if self._prev_close != 0.0:
close_price = float(candle.ClosePrice)
break_up = self._prev_close <= self._prev_upper and close_price > upper
break_down = self._prev_close >= self._prev_lower and close_price < lower
if break_up and self.Position == 0:
self.BuyMarket()
elif break_down and self.Position == 0:
self.SellMarket()
self._prev_upper = upper
self._prev_lower = lower
self._prev_close = float(candle.ClosePrice)
def OnReseted(self):
super(exp_ma_rounding_channel_strategy, self).OnReseted()
self._atr = None
self._prev_upper = 0.0
self._prev_lower = 0.0
self._prev_close = 0.0
def CreateClone(self):
return exp_ma_rounding_channel_strategy()