多周期交易者
该策略监控九个不同周期的K线,并为每个周期计算同一长度的线性回归通道。H1回归通道的斜率用于判断趋势方向:斜率为负时只允许做空,斜率为正时只允许做多。M5与M1通道负责在支撑/阻力附近给出精确的入场位置。
交易流程
- 数据来源:M1、M5、M15、M30、H1、H4、D1、W1、MN1九个周期的标准K线。
- 指标:每个周期使用线性回归通道,通道宽度由窗口内收盘价相对回归线的最大偏差确定,形成对称的上下轨道。
- 趋势过滤:读取H1通道的斜率,只在负斜率下寻找空头信号,在正斜率下寻找多头信号。
- 入场条件:
- 空头——最近一根M5高点和M1高点同时突破各自通道上轨,并且H1斜率为负。
- 多头——最近一根M5低点和M1低点同时触及各自通道下轨,并且H1斜率为正。
- 下单方式:使用设定的交易量发送市价单。止损取自M5通道半宽(上轨或下轨与中心线的一半距离),止盈为M5通道的中心线。
- 离场:在每根M1K线收盘时检查价格是否到达止损或止盈,满足任一条件即平仓。
- 仓位控制:策略在任何时刻只持有一个方向的仓位。
参数
| 参数 | 说明 |
|---|---|
EnableTrading |
是否允许策略发送订单。 |
BarsToCount |
所有回归通道的计算窗口长度(默认50)。 |
Volume |
每次市价单的交易量(手数)。 |
备注
- 增大
BarsToCount可使回归线更加平滑,但响应速度会下降。 - 多周期斜率信息有助于监控更高周期的一致性,尽管只有H1斜率用于决策。
- 每根新的M5K线都会重新计算止损和止盈,从而使风险控制紧贴当前通道宽度。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Linear regression channel strategy.
/// Uses LinearReg as the center line with Highest/Lowest to form a channel.
/// Sells at upper channel, buys at lower channel, with trend filter from regression slope.
/// </summary>
public class MultiTimeFrameRegressionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _regressionLength;
private readonly StrategyParam<int> _channelLength;
private decimal _prevLrValue;
private bool _hasPrev;
public MultiTimeFrameRegressionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_regressionLength = Param(nameof(RegressionLength), 20)
.SetDisplay("Regression Length", "Period for linear regression.", "Indicators");
_channelLength = Param(nameof(ChannelLength), 20)
.SetDisplay("Channel Length", "Period for highest/lowest channel.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RegressionLength
{
get => _regressionLength.Value;
set => _regressionLength.Value = value;
}
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevLrValue = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevLrValue = 0;
_hasPrev = false;
var lr = new LinearReg { Length = RegressionLength };
var highest = new Highest { Length = ChannelLength };
var lowest = new Lowest { Length = ChannelLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(lr, highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, lr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal lrValue, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Determine slope direction from regression
var slope = _hasPrev ? lrValue - _prevLrValue : 0m;
// Channel boundaries
var channelMid = (highestValue + lowestValue) / 2m;
var channelWidth = highestValue - lowestValue;
if (channelWidth <= 0)
{
_prevLrValue = lrValue;
_hasPrev = true;
return;
}
// Upper/lower thresholds
var upperThreshold = channelMid + channelWidth * 0.4m;
var lowerThreshold = channelMid - channelWidth * 0.4m;
// Exit conditions
if (Position > 0 && (close >= upperThreshold || slope < 0))
{
SellMarket();
}
else if (Position < 0 && (close <= lowerThreshold || slope > 0))
{
BuyMarket();
}
// Entry conditions
if (Position == 0)
{
if (close <= lowerThreshold && slope >= 0)
{
// Price near lower channel with flat/rising regression
BuyMarket();
}
else if (close >= upperThreshold && slope <= 0)
{
// Price near upper channel with flat/falling regression
SellMarket();
}
}
_prevLrValue = lrValue;
_hasPrev = true;
}
}
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 LinearReg, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class multi_time_frame_regression_strategy(Strategy):
"""
Multi Time Frame Regression: linear regression channel with highest/lowest boundaries.
"""
def __init__(self):
super(multi_time_frame_regression_strategy, self).__init__()
self._regression_length = self.Param("RegressionLength", 20).SetDisplay("Regression", "LinReg period", "Indicators")
self._channel_length = self.Param("ChannelLength", 20).SetDisplay("Channel", "Channel period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_lr = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multi_time_frame_regression_strategy, self).OnReseted()
self._prev_lr = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(multi_time_frame_regression_strategy, self).OnStarted2(time)
lr = LinearReg()
lr.Length = self._regression_length.Value
highest = Highest()
highest.Length = self._channel_length.Value
lowest = Lowest()
lowest.Length = self._channel_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(lr, highest, lowest, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, lr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, lr_val, high_val, low_val):
if candle.State != CandleStates.Finished:
return
lr = float(lr_val)
h = float(high_val)
l = float(low_val)
close = float(candle.ClosePrice)
slope = lr - self._prev_lr if self._has_prev else 0.0
ch_mid = (h + l) / 2.0
ch_width = h - l
if ch_width <= 0:
self._prev_lr = lr
self._has_prev = True
return
upper = ch_mid + ch_width * 0.4
lower = ch_mid - ch_width * 0.4
if self.Position > 0 and (close >= upper or slope < 0):
self.SellMarket()
elif self.Position < 0 and (close <= lower or slope > 0):
self.BuyMarket()
if self.Position == 0:
if close <= lower and slope >= 0:
self.BuyMarket()
elif close >= upper and slope <= 0:
self.SellMarket()
self._prev_lr = lr
self._has_prev = True
def CreateClone(self):
return multi_time_frame_regression_strategy()