Ver en GitHub

Estrategia Color Coppock

La Color Coppock Strategy implementa un sistema de trading basado en un oscilador Coppock modificado. El oscilador suma dos valores de Rate of Change (ROC) y suaviza el resultado con una media móvil. El momentum ascendente genera señales largas, mientras que el momentum descendente genera señales cortas.

Cómo Funciona

  1. Calcular dos valores ROC con diferentes períodos.
  2. Sumar ambos valores ROC y aplicar una Media Móvil Simple para el suavizado.
  3. Comparar el valor actual del oscilador con los dos valores anteriores:
    • Si el oscilador gira hacia arriba después de descender, la estrategia entra en una posición larga o cierra la posición corta existente.
    • Si el oscilador gira hacia abajo después de subir, la estrategia entra en una posición corta o cierra la posición larga existente.
  4. El volumen de la posición se toma de la propiedad Volume de la estrategia.

Parámetros

Nombre Descripción
Roc1Period Período para el primer cálculo ROC.
Roc2Period Período para el segundo cálculo ROC.
SmoothingPeriod Período SMA aplicado a la suma de ambos valores ROC.
CandleType Tipo de vela utilizado para los cálculos del indicador.

Uso

  1. Adjuntar la estrategia a un valor y establecer los parámetros deseados.
  2. La estrategia se suscribe a las velas especificadas y procesa solo las velas finalizadas.
  3. Las operaciones se ejecutan con órdenes de mercado usando el volumen predeterminado.

Notas

  • La estrategia utiliza solo llamadas de API de alto nivel como SubscribeCandles y ayudantes de órdenes de mercado.
  • Todos los comentarios dentro del código están escritos en inglés.
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>
/// Strategy based on the Coppock Curve oscillator.
/// Uses ROC sum smoothed by SMA.
/// </summary>
public class ColorCoppockStrategy : Strategy
{
	private readonly StrategyParam<int> _roc1Period;
	private readonly StrategyParam<int> _roc2Period;
	private readonly StrategyParam<int> _smoothingPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _closes = new();
	private readonly List<decimal> _coppockValues = new();
	private decimal? _prevCoppock;
	private decimal? _prevPrevCoppock;

	public int Roc1Period { get => _roc1Period.Value; set => _roc1Period.Value = value; }
	public int Roc2Period { get => _roc2Period.Value; set => _roc2Period.Value = value; }
	public int SmoothingPeriod { get => _smoothingPeriod.Value; set => _smoothingPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ColorCoppockStrategy()
	{
		_roc1Period = Param(nameof(Roc1Period), 14)
			.SetDisplay("ROC1 Period", "First ROC calculation period", "Parameters");
		_roc2Period = Param(nameof(Roc2Period), 10)
			.SetDisplay("ROC2 Period", "Second ROC calculation period", "Parameters");
		_smoothingPeriod = Param(nameof(SmoothingPeriod), 10)
			.SetDisplay("Smoothing Period", "SMA period for ROC sum", "Parameters");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for processing", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_closes.Clear();
		_coppockValues.Clear();
		_prevCoppock = _prevPrevCoppock = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var sma = new ExponentialMovingAverage { Length = Roc1Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_closes.Add(candle.ClosePrice);
		var maxPeriod = Math.Max(Roc1Period, Roc2Period);
		if (_closes.Count > maxPeriod + SmoothingPeriod + 5)
			_closes.RemoveAt(0);

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_closes.Count <= maxPeriod)
			return;

		// Calculate ROC values
		var idx = _closes.Count - 1;
		decimal roc1 = 0, roc2 = 0;

		if (idx >= Roc1Period && _closes[idx - Roc1Period] != 0)
			roc1 = (_closes[idx] - _closes[idx - Roc1Period]) / _closes[idx - Roc1Period] * 100m;
		if (idx >= Roc2Period && _closes[idx - Roc2Period] != 0)
			roc2 = (_closes[idx] - _closes[idx - Roc2Period]) / _closes[idx - Roc2Period] * 100m;

		_coppockValues.Add(roc1 + roc2);
		if (_coppockValues.Count > SmoothingPeriod + 5)
			_coppockValues.RemoveAt(0);

		if (_coppockValues.Count < SmoothingPeriod)
			return;

		// SMA of ROC sum
		var coppock = _coppockValues.Skip(_coppockValues.Count - SmoothingPeriod).Average();

		if (_prevCoppock is decimal prev && _prevPrevCoppock is decimal prevPrev)
		{
			// Buy when Coppock turns up from a bottom
			if (prev < prevPrev && coppock > prev && Position <= 0)
				BuyMarket();
			// Sell when Coppock turns down from a top
			else if (prev > prevPrev && coppock < prev && Position >= 0)
				SellMarket();
		}

		_prevPrevCoppock = _prevCoppock;
		_prevCoppock = coppock;
	}
}