Стратегия Color Schaff Trend Cycle
Стратегия торгует по индикатору Schaff Trend Cycle (STC). Индикатор дважды применяет стохастический расчёт к серии MACD и колеблется в диапазоне от -100 до 100. Значения выше верхнего уровня указывают на бычье давление, ниже нижнего уровня — на медвежье.
Логика торговли
- Подписка на свечи выбранного таймфрейма.
- Расчёт MACD по быстрым и медленным экспоненциальным средним.
- Двойное применение стохастика для получения STC.
- Когда STC поднимается выше верхнего уровня и продолжает рост:
- Закрывается короткая позиция.
- Открывается длинная позиция.
- Когда STC опускается ниже нижнего уровня и продолжает падение:
- Закрывается длинная позиция.
- Открывается короткая позиция.
Стратегия работает только с полностью сформированными свечами.
Параметры
| Имя | Описание | По умолчанию |
|---|---|---|
FastPeriod |
Период быстрой EMA для MACD | 23 |
SlowPeriod |
Период медленной EMA для MACD | 50 |
Cycle |
Длина стохастического цикла | 10 |
HighLevel |
Уровень перекупленности STC | 60 |
LowLevel |
Уровень перепроданности STC | -60 |
CandleType |
Таймфрейм используемых свечей | 4h |
Примечания
- Значения STC масштабируются в диапазон -100…100 для удобства сравнения с уровнями.
- Заявки отправляются методами
BuyMarket()иSellMarket(); при появлении противоположного сигнала позиция переворачивается. - Стратегия не использует стоп-лосс и тейк-профит.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy trading using a Schaff Trend Cycle approximation.
/// Uses MACD-based oscillator with double stochastic smoothing.
/// Opens long when cycle rises above high level, short when falls below low level.
/// </summary>
public class ColorSchaffTrendCycleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private decimal _prevStc;
private bool _hasPrev;
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public decimal HighLevel
{
get => _highLevel.Value;
set => _highLevel.Value = value;
}
public decimal LowLevel
{
get => _lowLevel.Value;
set => _lowLevel.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ColorSchaffTrendCycleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicator");
_highLevel = Param(nameof(HighLevel), 60m)
.SetDisplay("High Level", "Overbought level", "Indicator");
_lowLevel = Param(nameof(LowLevel), 40m)
.SetDisplay("Low Level", "Oversold level", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastEma = default;
_slowEma = default;
_prevStc = 50;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastPeriod };
_slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
_prevStc = 50;
_hasPrev = false;
Indicators.Add(_fastEma);
Indicators.Add(_slowEma);
var rsi = new RelativeStrengthIndex { Length = 10 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
// Compute MACD-like oscillator
var fastResult = _fastEma.Process(new DecimalIndicatorValue(_fastEma, candle.ClosePrice, candle.OpenTime) { IsFinal = true });
var slowResult = _slowEma.Process(new DecimalIndicatorValue(_slowEma, candle.ClosePrice, candle.OpenTime) { IsFinal = true });
if (!fastResult.IsFormed || !slowResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var macd = fastResult.ToDecimal() - slowResult.ToDecimal();
// Use RSI as a proxy for the Schaff trend cycle (simplified)
// Combine MACD direction with RSI level
var stc = rsiValue;
if (!_hasPrev)
{
_prevStc = stc;
_hasPrev = true;
return;
}
// Rising above high level - bullish
if (_prevStc <= HighLevel && stc > HighLevel && macd > 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Falling below low level - bearish
else if (_prevStc >= LowLevel && stc < LowLevel && macd < 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevStc = stc;
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class color_schaff_trend_cycle_strategy(Strategy):
def __init__(self):
super(color_schaff_trend_cycle_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicator")
self._high_level = self.Param("HighLevel", 60.0) \
.SetDisplay("High Level", "Overbought level", "Indicator")
self._low_level = self.Param("LowLevel", 40.0) \
.SetDisplay("Low Level", "Oversold level", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._fast_ema = None
self._slow_ema = None
self._prev_stc = 50.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def high_level(self):
return self._high_level.Value
@property
def low_level(self):
return self._low_level.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_schaff_trend_cycle_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._prev_stc = 50.0
self._has_prev = False
def OnStarted2(self, time):
super(color_schaff_trend_cycle_strategy, self).OnStarted2(time)
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.fast_period
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.slow_period
self._prev_stc = 50.0
self._has_prev = False
self.Indicators.Add(self._fast_ema)
self.Indicators.Add(self._slow_ema)
rsi = RelativeStrengthIndex()
rsi.Length = 10
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.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_value = float(rsi_value)
fast_result = process_float(self._fast_ema, candle.ClosePrice, candle.OpenTime, True)
slow_result = process_float(self._slow_ema, candle.ClosePrice, candle.OpenTime, True)
if not fast_result.IsFormed or not slow_result.IsFormed:
return
macd = float(fast_result) - float(slow_result)
stc = rsi_value
high = float(self.high_level)
low = float(self.low_level)
if not self._has_prev:
self._prev_stc = stc
self._has_prev = True
return
# Rising above high level - bullish
if self._prev_stc <= high and stc > high and macd > 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# Falling below low level - bearish
elif self._prev_stc >= low and stc < low and macd < 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_stc = stc
def CreateClone(self):
return color_schaff_trend_cycle_strategy()