Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия RangeEA Weekly Grid
Обзор
RangeEA Weekly Grid — это сеточная стратегия с отложенными лимитными заявками, портированная из оригинального эксперт-советника MetaTrader.
Алгоритм определяет актуальный недельный ценовой диапазон и распределяет внутри него заданное количество отложенных заявок.
Для каждой заявки рассчитываются динамические уровни стоп-лосса и тейк-профита: они масштабируются относительно расстояния до
текущей цены и дополнительно ограничиваются минимальными значениями в пунктах. При достижении целевой доходности стратегия
закрывает все позиции и заявки, фиксируя прибыль.
Реализация опирается на высокоуровневый API StockSharp: логика запускается по свечам, управление заявками осуществляется через
встроенные методы стратегии, а параметры риска доступны для оптимизации.
Логика торговли
Подписка на два потока свечей:
Пользовательский таймфрейм (по умолчанию 1 час) для обслуживания сетки.
Недельные свечи для оценки диапазона.
После закрытия каждой недельной свечи обновляются максимум и минимум за последние две недели. Разница между ними задаёт
рабочий диапазон.
На закрытии каждой рабочей свечи:
Проверяется торговое окно (StartTradeHour — EndTradeHour).
При необходимости сетка сбрасывается в начале нового торгового дня.
При отсутствии активных лимитных заявок формируется новая сетка, равномерно покрывающая диапазон.
Если уже исполнено минимум две заявки, то при сокращении сетки до NumberOfOrders - 2 заявок возобновляется заявка по цене
предпоследнего исполнения.
Контролируется прирост капитала, и при достижении целевого процента выполняется полная ликвидация позиций.
По окончании торгового окна и при включённом CloseAllAtEndTrade стратегия отменяет все отложенные заявки и закрывает
открытые позиции.
Параметры
Имя
Описание
Значение по умолчанию
CandleType
Таймфрейм для управления сеткой.
Часовые свечи
WeeklyCandleType
Таймфрейм для расчёта диапазона.
Недельные свечи
StartTradeHour
Час начала выставления заявок.
0
EndTradeHour
Час завершения торговли.
24
CloseAllAtEndTrade
Закрывать позиции и заявки вне торгового окна.
true
MaxOpenOrders
Максимум одновременно открытых заявок и позиций.
5
NumberOfOrders
Количество лимитных заявок в сетке.
10
OrderVolume
Объём каждой заявки.
0.01
ResetOrdersDaily
Перестраивать сетку в начале дня.
true
StopLossPoints
Минимальный стоп-лосс в пунктах.
60
TakeProfitPoints
Минимальный тейк-профит в пунктах.
60
StopLossMultiplier
Множитель динамического стоп-лосса.
3
TakeProfitMultiplier
Множитель динамического тейк-профита.
1
TargetPercentage
Рост капитала, после которого выполняется фиксация.
8
Управление рисками
Ограничение MaxOpenOrders удерживает число заявок и позиций под контролем.
Стоп-лосс и тейк-профит всегда находятся не ближе минимального числа пунктов и могут быть увеличены множителями.
Ежедневный сброс не позволяет переносить устаревшие заявки на следующий день.
Целевой процент прибыли фиксирует накопленный результат и предотвращает переразмеренный риск.
Примечания
Необходимо, чтобы по инструменту поставлялись недельные свечи, иначе диапазон не будет рассчитан.
Для инструментов с нестандартным шагом цены следует скорректировать параметры в пунктах под фактический тик.
Оптимизация NumberOfOrders, OrderVolume и множителей стопов/тейков помогает адаптировать стратегию к различной
волатильности.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Range based grid strategy that detects the trading range from recent price action
/// and places buy/sell limit orders at grid levels within the range.
/// Buys at lower grid levels, sells at upper grid levels.
/// </summary>
public class RangeWeeklyGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rangePeriod;
private readonly StrategyParam<int> _gridLevels;
private decimal _rangeHigh;
private decimal _rangeLow;
private bool _rangeSet;
private decimal _entryPrice;
private DateTimeOffset _lastTradeTime;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RangePeriod
{
get => _rangePeriod.Value;
set => _rangePeriod.Value = value;
}
public int GridLevels
{
get => _gridLevels.Value;
set => _gridLevels.Value = value;
}
public RangeWeeklyGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary candle type", "General");
_rangePeriod = Param(nameof(RangePeriod), 100)
.SetDisplay("Range Period", "Number of candles to determine range", "Logic");
_gridLevels = Param(nameof(GridLevels), 5)
.SetDisplay("Grid Levels", "Number of grid levels within the range", "Logic");
}
protected override void OnReseted()
{
base.OnReseted();
_rangeHigh = 0;
_rangeLow = 0;
_rangeSet = false;
_entryPrice = 0;
_lastTradeTime = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = RangePeriod };
var lowest = new Lowest { Length = RangePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (highestValue <= 0 || lowestValue <= 0 || highestValue <= lowestValue)
return;
_rangeHigh = highestValue;
_rangeLow = lowestValue;
_rangeSet = true;
if (!_rangeSet)
return;
var range = _rangeHigh - _rangeLow;
if (range <= 0)
return;
// Cooldown: at least 1 day between trades
if (_lastTradeTime != default && candle.CloseTime - _lastTradeTime < TimeSpan.FromDays(1))
return;
var gridStep = range / (GridLevels + 1);
var close = candle.ClosePrice;
var mid = (_rangeHigh + _rangeLow) / 2;
// Buy when price is in lower portion of range
if (close <= _rangeLow + gridStep && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_lastTradeTime = candle.CloseTime;
}
// Sell when price is in upper portion of range
else if (close >= _rangeHigh - gridStep && Position >= 0)
{
SellMarket();
_entryPrice = close;
_lastTradeTime = candle.CloseTime;
}
// Take profit at mid-range
else if (Position > 0 && close >= mid)
{
SellMarket();
_lastTradeTime = candle.CloseTime;
}
else if (Position < 0 && close <= mid)
{
BuyMarket();
_lastTradeTime = candle.CloseTime;
}
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class range_weekly_grid_strategy(Strategy):
def __init__(self):
super(range_weekly_grid_strategy, self).__init__()
self._range_period = self.Param("RangePeriod", 100).SetDisplay("Range Period", "Candles to determine range", "Logic")
self._grid_levels = self.Param("GridLevels", 5).SetDisplay("Grid Levels", "Grid levels within range", "Logic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Primary candle type", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnStarted2(self, time):
super(range_weekly_grid_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self._range_period.Value
lowest = Lowest()
lowest.Length = self._range_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(highest, lowest, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle, highest_val, lowest_val):
if candle.State != CandleStates.Finished:
return
if highest_val <= 0 or lowest_val <= 0 or highest_val <= lowest_val:
return
rng = highest_val - lowest_val
if rng <= 0:
return
grid_step = rng / (self._grid_levels.Value + 1)
close = candle.ClosePrice
mid = (highest_val + lowest_val) / 2.0
if close <= lowest_val + grid_step and self.Position <= 0:
self.BuyMarket()
elif close >= highest_val - grid_step and self.Position >= 0:
self.SellMarket()
elif self.Position > 0 and close >= mid:
self.SellMarket()
elif self.Position < 0 and close <= mid:
self.BuyMarket()
def CreateClone(self):
return range_weekly_grid_strategy()