MACD Power — многофреймовая импульсная стратегия, перенесённая из оригинального советника MetaTrader. Логика использует пару линейно взвешенных средних (LWMA) по типичной цене свечи, два варианта MACD, фильтр Momentum на старшем таймфрейме и месячный MACD в роли долгосрочного фильтра. Сделки открываются, когда импульс и старший тренд показывают согласованное движение.
Основные элементы
LWMA по типичной цене. Быстрая и медленная LWMA рассчитываются по формуле ((High + Low + Close) / 3). Сигналы рассматриваются только тогда, когда быстрая средняя находится ниже медленной (для покупок/продаж), как и в исходном советнике, который ждал отката в пределах доминирующего тренда.
Двойной MACD. Два индикатора MACD с параметрами (12, 26, 1) и (6, 13, 1) должны одновременно быть выше нуля для лонга и ниже нуля для шорта. Эти значения соответствуют условиям MacdMAIN1 и MacdMAIN2 из MQL и отражают краткосрочное ускорение цены.
Фильтр Momentum. Индикатор Momentum (период 14) рассчитывается на более высоком таймфрейме, автоматически подбираемом относительно основного (например, для 15‑минутного графика берётся часовой Momentum). Анализируется абсолютное отклонение от уровня 100 за три последние точки; хотя бы одно из значений должно превышать порог MomentumBuyThreshold/MomentumSellThreshold.
Месячный MACD. MACD с параметрами (12, 26, 9) на месячных свечах должен иметь основную линию выше сигнальной для лонга и ниже сигнальной для шорта. Это позволяет торговать только в сторону долгосрочного тренда.
Управление позицией
Размер позиции определяется параметром OrderVolume. При развороте система автоматически добавляет объём встречной позиции, чтобы сменить направление одной рыночной заявкой.
Тейк‑профит и стоп‑лосс задаются в пунктах инструмента и переводятся в цену через Security.PriceStep (с защитным значением 1, если шаг цены не задан).
Трейлинг‑стоп активируется после достижения прибыли, заданной в TrailingActivationPoints, и сопровождает экстремум цены с отступом TrailingOffsetPoints.
Безубыток. При достижении BreakEvenTriggerPoints уровень защиты переносится в точку входа с учётом отступа BreakEvenOffsetPoints. Возврат цены к этому уровню закрывает позицию.
Лимит сделок. Параметр MaxTrades ограничивает количество новых входов в рамках одного запуска стратегии.
Параметры
Параметр
Описание
Значение по умолчанию
CandleType
Основной таймфрейм стратегии.
Свечи 15 минут
FastMaLength
Период быстрой LWMA (типичная цена).
6
SlowMaLength
Период медленной LWMA (типичная цена).
85
MomentumLength
Длина окна Momentum на старшем таймфрейме.
14
MomentumBuyThreshold
Минимальное отклонение Momentum от 100 для покупок.
0.3
MomentumSellThreshold
Минимальное отклонение Momentum от 100 для продаж.
0.3
TakeProfitPoints
Тейк‑профит в пунктах.
50
StopLossPoints
Стоп‑лосс в пунктах.
20
TrailingActivationPoints
Прибыль (в пунктах), после которой включается трейлинг.
40
TrailingOffsetPoints
Отступ трейлинг‑стопа от экстремума.
40
BreakEvenTriggerPoints
Прибыль (в пунктах), активирующая защиту безубытком.
30
BreakEvenOffsetPoints
Отступ при переносе стопа в безубыток.
30
MaxTrades
Максимальное число сделок за запуск.
10
OrderVolume
Базовый объём сделки.
1
Отличия от версии MQL
Используется высокоуровневый API StockSharp (SubscribeCandles, Bind, BindEx), поэтому расчёты выполняются по завершённым свечам без ручного опроса тиков.
Денежный трейлинг и контроль просадки счёта из MQL не переносились: в экосистеме StockSharp эти задачи обычно решаются внешними модулями риск‑менеджмента. Оставлены пунктовые стопы, трейлинг и безубыток, соответствующие базовой логике эксперта.
Сообщения, почтовые уведомления и вспомогательные функции модификации ордеров убраны — стратегия работает только через рыночные заявки API.
Рекомендации по использованию
Подберите основной таймфрейм через CandleType. Вспомогательный таймфрейм для Momentum и месячный MACD подбираются автоматически.
Настройте значения TakeProfitPoints, StopLossPoints и других риск‑параметров под шаг цены конкретного инструмента.
При тестировании контролируйте лимит MaxTrades. Увеличьте его, если требуется разрешить множественные последовательные входы.
Для визуального анализа включите отображение свечей и LWMA на графике — стратегия рисует их автоматически при наличии области графика.
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 MacdPowerStrategy : 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 MacdPowerStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).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 macd_power_strategy(Strategy):
"""
MACD Power: EMA crossover with manual SL/TP via price steps.
"""
def __init__(self):
super(macd_power_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50).SetDisplay("Slow Period", "Slow EMA 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._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_power_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(macd_power_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if not self._fast_ind.IsFormed or not self._slow_ind.IsFormed:
self._prev_fast = fast
self._prev_slow = slow
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast
self._prev_slow = slow
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.Value > 0 and close <= self._entry_price - self._stop_loss_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close >= self._entry_price + self._take_profit_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close >= self._entry_price + self._stop_loss_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close <= self._entry_price - self._take_profit_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow 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 < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return macd_power_strategy()