Стратегия фиксированного риска представляет собой перенос эксперта MetaTrader 5 Money Fixed Risk.mq5. Оригинальный советник регулярно определяет максимально допустимый объём позиции при ограничении риска фиксированным процентом от капитала, после чего открывает рыночную покупку с симметричными уровнями стоп-лосса и тейк-профита. Реализация на StockSharp повторяет этот алгоритм, используя подписку на тиковые сделки и встроенные средства управления риском.
Стратегия обрабатывает каждый тик выбранного инструмента. Когда внутренний счётчик достигает заданного количества тиков, стратегия переоценивает размер позиции: считывает текущую стоимость портфеля, переводит заданный стоп в пунктах в абсолютное расстояние по цене и вычисляет объём, соответствующий целевому проценту риска. Если вычисленный объём удовлетворяет биржевым ограничениям, отправляется рыночная покупка, а уровни стоп-лосса и тейк-профита фиксируются на равном расстоянии от цены входа. В дальнейшем на каждом тике контролируется достижение этих уровней и позиция закрывается при первом срабатывании.
Требования к данным
Необходим поток тиковых сделок (SubscribeTrades()), поскольку именно по тикам ведётся счётчик.
Для корректного расчёта объёма должны быть заданы свойства инструмента: PriceStep, StepPrice, VolumeStep, MinVolume и при необходимости MaxVolume.
Логика работы
Подписаться на сделки по инструменту.
На каждом тике увеличивать счётчик.
При достижении счётчиком значения Ticks Interval выполнить пересчёт:
Определить размер пункта на основе PriceStep и количества знаков (Decimals). Для котировок с 3 или 5 знаками используется множитель 10.
Перевести стоп-лосс из пунктов в абсолютное расстояние по цене.
Получить величину капитала портфеля (CurrentValue, затем CurrentBalance, затем BeginValue).
Рассчитать денежный риск на один контракт и максимально допустимый объём с учётом заданного процента риска.
Привести объём к шагу торгов и ограничениям MinVolume/MaxVolume.
Если нормализованный объём положительный, закрыть возможный шорт и открыть длинную позицию рассчитанного размера.
Запомнить уровни стоп-лосса и тейк-профита и следить за их достижением по каждому тику.
Параметры
Stop Loss (pips) – величина стоп-лосса в пунктах (тейк-профит размещается на таком же расстоянии).
Risk % – доля капитала, которой стратегия готова рискнуть в одной сделке.
Ticks Interval – количество тиков между очередными расчётами позиции и попытками входа.
Все параметры проходят проверку на положительность и доступны для оптимизации.
Особенности управления риском
Риск на сделку = Equity * (Risk % / 100).
Расстояние стопа = Stop Loss (pips) * pip size, где pip size = PriceStep * 10 для инструментов с 3/5 знаками после запятой и PriceStep для остальных.
Денежный риск на контракт = (stop distance / PriceStep) * StepPrice.
Объём = risk amount / risk per contract, округляется вниз до ближайшего VolumeStep и ограничивается MinVolume/MaxVolume. Если результат меньше минимального объёма, сделка пропускается.
Отличия от оригинала
Полностью реализована на StockSharp и не требует библиотек MetaTrader.
Использует StartProtection() для подключения стандартных защитных механизмов платформы.
Данные об equity берутся из объекта портфеля стратегии, а не из MQL-классов.
Выход из позиции контролируется по тикам, поэтому для демонстрации не регистрируются отдельные стоп-заявки.
Рекомендации по использованию
Как и исходный эксперт, реализация открывает только длинные позиции. Для добавления шортов расширьте метод ProcessTrade.
При тестировании необходимо использовать тиковые данные достаточной плотности, иначе счётчик может не достигнуть порога.
Перед запуском на реальном рынке убедитесь, что шаг цены и допустимые объёмы инструмента настроены корректно.
Код не создаёт дополнительных коллекций, а хранит состояние во внутренних полях, что соответствует рекомендациям по конверсии.
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Recreates the Money Fixed Risk expert advisor using StockSharp's high level API.
/// Uses ATR to determine position sizing via risk percentage and opens long positions
/// with symmetric stop-loss and take-profit levels based on ATR.
/// </summary>
public class MoneyFixedRiskStrategy : Strategy
{
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _candleInterval;
private readonly StrategyParam<DataType> _candleType;
private int _candleCounter;
private decimal _stopPrice;
private decimal _takeProfitPrice;
private decimal _entryPrice;
/// <summary>
/// ATR multiplier for stop distance.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// ATR calculation period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Number of candles between position evaluations.
/// </summary>
public int CandleInterval
{
get => _candleInterval.Value;
set => _candleInterval.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MoneyFixedRiskStrategy"/>.
/// </summary>
public MoneyFixedRiskStrategy()
{
_atrMultiplier = Param(nameof(AtrMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for ATR-based stop distance", "Risk")
.SetOptimize(0.5m, 3m, 0.5m);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR calculation", "Indicators")
.SetOptimize(7, 28, 7);
_candleInterval = Param(nameof(CandleInterval), 10)
.SetGreaterThanZero()
.SetDisplay("Candle Interval", "Candles between position evaluations", "General")
.SetOptimize(5, 30, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for processing", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candleCounter = 0;
_stopPrice = 0m;
_takeProfitPrice = 0m;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
// Manage existing long position
if (Position > 0 && _stopPrice > 0m)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takeProfitPrice)
{
SellMarket(Position);
_stopPrice = 0m;
_takeProfitPrice = 0m;
_entryPrice = 0m;
}
}
// Manage existing short position
if (Position < 0 && _stopPrice > 0m)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takeProfitPrice)
{
BuyMarket(Math.Abs(Position));
_stopPrice = 0m;
_takeProfitPrice = 0m;
_entryPrice = 0m;
}
}
_candleCounter++;
if (_candleCounter < CandleInterval)
return;
_candleCounter = 0;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
return;
if (atrValue <= 0m)
return;
var stopDistance = atrValue * AtrMultiplier;
// Alternate between long and short based on price relative to previous entry
var goLong = _entryPrice == 0m || price > _entryPrice;
if (goLong)
{
BuyMarket(Volume);
_entryPrice = price;
_stopPrice = price - stopDistance;
_takeProfitPrice = price + stopDistance;
}
else
{
SellMarket(Volume);
_entryPrice = price;
_stopPrice = price + stopDistance;
_takeProfitPrice = price - stopDistance;
}
}
}