Стратегия Futures Trading Hours RSI
Стратегия работает только в часы торгов американских фьючерсов (08:30–15:00 по центральному времени). Осциллятор RSI используется для входа в длинную позицию при пересечении уровня перепроданности снизу и для входа в короткую позицию при пересечении уровня перекупленности сверху. После 15:00 CT все открытые позиции закрываются.
Детали
- Критерии входа:
- Длинная позиция: RSI пересекает уровень перепроданности снизу в пределах сессии
- Короткая позиция: RSI пересекает уровень перекупленности сверху в пределах сессии
- Длинные/короткие: обе стороны
- Критерии выхода:
- Закрытие всех позиций в конце сессии (15:00 CT)
- Стопы: отсутствуют
- Значения по умолчанию:
RsiLength= 14OverSoldLevel= 30OverBoughtLevel= 70SessionStart= 08:30SessionEnd= 15:00
- Фильтры:
- Категория: Осциллятор
- Направление: Оба
- Индикаторы: RSI
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Futures Trading Hours RSI strategy.
/// Trades only between SessionStart and SessionEnd in US Central Time. Inside the session an RSI crossing above OverSoldLevel goes
/// long and an RSI crossing below OverBoughtLevel goes short, reversing an opposite position. Any position still open at or after
/// SessionEnd is closed.
/// </summary>
public class FuturesTradingHoursRsiStrategy : Strategy
{
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _overSoldLevel;
private readonly StrategyParam<decimal> _overBoughtLevel;
private readonly StrategyParam<TimeSpan> _sessionStart;
private readonly StrategyParam<TimeSpan> _sessionEnd;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal OverSoldLevel
{
get => _overSoldLevel.Value;
set => _overSoldLevel.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal OverBoughtLevel
{
get => _overBoughtLevel.Value;
set => _overBoughtLevel.Value = value;
}
/// <summary>
/// Session start in US Central Time.
/// </summary>
public TimeSpan SessionStart
{
get => _sessionStart.Value;
set => _sessionStart.Value = value;
}
/// <summary>
/// Session end in US Central Time.
/// </summary>
public TimeSpan SessionEnd
{
get => _sessionEnd.Value;
set => _sessionEnd.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FuturesTradingHoursRsiStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_overSoldLevel = Param(nameof(OverSoldLevel), 30m)
.SetDisplay("Oversold", "RSI oversold level", "Indicators");
_overBoughtLevel = Param(nameof(OverBoughtLevel), 70m)
.SetDisplay("Overbought", "RSI overbought level", "Indicators");
_sessionStart = Param(nameof(SessionStart), new TimeSpan(8, 30, 0))
.SetDisplay("Session Start", "Session start in US Central Time", "Session");
_sessionEnd = Param(nameof(SessionEnd), new TimeSpan(15, 0, 0))
.SetDisplay("Session End", "Session end in US Central Time", "Session");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private static TimeZoneInfo GetCentralTimeZone()
{
try
{
return TimeZoneInfo.FindSystemTimeZoneById("America/Chicago");
}
catch (TimeZoneNotFoundException)
{
return TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var prevRsi = _prevRsi;
_prevRsi = rsi;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var utc = DateTime.SpecifyKind(candle.OpenTime, DateTimeKind.Utc);
var timeOfDay = TimeZoneInfo.ConvertTimeFromUtc(utc, GetCentralTimeZone()).TimeOfDay;
if (timeOfDay >= SessionEnd || timeOfDay < SessionStart)
{
if (timeOfDay >= SessionEnd)
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
return;
}
if (prevRsi is not decimal lastRsi)
return;
if (lastRsi <= OverSoldLevel && rsi > OverSoldLevel && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (lastRsi >= OverBoughtLevel && rsi < OverBoughtLevel && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, TimeZoneInfo, TimeZoneNotFoundException, DateTime, DateTimeKind
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class futures_trading_hours_rsi_strategy(Strategy):
"""
Futures Trading Hours RSI strategy.
Trades only between SessionStart and SessionEnd in US Central Time. Inside the session an RSI crossing above OverSoldLevel goes
long and an RSI crossing below OverBoughtLevel goes short, reversing an opposite position. Any position still open at or after
SessionEnd is closed.
"""
def __init__(self):
super(futures_trading_hours_rsi_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._over_sold_level = self.Param("OverSoldLevel", 30.0).SetDisplay("Oversold", "RSI oversold level", "Indicators")
self._over_bought_level = self.Param("OverBoughtLevel", 70.0).SetDisplay("Overbought", "RSI overbought level", "Indicators")
self._session_start = self.Param("SessionStart", TimeSpan(8, 30, 0)).SetDisplay("Session Start", "Session start in US Central Time", "Session")
self._session_end = self.Param("SessionEnd", TimeSpan(15, 0, 0)).SetDisplay("Session End", "Session end in US Central Time", "Session")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_rsi = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(futures_trading_hours_rsi_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(futures_trading_hours_rsi_strategy, self).OnStarted2(time)
self._prev_rsi = None
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _central_time_zone(self):
try:
return TimeZoneInfo.FindSystemTimeZoneById("America/Chicago")
except TimeZoneNotFoundException:
return TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi = float(rsi_value)
last_rsi = self._prev_rsi
self._prev_rsi = rsi
if not self.IsFormedAndOnlineAndAllowTrading():
return
utc = DateTime.SpecifyKind(candle.OpenTime, DateTimeKind.Utc)
minutes = TimeZoneInfo.ConvertTimeFromUtc(utc, self._central_time_zone()).TimeOfDay.TotalMinutes
start = self._session_start.Value.TotalMinutes
end = self._session_end.Value.TotalMinutes
if minutes >= end or minutes < start:
if minutes >= end:
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
return
if last_rsi is None:
return
oversold = float(self._over_sold_level.Value)
overbought = float(self._over_bought_level.Value)
if last_rsi <= oversold and rsi > oversold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif last_rsi >= overbought and rsi < overbought and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return futures_trading_hours_rsi_strategy()