Estrategia Color Trend CF
Esta estrategia es una conversión del experto MQL Exp_ColorTrend_CF. Utiliza dos medias móviles exponenciales para detectar cambios de tendencia. La EMA rápida reacciona rápidamente a los movimientos de precio, mientras que la EMA lenta actúa como filtro de tendencia. Se abre una posición larga cuando la EMA rápida cruza por encima de la EMA lenta. Se abre una posición corta cuando la EMA rápida cruza por debajo de la EMA lenta.
Parámetros
Period– período base para la EMA rápida; la EMA lenta usa el doble de este valor.StopLoss– distancia de stop-loss en unidades de precio.TakeProfit– distancia de take-profit en unidades de precio.AllowBuyOpen– permiso para abrir posiciones largas.AllowSellOpen– permiso para abrir posiciones cortas.AllowBuyClose– permiso para cerrar posiciones largas en señal de venta.AllowSellClose– permiso para cerrar posiciones cortas en señal de compra.CandleType– marco temporal para el cálculo de indicadores.
Lógica de trading
- Suscribirse a las velas del marco temporal seleccionado.
- Calcular las EMA rápida y lenta.
- Cuando la EMA rápida cruza por encima de la EMA lenta:
- Cerrar posiciones cortas si está permitido.
- Abrir posición larga si está permitido.
- Cuando la EMA rápida cruza por debajo de la EMA lenta:
- Cerrar posiciones largas si está permitido.
- Abrir posición corta si está permitido.
- Para posiciones abiertas, aplicar niveles de stop-loss y take-profit.
Esta implementación utiliza la API de alto nivel de StockSharp con enlace de indicadores.
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 crossing of two exponential moving averages.
/// Shorter EMA represents rising pressure while longer EMA acts as trend
/// filter.
/// </summary>
public class ColorTrendCfStrategy : Strategy {
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<bool> _allowBuyOpen;
private readonly StrategyParam<bool> _allowSellOpen;
private readonly StrategyParam<bool> _allowBuyClose;
private readonly StrategyParam<bool> _allowSellClose;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private bool _isLong;
/// <summary>
/// Base period for the fast EMA. Slow EMA uses double value.
/// </summary>
public int Period {
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss {
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit {
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Whether long entries are allowed.
/// </summary>
public bool AllowBuyOpen {
get => _allowBuyOpen.Value;
set => _allowBuyOpen.Value = value;
}
/// <summary>
/// Whether short entries are allowed.
/// </summary>
public bool AllowSellOpen {
get => _allowSellOpen.Value;
set => _allowSellOpen.Value = value;
}
/// <summary>
/// Whether closing long positions on sell signals is allowed.
/// </summary>
public bool AllowBuyClose {
get => _allowBuyClose.Value;
set => _allowBuyClose.Value = value;
}
/// <summary>
/// Whether closing short positions on buy signals is allowed.
/// </summary>
public bool AllowSellClose {
get => _allowSellClose.Value;
set => _allowSellClose.Value = value;
}
/// <summary>
/// Candle type used for indicator calculation.
/// </summary>
public DataType CandleType {
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ColorTrendCfStrategy"/>.
/// </summary>
public ColorTrendCfStrategy() {
_period = Param(nameof(Period), 30)
.SetGreaterThanZero()
.SetDisplay("CF Period", "Base period for fast EMA",
"Indicator")
.SetOptimize(10, 60, 10);
_stopLoss =
Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
.SetOptimize(100m, 2000m, 100m);
_takeProfit =
Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
.SetOptimize(100m, 4000m, 100m);
_allowBuyOpen = Param(nameof(AllowBuyOpen), true)
.SetDisplay("Allow Buy", "Permission to open long",
"Permissions");
_allowSellOpen =
Param(nameof(AllowSellOpen), true)
.SetDisplay("Allow Sell", "Permission to open short",
"Permissions");
_allowBuyClose =
Param(nameof(AllowBuyClose), true)
.SetDisplay("Close Long", "Allow closing long positions",
"Permissions");
_allowSellClose =
Param(nameof(AllowSellClose), true)
.SetDisplay("Close Short", "Allow closing short positions",
"Permissions");
_candleType =
Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator",
"General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)>
GetWorkingSecurities() {
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted() {
base.OnReseted();
_entryPrice = 0m;
_isLong = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time) {
base.OnStarted2(time);
var fastEma = new EMA { Length = Period };
var slowEma = new EMA { Length = Period * 2 };
var subscription = SubscribeCandles(CandleType);
var prevFast = 0m;
var prevSlow = 0m;
var initialized = false;
subscription
.Bind(fastEma, slowEma,
(candle, fast, slow) => {
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!initialized) {
prevFast = fast;
prevSlow = slow;
initialized = true;
return;
}
var crossUp = prevFast <= prevSlow && fast > slow;
var crossDown = prevFast >= prevSlow && fast < slow;
prevFast = fast;
prevSlow = slow;
if (crossUp) {
if (AllowSellClose && Position < 0)
BuyMarket(Math.Abs(Position));
if (AllowBuyOpen && Position <= 0) {
_entryPrice = candle.ClosePrice;
_isLong = true;
BuyMarket(Volume + Math.Abs(Position));
}
} else if (crossDown) {
if (AllowBuyClose && Position > 0)
SellMarket(Position);
if (AllowSellOpen && Position >= 0) {
_entryPrice = candle.ClosePrice;
_isLong = false;
SellMarket(Volume + Math.Abs(Position));
}
}
if (_entryPrice != 0m) {
if (_isLong && Position > 0) {
var stop = _entryPrice - StopLoss;
var take = _entryPrice + TakeProfit;
if (candle.LowPrice <= stop ||
candle.HighPrice >= take)
SellMarket(Position);
} else if (!_isLong && Position < 0) {
var stop = _entryPrice + StopLoss;
var take = _entryPrice - TakeProfit;
if (candle.HighPrice >= stop ||
candle.LowPrice <= take)
BuyMarket(Math.Abs(Position));
}
}
})
.Start();
StartProtection(null, null);
}
}
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
from StockSharp.Algo.Strategies import Strategy
class color_trend_cf_strategy(Strategy):
def __init__(self):
super(color_trend_cf_strategy, self).__init__()
self._period = self.Param("Period", 30) \
.SetDisplay("CF Period", "Base period for fast EMA", "Indicator")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
self._allow_buy_open = self.Param("AllowBuyOpen", True) \
.SetDisplay("Allow Buy", "Permission to open long", "Permissions")
self._allow_sell_open = self.Param("AllowSellOpen", True) \
.SetDisplay("Allow Sell", "Permission to open short", "Permissions")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Close Long", "Allow closing long positions", "Permissions")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Close Short", "Allow closing short positions", "Permissions")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for indicator", "General")
self._entry_price = 0.0
self._is_long = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def AllowBuyOpen(self):
return self._allow_buy_open.Value
@AllowBuyOpen.setter
def AllowBuyOpen(self, value):
self._allow_buy_open.Value = value
@property
def AllowSellOpen(self):
return self._allow_sell_open.Value
@AllowSellOpen.setter
def AllowSellOpen(self, value):
self._allow_sell_open.Value = value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@AllowBuyClose.setter
def AllowBuyClose(self, value):
self._allow_buy_close.Value = value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@AllowSellClose.setter
def AllowSellClose(self, value):
self._allow_sell_close.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(color_trend_cf_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.Period
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.Period * 2
self.SubscribeCandles(self.CandleType) \
.Bind(fast_ema, slow_ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
if not self._initialized:
self._prev_fast = fast
self._prev_slow = slow
self._initialized = True
return
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
self._prev_fast = fast
self._prev_slow = slow
if cross_up:
if self.AllowSellClose and self.Position < 0:
self.BuyMarket(abs(self.Position))
if self.AllowBuyOpen and self.Position <= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = True
self.BuyMarket()
elif cross_down:
if self.AllowBuyClose and self.Position > 0:
self.SellMarket(self.Position)
if self.AllowSellOpen and self.Position >= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long = False
self.SellMarket()
if self._entry_price != 0.0:
if self._is_long and self.Position > 0:
stop = self._entry_price - float(self.StopLoss)
take = self._entry_price + float(self.TakeProfit)
if float(candle.LowPrice) <= stop or float(candle.HighPrice) >= take:
self.SellMarket(self.Position)
elif not self._is_long and self.Position < 0:
stop = self._entry_price + float(self.StopLoss)
take = self._entry_price - float(self.TakeProfit)
if float(candle.HighPrice) >= stop or float(candle.LowPrice) <= take:
self.BuyMarket(abs(self.Position))
def OnReseted(self):
super(color_trend_cf_strategy, self).OnReseted()
self._entry_price = 0.0
self._is_long = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._initialized = False
def CreateClone(self):
return color_trend_cf_strategy()