Cidomo V1
Стратегия пробоя дневного диапазона, открывающая сделки при выходе цены из недавнего коридора.
Сводка
- Тип: Пробой
- Вход: Покупка при пробое максимума за период, продажа при пробое минимума.
- Выход: Стоп‑лосс, тейк‑профит, опциональные безубыток и трейлинг.
- Индикаторы: Highest, Lowest
Параметры
| Имя | Описание |
|---|---|
Lookback |
Количество свечей для расчёта диапазона. |
Delta |
Ценовое смещение добавляемое к уровням пробоя. |
StopLoss |
Размер стоп‑лосса в пунктах. |
TakeProfit |
Размер тейк‑профита в пунктах. |
NoLoss |
Перевод стопа в безубыток после прибыли (в пунктах). |
Trailing |
Дистанция трейлинга в пунктах. |
UseTimeFilter |
Если true, уровни рассчитываются после указанного времени. |
TradeTime |
Время расчёта уровней пробоя. |
CandleType |
Тип свечей для расчётов. |
Заметки
Стратегия обрабатывает только завершённые свечи. Уровни пересчитываются раз в день после TradeTime.
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>
/// Daily breakout strategy based on previous range.
/// </summary>
public class CidomoV1Strategy : Strategy
{
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<decimal> _delta;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _noLoss;
private readonly StrategyParam<decimal> _trailing;
private readonly StrategyParam<bool> _useTimeFilter;
private readonly StrategyParam<TimeSpan> _tradeTime;
private readonly StrategyParam<DataType> _candleType;
private decimal _longLevel;
private decimal _shortLevel;
private DateTime _lastTradeDay;
private decimal _entryPrice;
private decimal _stopPrice;
private Highest _highest = null!;
private Lowest _lowest = null!;
/// <summary>
/// Number of candles used to calculate range.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Price offset added to breakout levels.
/// </summary>
public decimal Delta
{
get => _delta.Value;
set => _delta.Value = value;
}
/// <summary>
/// Stop loss in price points.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price points.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Move stop to entry after this profit (points).
/// </summary>
public decimal NoLoss
{
get => _noLoss.Value;
set => _noLoss.Value = value;
}
/// <summary>
/// Trailing distance in points.
/// </summary>
public decimal Trailing
{
get => _trailing.Value;
set => _trailing.Value = value;
}
/// <summary>
/// Trade only after specified time.
/// </summary>
public bool UseTimeFilter
{
get => _useTimeFilter.Value;
set => _useTimeFilter.Value = value;
}
/// <summary>
/// Time to calculate breakout levels.
/// </summary>
public TimeSpan TradeTime
{
get => _tradeTime.Value;
set => _tradeTime.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CidomoV1Strategy()
{
_lookback = Param(nameof(Lookback), 32)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Number of candles to look back", "General")
.SetOptimize(10, 60, 10);
_delta = Param(nameof(Delta), 0m)
.SetDisplay("Delta", "Price offset added to breakout levels", "General");
_stopLoss = Param(nameof(StopLoss), 60m)
.SetDisplay("Stop Loss", "Stop loss in points", "Risk");
_takeProfit = Param(nameof(TakeProfit), 70m)
.SetDisplay("Take Profit", "Take profit in points", "Risk");
_noLoss = Param(nameof(NoLoss), 35m)
.SetDisplay("Break-even", "Move stop to entry after profit", "Risk");
_trailing = Param(nameof(Trailing), 5m)
.SetDisplay("Trailing", "Trailing distance in points", "Risk");
_useTimeFilter = Param(nameof(UseTimeFilter), true)
.SetDisplay("Use Time Filter", "Trade only after specified time", "General");
_tradeTime = Param(nameof(TradeTime), new TimeSpan(9, 0, 0))
.SetDisplay("Trade Time", "Time to calculate breakout", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_longLevel = 0;
_shortLevel = 0;
_lastTradeDay = default;
_entryPrice = 0;
_stopPrice = 0;
_highest = default;
_lowest = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = Lookback };
_lowest = new Lowest { Length = Lookback };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_highest, _lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _highest);
DrawIndicator(area, _lowest);
DrawOwnTrades(area);
}
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var step = Security.PriceStep ?? 1m;
var time = candle.OpenTime.TimeOfDay;
if ((!UseTimeFilter || time >= TradeTime) && _lastTradeDay != candle.OpenTime.Date)
{
_longLevel = highest + Delta * step;
_shortLevel = lowest - Delta * step;
_lastTradeDay = candle.OpenTime.Date;
// Levels updated
}
if (Position == 0)
{
if (candle.HighPrice >= _longLevel)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLoss * step;
}
else if (candle.LowPrice <= _shortLevel)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLoss * step;
}
}
else if (Position > 0)
{
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
return;
}
if (TakeProfit > 0 && candle.HighPrice >= _entryPrice + TakeProfit * step)
{
SellMarket();
return;
}
if (NoLoss > 0 && _stopPrice < _entryPrice && candle.HighPrice >= _entryPrice + NoLoss * step)
_stopPrice = _entryPrice;
if (Trailing > 0 && candle.HighPrice >= _entryPrice + Trailing * step)
{
var newStop = candle.ClosePrice - Trailing * step;
if (newStop > _stopPrice)
_stopPrice = newStop;
}
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
return;
}
if (TakeProfit > 0 && candle.LowPrice <= _entryPrice - TakeProfit * step)
{
BuyMarket();
return;
}
if (NoLoss > 0 && _stopPrice > _entryPrice && candle.LowPrice <= _entryPrice - NoLoss * step)
_stopPrice = _entryPrice;
if (Trailing > 0 && candle.LowPrice <= _entryPrice - Trailing * step)
{
var newStop = candle.ClosePrice + Trailing * step;
if (newStop < _stopPrice)
_stopPrice = newStop;
}
}
}
}
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 cidomo_v1_strategy(Strategy):
"""
Daily breakout strategy based on previous range with trailing stop and break-even.
"""
def __init__(self):
super(cidomo_v1_strategy, self).__init__()
self._lookback = self.Param("Lookback", 32) \
.SetDisplay("Lookback", "Number of candles to look back", "General")
self._delta = self.Param("Delta", 0.0) \
.SetDisplay("Delta", "Price offset added to breakout levels", "General")
self._stop_loss = self.Param("StopLoss", 60.0) \
.SetDisplay("Stop Loss", "Stop loss in points", "Risk")
self._take_profit = self.Param("TakeProfit", 70.0) \
.SetDisplay("Take Profit", "Take profit in points", "Risk")
self._no_loss = self.Param("NoLoss", 35.0) \
.SetDisplay("Break-even", "Move stop to entry after profit", "Risk")
self._trailing = self.Param("Trailing", 5.0) \
.SetDisplay("Trailing", "Trailing distance in points", "Risk")
self._use_time_filter = self.Param("UseTimeFilter", True) \
.SetDisplay("Use Time Filter", "Trade only after specified time", "General")
self._trade_time = self.Param("TradeTime", TimeSpan(9, 0, 0)) \
.SetDisplay("Trade Time", "Time to calculate breakout", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type for analysis", "General")
self._long_level = 0.0
self._short_level = 0.0
self._last_trade_day = None
self._entry_price = 0.0
self._stop_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cidomo_v1_strategy, self).OnReseted()
self._long_level = 0.0
self._short_level = 0.0
self._last_trade_day = None
self._entry_price = 0.0
self._stop_price = 0.0
def OnStarted2(self, time):
super(cidomo_v1_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self._lookback.Value
lowest = Lowest()
lowest.Length = self._lookback.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def on_process(self, candle, highest_val, lowest_val):
if candle.State != CandleStates.Finished:
return
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and float(self.Security.PriceStep) > 0:
step = float(self.Security.PriceStep)
t = candle.OpenTime.TimeOfDay
current_date = candle.OpenTime.Date
if (not self._use_time_filter.Value or t >= self._trade_time.Value) and self._last_trade_day != current_date:
self._long_level = float(highest_val) + self._delta.Value * step
self._short_level = float(lowest_val) - self._delta.Value * step
self._last_trade_day = current_date
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position == 0:
if high >= self._long_level:
self.BuyMarket()
self._entry_price = close
self._stop_price = self._entry_price - self._stop_loss.Value * step
elif low <= self._short_level:
self.SellMarket()
self._entry_price = close
self._stop_price = self._entry_price + self._stop_loss.Value * step
elif self.Position > 0:
if low <= self._stop_price:
self.SellMarket()
return
if self._take_profit.Value > 0 and high >= self._entry_price + self._take_profit.Value * step:
self.SellMarket()
return
if self._no_loss.Value > 0 and self._stop_price < self._entry_price and high >= self._entry_price + self._no_loss.Value * step:
self._stop_price = self._entry_price
if self._trailing.Value > 0 and high >= self._entry_price + self._trailing.Value * step:
new_stop = close - self._trailing.Value * step
if new_stop > self._stop_price:
self._stop_price = new_stop
elif self.Position < 0:
if high >= self._stop_price:
self.BuyMarket()
return
if self._take_profit.Value > 0 and low <= self._entry_price - self._take_profit.Value * step:
self.BuyMarket()
return
if self._no_loss.Value > 0 and self._stop_price > self._entry_price and low <= self._entry_price - self._no_loss.Value * step:
self._stop_price = self._entry_price
if self._trailing.Value > 0 and low <= self._entry_price - self._trailing.Value * step:
new_stop = close + self._trailing.Value * step
if new_stop < self._stop_price:
self._stop_price = new_stop
def CreateClone(self):
return cidomo_v1_strategy()