在 GitHub 上查看
FIBO1 策略(MQL 24845 移植)
概述
FIBO1 Strategy 将 Aharon Tzadik 编写的 FIBO1.mq4 专家顾问迁移到
StockSharp 高级 API。策略基于单一交易品种,并使用三组过滤器:
- 趋势过滤 – 典型价的快/慢线性加权均线(LWMA)。做多要求快线位于慢线之上,
做空则相反。
- 动量确认 – 连续三根动量值与买入/卖出阈值比较,保持 MQL 版本中“偏离100的
绝对值”这一逻辑。
- MACD 过滤 – 在更高周期上计算 MACD,只有当主线位于信号线上方(多头)或
下方(空头)时才允许开仓。
持仓后,策略重现 FIBO1.mq4 中复杂的离场模块:
- 以点数表示的止盈/止损。
- 货币金额和百分比目标,以及浮动利润的资金拖尾管理。
- 依据最近若干根K线极值的拖尾止损(包含 PAD AMOUNT 的偏移量)。
- 固定距离的经典拖尾。
- 启动条件可配置的保本保护。
- 监控历史最高权益的回撤,并触发全局平仓的权益风控。
提示: MQL 原版在实盘模式下依赖用户绘制的 "FIBO" 线。StockSharp 无法访问
终端图形对象,因此移植版本始终采用测试分支(忽略 Fibo 过滤)。其它功能均已
迁移,并在下文详细说明。
交易逻辑
信号判定
- 等待主时间框架的收盘K线。
- 检查快LWMA与慢LWMA的相对位置。
- 复现
Low[2] < High[1](多)与 Low[1] < High[2](空)的K线形态过滤。
- 计算最近三根动量值与100之间的最大偏离,并与阈值比较。
- 确认高周期 MACD 主线与信号线的相对顺序。
- 所有条件满足时,如有反向持仓先平掉,再按设定手数以市价进场。
风险控制
- 每次建仓立即调用 StockSharp 保护接口设置点数止盈/止损。
- 到达激活点后将止损移动到保本价,并可额外加点差。
- 拖尾逻辑可选择“按K线极值+缓冲”或“固定点差”两种模式。
- 资金管理模块负责货币目标、百分比目标和浮动利润拖尾。
- 权益风控记录历史最高权益并在回撤超限时强制平仓。
参数
| 参数 |
默认值 |
说明 |
UseMoneyTakeProfit |
false |
达到 MoneyTakeProfit(账户货币)时全部平仓。 |
MoneyTakeProfit |
10 |
货币止盈目标,仅在 UseMoneyTakeProfit = true 时生效。 |
UsePercentTakeProfit |
false |
启用相对于初始权益的百分比止盈。 |
PercentTakeProfit |
10 |
百分比止盈阈值。 |
EnableMoneyTrailing |
true |
浮盈达到 MoneyTrailTarget 后启动资金拖尾。 |
MoneyTrailTarget |
40 |
启动资金拖尾所需的最小浮盈。 |
MoneyTrailStop |
10 |
资金拖尾激活后允许回吐的最大金额。 |
UseEquityStop |
true |
启用权益回撤保护。 |
EquityRiskPercent |
1 |
允许的最大回撤,占历史最高权益的百分比。 |
TradeVolume |
1 |
市价单的基础手数。 |
FastMaPeriod |
20 |
快速 LWMA 的周期。 |
SlowMaPeriod |
100 |
慢速 LWMA 的周期。 |
MomentumPeriod |
14 |
动量指标的周期。 |
MomentumBuyThreshold |
0.3 |
多头动量偏离阈值。 |
MomentumSellThreshold |
0.3 |
空头动量偏离阈值。 |
MacdFastPeriod |
12 |
MACD 快速 EMA 的周期。 |
MacdSlowPeriod |
26 |
MACD 慢速 EMA 的周期。 |
MacdSignalPeriod |
9 |
MACD 信号 EMA 的周期。 |
TakeProfitPips |
50 |
止盈距离(点)。 |
StopLossPips |
20 |
止损距离(点)。 |
TrailingActivationPips |
40 |
固定距离拖尾的启动盈利(点)。 |
TrailingDistancePips |
40 |
固定距离拖尾保持的点差。 |
UseCandleTrailing |
true |
使用K线极值拖尾;关闭后改为固定点差。 |
CandleTrailingLength |
3 |
计算拖尾极值时包含的收盘K线数量。 |
CandleTrailingOffsetPips |
3 |
K线极值拖尾的额外偏移(点)。 |
MoveToBreakEven |
true |
启用保本保护。 |
BreakEvenActivationPips |
30 |
触发保本的盈利(点)。 |
BreakEvenOffsetPips |
30 |
保本时附加的偏移量(点)。 |
CandleType |
15m |
主信号时间框架。 |
MomentumCandleType |
15m |
动量指标使用的时间框架。 |
MacdCandleType |
1d |
MACD 使用的高周期。 |
使用说明
- 默认时间框架遵循原始 EA 的多周期思路:主图与动量使用相同的周期,MACD 采用更
高一档的周期(默认为日线)。
- 点值换算会在三位/五位报价的外汇品种上自动乘以10,其它品种直接使用
PriceStep。
- 策略仅处理收盘K线,数据源必须提供
Finished 状态。
- 在净持仓模式下,开反向仓位会先平掉旧仓,再按设定手数建新仓,与 MT4 版本一致。
与原版的差异
- 由于无法读取 MT4 图形对象,不再检测手动绘制的 "FIBO" 线,策略始终沿用测试
分支逻辑。
Lots、LotExponent、Max_Trades 等参数被统一为 TradeVolume,因为 StockSharp
使用净头寸模型;如需马丁倍量,可通过外部优化器实现。
- 为保持示例精炼,移除了
Alert、SendMail、SendNotification 等通知函数。
在这些调整下,移植版本完整保留了 FIBO1.mq4 的交易思想,并提供了详尽的参数
说明,方便在 StockSharp 环境中测试与扩展。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Fibo1Strategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public Fibo1Strategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fibo1_strategy(Strategy):
def __init__(self):
super(fibo1_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(fibo1_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(fibo1_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return fibo1_strategy()