FIBO1 Strategy переносит торговую логику эксперта FIBO1.mq4 автора Aharon Tzadik
в инфраструктуру StockSharp. Стратегия работает с одним инструментом и сочетает три
группы фильтров:
Трендовый фильтр – быстрая и медленная линейно-взвешенные скользящие (LWMA)
по типичной цене. Для покупок быстрая LWMA должна быть выше медленной, для продаж –
наоборот.
Подтверждение моментума – три последовательных значения индикатора Momentum
сравниваются с порогами на покупку и продажу. В расчёте используется абсолютное
отклонение от уровня 100, как и в оригинале.
Фильтр MACD – на старшем таймфрейме MACD подтверждает направление сделки. В
реализации сохранены настройки 12/26/9 и проверка взаимного положения основной и
сигнальной линий.
После открытия позиции стратегия воспроизводит расширенный блок сопровождения из
FIBO1.mq4:
Стоп-лосс и тейк-профит в пунктах.
Денежный и процентный тейк-профит + денежное трейлинг-сопровождение.
Трейлинг по экстремумам последних свечей с отступом (аналог параметра PAD AMOUNT).
Классический трейлинг по фиксированному расстоянию.
Перевод в безубыток с заданным отступом.
Эквити-стоп по просадке от максимального значения капитала.
Важно. В живой торговле MQL-версия использовала вручную нанесённую линию "FIBO"
на графике. StockSharp не имеет доступа к графическим объектам терминала, поэтому
стратегия всегда работает по ветке тестера (без фильтра по линиям Фибо). Остальные
возможности полностью перенесены и описаны ниже.
Логика торговли
Поиск сигнала
Ожидаем закрытую свечу на рабочем таймфрейме.
Проверяем, что быстрая LWMA находится выше/ниже медленной в зависимости от
направления сделки.
Сравниваем экстремумы предыдущих свечей (Low[2] < High[1] для лонгов и
Low[1] < High[2] для шортов).
Вычисляем максимум абсолютного отклонения трёх последних значений Momentum от
уровня 100. Если он превышает заданный порог, фильтр пропускает сигнал.
Убеждаемся, что на старшем таймфрейме линия MACD находится выше/ниже сигнальной.
При выполнении всех условий стратегия закрывает встречную позицию и открывает
рыночную сделку требуемого объёма.
Управление рисками
Каждая новая позиция сразу получает защитные ордера стоп-лосса и тейк-профита.
При достижении заданной прибыли стоп переносится в безубыток с указанным отступом.
Трейлинг может работать по минимумам/максимумам свечей либо по фиксированному
расстоянию в пунктах.
Денежные цели отслеживают плавающую прибыль, процент от стартового equity и
реализуют трейлинг в валюте счёта.
Эквити-стоп следит за просадкой относительно исторического максимума equity и
закрывает все позиции при превышении допустимого значения.
Параметры
Название
Значение по умолчанию
Описание
UseMoneyTakeProfit
false
Закрывать позиции при достижении MoneyTakeProfit в валюте счёта.
MoneyTakeProfit
10
Денежный тейк-профит. Работает только если UseMoneyTakeProfit = true.
UsePercentTakeProfit
false
Включить тейк-профит в процентах от стартового equity.
PercentTakeProfit
10
Процент прибыли от стартового equity.
EnableMoneyTrailing
true
Активировать денежный трейлинг после достижения MoneyTrailTarget.
MoneyTrailTarget
40
Минимальная плавающая прибыль для запуска денежного трейлинга.
MoneyTrailStop
10
Допустимый откат прибыли (в валюте счёта) после активации денежного трейлинга.
UseEquityStop
true
Включить защиту по просадке equity.
EquityRiskPercent
1
Максимальная просадка (в % от пика equity), при которой закрываются все позиции.
TradeVolume
1
Базовый объём рыночных сделок.
FastMaPeriod
20
Период быстрой LWMA по типичной цене.
SlowMaPeriod
100
Период медленной LWMA по типичной цене.
MomentumPeriod
14
Период индикатора Momentum.
MomentumBuyThreshold
0.3
Минимальное отклонение Momentum от 100 для входа в лонг.
MomentumSellThreshold
0.3
Минимальное отклонение Momentum от 100 для входа в шорт.
MacdFastPeriod
12
Период быстрой EMA внутри MACD.
MacdSlowPeriod
26
Период медленной EMA внутри MACD.
MacdSignalPeriod
9
Период сигнальной EMA внутри MACD.
TakeProfitPips
50
Расстояние тейк-профита в пунктах.
StopLossPips
20
Расстояние стоп-лосса в пунктах.
TrailingActivationPips
40
Прибыль (в пунктах), необходимая для запуска ценового трейлинга.
TrailingDistancePips
40
Расстояние, поддерживаемое ценовым трейлингом.
UseCandleTrailing
true
Использовать трейлинг по экстремумам свечей вместо фиксированного расстояния.
CandleTrailingLength
3
Количество закрытых свечей для расчёта экстремума трейлинга.
CandleTrailingOffsetPips
3
Дополнительный отступ (в пунктах) к цене трейлинга по свечам.
MoveToBreakEven
true
Переводить позицию в безубыток.
BreakEvenActivationPips
30
Прибыль (в пунктах), после которой стоп переносится в безубыток.
BreakEvenOffsetPips
30
Отступ (в пунктах) при переносе стопа в безубыток.
CandleType
15m
Основной поток свечей для сигналов.
MomentumCandleType
15m
Таймфрейм для расчёта Momentum.
MacdCandleType
1d
Старший таймфрейм для MACD.
Особенности использования
Значения таймфреймов подобраны по аналогии с MQL-экспертом: рабочий и моментумный
потоки совпадают, MACD берёт данные со старшего таймфрейма (по умолчанию дневного).
Пересчёт пунктов автоматически учитывает 3/5-знаковые форекс-тикеры, умножая шаг цены
на 10. Для других инструментов используется исходный PriceStep.
Стратегия работает только с закрытыми свечами. Поставщик данных обязан публиковать
состояние свечи (Finished), иначе условия не выполнятся.
В режиме неттинга разворот позиции осуществляется через закрытие противоположного
объёма и открытие новой сделки, что соответствует поведению оригинальной версии.
Отличия от оригинального эксперта
Проверка линии "FIBO" отсутствует из-за невозможности читать графические объекты
MT4. Стратегия всегда работает по тестовой ветке кода.
Параметры управления объёмом (Lots, LotExponent, Max_Trades) заменены одним
свойством TradeVolume, поскольку StockSharp ведёт агрегированную позицию. Масштаб
объёма можно реализовать внешним оптимизатором.
Удалены все уведомления (Alert, SendMail, SendNotification), чтобы стратегия
оставалась самодостаточной внутри StockSharp.
Порт сохраняет ключевые торговые идеи 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()