在 GitHub 上查看
Nevalyashka Breakdown Level 策略
概述
Nevalyashka Breakdown Level 策略是 MT4 智能交易系统 Nevalyashka_BreakdownLevel 的直接移植版本。策略在两个可配置时间之间构建开盘区间,并在价格突破该区间时入场。当突破失败并触发止损后,系统会立即反向下单,并按马丁系数放大手数以尝试弥补亏损。只要当日出现盈利平仓,策略便会停止当日剩余时间的所有新交易,与原始 EA 的逻辑完全一致。
核心要点
- 开盘区间: 在
RangeStart 与 RangeEnd 之间形成的最高价与最低价构成当日的突破通道。
- 突破入场: 收盘价高于区间上沿时做多,收盘价低于区间下沿时做空。
- 保护位: 止损始终放在区间另一侧,止盈距离等于区间宽度。
- 保本移动: 若启用
UseBreakeven,当浮动盈利达到目标距离的一半时,将止损移动至开仓价。
- 马丁恢复: 触发止损后立即反向下单,并把下一笔交易的手数乘以
MartingaleMultiplier,新的止盈/止损距离经过计算后能够覆盖上一笔亏损。
- 当日锁定: 只要有交易盈利或以非负收益平仓,就会锁定当日剩余时间不再开仓。
- 强制平仓: 若
OrdersCloseTime 晚于 RangeEnd,到达该时间点时会强制平掉全部头寸并阻止新的入场。
参数
| 名称 |
说明 |
默认值 |
RangeStart |
参与计算开盘区间的起始时间(含)。 |
04:00 |
RangeEnd |
开盘区间的结束时间(含)。 |
09:00 |
OrdersCloseTime |
强制平仓时间。若该时间晚于 RangeEnd,也会阻止之后的任何入场。 |
23:30 |
OrderVolume |
每次突破入场的手数。 |
0.1 |
MartingaleMultiplier |
止损后下一笔交易的手数放大倍数。 |
2 |
UseBreakeven |
是否在达到目标一半时把止损移动到开仓价。 |
true |
CandleType |
用于计算区间与信号的蜡烛类型。 |
1 小时蜡烛 |
交易规则
- 区间计算: 每个交易日重新记录在
RangeStart 与 RangeEnd 之间所有已完成蜡烛的最高价和最低价。
- 入场条件:
- 当前蜡烛收盘价高于区间上沿时做多。
- 当前蜡烛收盘价低于区间下沿时做空。
- 如果存在待执行的马丁反手、当日已出现盈利、或当前时间晚于
OrdersCloseTime(且 OrdersCloseTime > RangeEnd),则跳过入场。
- 风险控制:
- 止损位固定在区间另一侧。
- 止盈距离等于开仓价与区间宽度之和(或差)。
- 启用
UseBreakeven 时,在达到目标距离一半后把止损移动到开仓价。
- 马丁反手:
- 止损触发后立即平仓,并用
MartingaleMultiplier 放大手数,在相反方向市价开仓。
- 新的止盈与止损距离等于上一笔亏损(按手数换算)除以马丁系数,与原 EA 的恢复逻辑一致。
- 当日锁仓:
- 任何非负收益的平仓都会锁定当日剩余时间,直到交易日切换。
- 强制离场:
- 若
OrdersCloseTime 晚于区间结束时间,当时间到达该值时会立即平掉所有头寸并锁定当日。
说明
- 策略使用 StockSharp 的高级 API(
SubscribeCandles().Bind(...))实现,符合框架推荐的事件驱动模式。
- 所有所需状态(区间上下沿、马丁参数、保本标志等)均保存在策略内部,不依赖额外的历史查询。
- 转换保留了原 EA 以日历日期判断“当日”的方式,以及止损后立即按马丁反手的流程。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Nevalyashka BreakdownLevel: Previous candle breakout with EMA filter and ATR stops.
/// </summary>
public class NevalyashkaBreakdownLevelStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
public NevalyashkaBreakdownLevelStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevHigh == 0 || atrVal <= 0) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 1.5m || close >= _entryPrice + atrVal * 2.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 1.5m || close <= _entryPrice - atrVal * 2.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < _prevLow && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class nevalyashka_breakdown_level_strategy(Strategy):
def __init__(self):
super(nevalyashka_breakdown_level_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(nevalyashka_breakdown_level_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_high == 0 or av <= 0:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position > 0:
if close <= self._entry_price - av * 1.5 or close >= self._entry_price + av * 2.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 1.5 or close <= self._entry_price - av * 2.5:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def OnReseted(self):
super(nevalyashka_breakdown_level_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
def CreateClone(self):
return nevalyashka_breakdown_level_strategy()