Estrategia Harmony Signal Flow By Arun
Harmony Signal Flow By Arun utiliza un RSI de período corto para capturar reversiones con niveles fijos de stop-loss y objetivo. La estrategia va largo cuando el RSI cruza por encima del umbral inferior y corto cuando cruza por debajo del umbral superior. Las posiciones se cierran por stop, objetivo o a las 15:25 de cada día.
Detalles
- Datos: Velas de precio.
- Criterios de entrada:
- Largo: El RSI cruza por encima de
LowerThreshold. - Corto: El RSI cruza por debajo de
UpperThreshold.
- Largo: El RSI cruza por encima de
- Criterios de salida: Stop-loss u objetivo alcanzado, o cierre a las 15:25.
- Stops: Stop-loss y objetivo fijos.
- Valores predeterminados:
RsiPeriod= 5LowerThreshold= 30UpperThreshold= 70BuyStopLoss= 100BuyTarget= 150SellStopLoss= 100SellTarget= 150
- Filtros:
- Categoría: Reversión a la media
- Dirección: Largo y Corto
- Indicadores: RSI
- Complejidad: Bajo
- Nivel de riesgo: Medio
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>
/// Harmony Signal Flow By Arun strategy.
/// An RSI crossing above LowerThreshold buys and an RSI crossing below UpperThreshold sells, reversing an opposite position.
/// Longs use BuyStopLoss and BuyTarget, shorts SellStopLoss and SellTarget, all in price steps from the entry. Any open position
/// is closed once a day at 15:25 (UTC candle open time).
/// </summary>
public class HarmonySignalFlowByArunStrategy : Strategy
{
private static readonly TimeSpan _sessionClose = new(15, 25, 0);
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _lowerThreshold;
private readonly StrategyParam<decimal> _upperThreshold;
private readonly StrategyParam<decimal> _buyStopLoss;
private readonly StrategyParam<decimal> _buyTarget;
private readonly StrategyParam<decimal> _sellStopLoss;
private readonly StrategyParam<decimal> _sellTarget;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
private decimal _entryPrice;
private DateTime _lastSessionClose;
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI level whose upward cross buys.
/// </summary>
public decimal LowerThreshold
{
get => _lowerThreshold.Value;
set => _lowerThreshold.Value = value;
}
/// <summary>
/// RSI level whose downward cross sells.
/// </summary>
public decimal UpperThreshold
{
get => _upperThreshold.Value;
set => _upperThreshold.Value = value;
}
/// <summary>
/// Long stop loss in price steps.
/// </summary>
public decimal BuyStopLoss
{
get => _buyStopLoss.Value;
set => _buyStopLoss.Value = value;
}
/// <summary>
/// Long target in price steps.
/// </summary>
public decimal BuyTarget
{
get => _buyTarget.Value;
set => _buyTarget.Value = value;
}
/// <summary>
/// Short stop loss in price steps.
/// </summary>
public decimal SellStopLoss
{
get => _sellStopLoss.Value;
set => _sellStopLoss.Value = value;
}
/// <summary>
/// Short target in price steps.
/// </summary>
public decimal SellTarget
{
get => _sellTarget.Value;
set => _sellTarget.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HarmonySignalFlowByArunStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_lowerThreshold = Param(nameof(LowerThreshold), 30m)
.SetDisplay("Lower Threshold", "RSI level whose upward cross buys", "Indicators");
_upperThreshold = Param(nameof(UpperThreshold), 70m)
.SetDisplay("Upper Threshold", "RSI level whose downward cross sells", "Indicators");
_buyStopLoss = Param(nameof(BuyStopLoss), 100m)
.SetNotNegative()
.SetDisplay("Buy Stop Loss", "Long stop loss in price steps", "Risk");
_buyTarget = Param(nameof(BuyTarget), 150m)
.SetNotNegative()
.SetDisplay("Buy Target", "Long target in price steps", "Risk");
_sellStopLoss = Param(nameof(SellStopLoss), 100m)
.SetNotNegative()
.SetDisplay("Sell Stop Loss", "Short stop loss in price steps", "Risk");
_sellTarget = Param(nameof(SellTarget), 150m)
.SetNotNegative()
.SetDisplay("Sell Target", "Short target in price steps", "Risk");
_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();
ResetState();
}
private void ResetState()
{
_prevRsi = null;
_entryPrice = 0m;
_lastSessionClose = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
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 void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var prevRsi = _prevRsi;
_prevRsi = rsi;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var step = Security?.PriceStep ?? 1m;
// Daily close at 15:25.
var openTime = candle.OpenTime;
if (openTime.TimeOfDay >= _sessionClose && _lastSessionClose != openTime.Date)
{
_lastSessionClose = openTime.Date;
if (Position > 0)
{
SellMarket(Position);
return;
}
if (Position < 0)
{
BuyMarket(-Position);
return;
}
}
if (Position > 0)
{
var stop = BuyStopLoss > 0 && candle.LowPrice <= _entryPrice - BuyStopLoss * step;
var target = BuyTarget > 0 && candle.HighPrice >= _entryPrice + BuyTarget * step;
if (stop || target)
{
SellMarket(Position);
return;
}
}
else if (Position < 0)
{
var stop = SellStopLoss > 0 && candle.HighPrice >= _entryPrice + SellStopLoss * step;
var target = SellTarget > 0 && candle.LowPrice <= _entryPrice - SellTarget * step;
if (stop || target)
{
BuyMarket(-Position);
return;
}
}
if (prevRsi is not decimal lastRsi)
return;
if (lastRsi <= LowerThreshold && rsi > LowerThreshold && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
}
else if (lastRsi >= UpperThreshold && rsi < UpperThreshold && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class harmony_signal_flow_by_arun_strategy(Strategy):
"""
Harmony Signal Flow By Arun strategy.
An RSI crossing above LowerThreshold buys and an RSI crossing below UpperThreshold sells, reversing an opposite position.
Longs use BuyStopLoss and BuyTarget, shorts SellStopLoss and SellTarget, all in price steps from the entry. Any open position
is closed once a day at 15:25 (UTC candle open time).
"""
SESSION_CLOSE_MINUTES = 15 * 60 + 25
def __init__(self):
super(harmony_signal_flow_by_arun_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 5).SetGreaterThanZero().SetDisplay("RSI Period", "RSI period", "Indicators")
self._lower_threshold = self.Param("LowerThreshold", 30.0).SetDisplay("Lower Threshold", "RSI level whose upward cross buys", "Indicators")
self._upper_threshold = self.Param("UpperThreshold", 70.0).SetDisplay("Upper Threshold", "RSI level whose downward cross sells", "Indicators")
self._buy_stop_loss = self.Param("BuyStopLoss", 100.0).SetNotNegative().SetDisplay("Buy Stop Loss", "Long stop loss in price steps", "Risk")
self._buy_target = self.Param("BuyTarget", 150.0).SetNotNegative().SetDisplay("Buy Target", "Long target in price steps", "Risk")
self._sell_stop_loss = self.Param("SellStopLoss", 100.0).SetNotNegative().SetDisplay("Sell Stop Loss", "Short stop loss in price steps", "Risk")
self._sell_target = self.Param("SellTarget", 150.0).SetNotNegative().SetDisplay("Sell Target", "Short target in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_rsi = None
self._entry_price = 0.0
self._last_session_close = None
def OnReseted(self):
super(harmony_signal_flow_by_arun_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(harmony_signal_flow_by_arun_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.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 _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi = float(rsi_value)
prev_rsi = self._prev_rsi
self._prev_rsi = rsi
if not self.IsFormedAndOnlineAndAllowTrading():
return
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
# Daily close at 15:25.
open_time = candle.OpenTime
minutes = open_time.Hour * 60 + open_time.Minute
day = open_time.Date
if minutes >= self.SESSION_CLOSE_MINUTES and self._last_session_close != day:
self._last_session_close = day
if self.Position > 0:
self.SellMarket(self.Position)
return
if self.Position < 0:
self.BuyMarket(-self.Position)
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
sl = float(self._buy_stop_loss.Value)
tp = float(self._buy_target.Value)
stop = sl > 0 and low <= self._entry_price - sl * step
target = tp > 0 and high >= self._entry_price + tp * step
if stop or target:
self.SellMarket(self.Position)
return
elif self.Position < 0:
sl = float(self._sell_stop_loss.Value)
tp = float(self._sell_target.Value)
stop = sl > 0 and high >= self._entry_price + sl * step
target = tp > 0 and low <= self._entry_price - tp * step
if stop or target:
self.BuyMarket(-self.Position)
return
if prev_rsi is None:
return
lower = float(self._lower_threshold.Value)
upper = float(self._upper_threshold.Value)
if prev_rsi <= lower and rsi > lower and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._entry_price = float(candle.ClosePrice)
elif prev_rsi >= upper and rsi < upper and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._entry_price = float(candle.ClosePrice)
def CreateClone(self):
return harmony_signal_flow_by_arun_strategy()