Это порт советника MetaTrader 4 up3x1_Investor. Стратегия торгует одним инструментом по закрытым свечам выбранного таймфрейма (по умолчанию H1). В реализации для StockSharp сохранены все ключевые правила оригинала и добавлены наглядные параметры управления рисками.
Логика входов
Анализируется последняя завершённая свеча, и проверяется:
Размах свечи (High − Low) больше 0.0060 ценовых единиц.
Тело свечи (|Open − Close|) больше 0.0050 ценовых единиц.
Если свеча закрылась ростом и выполняются оба условия, открывается рыночная покупка.
Если свеча закрылась падением и выполняются оба условия, открывается рыночная продажа.
Торговля полностью отключается по понедельникам (аналог условия DayOfWeek()==1 в MQL).
Сопровождение позиции
При входе рассчитываются внутренние уровни:
TakeProfitPoints — расстояние до целевой прибыли.
StopLossPoints — защитный стоп.
TrailingStopPoints — шаг для последующего подтягивания стопа.
На каждой завершённой свече проверяется:
Достигнута ли цель — позиция закрывается по тейк-профиту.
Сработал ли стоп — позиция закрывается для ограничения убытка.
Продвинулся ли рынок дальше trailing-расстояния — стоп подтягивается ближе к цене.
Дополнительно позиция закрывается, если простые средние за 24 и 60 периодов совпали (с точностью до одного шага цены). Это повторяет поведение iMA из исходника.
Управление объёмом и риском
BaseVolume — базовый объём сделки, если не удаётся рассчитать его от капитала.
MaximumRisk повторяет формулу AccountFreeMargin()*MaximumRisk/1000. При наличии стоимости портфеля объём = value * MaximumRisk / 1000, округление до одной десятой.
DecreaseFactor уменьшает объём после серии убытков: при более чем одном подряд убытке объём сокращается пропорционально losses / DecreaseFactor.
MinimumVolume ограничивает объём снизу (минимум 0.1 лота, как в MQL).
Параметры
Имя
Значение по умолчанию
Назначение
BaseVolume
0.1
Базовый размер позиции.
MaximumRisk
0.2
Коэффициент риска для расчёта объёма от капитала.
DecreaseFactor
3
Множитель уменьшения объёма после убыточных сделок.
MinimumVolume
0.1
Минимально допустимый объём.
TakeProfitPoints
20
Расстояние до тейк-профита в шагах цены.
StopLossPoints
50
Расстояние до стоп-лосса в шагах цены.
TrailingStopPoints
10
Расстояние для трейлинг-стопа.
SkipMondays
true
Запрет на торговлю по понедельникам.
CandleType
1 час
Таймфрейм свечей.
Дополнительно
Одновременно может быть открыта только одна позиция, что соответствует функции CalculateCurrentOrders в MQL.
Счётчик серий убытков ведётся внутри стратегии, так как StockSharp не предоставляет историю сделок MetaTrader.
Используются только рыночные заявки (BuyMarket и SellMarket).
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1InvestorRangeFilterStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
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 int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1InvestorRangeFilterStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR 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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class up3x1_investor_range_filter_strategy(Strategy):
def __init__(self):
super(up3x1_investor_range_filter_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).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 ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(up3x1_investor_range_filter_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(up3x1_investor_range_filter_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): 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 CreateClone(self): return up3x1_investor_range_filter_strategy()