Эта стратегия реализует скальперскую систему разворота "Genie", изначально написанную на MQL4. Она анализирует последние восемь свечей, чтобы обнаруживать резкие развороты на ключевых уровнях. Длинная позиция открывается, когда семь подряд минимумов понижаются и текущая свеча формирует более высокий минимум и закрывается выше предыдущего максимума. Короткая позиция открывается, когда семь подряд максимумов повышаются и текущая свеча формирует более низкий максимум и закрывается ниже предыдущего минимума.
Стратегия использует фиксированный размер позиции (Strategy.Volume) и применяет как трейлинг-стоп, так и тейк-профит, измеряемые в абсолютных единицах цены. Эти параметры можно оптимизировать, что позволяет захватывать быстрые развороты и защищать накопленную прибыль.
Детали
Условия входа:
Лонг: Low[7] > Low[6] > ... > Low[1] и Low[1] < Low[0] и High[1] < Close[0].
Шорт: High[7] < High[6] < ... < High[1] и High[1] > High[0] и Low[1] > Close[0].
Лонг/Шорт: обе стороны.
Условия выхода:
Срабатывание трейлинг-стопа или тейк-профита.
Стопы:
Тейк-профит: абсолютное расстояние от точки входа.
Трейлинг-стоп: абсолютное расстояние, подтягивающееся по мере движения сделки.
Значения по умолчанию:
TakeProfit = 500.
TrailingStop = 200.
CandleType = 1 минута.
Фильтры:
Категория: Разворот.
Направление: Обе стороны.
Индикаторы: Нет.
Стопы: Да.
Сложность: Простая.
Таймфрейм: Краткосрочный.
Сезонность: Нет.
Нейросети: Нет.
Дивергенция: Нет.
Уровень риска: Средний.
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>
/// Genie pivot reversal strategy.
/// Enters long after a sequence of falling lows followed by an upside breakout.
/// Enters short after a sequence of rising highs followed by a downside breakout.
/// Applies trailing stop and take-profit in absolute price units.
/// </summary>
public class GeniePivotFixedStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly decimal[] _lows = new decimal[8];
private readonly decimal[] _highs = new decimal[8];
private int _stored;
private int _cooldownRemaining;
/// <summary>
/// Take-profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Trailing stop distance in price units.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public GeniePivotFixedStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management")
.SetOptimize(100m, 1000m, 100m);
_trailingStop = Param(nameof(TrailingStop), 200m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management")
.SetOptimize(50m, 500m, 50m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stored = 0;
_cooldownRemaining = 0;
Array.Clear(_lows);
Array.Clear(_highs);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(TrailingStop, UnitTypes.Absolute),
isStopTrailing: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
for (var i = 7; i > 0; i--)
{
_lows[i] = _lows[i - 1];
_highs[i] = _highs[i - 1];
}
_lows[0] = candle.LowPrice;
_highs[0] = candle.HighPrice;
if (_stored < 8)
_stored++;
if (_stored < 5)
return;
if (_cooldownRemaining > 0)
return;
var buySeq = _lows[4] > _lows[3] && _lows[3] > _lows[2] && _lows[2] > _lows[1];
if (buySeq && _lows[1] < _lows[0] && _highs[1] < candle.ClosePrice && candle.ClosePrice > candle.OpenPrice)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
_cooldownRemaining = CooldownBars;
return;
}
var sellSeq = _highs[4] < _highs[3] && _highs[3] < _highs[2] && _highs[2] < _highs[1];
if (sellSeq && _highs[1] > _highs[0] && _lows[1] > candle.ClosePrice && candle.ClosePrice < candle.OpenPrice)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
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 genie_pivot_fixed_strategy(Strategy):
def __init__(self):
super(genie_pivot_fixed_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 500.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management") \
.SetOptimize(100.0, 1000.0, 100.0)
self._trailing_stop = self.Param("TrailingStop", 200.0) \
.SetGreaterThanZero() \
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management") \
.SetOptimize(50.0, 500.0, 50.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management")
self._lows = [0.0] * 8
self._highs = [0.0] * 8
self._stored = 0
self._cooldown_remaining = 0
@property
def take_profit(self):
return self._take_profit.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(genie_pivot_fixed_strategy, self).OnReseted()
self._stored = 0
self._cooldown_remaining = 0
self._lows = [0.0] * 8
self._highs = [0.0] * 8
def OnStarted2(self, time):
super(genie_pivot_fixed_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit), UnitTypes.Absolute),
Unit(float(self.trailing_stop), UnitTypes.Absolute),
True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
# Shift arrays
for i in range(7, 0, -1):
self._lows[i] = self._lows[i - 1]
self._highs[i] = self._highs[i - 1]
self._lows[0] = float(candle.LowPrice)
self._highs[0] = float(candle.HighPrice)
if self._stored < 8:
self._stored += 1
if self._stored < 5:
return
if self._cooldown_remaining > 0:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
# Buy sequence: falling lows [4] > [3] > [2] > [1]
buy_seq = self._lows[4] > self._lows[3] and self._lows[3] > self._lows[2] and self._lows[2] > self._lows[1]
if buy_seq and self._lows[1] < self._lows[0] and self._highs[1] < close and close > open_price:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
return
# Sell sequence: rising highs [4] < [3] < [2] < [1]
sell_seq = self._highs[4] < self._highs[3] and self._highs[3] < self._highs[2] and self._highs[2] < self._highs[1]
if sell_seq and self._highs[1] > self._highs[0] and self._lows[1] > close and close < open_price:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
def CreateClone(self):
return genie_pivot_fixed_strategy()