HPCS Inter5 Strategy — это одноразовая импульсная стратегия, перенесённая из MetaTrader 4 (скрипт _HPCS_Inter5_MT4_EA_V01_WE). После запуска стратегия анализирует последние завершённые свечи и, если закрытие пятой свечи назад выше ближайшего закрытия, отправляет один рыночный ордер на покупку. Опциональные уровни стоп-лосса и тейк-профита рассчитываются в «пунктах» MetaTrader, повторяя оригинальную реализацию.
Логика работы
Подписаться на выбранный тип свечей и хранить шесть последних закрытий.
После заполнения буфера сравнить закрытие пятой свечи назад с последним закрытием (Close[5] > Close[1]).
Если условие выполняется и сделка ещё не совершена, отправить рыночный ордер на покупку с заданным объёмом.
В момент старта вызвать StartProtection, чтобы активировать защитные приказы. Размер пункта рассчитывается так же, как в MetaTrader: для инструментов с 3 или 5 знаками после запятой используется PriceStep * 10, иначе берётся PriceStep без изменения.
После открытия позиции стратегия игнорирует все новые сигналы и не совершает дополнительных сделок.
Параметры
Имя
Значение по умолчанию
Описание
Candle Type
Таймфрейм 1 минута
Тип свечей, по которым собираются цены закрытия. Настройте в соответствии с нужным горизонтом анализа.
Stop Loss (pips)
10
Расстояние защитного стоп-лосса в пунктах MetaTrader. Значение 0 отключает стоп.
Take Profit (pips)
10
Расстояние защитного тейк-профита в пунктах MetaTrader. Значение 0 отключает тейк-профит.
Trade Volume
1
Объём рыночного ордера при срабатывании сигнала.
Особенности реализации
Для расчёта защитных уровней необходимо корректно заполнить Security.PriceStep (или Security.Step). При его отсутствии защитные отступы не активируются, но вход по сигналу остаётся рабочим.
Обрабатываются только завершённые свечи (CandleStates.Finished), что соответствует логике MetaTrader с использованием Close[1] и более старых значений.
Внутренний буфер содержит ровно шесть закрытий без использования индикаторов, что подчёркивает минималистичность исходного скрипта.
Перед отправкой ордера вызывается IsFormedAndOnlineAndAllowTrading(), чтобы убедиться в готовности соединения и разрешениях на торговлю.
Рекомендации по использованию
Выберите валютную пару с корректно настроенными шагом цены и объёмами.
Настройте Candle Type под нужный таймфрейм.
Установите 0 в параметрах стопа или тейк-профита, если планируете управлять выходом вручную.
Перезапускайте стратегию каждый раз, когда хотите повторно оценить условие входа: сигнал исполняется только один раз за запуск.
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Single-shot momentum strategy converted from the "_HPCS_Inter5" MetaTrader script.
/// Places one market buy when the close price from five bars ago exceeds the most recent close.
/// </summary>
public class HpcsInter5Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly decimal?[] _recentCloses = new decimal?[6];
private decimal _pipSize;
private bool _wasLongSignal;
private bool _hasSignal;
/// <summary>
/// Candle type used to evaluate the closing prices.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in MetaTrader-style pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance expressed in MetaTrader-style pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trade volume submitted with the market entry.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="HpcsInter5Strategy"/> class.
/// </summary>
public HpcsInter5Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
.SetDisplay("Candle Type", "Candle type used for the close comparison", "General");
_stopLossPips = Param(nameof(StopLossPips), 20)
.SetDisplay("Stop Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management");
_takeProfitPips = Param(nameof(TakeProfitPips), 20)
.SetDisplay("Take Profit (pips)", "Take-profit distance expressed in pips", "Risk Management");
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Volume submitted with the market entry", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Array.Clear(_recentCloses, 0, _recentCloses.Length);
_pipSize = 0m;
_wasLongSignal = false;
_hasSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
if (Security is null)
throw new InvalidOperationException("Security must be assigned before starting the strategy.");
base.OnStarted2(time);
InitializePipSize();
Volume = TradeVolume;
_wasLongSignal = false;
_hasSignal = false;
var stopLoss = StopLossPips > 0 && _pipSize > 0m
? new Unit(StopLossPips * _pipSize, UnitTypes.Absolute)
: null;
var takeProfit = TakeProfitPips > 0 && _pipSize > 0m
? new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute)
: null;
if (stopLoss != null || takeProfit != null)
StartProtection(stopLoss: stopLoss, takeProfit: takeProfit);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
ShiftCloses(candle.ClosePrice);
if (_recentCloses[1] is not decimal lastClose || _recentCloses[5] is not decimal olderClose)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var threshold = lastClose * 0.005m;
var longSignal = olderClose - lastClose > threshold;
var shortSignal = lastClose - olderClose > threshold;
var crossedLong = longSignal && (!_hasSignal || !_wasLongSignal);
var crossedShort = shortSignal && (!_hasSignal || _wasLongSignal);
if (crossedLong && Position <= 0)
{
BuyMarket();
}
else if (crossedShort && Position >= 0)
{
SellMarket();
}
if (longSignal || shortSignal)
{
_wasLongSignal = longSignal;
_hasSignal = true;
}
}
private void ShiftCloses(decimal close)
{
for (var i = _recentCloses.Length - 1; i > 0; i--)
_recentCloses[i] = _recentCloses[i - 1];
_recentCloses[0] = close;
}
private void InitializePipSize()
{
var step = Security.PriceStep ?? 0.01m;
if (step <= 0m)
step = 0.01m;
var pipFactor = Security.Decimals is 3 or 5 ? 10m : 1m;
_pipSize = step * pipFactor;
}
}
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.Strategies import Strategy
class hpcs_inter5_strategy(Strategy):
"""
HPCS Inter5: momentum strategy comparing close from 5 bars ago
vs current close. Buys when older close exceeds recent by threshold.
Sells on reverse. Uses StartProtection for SL/TP.
"""
def __init__(self):
super(hpcs_inter5_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 20) \
.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk Management")
self._take_profit_pips = self.Param("TakeProfitPips", 20) \
.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(120))) \
.SetDisplay("Candle Type", "Candle type for close comparison", "General")
self._recent_closes = [None] * 6
self._pip_size = 0.0
self._was_long_signal = False
self._has_signal = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hpcs_inter5_strategy, self).OnReseted()
self._recent_closes = [None] * 6
self._pip_size = 0.0
self._was_long_signal = False
self._has_signal = False
def OnStarted2(self, time):
super(hpcs_inter5_strategy, self).OnStarted2(time)
step = 0.01
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 0.01
decimals = 0
if self.Security is not None and self.Security.Decimals is not None:
decimals = int(self.Security.Decimals)
pip_factor = 10.0 if decimals in (3, 5) else 1.0
self._pip_size = step * pip_factor
sl_pips = self._stop_loss_pips.Value
tp_pips = self._take_profit_pips.Value
sl = None
tp = None
if sl_pips > 0 and self._pip_size > 0:
sl = Unit(sl_pips * self._pip_size, UnitTypes.Absolute)
if tp_pips > 0 and self._pip_size > 0:
tp = Unit(tp_pips * self._pip_size, UnitTypes.Absolute)
if sl is not None or tp is not None:
self.StartProtection(tp, sl)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
for i in range(len(self._recent_closes) - 1, 0, -1):
self._recent_closes[i] = self._recent_closes[i - 1]
self._recent_closes[0] = close
last_close = self._recent_closes[1]
older_close = self._recent_closes[5]
if last_close is None or older_close is None:
return
threshold = last_close * 0.005
long_signal = older_close - last_close > threshold
short_signal = last_close - older_close > threshold
crossed_long = long_signal and (not self._has_signal or not self._was_long_signal)
crossed_short = short_signal and (not self._has_signal or self._was_long_signal)
if crossed_long and self.Position <= 0:
self.BuyMarket()
elif crossed_short and self.Position >= 0:
self.SellMarket()
if long_signal or short_signal:
self._was_long_signal = long_signal
self._has_signal = True
def CreateClone(self):
return hpcs_inter5_strategy()