Эта стратегия представляет собой портирование эксперта MetaTrader 5 «SimpleTrade (barabashkakvn's edition)» на платформу StockSharp. Алгоритм сравнивает цену открытия текущей свечи с ценой открытия свечи три бара назад: если текущая цена открытия выше, открывается длинная позиция, иначе — короткая. Каждая сделка удерживается ровно одну завершённую свечу и защищается фиксированным стоп-лоссом, заданным в пунктах.
Реализация использует высокоуровневый API StockSharp: стратегия подписывается на выбранный поток свечей и обрабатывает только свечи в состоянии Finished, чтобы принимать решения на основе полностью сформированных данных. Позиция закрывается при смене свечи либо раньше, если цена касается стоп-уровня внутри бара.
Логика торговли
Вход
После закрытия каждой свечи стратегия сохраняет её цену открытия и поддерживает скользящую историю из четырёх значений.
Когда позиций нет и доступно минимум четыре цены открытия, текущая цена сравнивается со значением трёх свечей назад.
Если текущая цена открытия выше, совершается покупка; в противном случае выполняется продажа.
Выход
Для каждой сделки рассчитывается стоп-уровень как произведение StopLossPips × размер пункта и смещение от цены открытия входной свечи.
На следующем баре позиция закрывается независимо от результата, что соответствует оригинальному советнику, который не держит сделку дольше одной свечи.
Если максимум (для шорта) или минимум (для лонга) текущей свечи пересекает стоп-уровень, стратегия пытается немедленно закрыть позицию по рынку.
Параметры
Параметр
Значение по умолчанию
Описание
StopLossPips
120
Расстояние до стоп-уровня в пунктах. Код воспроизводит поведение MetaTrader: для инструментов с 3 или 5 знаками после запятой шаг цены умножается на 10, чтобы получить стандартный размер пункта.
TradeVolume
1
Объём рыночной заявки. Настройте параметр в соответствии с размером контракта выбранного инструмента.
CandleType
Таймфрейм 1 час
Тип свечей, на которые подписывается стратегия. Выберите таймфрейм, соответствующий используемому в MetaTrader.
Все параметры представлены через StrategyParam<T>, поэтому их можно настраивать в интерфейсе и использовать в оптимизации.
Особенности реализации
История цен открытия хранится в наборе полей без дополнительных коллекций, что соответствует правилам репозитория.
Стоп-ордера не отправляются как отдельные заявки: стратегия анализирует диапазон свечи и при пробое уровня выставляет рыночную заявку на закрытие.
Поскольку обновление позиции в StockSharp происходит асинхронно, стратегия сначала закрывает текущую сделку, а затем переходит к поиску новой точки входа, что повторяет последовательность «сначала закрыть, потом открыть» из оригинального эксперта и исключает пересечение заявок.
Размер пункта вычисляется на основе Security.PriceStep. Для инструментов с 3 или 5 десятичными знаками шаг дополнительно умножается на 10, чтобы соответствовать определению pip в MetaTrader.
Рекомендации по применению
Используйте стратегию на инструментах с стабильным шагом цены, например на основных валютных парах, где понятие пункта имеет практический смысл.
Подбирайте значение StopLossPips для каждого инструмента: большие значения обеспечивают больший буфер, малые — повышают чувствительность к внутридневным колебаниям.
Убедитесь, что подключение к брокеру передаёт свечи в состоянии Finished, чтобы стратегия получала корректные цены открытия.
Риски и ограничения
Поскольку сделки удерживаются всего одну свечу, результат сильно зависит от выбранного таймфрейма. Рекомендуется тестировать разные периоды.
Эмуляция исполнения стопа по экстремумам свечи может отличаться от фактического исполнения стоп-заявок в условиях высокой волатильности.
После формирования стартовой истории стратегия почти постоянно находится в рынке (лонг или шорт), что может приводить к частой смене позиций во флэте.
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>
/// Simple trade strategy converted from MetaTrader that compares the current open with the open three bars ago.
/// The logic holds positions for a single bar and protects the trade with a fixed stop distance.
/// </summary>
public class SimpleTradeStrategy : Strategy
{
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _openCurrent;
private decimal _openMinus1;
private decimal _openMinus2;
private decimal _openMinus3;
private int _historyCount;
private decimal? _stopPrice;
/// <summary>
/// Stop loss distance expressed in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Trade volume used for market orders.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="SimpleTradeStrategy"/> parameters.
/// </summary>
public SimpleTradeStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 120)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pips)", "Fixed protective distance in pips", "Risk Management")
;
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume in lots", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle source for decisions", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_openCurrent = 0m;
_openMinus1 = 0m;
_openMinus2 = 0m;
_openMinus3 = 0m;
_historyCount = 0;
_stopPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Exit existing trades before evaluating new entries to mimic the original MQL behaviour.
if (TryCloseExistingPosition(candle))
return;
UpdateHistory(candle.OpenPrice);
// Need at least four opens to compare with the value three bars ago.
if (_historyCount < 4)
return;
ExecuteEntry(candle);
}
private bool TryCloseExistingPosition(ICandleMessage candle)
{
if (Position > 0)
{
var volume = Position;
// Close long trades at the protective stop or at the bar change.
if (_stopPrice is decimal stop && candle.LowPrice <= stop)
{
SellMarket();
}
else
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
}
_stopPrice = null;
return true;
}
if (Position < 0)
{
var volume = Math.Abs(Position);
// Close short trades at the protective stop or at the bar change.
if (_stopPrice is decimal stop && candle.HighPrice >= stop)
{
BuyMarket();
}
else
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
}
_stopPrice = null;
return true;
}
return false;
}
private void UpdateHistory(decimal currentOpen)
{
// Shift the stored opens so that _openMinus3 keeps the value three candles back.
_openMinus3 = _openMinus2;
_openMinus2 = _openMinus1;
_openMinus1 = _openCurrent;
_openCurrent = currentOpen;
if (_historyCount < 4)
_historyCount++;
}
private void ExecuteEntry(ICandleMessage candle)
{
var pipSize = CalculatePipSize();
var stopOffset = pipSize * StopLossPips;
// Enter long when the current open is above the open three bars ago, otherwise enter short.
if (_openCurrent > _openMinus3)
{
BuyMarket();
_stopPrice = candle.OpenPrice - stopOffset;
}
else
{
SellMarket();
_stopPrice = candle.OpenPrice + stopOffset;
}
}
private decimal CalculatePipSize()
{
// Reproduce the MetaTrader adjustment: multiply by ten for symbols with 3 or 5 decimals.
var step = Security?.PriceStep ?? 1m;
var decimals = Security?.Decimals;
if (decimals == 3 || decimals == 5)
step *= 10m;
return step;
}
}
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
class simple_trade_strategy(Strategy):
def __init__(self):
super(simple_trade_strategy, self).__init__()
self._sl_pips = self.Param("StopLossPips", 120)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(simple_trade_strategy, self).OnReseted()
self._opens = []
self._stop_price = None
def OnStarted2(self, time):
super(simple_trade_strategy, self).OnStarted2(time)
self._opens = []
self._stop_price = None
self._pip_size = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
self._pip_size = float(self.Security.PriceStep)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
# Close existing position first (hold only 1 bar)
if self.Position > 0:
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
else:
self.SellMarket()
self._stop_price = None
self._update_opens(float(candle.OpenPrice))
return
elif self.Position < 0:
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
else:
self.BuyMarket()
self._stop_price = None
self._update_opens(float(candle.OpenPrice))
return
self._update_opens(float(candle.OpenPrice))
if len(self._opens) < 4:
return
open_current = self._opens[-1]
open_minus3 = self._opens[-4]
stop_offset = self._pip_size * self._sl_pips.Value
if open_current > open_minus3:
self.BuyMarket()
self._stop_price = float(candle.OpenPrice) - stop_offset
else:
self.SellMarket()
self._stop_price = float(candle.OpenPrice) + stop_offset
def _update_opens(self, val):
self._opens.append(val)
if len(self._opens) > 5:
self._opens.pop(0)
def CreateClone(self):
return simple_trade_strategy()