Estrategia Scalpel EA
Esta estrategia es una conversión simplificada del Scalpel EA original escrito para MetaTrader. Combina un filtro de Índice de Canal de Materias Primas (CCI) con análisis de ruptura en múltiples marcos temporales. El objetivo es operar en la dirección del momentum a corto plazo cuando varios marcos temporales superiores confirman el movimiento.
Lógica
- Indicador – CCI calculado en el marco temporal principal. Las operaciones solo se permiten cuando el valor CCI permanece dentro de una banda configurable alrededor de cero.
- Confirmación de tendencia – Para velas de 30 minutos, 1 hora y 4 horas, los máximos y mínimos más recientes se comparan con los anteriores.
- Las operaciones largas requieren mínimos ascendentes en los tres marcos temporales.
- Las operaciones cortas requieren máximos descendentes en los tres marcos temporales.
- Ruptura – La entrada se activa cuando el precio de cierre de la vela principal rompe el máximo (para largos) o el mínimo (para cortos) de la vela anterior.
- Control de riesgo –
StartProtectioncoloca un take-profit y stop-loss fijos medidos en unidades de precio.
Parámetros
| Nombre | Descripción |
|---|---|
CciPeriod |
Período del Índice de Canal de Materias Primas. |
CciLimit |
Umbral absoluto de CCI. Las entradas solo se permiten dentro de ±límite. |
TakeProfit |
Valor de take profit en unidades de precio. |
StopLoss |
Valor de stop loss en unidades de precio. |
CandleType |
Marco temporal principal para el trading (predeterminado 1 minuto). |
Notas
- La estrategia se suscribe a velas adicionales de 30 minutos, 1 hora y 4 horas para evaluar las tendencias de marcos temporales superiores.
- El volumen se toma de la propiedad
Strategy.Volumede la clase base. - Solo hay una posición abierta a la vez. Las señales opuestas cierran la posición existente y abren una nueva.
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>
/// Simplified version of Scalpel EA using multi-timeframe breakout with CCI filter.
/// </summary>
public class ScalpelEaStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLimit;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private CommodityChannelIndex _cci = null!;
private decimal _prevHighMain;
private decimal _prevLowMain;
private decimal _prevHighH4, _prevLowH4, _currHighH4, _currLowH4;
private decimal _prevHighH1, _prevLowH1, _currHighH1, _currLowH1;
private decimal _prevHighM30, _prevLowM30, _currHighM30, _currLowM30;
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Threshold for CCI entries.
/// </summary>
public decimal CciLimit
{
get => _cciLimit.Value;
set => _cciLimit.Value = value;
}
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Primary candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public ScalpelEaStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period of CCI indicator", "Indicators")
.SetOptimize(5, 40, 5);
_cciLimit = Param(nameof(CciLimit), 100m)
.SetDisplay("CCI Limit", "CCI threshold for entries", "Indicators")
.SetOptimize(1m, 10m, 1m);
_takeProfit = Param(nameof(TakeProfit), 30m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
.SetOptimize(10m, 100m, 10m);
_stopLoss = Param(nameof(StopLoss), 21m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
.SetOptimize(10m, 100m, 10m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
yield return (Security, TimeSpan.FromMinutes(30).TimeFrame());
yield return (Security, TimeSpan.FromHours(1).TimeFrame());
yield return (Security, TimeSpan.FromHours(4).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cci?.Reset();
_prevHighMain = _prevLowMain = 0m;
_prevHighH4 = _prevLowH4 = _currHighH4 = _currLowH4 = 0m;
_prevHighH1 = _prevLowH1 = _currHighH1 = _currLowH1 = 0m;
_prevHighM30 = _prevLowM30 = _currHighM30 = _currLowM30 = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cci = new CommodityChannelIndex { Length = CciPeriod };
var main = SubscribeCandles(CandleType);
var m30 = SubscribeCandles(TimeSpan.FromMinutes(30).TimeFrame());
var h1 = SubscribeCandles(TimeSpan.FromHours(1).TimeFrame());
var h4 = SubscribeCandles(TimeSpan.FromHours(4).TimeFrame());
main.Bind(_cci, ProcessMain).Start();
m30.Bind(ProcessM30).Start();
h1.Bind(ProcessH1).Start();
h4.Bind(ProcessH4).Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss, UnitTypes.Absolute)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, main);
DrawIndicator(area, _cci);
DrawOwnTrades(area);
}
}
private void ProcessM30(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_prevHighM30 = _currHighM30;
_prevLowM30 = _currLowM30;
_currHighM30 = candle.HighPrice;
_currLowM30 = candle.LowPrice;
}
private void ProcessH1(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_prevHighH1 = _currHighH1;
_prevLowH1 = _currLowH1;
_currHighH1 = candle.HighPrice;
_currLowH1 = candle.LowPrice;
}
private void ProcessH4(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_prevHighH4 = _currHighH4;
_prevLowH4 = _currLowH4;
_currHighH4 = candle.HighPrice;
_currLowH4 = candle.LowPrice;
}
private void ProcessMain(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
var cciBuy = cciValue > 0m && cciValue < CciLimit;
var cciSell = cciValue < 0m && -cciValue < CciLimit;
var breakoutHigh = candle.ClosePrice > _prevHighMain;
var breakoutLow = candle.ClosePrice < _prevLowMain;
if (cciBuy &&
_currLowH4 > _prevLowH4 &&
_currLowH1 > _prevLowH1 &&
_currLowM30 > _prevLowM30 &&
breakoutHigh &&
Position <= 0)
{
BuyMarket();
}
else if (cciSell &&
_currHighH4 < _prevHighH4 &&
_currHighH1 < _prevHighH1 &&
_currHighM30 < _prevHighM30 &&
breakoutLow &&
Position >= 0)
{
SellMarket();
}
_prevHighMain = candle.HighPrice;
_prevLowMain = candle.LowPrice;
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class scalpel_ea_strategy(Strategy):
def __init__(self):
super(scalpel_ea_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 15)
self._cci_limit = self.Param("CciLimit", 100.0)
self._take_profit = self.Param("TakeProfit", 30.0)
self._stop_loss = self.Param("StopLoss", 21.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._cci = None
self._prev_high_main = 0.0
self._prev_low_main = 0.0
self._prev_high_h4 = 0.0
self._prev_low_h4 = 0.0
self._curr_high_h4 = 0.0
self._curr_low_h4 = 0.0
self._prev_high_h1 = 0.0
self._prev_low_h1 = 0.0
self._curr_high_h1 = 0.0
self._curr_low_h1 = 0.0
self._prev_high_m30 = 0.0
self._prev_low_m30 = 0.0
self._curr_high_m30 = 0.0
self._curr_low_m30 = 0.0
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def CciLimit(self):
return self._cci_limit.Value
@CciLimit.setter
def CciLimit(self, value):
self._cci_limit.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(scalpel_ea_strategy, self).OnStarted2(time)
self._cci = CommodityChannelIndex()
self._cci.Length = self.CciPeriod
main = self.SubscribeCandles(self.CandleType)
m30 = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
h1 = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromHours(1)))
h4 = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromHours(4)))
main.Bind(self._cci, self.ProcessMain).Start()
m30.Bind(self.ProcessM30).Start()
h1.Bind(self.ProcessH1).Start()
h4.Bind(self.ProcessH4).Start()
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessM30(self, candle):
if candle.State != CandleStates.Finished:
return
self._prev_high_m30 = self._curr_high_m30
self._prev_low_m30 = self._curr_low_m30
self._curr_high_m30 = float(candle.HighPrice)
self._curr_low_m30 = float(candle.LowPrice)
def ProcessH1(self, candle):
if candle.State != CandleStates.Finished:
return
self._prev_high_h1 = self._curr_high_h1
self._prev_low_h1 = self._curr_low_h1
self._curr_high_h1 = float(candle.HighPrice)
self._curr_low_h1 = float(candle.LowPrice)
def ProcessH4(self, candle):
if candle.State != CandleStates.Finished:
return
self._prev_high_h4 = self._curr_high_h4
self._prev_low_h4 = self._curr_low_h4
self._curr_high_h4 = float(candle.HighPrice)
self._curr_low_h4 = float(candle.LowPrice)
def ProcessMain(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cci_val = float(cci_value)
cci_buy = cci_val > 0.0 and cci_val < float(self.CciLimit)
cci_sell = cci_val < 0.0 and -cci_val < float(self.CciLimit)
breakout_high = float(candle.ClosePrice) > self._prev_high_main
breakout_low = float(candle.ClosePrice) < self._prev_low_main
if (cci_buy
and self._curr_low_h4 > self._prev_low_h4
and self._curr_low_h1 > self._prev_low_h1
and self._curr_low_m30 > self._prev_low_m30
and breakout_high
and self.Position <= 0):
self.BuyMarket()
elif (cci_sell
and self._curr_high_h4 < self._prev_high_h4
and self._curr_high_h1 < self._prev_high_h1
and self._curr_high_m30 < self._prev_high_m30
and breakout_low
and self.Position >= 0):
self.SellMarket()
self._prev_high_main = float(candle.HighPrice)
self._prev_low_main = float(candle.LowPrice)
def OnReseted(self):
super(scalpel_ea_strategy, self).OnReseted()
self._prev_high_main = 0.0
self._prev_low_main = 0.0
self._prev_high_h4 = 0.0
self._prev_low_h4 = 0.0
self._curr_high_h4 = 0.0
self._curr_low_h4 = 0.0
self._prev_high_h1 = 0.0
self._prev_low_h1 = 0.0
self._curr_high_h1 = 0.0
self._curr_low_h1 = 0.0
self._prev_high_m30 = 0.0
self._prev_low_m30 = 0.0
self._curr_high_m30 = 0.0
self._curr_low_m30 = 0.0
def CreateClone(self):
return scalpel_ea_strategy()