在 GitHub 上查看
ADX & MA 策略
概述
该策略是 MetaTrader 专家顾问 ADX_MA (fortrader) 的 StockSharp 移植版本。
它将平滑移动平均线(SMMA)趋势过滤器与平均方向指数(ADX)结合,
只有在出现均线穿越并且 ADX 显示出足够强势的情况下才允许开仓。
移植版本保留了原始机器人的非对称风险控制:
多头采用更宽的止盈和跟踪止损距离,空头则使用更紧的目标和保护。
交易逻辑
- 使用设定周期在 K 线中位价上构建 SMMA,并计算相同周期的 ADX。
- 仅在收盘 K 线上进行信号判断,以模仿 MQL4 中
iClose(...,1) 和 iClose(...,2) 的行为。
- 当上一根 K 线收盘价高于 SMMA、再前一根收盘价低于同一 SMMA 值且上一根 ADX 大于阈值时开多。
- 当上一根 K 线收盘价低于 SMMA、再前一根收盘价高于同一 SMMA 值且 ADX 高于阈值时开空。
- 持仓后的离场条件包括:
- 均线重新穿越到反方向;
- 依据各自的止损/止盈距离(以点数表示);
- 可选的跟踪止损,在价格朝有利方向移动后逐步抬升或下移。
所有价差都会根据标的的最小报价步长进行点值换算,若证券未提供有效步长则使用 1 作为兜底值。
参数
| 名称 |
说明 |
SMMA Period |
平滑移动平均线周期(默认 21)。 |
ADX Period |
ADX 指标周期(默认 14)。 |
ADX Threshold |
允许开仓所需的最小 ADX 值(默认 16)。 |
Long Take Profit (pips) |
多头止盈点数(默认 1300 点)。 |
Long Stop Loss (pips) |
多头止损点数(默认 30 点)。 |
Long Trailing Stop (pips) |
多头跟踪止损点数(默认 270 点)。 |
Short Take Profit (pips) |
空头止盈点数(默认 160 点)。 |
Short Stop Loss (pips) |
空头止损点数(默认 50 点)。 |
Short Trailing Stop (pips) |
空头跟踪止损点数(默认 20 点)。 |
Volume |
新开仓使用的下单量(默认 0.1)。 |
Candle Type |
计算所使用的主 K 线序列(默认 1 分钟)。 |
所有参数都可用于优化,默认值与原始 EA 设置保持一致。
注意事项
- 跟踪止损只有在价格至少向有利方向移动指定距离后才会启动。
- 反向信号会在开新仓之前平掉现有仓位。
- 如果图表区域可用,策略会自动绘制 K 线、指标以及自身成交记录。
- 本策略没有自动化测试,请使用回测或模拟环境验证其在目标市场上的表现。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class AdxMaStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AdxMaStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 50).SetDisplay("SMA Period", "SMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); }
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); }
_prevClose = close; _prevEma = ema;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class adx_ma_strategy(Strategy):
"""
ADX MA strategy: EMA crossover with price for trend-following entries.
"""
def __init__(self):
super(adx_ma_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._sma_period = self.Param("SmaPeriod", 50) \
.SetDisplay("SMA Period", "SMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def SmaPeriod(self):
return self._sma_period.Value
@SmaPeriod.setter
def SmaPeriod(self, value):
self._sma_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(adx_ma_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(adx_ma_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema
self._has_prev = True
return
if self._prev_close <= self._prev_ema and close > ema and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_ema and close < ema and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_ema = ema
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return adx_ma_strategy()