Kloss 策略
Kloss 策略结合加权移动平均线 (WMA)、商品通道指数 (CCI) 和随机振荡指标。所有指标都基于带有偏移的历史值计算,使信号能够参考过去的市场环境。当 CCI 低于负阈值、Stochastic 主线低于 50 - StochDiffer 且偏移后的价格高于偏移后的 WMA 时开多单。反向条件下开空单。若启用 RevClose,出现反向信号时会平掉当前仓位。止损和止盈以点数形式设置。
细节
- 入场条件:
- 多头:偏移后的 CCI 低于
-CciDiffer,偏移后的 Stochastic 低于50 - StochDiffer,且偏移后的价格高于偏移后的 WMA。 - 空头:偏移后的 CCI 高于
CciDiffer,偏移后的 Stochastic 高于50 + StochDiffer,且偏移后的价格低于偏移后的 WMA。
- 多头:偏移后的 CCI 低于
- 多/空:均可。
- 出场条件:
- 启用
RevClose时的反向信号或预设的止损/止盈。
- 启用
- 止损:以点数计算的绝对值。
- 过滤器:
- 通过
CommonShift设置所有指标和价格的历史偏移。
- 通过
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>
/// Strategy that combines Weighted Moving Average, CCI and Stochastic oscillator.
/// </summary>
public class KlossStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLevel;
private readonly StrategyParam<decimal> _stochLevel;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _previousSignal;
private int _cooldownRemaining;
/// <summary>Moving Average period.</summary>
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
/// <summary>CCI calculation period.</summary>
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
/// <summary>CCI level for signals.</summary>
public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
/// <summary>Stochastic level offset from 50.</summary>
public decimal StochLevel { get => _stochLevel.Value; set => _stochLevel.Value = value; }
/// <summary>Stop loss in price steps.</summary>
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>Take profit in price steps.</summary>
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>Candle type used for calculations.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Completed candles to wait after a position change.</summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Initialize <see cref="KlossStrategy"/>.
/// </summary>
public KlossStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Length of weighted MA", "Indicators")
.SetOptimize(5, 50, 5);
_cciPeriod = Param(nameof(CciPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Length of CCI", "Indicators")
.SetOptimize(5, 30, 5);
_cciLevel = Param(nameof(CciLevel), 50m)
.SetGreaterThanZero()
.SetDisplay("CCI Level", "Distance from zero to trigger signal", "Indicators")
.SetOptimize(50m, 200m, 10m);
_stochLevel = Param(nameof(StochLevel), 10m)
.SetGreaterThanZero()
.SetDisplay("Stochastic Level", "Distance from 50 to trigger", "Indicators")
.SetOptimize(5m, 40m, 5m);
_stopLoss = Param(nameof(StopLoss), 550m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss in price steps", "Risk");
_takeProfit = Param(nameof(TakeProfit), 550m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles for calculations", "General");
_cooldownBars = Param(nameof(CooldownBars), 3)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousSignal = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma = new WeightedMovingAverage { Length = MaPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
var stoch = new StochasticOscillator();
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(ma, cci, stoch, ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue maValue, IIndicatorValue cciValue, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished || !maValue.IsFinal || !cciValue.IsFinal || !stochValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var ma = maValue.ToDecimal();
var cci = cciValue.ToDecimal();
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal stochK || stoch.D is not decimal stochD)
return;
var price = candle.ClosePrice;
var buySignal = cci < -CciLevel && stochK < 50m - StochLevel && stochD < 50m - StochLevel && price > ma;
var sellSignal = cci > CciLevel && stochK > 50m + StochLevel && stochD > 50m + StochLevel && price < ma;
var currentSignal = buySignal ? 1 : sellSignal ? -1 : 0;
if (_cooldownRemaining == 0 && Position == 0)
{
if (currentSignal > 0 && _previousSignal <= 0)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (currentSignal < 0 && _previousSignal >= 0)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
if (currentSignal != 0)
_previousSignal = currentSignal;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import WeightedMovingAverage, CommodityChannelIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class kloss_strategy(Strategy):
def __init__(self):
super(kloss_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Length of weighted MA", "Indicators") \
.SetOptimize(5, 50, 5)
self._cci_period = self.Param("CciPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("CCI Period", "Length of CCI", "Indicators") \
.SetOptimize(5, 30, 5)
self._cci_level = self.Param("CciLevel", 50.0) \
.SetGreaterThanZero() \
.SetDisplay("CCI Level", "Distance from zero to trigger signal", "Indicators") \
.SetOptimize(50.0, 200.0, 10.0)
self._stoch_level = self.Param("StochLevel", 10.0) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic Level", "Distance from 50 to trigger", "Indicators") \
.SetOptimize(5.0, 40.0, 5.0)
self._stop_loss = self.Param("StopLoss", 550.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss", "Stop loss in price steps", "Risk")
self._take_profit_param = self.Param("TakeProfit", 550.0) \
.SetNotNegative() \
.SetDisplay("Take Profit", "Take profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candles for calculations", "General")
self._cooldown_bars = self.Param("CooldownBars", 3) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._previous_signal = 0
self._cooldown_remaining = 0
@property
def ma_period(self):
return self._ma_period.Value
@property
def cci_period(self):
return self._cci_period.Value
@property
def cci_level(self):
return self._cci_level.Value
@property
def stoch_level(self):
return self._stoch_level.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def take_profit(self):
return self._take_profit_param.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(kloss_strategy, self).OnReseted()
self._previous_signal = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(kloss_strategy, self).OnStarted2(time)
ma = WeightedMovingAverage()
ma.Length = self.ma_period
cci = CommodityChannelIndex()
cci.Length = self.cci_period
stoch = StochasticOscillator()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ma, cci, stoch, self.process_candle).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, ma_value, cci_value, stoch_value):
if candle.State != CandleStates.Finished:
return
if not ma_value.IsFinal or not cci_value.IsFinal or not stoch_value.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
ma = float(ma_value)
cci = float(cci_value)
stoch_k = stoch_value.K
stoch_d = stoch_value.D
if stoch_k is None or stoch_d is None:
return
k = float(stoch_k)
d = float(stoch_d)
price = float(candle.ClosePrice)
cci_lvl = float(self.cci_level)
stoch_lvl = float(self.stoch_level)
buy_signal = cci < -cci_lvl and k < 50.0 - stoch_lvl and d < 50.0 - stoch_lvl and price > ma
sell_signal = cci > cci_lvl and k > 50.0 + stoch_lvl and d > 50.0 + stoch_lvl and price < ma
if buy_signal:
current_signal = 1
elif sell_signal:
current_signal = -1
else:
current_signal = 0
if self._cooldown_remaining == 0 and self.Position == 0:
if current_signal > 0 and self._previous_signal <= 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif current_signal < 0 and self._previous_signal >= 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
if current_signal != 0:
self._previous_signal = current_signal
def CreateClone(self):
return kloss_strategy()