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 that opens a position during a short time window.
/// Uses the previous daily candle direction to decide whether to buy or sell.
/// Applies configurable take-profit and stop-loss levels expressed in price steps.
/// </summary>
public class SheKanskigorDailyStrategy : Strategy
{
private readonly StrategyParam<TimeSpan> _startTime;
private readonly StrategyParam<int> _tradeWindowMinutes;
private readonly StrategyParam<decimal> _takeProfitSteps;
private readonly StrategyParam<decimal> _stopLossSteps;
private readonly StrategyParam<DataType> _intradayCandleType;
private readonly DataType _dailyCandleType;
private DateTime _currentDate;
private bool _tradePlaced;
private bool _dailyReady;
private decimal _previousOpen;
private decimal _previousClose;
private decimal _entryPrice;
/// <summary>
/// Start time of the trading window.
/// </summary>
public TimeSpan StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Width of the trading window in minutes.
/// </summary>
public int TradeWindowMinutes
{
get => _tradeWindowMinutes.Value;
set => _tradeWindowMinutes.Value = value;
}
/// <summary>
/// Take-profit distance expressed in security price steps.
/// </summary>
public decimal TakeProfitSteps
{
get => _takeProfitSteps.Value;
set => _takeProfitSteps.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in security price steps.
/// </summary>
public decimal StopLossSteps
{
get => _stopLossSteps.Value;
set => _stopLossSteps.Value = value;
}
/// <summary>
/// Intraday candle type used to evaluate the trading window.
/// </summary>
public DataType IntradayCandleType
{
get => _intradayCandleType.Value;
set => _intradayCandleType.Value = value;
}
/// <summary>
/// Initializes <see cref="SheKanskigorDailyStrategy"/>.
/// </summary>
public SheKanskigorDailyStrategy()
{
_takeProfitSteps = Param(nameof(TakeProfitSteps), 35m)
.SetDisplay("Take Profit", "Profit target in steps", "Risk")
;
_stopLossSteps = Param(nameof(StopLossSteps), 55m)
.SetDisplay("Stop Loss", "Loss limit in steps", "Risk")
;
_startTime = Param(nameof(StartTime), new TimeSpan(0, 5, 0))
.SetDisplay("Start Time", "Time of day to evaluate entries", "Schedule");
_tradeWindowMinutes = Param(nameof(TradeWindowMinutes), 5)
.SetDisplay("Window (min)", "Trading window duration in minutes", "Schedule")
;
_intradayCandleType = Param(nameof(IntradayCandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Intraday Candle", "Candle type for intraday checks", "Data");
_dailyCandleType = TimeSpan.FromMinutes(5).TimeFrame();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, IntradayCandleType), (Security, _dailyCandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentDate = default;
_tradePlaced = false;
_dailyReady = false;
_previousOpen = 0m;
_previousClose = 0m;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var intraday = SubscribeCandles(IntradayCandleType);
intraday.Bind(ProcessIntraday).Start();
var daily = SubscribeCandles(_dailyCandleType);
daily.Bind(ProcessDaily).Start();
StartProtection(null, null);
}
private void ProcessDaily(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Store the direction of the last completed daily candle.
_previousOpen = candle.OpenPrice;
_previousClose = candle.ClosePrice;
_dailyReady = true;
}
private void ProcessIntraday(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var openTime = candle.OpenTime;
if (openTime.Date != _currentDate)
{
_currentDate = openTime.Date;
_tradePlaced = false;
}
ManagePosition(candle.ClosePrice);
var start = StartTime;
var end = start.Add(TimeSpan.FromMinutes(TradeWindowMinutes));
var currentTod = openTime.TimeOfDay;
if (currentTod < start || currentTod > end)
return;
if (_tradePlaced)
return;
if (!_dailyReady)
return;
if (Position != 0)
{
_tradePlaced = true;
return;
}
if (_previousOpen > _previousClose)
{
BuyMarket(Volume);
_tradePlaced = true;
}
else if (_previousOpen < _previousClose)
{
SellMarket(Volume);
_tradePlaced = true;
}
else
{
// Skip trading when the previous day closed unchanged.
_tradePlaced = true;
}
}
private void ManagePosition(decimal closePrice)
{
if (Position == 0)
return;
var step = Security?.PriceStep ?? 0m;
if (step <= 0m || _entryPrice == 0m)
return;
if (Position > 0)
{
var target = _entryPrice + TakeProfitSteps * step;
var stop = _entryPrice - StopLossSteps * step;
if (TakeProfitSteps > 0m && closePrice >= target)
{
SellMarket(Position);
return;
}
if (StopLossSteps > 0m && closePrice <= stop)
{
SellMarket(Position);
}
}
else
{
var target = _entryPrice - TakeProfitSteps * step;
var stop = _entryPrice + StopLossSteps * step;
if (TakeProfitSteps > 0m && closePrice <= target)
{
BuyMarket(-Position);
return;
}
if (StopLossSteps > 0m && closePrice >= stop)
{
BuyMarket(-Position);
}
}
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade.Order.Security != Security)
return;
// Track the latest fill price to evaluate protective exits.
_entryPrice = trade.Trade.Price;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTime
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class she_kanskigor_daily_strategy(Strategy):
def __init__(self):
super(she_kanskigor_daily_strategy, self).__init__()
self._take_profit_steps = self.Param("TakeProfitSteps", 35.0) \
.SetDisplay("Take Profit", "Profit target in steps", "Risk")
self._stop_loss_steps = self.Param("StopLossSteps", 55.0) \
.SetDisplay("Stop Loss", "Loss limit in steps", "Risk")
self._start_time = self.Param("StartTime", TimeSpan(0, 5, 0)) \
.SetDisplay("Start Time", "Time of day to evaluate entries", "Schedule")
self._trade_window_minutes = self.Param("TradeWindowMinutes", 5) \
.SetDisplay("Window (min)", "Trading window duration in minutes", "Schedule")
self._intraday_candle_type = self.Param("IntradayCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Intraday Candle", "Candle type for intraday checks", "Data")
self._daily_candle_type = DataType.TimeFrame(TimeSpan.FromMinutes(5))
self._current_date = DateTime.MinValue
self._trade_placed = False
self._daily_ready = False
self._previous_open = 0.0
self._previous_close = 0.0
self._entry_price = 0.0
@property
def TakeProfitSteps(self):
return self._take_profit_steps.Value
@TakeProfitSteps.setter
def TakeProfitSteps(self, value):
self._take_profit_steps.Value = value
@property
def StopLossSteps(self):
return self._stop_loss_steps.Value
@StopLossSteps.setter
def StopLossSteps(self, value):
self._stop_loss_steps.Value = value
@property
def StartTime(self):
return self._start_time.Value
@StartTime.setter
def StartTime(self, value):
self._start_time.Value = value
@property
def TradeWindowMinutes(self):
return self._trade_window_minutes.Value
@TradeWindowMinutes.setter
def TradeWindowMinutes(self, value):
self._trade_window_minutes.Value = value
@property
def IntradayCandleType(self):
return self._intraday_candle_type.Value
@IntradayCandleType.setter
def IntradayCandleType(self, value):
self._intraday_candle_type.Value = value
def OnStarted2(self, time):
super(she_kanskigor_daily_strategy, self).OnStarted2(time)
intraday = self.SubscribeCandles(self.IntradayCandleType)
intraday.Bind(self.ProcessIntraday).Start()
daily = self.SubscribeCandles(self._daily_candle_type)
daily.Bind(self.ProcessDaily).Start()
self.StartProtection(None, None)
def ProcessDaily(self, candle):
if candle.State != CandleStates.Finished:
return
self._previous_open = float(candle.OpenPrice)
self._previous_close = float(candle.ClosePrice)
self._daily_ready = True
def ProcessIntraday(self, candle):
if candle.State != CandleStates.Finished:
return
open_time = candle.OpenTime
if open_time.Date != self._current_date:
self._current_date = open_time.Date
self._trade_placed = False
self._manage_position(float(candle.ClosePrice))
start = self.StartTime
end = start.Add(TimeSpan.FromMinutes(self.TradeWindowMinutes))
current_tod = open_time.TimeOfDay
if current_tod < start or current_tod > end:
return
if self._trade_placed:
return
if not self._daily_ready:
return
if self.Position != 0:
self._trade_placed = True
return
if self._previous_open > self._previous_close:
self.BuyMarket(self.Volume)
self._trade_placed = True
elif self._previous_open < self._previous_close:
self.SellMarket(self.Volume)
self._trade_placed = True
else:
self._trade_placed = True
def _manage_position(self, close_price):
if self.Position == 0:
return
ps = self.Security.PriceStep if self.Security is not None else None
step = float(ps) if ps is not None else 0.0
if step <= 0 or self._entry_price == 0:
return
tp = float(self.TakeProfitSteps)
sl = float(self.StopLossSteps)
if self.Position > 0:
target = self._entry_price + tp * step
stop = self._entry_price - sl * step
if tp > 0 and close_price >= target:
self.SellMarket(self.Position)
return
if sl > 0 and close_price <= stop:
self.SellMarket(self.Position)
else:
target = self._entry_price - tp * step
stop = self._entry_price + sl * step
if tp > 0 and close_price <= target:
self.BuyMarket(-self.Position)
return
if sl > 0 and close_price >= stop:
self.BuyMarket(-self.Position)
def OnOwnTradeReceived(self, trade):
super(she_kanskigor_daily_strategy, self).OnOwnTradeReceived(trade)
if trade.Order.Security != self.Security:
return
self._entry_price = float(trade.Trade.Price)
def OnReseted(self):
super(she_kanskigor_daily_strategy, self).OnReseted()
self._current_date = DateTime.MinValue
self._trade_placed = False
self._daily_ready = False
self._previous_open = 0.0
self._previous_close = 0.0
self._entry_price = 0.0
def CreateClone(self):
return she_kanskigor_daily_strategy()