Harmony Signal Flow By Arun 策略
Harmony Signal Flow By Arun 使用短周期 RSI 捕捉反转,并设定固定止损和目标。当 RSI 上穿 LowerThreshold 时做多,下穿 UpperThreshold 时做空。每笔交易在触及止损、目标或每天 15:25 时平仓。
细节
- 数据: 价格K线。
- 入场条件:
- 多头: 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()