在 GitHub 上查看

Elite eFibo Trader 策略

概述

Elite eFibo Trader 复刻了经典的马丁网格专家顾问:在趋势过滤条件满足时,以斐波那契序列的手数逐级加仓。该 StockSharp 版本保留了原始的分批建仓结构——首单以市价成交,其余订单按照设定的点距挂入止损单;每当上一级被触发后,下一层继续排队。策略在浮动盈亏达到现金目标或趋势过滤器转向时关闭整篮仓位。

市场数据

  • 仅订阅一个可配置的蜡烛类型(默认 15 分钟蜡烛)。
  • 指标与止损判断均基于蜡烛收盘价。

入场逻辑

  1. 方向由均线交叉逻辑(默认开启)或手动开关 ManualOpenBuy/ManualOpenSell 决定。
  2. 当启用均线逻辑时,快速均线向上穿越慢速均线会激活做多阶梯,反向穿越激活做空阶梯;每根蜡烛只触发一次信号。
  3. 启用 RSI 过滤后,仅当 RSI > RsiHigh 才允许建立多头篮子,RSI < RsiLow 才允许空头篮子。
  4. 只有在策略没有任何未完成订单或持仓且允许继续交易(TradeAgainAfterProfit)时才会开启新的阶梯。
  5. 第一层使用市价单,其余层按照 LevelDistancePips 的点距挂出止损单;各层手数默认为斐波那契数列,可逐层调整。

出场逻辑

  • 每个已成交的层级都会获得基于 StopLossPips 的初始止损,并在均线给出反向信号时参与拖移。
  • 多头篮子的止损会跟随到 收盘价 - TrailingStopPips,空头篮子跟随到 收盘价 + TrailingStopPips,且永远不会远离当前止损。
  • 当蜡烛的最高/最低触及某一层的止损价时,该层剩余仓位以市价平仓。
  • 若整篮浮动盈亏(基于 PriceStepStepPrice 计算)达到 MoneyTakeProfit,策略立即平掉所有仓位并撤销未成交的挂单。
  • 当篮子完全出清后会自动撤销全部止损单;若 TradeAgainAfterProfit 设为 false,策略会保持空闲直至重新启动。

参数

参数 说明
UseMaLogic 启用/关闭均线交叉方向判定。
MaSlowPeriod, MaFastPeriod 慢速与快速 SMA 的周期。
TrailingStopPips 均线反向时使用的拖移止损距离(点)。
UseRsiFilter, RsiPeriod, RsiHigh, RsiLow RSI 过滤器设置。仅当 RSI 高于 RsiHigh 才能做多,低于 RsiLow 才能做空。
ManualOpenBuy, ManualOpenSell 关闭均线逻辑后用于手动控制方向的开关。
TradeAgainAfterProfit 达到现金止盈后是否重新启动新的篮子。
LevelDistancePips 各层挂单之间的点距。
StopLossPips 每层的初始止损距离。
MoneyTakeProfit 以账户货币计价的篮子止盈目标。
Level1VolumeLevel14Volume 各层手数;设为 0 可禁用该层。
CandleType 指标计算所用的蜡烛类型/周期。

实现细节

  • 点值换算与 MQL 版本一致:若品种的 Decimals 为 3 或 5,则使用 PriceStep * 10 作为 1 点,对应原策略中的 MyPoint 处理。
  • 每个层级单独跟踪其入场价、剩余仓位与止损价,因此能够复现部分成交及单独止损的行为。
  • 浮动盈亏通过 PriceStepStepPrice 计算,请确认品种的这两个属性已正确配置,否则现金止盈无法触发。
  • 启动时调用一次 StartProtection() 以启用 StockSharp 的保护机制。
  • 当没有任何持仓时会自动执行撤单,重现原脚本多次调用 subCloseAllPending() 的做法。

使用建议

  • 检查交易品种的 PriceStepStepPriceVolumeStep 以及最小/最大交易量,确保斐波那契手数合法。
  • 请选择与 MetaTrader 原策略一致的蜡烛周期,以避免信号偏移。
  • 马丁策略在单边行情中会迅速累积仓位,请先在模拟或历史数据上验证。
  • 关闭 UseMaLogic 可手动控制方向,保持开启则由均线自动判断趋势。
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Elite eFibo Trader: MA crossover with RSI filter and grid averaging.
/// Adds to winning positions on pullbacks using Fibonacci-style scaling.
/// </summary>
public class EliteEfiboTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _addCount;

	public EliteEfiboTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast MA", "Fast SMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow MA", "Slow SMA period.", "Indicators");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI filter period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_addCount = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_addCount = 0;

		var fast = new SimpleMovingAverage { Length = FastLength };
		var slow = new SimpleMovingAverage { Length = SlowLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, rsi, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		// Position management
		if (Position > 0)
		{
			// Take profit or stop
			if (close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
				_addCount = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
				_addCount = 0;
			}
			else if (_addCount < 2 && close <= _entryPrice - atrVal * 0.8m && rsiVal < 40)
			{
				// Fibonacci add: buy more on pullback
				_entryPrice = (_entryPrice + close) / 2m;
				_addCount++;
				BuyMarket();
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
				_addCount = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
				_addCount = 0;
			}
			else if (_addCount < 2 && close >= _entryPrice + atrVal * 0.8m && rsiVal > 60)
			{
				_entryPrice = (_entryPrice + close) / 2m;
				_addCount++;
				SellMarket();
			}
		}

		// Entry: MA crossover with RSI confirmation
		if (Position == 0)
		{
			if (_prevFast <= _prevSlow && fastVal > slowVal && rsiVal > 50)
			{
				_entryPrice = close;
				_addCount = 0;
				BuyMarket();
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal && rsiVal < 50)
			{
				_entryPrice = close;
				_addCount = 0;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}