Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия Parallax Sell
Обзор
Parallax Sell — это конвертированная в StockSharp короткая маржинальная стратегия из советника MetaTrader parallax_sell. Исходный робот торговал кроссы с JPY (CAD/JPY и CHF/JPY) и использовал сочетание индикаторов Williams %R, MACD и стохастика для поиска перекупленных участков рынка. Выходы строятся на признаках ослабления импульса по Williams %R или медленному стохастику, а схема управления объёмом с элементами мартингейла увеличивает позицию после убыточных серий.
Логика входа
Работает на настраиваемом таймфрейме (по умолчанию свечи 1 час).
Ожидает закрытия новой свечи.
Требует, чтобы Williams %R с периодом 350 поднялся выше порога перекупленности (по умолчанию -10).
Требует, чтобы основная линия MACD (12/120/9) находилась выше заданного порога (по умолчанию 0.178), что подтверждает восходящий импульс перед продажей.
Отслеживает пересечение сверху вниз быстрой линии стохастика %K (период 10, замедление 3) ниже входного уровня (по умолчанию 90). Только такое пересечение инициирует новую короткую позицию.
Каждый сигнал открывает дополнительный рыночный ордер на продажу, что позволяет накапливать несколько коротких сделок в соответствии с логикой мартингейла.
Логика выхода
Рассчитывает плавающую прибыль всех открытых шортов в пунктах относительно размера пункта инструмента.
Если открыт только один шорт и средняя прибыль превышает целевой уровень одиночной сделки (по умолчанию 10 пунктов) и Williams %R падает ниже порога выхода (по умолчанию -80), позиция закрывается.
Если открыто более одной короткой позиции и средняя прибыль корзины превышает целевой уровень (по умолчанию 15 пунктов) и медленный стохастик %K (период 90, замедление 1) падает ниже триггера перепроданности (по умолчанию 12), закрывается вся корзина.
Дополнительно используется страховочный тейк-профит: корзина закрывается, когда средняя прибыль достигает заданного расстояния (по умолчанию 100 пунктов).
Управление объёмом
Стартовый объём — базовый лот (по умолчанию 0.01).
После прибыльного цикла (рост реализованной прибыли) следующий ордер возвращается к базовому объёму.
После убыточного цикла (снижение реализованной прибыли) объём следующего ордера умножается на коэффициент мартингейла (по умолчанию 1.6). Значения автоматически приводятся к шагу объёма инструмента.
Риск-менеджмент
Регистрируется защитный тейк-профит на заданном расстоянии в пунктах. Жёсткий стоп-лосс не используется — выходы управляются индикаторами.
Защита (StartProtection) запускается один раз, в соответствии с требованиями к конвертации.
Параметры
Имя
Описание
Значение по умолчанию
CandleType
Таймфрейм расчётов.
Свечи 1 часа
EntryWilliamsLength
Период Williams %R для входов.
350
ExitWilliamsLength
Период Williams %R для выходов.
350
EntryStochasticLength / Signal / Slowing
Параметры быстрого стохастика для входа.
10 / 1 / 3
ExitStochasticLength / Signal / Slowing
Параметры медленного стохастика для выхода.
90 / 7 / 1
MacdFastLength / MacdSlowLength / MacdSignalLength
Настройки MACD.
12 / 120 / 9
EntryWilliamsThreshold
Минимальное значение Williams %R перед открытием шорта.
-10
ExitWilliamsThreshold
Значение Williams %R, подтверждающее выход из одиночного шорта.
-80
EntryStochasticTrigger
Уровень, ниже которого должна упасть %K для входа.
90
ExitStochasticTrigger
Уровень, ниже которого должен опуститься медленный стохастик для выхода.
12
MacdThreshold
Минимальное значение основной линии MACD.
0.178
SingleTradeTargetPips
Цель по прибыли (пункты) для одной позиции.
10
MultiTradeTargetPips
Цель по прибыли (пункты) для корзины.
15
TakeProfitPips
Жёсткий тейк-профит (пункты).
100
InitialVolume
Базовый объём сделки.
0.01
MartingaleMultiplier
Множитель объёма после убытка.
1.6
UseMartingale
Включение логики мартингейла.
true
Примечания
Стратегия работает только в короткую сторону и предполагает форексную трактовку пунктов.
Средняя прибыль вычисляется как среднее арифметическое по входным ценам, как и в исходном советнике.
Для снижения риска на волатильных парах измените пороги или отключите мартингейл (UseMartingale = false).
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ParallaxSellStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ParallaxSellStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class parallax_sell_strategy(Strategy):
def __init__(self):
super(parallax_sell_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(parallax_sell_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(parallax_sell_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return parallax_sell_strategy()