Daydream — конвертация эксперта MQL4 Daydream by Cothool для StockSharp. Исходный советник торгует USD/JPY на H1: как только цена закрывается за пределами недавнего ценового канала, открывается позиция и ведётся виртуальный трал по тейк-профиту. В версии StockSharp расчёт уровней выполняет индикатор Donchian Channels, сделки отправляются методами BuyMarket / SellMarket, а сопровождение позиции выполняется внутри стратегии без выставления заявок на биржу.
Ключевые особенности:
В рынке находится только одна позиция; разворот происходит после закрытия свечи вне предыдущих границ канала.
Виртуальный тейк-профит в пунктах подтягивается вслед за ценой и закрывает сделку при достижении уровня.
Блокировка повторных операций внутри одной свечи — аналог переменной LastOrderTime в MQL4.
Логика торговли
Построить Donchian Channel по ChannelPeriod завершённым свечам и сохранить предыдущие верхнюю/нижнюю границы.
Если свеча закрывается ниже прошлой нижней границы:
закрыть открытую короткую позицию;
на следующей свече открыть лонг объёмом OrderVolume и установить виртуальный тейк-профит на уровне close + TakeProfitPips * pipSize.
Если свеча закрывается выше прошлой верхней границы:
закрыть открытую длинную позицию;
на следующей свече открыть шорт и выставить виртуальный тейк-профит close - TakeProfitPips * pipSize.
Пока позиция активна, уровень виртуального тейк-профита подтягивается вслед за ценой. При достижении — позиция закрывается рыночным ордером.
Размер пункта вычисляется по PriceStep инструмента. Для валютных пар с иеной шаг 0.001 превращается в пункт 0.01, как в MetaTrader.
Параметры
Параметр
Описание
Значение по умолчанию
Примечание
OrderVolume
Объём каждой новой позиции.
1
Соответствует входному параметру Lots.
ChannelPeriod
Количество завершённых свечей в канале Дончиана.
25
Аналог одноимённого параметра MQL.
Slippage
Допустимое проскальзывание в пунктах.
3
Хранится для совместимости; рыночные заявки его не используют.
TakeProfitPips
Дистанция виртуального тейк-профита в пунктах.
15
Сдвигается по мере движения цены.
CandleType
Таймфрейм для расчёта Donchian Channel.
1 час
Рекомендуемый период исходного советника.
Схема работы
Закрытие свечи
│
├─► Обновление канала Дончиана (предыдущие границы)
│
├─► Пробой вниз? ──► Закрыть шорт → готовим вход в лонг на следующей свече
│
├─► Пробой вверх? ─► Закрыть лонг → готовим вход в шорт на следующей свече
│
└─► Трал виртуального тейк-профита по направлению позиции
└─► Цена достигла уровня? → Закрыть позицию
Практические рекомендации
Запускайте стратегию на любом инструменте с поступающими свечами. Значения по умолчанию ориентированы на USD/JPY H1.
В стратегии всегда одна позиция. Повторные сделки внутри текущей свечи блокируются, что повторяет поведение MQL4.
Тейк-профит виртуальный: фактическое закрытие происходит рыночной заявкой, реальные TP заявки не выставляются.
Изменяйте CandleType, если нужен другой таймфрейм. Для длинных периодов требуется достаточная история для прогрева канала.
Отличия от версии MQL4
Используется стандартный индикатор DonchianChannels, вместо ручного поиска максимумов и минимумов.
Логика трала и блокировки действий сохранена, но исполнение осуществляется через инфраструктуру StockSharp без работы с тикетами MT4.
Параметр Slippage оставлен для совместимости, хотя в StockSharp рыночное исполнение работает иначе.
Состав пакета
CS/DaydreamStrategy.cs — реализация стратегии на C#.
Python-версия пока не реализована.
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>
/// Daydream strategy - Donchian channel breakout.
/// Buys when price crosses above the midpoint of the channel.
/// Sells when price crosses below the midpoint.
/// </summary>
public class DaydreamStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DaydreamStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev)
{
_prevClose = close;
_prevMid = mid;
_hasPrev = true;
return;
}
// Cross above midpoint
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Cross below midpoint
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevMid = mid;
}
}
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 daydream_strategy(Strategy):
def __init__(self):
super(daydream_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20) \
.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
@property
def channel_period(self):
return self._channel_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(daydream_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(daydream_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high_val = float(highest)
low_val = float(lowest)
mid = (high_val + low_val) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return daydream_strategy()