Harmony Signal Flow By Arun 戦略
Harmony Signal Flow By Arunは短期RSIを使って、固定のストップロスと目標レベルで転換点を捉えます。RSIが下限閾値を上抜けするとロング、上限閾値を下抜けするとショートに入ります。ポジションはストップ・目標到達、または毎日15:25にクローズされます。
詳細
- データ: 価格ローソク足。
- エントリー条件:
- ロング: RSIが
LowerThresholdを上抜け。 - ショート: RSIが
UpperThresholdを下抜け。
- ロング: RSIが
- エグジット条件: ストップロスまたは目標到達、あるいは15:25のクローズ。
- ストップ: 固定ストップロスと目標。
- デフォルト値:
RsiPeriod= 5LowerThreshold= 30UpperThreshold= 70BuyStopLoss= 100BuyTarget= 150SellStopLoss= 100SellTarget= 150
- フィルター:
- カテゴリ: 平均回帰
- 方向: ロング & ショート
- インジケーター: 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>
/// 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()