Elite eFibo Trader 策略
概述
Elite eFibo Trader Strategy 源自 MQL5 顾问程序 “Elite eFibo Trader”。策略基于斐波那契数列构建加仓网格:先以 LotsLevel1 的手数开仓,然后按照固定间距批量挂入止损买/卖单。策略依赖逐笔成交数据,并会随着网格扩展自动上移/下移保护性止损。
工作流程
- 当没有持仓和挂单且允许交易时,策略会在选定方向(多或空)启动新的网格周期。
- 首单按照市价成交,其余 13 张挂单分别位于
LevelDistance的倍数位置,手数由LotsLevel2…LotsLevel14设置(默认为斐波那契序列)。 - 每次成交都会在入场价附近放置一个距离为
StopLossPoints的保护止损。对于多头取所有止损中的最大值,空头取最小值,并将其同步给整套仓位形成追踪止损。 - 一旦价格触及追踪止损,策略立即平掉全部仓位并撤销剩余挂单。
- 策略实时计算以账户货币计价的浮动盈亏。当达到
MoneyTakeProfit后关闭网格;若TradeAgainAfterProfit为真,则自动重新开始新一轮,否则保持等待。
参数
OpenBuy:仅运行多头网格。OpenSell:仅运行空头网格。TradeAgainAfterProfit:达到盈利目标后是否自动重新启动。LevelDistance:相邻挂单之间的距离,以合约最小价位(PriceStep)为单位。StopLossPoints:单笔交易的固定止损距离,以价位为单位。MoneyTakeProfit:以账户货币表示的浮动盈利目标。LotsLevel1…LotsLevel14:每一层网格的手数,默认采用斐波那契序列 1, 1, 2, 3, 5, …, 377。
实现细节
- 所有价格偏移均依据
Security.PriceStep计算;若最小价位未定义(为 0),策略不会发送订单。 - 任意时刻只允许一个网格周期运行,所有挂单在周期开始时一次性创建。
- 追踪止损会在每次成交或减仓时重新计算,确保全部持仓共享最佳保护价位。
- 盈亏评估依赖
Position、PositionPrice以及StepPrice等市场数据。 - 当
TradeAgainAfterProfit设为false时,策略在实现盈利目标后会保持停用状态,直至手动重新启用。
使用建议
- 启动前请只勾选一个方向(多或空),同时启用两个方向会阻止网格启动。
- 根据交易品种的波动率与合约规模调整间距和手数。较大的斐波那契手数会快速放大仓位,需配合充分的回测验证。
- 确认经纪商支持所计算出的止损挂单价格,否则可能出现下单被拒的情况。
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>
/// Elite eFibo Trader grid strategy converted from MQL5.
/// Builds a Fibonacci-based sequence of market entries at price levels.
/// Buys or sells at progressively worse prices with increasing volume (Fibonacci sequence).
/// Exits when total PnL target is reached or stop loss is hit.
/// </summary>
public class EliteEFiboTraderStrategy : Strategy
{
private readonly StrategyParam<int> _levelsCount;
private readonly StrategyParam<bool> _openBuy;
private readonly StrategyParam<bool> _openSell;
private readonly StrategyParam<decimal> _levelDistance;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private int _currentLevel;
private int _activeDirection;
private bool _cycleActive;
// Fibonacci volumes for grid levels
private static readonly decimal[] FibVolumes = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
/// <summary>
/// Enable buy-only mode.
/// </summary>
public bool OpenBuy
{
get => _openBuy.Value;
set => _openBuy.Value = value;
}
/// <summary>
/// Enable sell-only mode.
/// </summary>
public bool OpenSell
{
get => _openSell.Value;
set => _openSell.Value = value;
}
/// <summary>
/// Number of Fibonacci grid levels.
/// </summary>
public int LevelsCount
{
get => _levelsCount.Value;
set => _levelsCount.Value = value;
}
/// <summary>
/// Distance between successive pending levels in price steps.
/// </summary>
public decimal LevelDistance
{
get => _levelDistance.Value;
set => _levelDistance.Value = value;
}
/// <summary>
/// Stop-loss size in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit size in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public EliteEFiboTraderStrategy()
{
_levelsCount = Param(nameof(LevelsCount), 6)
.SetGreaterThanZero()
.SetDisplay("Levels Count", "Number of Fibonacci levels", "Grid");
_openBuy = Param(nameof(OpenBuy), true)
.SetDisplay("Open Buy", "Enable buying", "General");
_openSell = Param(nameof(OpenSell), true)
.SetDisplay("Open Sell", "Enable selling", "General");
_levelDistance = Param(nameof(LevelDistance), 50m)
.SetGreaterThanZero()
.SetDisplay("Level Distance", "Distance between orders in price steps", "Grid");
_stopLossPoints = Param(nameof(StopLossPoints), 200m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop-loss size in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 100m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit size in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cycleActive = false;
_currentLevel = 0;
_activeDirection = 0;
_entryPrice = 0;
var sma = new SMA { Length = 20 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var step = Security.PriceStep ?? 1m;
if (!_cycleActive && Position == 0)
{
// Start a new cycle based on trend direction
if (OpenBuy && close > smaValue)
{
_activeDirection = 1;
_entryPrice = close;
_currentLevel = 0;
_cycleActive = true;
BuyMarket();
}
else if (OpenSell && close < smaValue)
{
_activeDirection = -1;
_entryPrice = close;
_currentLevel = 0;
_cycleActive = true;
SellMarket();
}
}
else if (_cycleActive)
{
var stopDistance = StopLossPoints * step;
var tpDistance = TakeProfitPoints * step;
var levelDist = LevelDistance * step;
// Check stop loss
if (_activeDirection == 1 && close <= _entryPrice - stopDistance)
{
SellMarket(Math.Abs(Position));
ResetCycle();
return;
}
else if (_activeDirection == -1 && close >= _entryPrice + stopDistance)
{
BuyMarket(Math.Abs(Position));
ResetCycle();
return;
}
// Check take profit
if (_activeDirection == 1 && close >= _entryPrice + tpDistance)
{
SellMarket(Math.Abs(Position));
ResetCycle();
return;
}
else if (_activeDirection == -1 && close <= _entryPrice - tpDistance)
{
BuyMarket(Math.Abs(Position));
ResetCycle();
return;
}
// Check for grid level additions (averaging into losing positions)
var nextLevel = _currentLevel + 1;
if (nextLevel < LevelsCount && nextLevel < FibVolumes.Length)
{
if (_activeDirection == 1 && close <= _entryPrice - levelDist * nextLevel)
{
BuyMarket(FibVolumes[nextLevel]);
_currentLevel = nextLevel;
}
else if (_activeDirection == -1 && close >= _entryPrice + levelDist * nextLevel)
{
SellMarket(FibVolumes[nextLevel]);
_currentLevel = nextLevel;
}
}
}
}
private void ResetCycle()
{
_cycleActive = false;
_currentLevel = 0;
_activeDirection = 0;
_entryPrice = 0;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
FIB_VOLUMES = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
class elite_e_fibo_trader_strategy(Strategy):
def __init__(self):
super(elite_e_fibo_trader_strategy, self).__init__()
self._levels_count = self.Param("LevelsCount", 6)
self._open_buy = self.Param("OpenBuy", True)
self._open_sell = self.Param("OpenSell", True)
self._level_distance = self.Param("LevelDistance", 50.0)
self._stop_loss_points = self.Param("StopLossPoints", 200.0)
self._take_profit_points = self.Param("TakeProfitPoints", 100.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._entry_price = 0.0
self._current_level = 0
self._active_direction = 0
self._cycle_active = False
@property
def LevelsCount(self):
return self._levels_count.Value
@LevelsCount.setter
def LevelsCount(self, value):
self._levels_count.Value = value
@property
def OpenBuy(self):
return self._open_buy.Value
@OpenBuy.setter
def OpenBuy(self, value):
self._open_buy.Value = value
@property
def OpenSell(self):
return self._open_sell.Value
@OpenSell.setter
def OpenSell(self, value):
self._open_sell.Value = value
@property
def LevelDistance(self):
return self._level_distance.Value
@LevelDistance.setter
def LevelDistance(self, value):
self._level_distance.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(elite_e_fibo_trader_strategy, self).OnStarted2(time)
self._cycle_active = False
self._current_level = 0
self._active_direction = 0
self._entry_price = 0.0
sma = SimpleMovingAverage()
sma.Length = 20
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma_value)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if not self._cycle_active and self.Position == 0:
if self.OpenBuy and close > sma_val:
self._active_direction = 1
self._entry_price = close
self._current_level = 0
self._cycle_active = True
self.BuyMarket()
elif self.OpenSell and close < sma_val:
self._active_direction = -1
self._entry_price = close
self._current_level = 0
self._cycle_active = True
self.SellMarket()
elif self._cycle_active:
stop_distance = float(self.StopLossPoints) * step
tp_distance = float(self.TakeProfitPoints) * step
level_dist = float(self.LevelDistance) * step
if self._active_direction == 1 and close <= self._entry_price - stop_distance:
self.SellMarket()
self._reset_cycle()
return
elif self._active_direction == -1 and close >= self._entry_price + stop_distance:
self.BuyMarket()
self._reset_cycle()
return
if self._active_direction == 1 and close >= self._entry_price + tp_distance:
self.SellMarket()
self._reset_cycle()
return
elif self._active_direction == -1 and close <= self._entry_price - tp_distance:
self.BuyMarket()
self._reset_cycle()
return
next_level = self._current_level + 1
if next_level < int(self.LevelsCount) and next_level < len(FIB_VOLUMES):
if self._active_direction == 1 and close <= self._entry_price - level_dist * next_level:
self.BuyMarket()
self._current_level = next_level
elif self._active_direction == -1 and close >= self._entry_price + level_dist * next_level:
self.SellMarket()
self._current_level = next_level
def _reset_cycle(self):
self._cycle_active = False
self._current_level = 0
self._active_direction = 0
self._entry_price = 0.0
def OnReseted(self):
super(elite_e_fibo_trader_strategy, self).OnReseted()
self._entry_price = 0.0
self._current_level = 0
self._active_direction = 0
self._cycle_active = False
def CreateClone(self):
return elite_e_fibo_trader_strategy()