三重MA高阶时间框架动态平滑策略
该策略比较在更高时间框架上计算的三条移动平均线。 每条均线根据其时间框架与基础时间框架的比例进行平滑处理。 当第一条均线与第二条均线交叉且第三条均线确认方向时产生信号。
详情
- 入场条件: MA1与MA2交叉并由MA3确认趋势。
- 多空方向: 双向。
- 退出条件: 反向信号。
- 止损: 无。
- 默认值:
CandleType= TimeSpan.FromMinutes(5)HigherTimeFrame1= TimeSpan.FromMinutes(15)HigherTimeFrame2= TimeSpan.FromMinutes(60)HigherTimeFrame3= TimeSpan.FromMinutes(240)Length1= 21Length2= 21Length3= 50
- 过滤器:
- 类型: 趋势
- 方向: 双向
- 指标: MA
- 止损: 无
- 复杂度: 中等
- 时间框架: 日内 (基础5m)
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险等级: 中
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Triple moving average trend following using EMA crossovers with RSI confirmation.
/// </summary>
public class TripleMaHtfDynamicSmoothingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length1;
private readonly StrategyParam<int> _length2;
private readonly StrategyParam<int> _length3;
private decimal _prevMa1;
private decimal _prevMa2;
private decimal _prevRsi;
private int _cooldown;
public TripleMaHtfDynamicSmoothingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Base timeframe", "General");
_length1 = Param(nameof(Length1), 10)
.SetDisplay("MA1 Length", "Length for fast EMA", "Trend");
_length2 = Param(nameof(Length2), 30)
.SetDisplay("MA2 Length", "Length for slow EMA", "Trend");
_length3 = Param(nameof(Length3), 14)
.SetDisplay("RSI Length", "RSI period", "Trend");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Length1
{
get => _length1.Value;
set => _length1.Value = value;
}
public int Length2
{
get => _length2.Value;
set => _length2.Value = value;
}
public int Length3
{
get => _length3.Value;
set => _length3.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMa1 = 0;
_prevMa2 = 0;
_prevRsi = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema1 = new ExponentialMovingAverage { Length = Length1 };
var ema2 = new ExponentialMovingAverage { Length = Length2 };
var rsi = new RelativeStrengthIndex { Length = Length3 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema1, ema2, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema1);
DrawIndicator(area, ema2);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ma1, decimal ma2, decimal rsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevMa1 == 0 || _prevMa2 == 0 || _prevRsi == 0)
{
_prevMa1 = ma1;
_prevMa2 = ma2;
_prevRsi = rsiVal;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevMa1 = ma1;
_prevMa2 = ma2;
_prevRsi = rsiVal;
return;
}
var price = candle.ClosePrice;
// EMA crossover
var crossUp = _prevMa1 <= _prevMa2 && ma1 > ma2;
var crossDown = _prevMa1 >= _prevMa2 && ma1 < ma2;
// EMA trend direction
var trendUp = ma1 > ma2;
var trendDown = ma1 < ma2;
// Exit on EMA cross in opposite direction
if (Position > 0 && crossDown)
{
SellMarket();
_cooldown = 30;
}
else if (Position < 0 && crossUp)
{
BuyMarket();
_cooldown = 30;
}
// Entry: EMA crossover + RSI filter
if (Position == 0)
{
if (crossUp && rsiVal > 45m && rsiVal < 75m)
{
BuyMarket();
_cooldown = 30;
}
else if (crossDown && rsiVal > 25m && rsiVal < 55m)
{
SellMarket();
_cooldown = 30;
}
// Re-entry: RSI cross 50 in trend direction when flat
else if (trendUp && _prevRsi <= 50m && rsiVal > 50m)
{
BuyMarket();
_cooldown = 30;
}
else if (trendDown && _prevRsi >= 50m && rsiVal < 50m)
{
SellMarket();
_cooldown = 30;
}
}
_prevMa1 = ma1;
_prevMa2 = ma2;
_prevRsi = rsiVal;
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class triple_ma_htf_dynamic_smoothing_strategy(Strategy):
"""EMA crossover with RSI confirmation and re-entry on RSI crossing 50."""
def __init__(self):
super(triple_ma_htf_dynamic_smoothing_strategy, self).__init__()
self._len1 = self.Param("Length1", 10).SetDisplay("MA1 Length", "Fast EMA", "Trend")
self._len2 = self.Param("Length2", 30).SetDisplay("MA2 Length", "Slow EMA", "Trend")
self._len3 = self.Param("Length3", 14).SetDisplay("RSI Length", "RSI period", "Trend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(triple_ma_htf_dynamic_smoothing_strategy, self).OnReseted()
self._prev_ma1 = 0
self._prev_ma2 = 0
self._prev_rsi = 0
self._cooldown = 0
def OnStarted2(self, time):
super(triple_ma_htf_dynamic_smoothing_strategy, self).OnStarted2(time)
self._prev_ma1 = 0
self._prev_ma2 = 0
self._prev_rsi = 0
self._cooldown = 0
ema1 = ExponentialMovingAverage()
ema1.Length = self._len1.Value
ema2 = ExponentialMovingAverage()
ema2.Length = self._len2.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._len3.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema1, ema2, rsi, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema1)
self.DrawIndicator(area, ema2)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ma1, ma2, rsi_val):
if candle.State != CandleStates.Finished:
return
ma1 = float(ma1)
ma2 = float(ma2)
rsi_val = float(rsi_val)
if self._prev_ma1 == 0 or self._prev_ma2 == 0 or self._prev_rsi == 0:
self._prev_ma1 = ma1
self._prev_ma2 = ma2
self._prev_rsi = rsi_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_ma1 = ma1
self._prev_ma2 = ma2
self._prev_rsi = rsi_val
return
cross_up = self._prev_ma1 <= self._prev_ma2 and ma1 > ma2
cross_down = self._prev_ma1 >= self._prev_ma2 and ma1 < ma2
trend_up = ma1 > ma2
trend_down = ma1 < ma2
# Exit on opposite cross
if self.Position > 0 and cross_down:
self.SellMarket()
self._cooldown = 30
elif self.Position < 0 and cross_up:
self.BuyMarket()
self._cooldown = 30
# Entry
if self.Position == 0:
if cross_up and rsi_val > 45 and rsi_val < 75:
self.BuyMarket()
self._cooldown = 30
elif cross_down and rsi_val > 25 and rsi_val < 55:
self.SellMarket()
self._cooldown = 30
elif trend_up and self._prev_rsi <= 50 and rsi_val > 50:
self.BuyMarket()
self._cooldown = 30
elif trend_down and self._prev_rsi >= 50 and rsi_val < 50:
self.SellMarket()
self._cooldown = 30
self._prev_ma1 = ma1
self._prev_ma2 = ma2
self._prev_rsi = rsi_val
def CreateClone(self):
return triple_ma_htf_dynamic_smoothing_strategy()