在 GitHub 上查看

Nevalyashka Breakdown Level 策略

概述

Nevalyashka Breakdown Level 策略是 MT4 智能交易系统 Nevalyashka_BreakdownLevel 的直接移植版本。策略在两个可配置时间之间构建开盘区间,并在价格突破该区间时入场。当突破失败并触发止损后,系统会立即反向下单,并按马丁系数放大手数以尝试弥补亏损。只要当日出现盈利平仓,策略便会停止当日剩余时间的所有新交易,与原始 EA 的逻辑完全一致。

核心要点

  • 开盘区间:RangeStartRangeEnd 之间形成的最高价与最低价构成当日的突破通道。
  • 突破入场: 收盘价高于区间上沿时做多,收盘价低于区间下沿时做空。
  • 保护位: 止损始终放在区间另一侧,止盈距离等于区间宽度。
  • 保本移动: 若启用 UseBreakeven,当浮动盈利达到目标距离的一半时,将止损移动至开仓价。
  • 马丁恢复: 触发止损后立即反向下单,并把下一笔交易的手数乘以 MartingaleMultiplier,新的止盈/止损距离经过计算后能够覆盖上一笔亏损。
  • 当日锁定: 只要有交易盈利或以非负收益平仓,就会锁定当日剩余时间不再开仓。
  • 强制平仓:OrdersCloseTime 晚于 RangeEnd,到达该时间点时会强制平掉全部头寸并阻止新的入场。

参数

名称 说明 默认值
RangeStart 参与计算开盘区间的起始时间(含)。 04:00
RangeEnd 开盘区间的结束时间(含)。 09:00
OrdersCloseTime 强制平仓时间。若该时间晚于 RangeEnd,也会阻止之后的任何入场。 23:30
OrderVolume 每次突破入场的手数。 0.1
MartingaleMultiplier 止损后下一笔交易的手数放大倍数。 2
UseBreakeven 是否在达到目标一半时把止损移动到开仓价。 true
CandleType 用于计算区间与信号的蜡烛类型。 1 小时蜡烛

交易规则

  1. 区间计算: 每个交易日重新记录在 RangeStartRangeEnd 之间所有已完成蜡烛的最高价和最低价。
  2. 入场条件:
    • 当前蜡烛收盘价高于区间上沿时做多。
    • 当前蜡烛收盘价低于区间下沿时做空。
    • 如果存在待执行的马丁反手、当日已出现盈利、或当前时间晚于 OrdersCloseTime(且 OrdersCloseTime > RangeEnd),则跳过入场。
  3. 风险控制:
    • 止损位固定在区间另一侧。
    • 止盈距离等于开仓价与区间宽度之和(或差)。
    • 启用 UseBreakeven 时,在达到目标距离一半后把止损移动到开仓价。
  4. 马丁反手:
    • 止损触发后立即平仓,并用 MartingaleMultiplier 放大手数,在相反方向市价开仓。
    • 新的止盈与止损距离等于上一笔亏损(按手数换算)除以马丁系数,与原 EA 的恢复逻辑一致。
  5. 当日锁仓:
    • 任何非负收益的平仓都会锁定当日剩余时间,直到交易日切换。
  6. 强制离场:
    • 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;
	}
}