Pipsover 8167 — это перенос на StockSharp советника MetaTrader 4 Pipsover.mq4 (сборка 8167). Советник ищет мощные всплески осциллятора Chaikin, возникающие сразу после отката предыдущей свечи к простой скользящей средней с периодом 20. При выполнении этих условий открывается позиция по направлению импульса, а защита осуществляется фиксированным стоп-лоссом и тейк-профитом (70 и 140 пунктов в оригинальном MQL-коде). Версия на C# воспроизводит ту же логику, используя высокоуровневые компоненты StockSharp без обращения к низкоуровневым буферам индикаторов.
Для восстановления значений Chaikin применяется линия накопления/распределения (ADL) и две экспоненциальные скользящие средние. Все торговые решения принимаются только после закрытия свечи, что повторяет проверки OrdersTotal() и обращения Close[1] / Open[1] из исходного скрипта.
Индикаторы и сигналы
Простая скользящая средняя (SMA 20) — рассчитывается по закрытиям свечей. Предыдущая свеча должна проколоть SMA (минимум ниже для покупок, максимум выше для продаж) при сохранении тела в сторону сигнала.
Осциллятор Chaikin (EMA 3 – EMA 10 от ADL) — пересчитывается из потока ADL и полностью соответствует значениям iCustom("Chaikin", 0, 0, 1) в MetaTrader. Пороговые уровни задаются в абсолютных единицах осциллятора.
Фильтр по свечному анализу — направление тела предыдущей свечи определяет допустимое направление сделки: бычья свеча разрешает покупки, медвежья — продажи.
Правила торговли
Вход в длинную позицию
Предыдущая свеча закрылась выше открытия (Close[1] > Open[1]).
Минимум предыдущей свечи ушёл ниже значения SMA20 на той же свече.
Значение Chaikin на предыдущей свече ниже -OpenLevel (по умолчанию 55).
Текущая позиция равна нулю.
Вход в короткую позицию
Предыдущая свеча закрылась ниже открытия (Close[1] < Open[1]).
Максимум предыдущей свечи находится выше значения SMA20.
Chaikin на предыдущей свече превышает OpenLevel.
Позиция отсутствует.
Правила выхода
Длинные позиции закрываются, когда следующая свеча имеет медвежье тело, её максимум выше SMA20, а Chaikin превышает CloseLevel (по умолчанию 90).
Короткие позиции закрываются, если следующая свеча бычья, её минимум ниже SMA20, а Chaikin опускается ниже -CloseLevel.
Дополнительно каждая сделка сопровождается стоп-лоссом StopLossPoints и тейк-профитом TakeProfitPoints, рассчитанными в шагах цены выбранного инструмента.
Управление рисками
Дистанция стоп-лосса: StopLossPoints × PriceStep (по умолчанию 70 пунктов).
Дистанция тейк-профита: TakeProfitPoints × PriceStep (по умолчанию 140 пунктов).
Объём позиции задаётся параметром TradeVolume, который напрямую назначается свойству Volume стратегии.
Параметры
Параметр
Значение по умолчанию
Описание
TradeVolume
0.1
Объём рыночных заявок (в лотах или контрактах).
MaLength
20
Период SMA, применяемой для фильтра откатов.
StopLossPoints
70
Дистанция стоп-лосса в шагах цены.
TakeProfitPoints
140
Дистанция тейк-профита в шагах цены.
OpenLevel
55
Абсолютный порог Chaikin для открытия позиций.
CloseLevel
90
Абсолютный порог Chaikin для принудительного выхода.
ChaikinFastLength
3
Период быстрой EMA при расчёте Chaikin.
ChaikinSlowLength
10
Период медленной EMA при расчёте Chaikin.
CandleType
H1
Таймфрейм свечей, используемый в расчётах.
Особенности реализации
Связка свечей и индикаторов выполнена через SubscribeCandles().Bind(...), что полностью соответствует рекомендациям по использованию высокоуровневого API.
Значения Chaikin вычисляются в памяти путём подачи данных ADL на две EMA, поэтому не используются запрещённые операции GetValue().
Данные последней закрытой свечи кэшируются в полях стратегии, чтобы воспроизвести обращения Close[1], Low[1], High[1] и iCustom(...,1) из MQL.
Стоп-лосс и тейк-профит контролируются внутри стратегии, так как оригинальный советник отправлял только рыночные ордера со статическими отступами.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Pipsover8167Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Pipsover8167Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage
class pipsover8167_strategy(Strategy):
def __init__(self):
super(pipsover8167_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown = 0
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.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(pipsover8167_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 6
self._prev_close = close
self._prev_ema = ema_val
def OnReseted(self):
super(pipsover8167_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown = 0
def CreateClone(self):
return pipsover8167_strategy()