基于RSI的自动挂单策略
当相对强弱指标(RSI)在超买或超卖区域连续保持指定数量的K线时,本策略会自动放置限价挂单。
当RSI连续 MatchCount 根K线低于超卖水平时,在收盘价下方 PendingOffset 个价格点处挂出买入限价单;当RSI连续位于超买水平以上时,在收盘价上方同样距离处挂出卖出限价单。
参数
RsiPeriod– RSI的计算周期。RsiOverbought– 判断超买区域的水平。RsiOversold– 判断超卖区域的水平。PendingOffset– 挂单距离收盘价的偏移量(价格点)。MatchCount– 触发挂单所需的连续K线数量。CandleType– 用于分析的K线周期。
默认设置仿照原始MQL脚本并使用4小时K线。
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>
/// Enters after RSI stays in extreme zones for several consecutive candles.
/// Buys after sustained oversold, sells after sustained overbought.
/// </summary>
public class AutoPendingByRsiStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<int> _matchCount;
private readonly StrategyParam<DataType> _candleType;
private int _overboughtCount;
private int _oversoldCount;
/// <summary>RSI calculation period.</summary>
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
/// <summary>RSI overbought level.</summary>
public decimal RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
/// <summary>RSI oversold level.</summary>
public decimal RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
/// <summary>Number of consecutive candles before placing orders.</summary>
public int MatchCount { get => _matchCount.Value; set => _matchCount.Value = value; }
/// <summary>Candle type for calculations.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public AutoPendingByRsiStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
.SetOptimize(7, 21, 7);
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
.SetOptimize(60m, 80m, 5m);
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
.SetOptimize(20m, 40m, 5m);
_matchCount = Param(nameof(MatchCount), 3)
.SetDisplay("Match Count", "Consecutive candles before entry", "General")
.SetOptimize(1, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_overboughtCount = 0;
_oversoldCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, Process)
.Start();
StartProtection(null, null);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void Process(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (rsi < RsiOversold)
{
_oversoldCount++;
_overboughtCount = 0;
}
else if (rsi > RsiOverbought)
{
_overboughtCount++;
_oversoldCount = 0;
}
else
{
_overboughtCount = 0;
_oversoldCount = 0;
}
if (_oversoldCount >= MatchCount && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_oversoldCount = 0;
}
if (_overboughtCount >= MatchCount && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_overboughtCount = 0;
}
}
}
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 auto_pending_by_rsi_strategy(Strategy):
def __init__(self):
super(auto_pending_by_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators") \
.SetOptimize(7, 21, 7)
self._rsi_overbought = self.Param("RsiOverbought", 70.0) \
.SetDisplay("RSI Overbought", "Overbought level", "Indicators") \
.SetOptimize(60.0, 80.0, 5.0)
self._rsi_oversold = self.Param("RsiOversold", 30.0) \
.SetDisplay("RSI Oversold", "Oversold level", "Indicators") \
.SetOptimize(20.0, 40.0, 5.0)
self._match_count = self.Param("MatchCount", 3) \
.SetDisplay("Match Count", "Consecutive candles before entry", "General") \
.SetOptimize(1, 10, 1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time frame for analysis", "General")
self._overbought_count = 0
self._oversold_count = 0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def rsi_overbought(self):
return self._rsi_overbought.Value
@property
def rsi_oversold(self):
return self._rsi_oversold.Value
@property
def match_count(self):
return self._match_count.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(auto_pending_by_rsi_strategy, self).OnReseted()
self._overbought_count = 0
self._oversold_count = 0
def OnStarted2(self, time):
super(auto_pending_by_rsi_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.process).Start()
self.StartProtection(None, None)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process(self, candle, rsi):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi)
if rsi_val < float(self.rsi_oversold):
self._oversold_count += 1
self._overbought_count = 0
elif rsi_val > float(self.rsi_overbought):
self._overbought_count += 1
self._oversold_count = 0
else:
self._overbought_count = 0
self._oversold_count = 0
if self._oversold_count >= self.match_count and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._oversold_count = 0
if self._overbought_count >= self.match_count and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._overbought_count = 0
def CreateClone(self):
return auto_pending_by_rsi_strategy()