MA CCI Strategie
Strategie, die Moving Average und den CCI-Indikator kombiniert. Kauft, wenn der Preis über dem MA liegt und der CCI überverkauft ist. Verkauft, wenn der Preis unter dem MA liegt und der CCI überkauft ist.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 49%. Am besten geeignet für den Kryptomarkt.
Ein Moving Average gibt die Trendrichtung vor, während der CCI nach Abweichungen von diesem Durchschnitt sucht. Einstiege erfolgen bei CCI-Extremwerten in Richtung des MA.
Ideal für Swing-Trader, die bei Rücksetzern einsteigen. Ein prozentualer Stop schützt vor plötzlichen Kursausschlägen.
Details
- Einstiegskriterien:
- Long:
Close > MA && CCI < OversoldLevel
- Short:
Close < MA && CCI > OverboughtLevel
- Long/Short: Beide
- Ausstiegskriterien:
- CCI kehrt zur Nulllinie zurück
- Stops: Prozentbasiert mit
StopLossPercent
- Standardwerte:
MaPeriod = 50
CciPeriod = 20
OverboughtLevel = 100m
OversoldLevel = -100m
StopLossPercent = 2.0m
CandleType = TimeSpan.FromMinutes(5).TimeFrame()
Ein 20-Kerzen-SMA bewegt sich mit dem CCI über dieselben 20 Kerzen, daher bleibt der Kurs fast nie darüber, während CCI unter -100 liegt: Das Archiv vom März 2024 enthält höchstens eine solche Kerze je Instrument. Der Trend-SMA verwendet daher standardmäßig 50 Kerzen. Ein Einstiegssignal gegen eine offene Position dreht sie.
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: Moving Average, CCI
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Mittelfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// MA CCI strategy.
/// A close above the MaPeriod SMA with CCI below OversoldLevel goes long and a close below it with CCI above OverboughtLevel goes short,
/// reversing an opposite position. The position closes once CCI returns to the zero line, and a percent stop limits the loss.
/// </summary>
public class MaCciStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of the trend SMA.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Period of CCI.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// CCI level for shorts.
/// </summary>
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// CCI level for longs.
/// </summary>
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaCciStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the trend SMA", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period of CCI", "Indicators");
_overboughtLevel = Param(nameof(OverboughtLevel), 100m)
.SetDisplay("Overbought Level", "CCI level for shorts", "Indicators");
_oversoldLevel = Param(nameof(OversoldLevel), -100m)
.SetDisplay("Oversold Level", "CCI level for longs", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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 OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = MaPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, cci, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, cci);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!smaValue.IsFormed || !cciValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ma = smaValue.GetValue<decimal>();
var cci = cciValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (close > ma && cci < OversoldLevel && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < ma && cci > OverboughtLevel && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && cci >= 0)
SellMarket(Position);
else if (Position < 0 && cci <= 0)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class ma_cci_strategy(Strategy):
"""
MA CCI strategy.
A close above the MaPeriod SMA with CCI below OversoldLevel goes long and a close below it with CCI above OverboughtLevel goes short,
reversing an opposite position. The position closes once CCI returns to the zero line, and a percent stop limits the loss.
"""
def __init__(self):
super(ma_cci_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 50).SetGreaterThanZero().SetDisplay("MA Period", "Period of the trend SMA", "Indicators")
self._cci_period = self.Param("CciPeriod", 20).SetGreaterThanZero().SetDisplay("CCI Period", "Period of CCI", "Indicators")
self._overbought_level = self.Param("OverboughtLevel", 100.0).SetDisplay("Overbought Level", "CCI level for shorts", "Indicators")
self._oversold_level = self.Param("OversoldLevel", -100.0).SetDisplay("Oversold Level", "CCI level for longs", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(ma_cci_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, cci, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, cci)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, sma_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not sma_value.IsFormed or not cci_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ma = sma_value.GetValue[Decimal](None)
cci = cci_value.GetValue[Decimal](None)
close = candle.ClosePrice
zero = Decimal(0)
if close > ma and cci < Decimal(self._oversold_level.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < ma and cci > Decimal(self._overbought_level.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and cci >= zero:
self.SellMarket(self.Position)
elif self.Position < 0 and cci <= zero:
self.BuyMarket(-self.Position)
def CreateClone(self):
return ma_cci_strategy()