Ten Pips EURUSD 策略
概述
Ten Pips EURUSD 是一套复制自 MetaTrader 专家的区间突破策略。策略记录上一根已完成 K 线的最高价与最低价,然后在区间上下各挂一组止损单,并使用点数(pip)来设定止损及止盈距离。通过 StockSharp 的高级烛图订阅与盘口数据,代码保持了原始 EA 的行为,同时可以在任意券商环境下运行。
交易流程
- 订阅选定的烛图类型,等待新的蜡烛进入激活状态。
- 当蜡烛收盘时,保存其最高价与最低价,并取消所有尚未执行的挂单——原 EA 仅允许挂单存续一个周期。
- 在新蜡烛的第一个报价上执行以下检查:
- 开盘价必须落在上一根蜡烛的范围内,以过滤跳空情况。
- 当前价格距离上一根蜡烛的高点与低点至少三个点,用于模拟经纪商最小止损距离的限制。
- 通过最新的买一/卖一报价计算当前点差。如无盘口数据,则退回到预估的点值。
- 同时挂出两张 Stop 订单:
- Buy Stop 触发价为
上一高点 + 2 × 点差,止损为StopLossPips点,若未启用追踪止损,则在TakeProfitPips点处设定止盈。 - Sell Stop 触发价为
上一低点 − 点差,止损与止盈设置对称。
- Buy Stop 触发价为
- 当蜡烛收盘或挂单全部完成/取消后,进入下一周期重复上述流程。
持仓管理
- 持仓期间,策略在每次盘口刷新时检查最新的买价或卖价并调整保护水平。
- 若未启用追踪止损,则价格触及固定的止损或止盈后立即平仓。
- 若启用追踪止损:
- 多头在盈利达到
TrailingStopPips点后启动追踪止损,止损价被设为买价 − TrailingStopPips,并在盈利额外超过TrailingStepPips点时向上移动。 - 空头逻辑以卖价为基准镜像多头处理。
- 多头在盈利达到
- 若通过市价或其他方式退出,所有保护参数将重置,而剩余的反向挂单会保留到蜡烛结束,以保持与原 EA 相同的“跨栏”效果。
参数说明
| 参数 | 默认值 | 描述 |
|---|---|---|
Volume |
0.01 |
委托数量,单位为手或合约。 |
StopLossPips |
50 |
入场价到止损价之间的点数距离。 |
TakeProfitPips |
150 |
固定止盈距离,仅在关闭追踪止损时生效。 |
UseTrailing |
false |
是否启用追踪止损。 |
TrailingStopPips |
50 |
追踪止损的初始距离,单位为点。 |
TrailingStepPips |
25 |
触发追踪止损更新所需的额外盈利点数。 |
CandleType |
15 分钟 |
用于计算突破区间的蜡烛类型。 |
使用建议
- 点值根据
Security.PriceStep自动推导,并复刻了 MT5 中对 3 位及 5 位报价的处理方式。 - 所有点数在下单前会换算成价格增量,因此策略同样适用于非外汇品种,只要定义了最小报价单位。
- 当经纪商未提供最小止损限制时,策略默认使用三个点作为安全缓冲,与原始 EA 保持一致。
- 请确保所选时间框架的数据流连续,因为挂单仅在单根蜡烛内有效。
- 实盘前务必在模拟环境下测试不同的点差、佣金与滑点设置,合理控制风险。
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>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossMult;
private readonly StrategyParam<decimal> _takeProfitMult;
private readonly StrategyParam<decimal> _trailingMult;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _hasPrev;
public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TenPipsEurusdStrategy()
{
_stopLossMult = Param(nameof(StopLossMult), 1.0m)
.SetGreaterThanZero()
.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");
_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
.SetGreaterThanZero()
.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");
_trailingMult = Param(nameof(TrailingMult), 0.8m)
.SetGreaterThanZero()
.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_entryPrice = 0;
_stopPrice = null;
_takePrice = null;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Manage existing position
if (Position > 0)
{
// Trail
var trail = close - TrailingMult * atr;
if (_stopPrice == null || trail > _stopPrice)
_stopPrice = trail;
if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
{
SellMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
else if (Position < 0)
{
var trail = close + TrailingMult * atr;
if (_stopPrice == null || trail < _stopPrice)
_stopPrice = trail;
if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
{
BuyMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
// Entry on breakout
if (_hasPrev && Position == 0)
{
if (close > _prevHigh + atr * 0.5m)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - StopLossMult * atr;
_takePrice = close + TakeProfitMult * atr;
}
else if (close < _prevLow - atr * 0.5m)
{
SellMarket();
_entryPrice = close;
_stopPrice = close + StopLossMult * atr;
_takePrice = close - TakeProfitMult * atr;
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ten_pips_eurusd_strategy(Strategy):
def __init__(self):
super(ten_pips_eurusd_strategy, self).__init__()
self._sl_mult = self.Param("StopLossMult", 1.0)
self._tp_mult = self.Param("TakeProfitMult", 2.0)
self._trail_mult = self.Param("TrailingMult", 0.8)
self._atr_period = self.Param("AtrPeriod", 14)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ten_pips_eurusd_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
def OnStarted2(self, time):
super(ten_pips_eurusd_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
return
atr_v = float(atr_val)
close = float(candle.ClosePrice)
pos = float(self.Position)
if pos > 0:
trail = close - float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail > self._stop_price:
self._stop_price = trail
if close <= self._stop_price or (self._take_price is not None and close >= self._take_price):
self.SellMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
elif pos < 0:
trail = close + float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail < self._stop_price:
self._stop_price = trail
if close >= self._stop_price or (self._take_price is not None and close <= self._take_price):
self.BuyMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
pos = float(self.Position)
if self._has_prev and pos == 0:
if close > self._prev_high + atr_v * 0.5:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - float(self._sl_mult.Value) * atr_v
self._take_price = close + float(self._tp_mult.Value) * atr_v
elif close < self._prev_low - atr_v * 0.5:
self.SellMarket()
self._entry_price = close
self._stop_price = close + float(self._sl_mult.Value) * atr_v
self._take_price = close - float(self._tp_mult.Value) * atr_v
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
def CreateClone(self):
return ten_pips_eurusd_strategy()