La estrategia Precipice es una conversión directa del asesor experto de MetaTrader Precipice (barabashkakvn's edition). El sistema no analiza la estructura del mercado ni usa indicadores; en cambio, espera a que la posición anterior se cierre y luego lanza una moneda para decidir si entrar largo o corto. Si el trader habilita ambas direcciones, cada vela terminada tiene un 50% de probabilidad de generar una nueva posición siempre que la cuenta esté plana. Las órdenes de protección opcionales reflejan el comportamiento de MetaTrader adjuntando la misma distancia de stop-loss y take-profit en "pips" a cada operación.
La implementación en StockSharp mantiene la naturaleza aleatoria del código original y reproduce sus configuraciones de gestión del dinero. Convierte automáticamente la distancia de pip de MetaTrader al paso de precio nativo del instrumento para que el stop-loss y el take-profit permanezcan simétricos independientemente del número de decimales utilizado por el valor.
Lógica de trading
Suscribirse a la serie de velas primaria definida por CandleType y procesar solo las velas completadas para que el timing de la operación coincida con la lógica OnTick de MetaTrader después de que cierre la barra.
Ignorar todas las señales mientras una posición esté abierta. El experto coloca como máximo una operación a la vez.
Cuando la estrategia está plana, generar un número aleatorio para la rama de compra. Si UseBuy está habilitado y el resultado es inferior a 0.5, enviar una orden de compra de mercado con TradeVolume lotes.
Si no se abrió ninguna posición larga, generar otro número aleatorio para la rama de venta. Cuando UseSell está habilitado y el resultado supera 0.5, enviar una orden de venta de mercado.
Tras la entrada, adjuntar órdenes opcionales de stop-loss y take-profit a StopLossTakeProfitPips pips de MetaTrader del cierre de la vela. Las órdenes de protección se cancelan automáticamente cuando la posición vuelve a cero.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
CandleType
DataType
Marco temporal de 1 minuto
Marco temporal primario procesado por la estrategia.
TradeVolume
decimal
1
Tamaño de la orden usado para cada entrada de mercado.
StopLossTakeProfitPips
int
100
Distancia (en pips de MetaTrader) entre el precio de entrada y ambas órdenes de protección. Establezca en 0 para deshabilitar la colocación de stop-loss y take-profit.
UseBuy
bool
true
Habilitar entradas largas aleatorias.
UseSell
bool
true
Habilitar entradas cortas aleatorias.
Diferencias con el experto original de MetaTrader
MetaTrader expone los niveles de freeze y stop del instrumento; el puerto de StockSharp emula solo la conversión de distancia en pips y depende del broker para rechazar distancias de stop inválidas si es necesario.
El EA original usa las cotizaciones actuales de Bid/Ask. La estrategia de StockSharp basa las órdenes de protección en el precio de cierre de la vela porque la API de alto nivel recibe datos de vela agregados; los efectos de slippage y spread deben manejarse externamente.
MetaTrader trabaja con tickets individuales, mientras que StockSharp gestiona posiciones netas. La conversión mantiene como máximo una posición neta y elimina las órdenes de protección en cuanto la exposición vuelve a cero.
Consejos de uso
Elija un TradeVolume realista que coincida con el paso de lote del valor. El constructor también aplica este valor a Strategy.Volume para que los métodos auxiliares envíen órdenes con la cantidad deseada.
Ajuste StopLossTakeProfitPips según la volatilidad del instrumento. La estrategia multiplica los pips por el paso de precio del valor (escalado para cotizaciones de 3/5 dígitos) para obtener una distancia de precio nativa.
Habilite solo UseBuy o UseSell si desea que el generador aleatorio abra operaciones en una sola dirección, por ejemplo para probar controles de riesgo direccional.
Dado que las entradas son aleatorias, monitoree la estrategia con límites de riesgo adicionales o una duración máxima de posición si necesita condiciones de salida deterministas.
Indicadores
Ninguno. La estrategia se basa puramente en la generación aleatoria de operaciones y órdenes de protección opcionales.
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>
/// Precipice strategy using EMA crossover for trend direction.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class PrecipiceStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="PrecipiceStrategy"/> class.
/// </summary>
public PrecipiceStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 80)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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
from datatype_extensions import *
from indicator_extensions import *
class precipice_strategy(Strategy):
def __init__(self):
super(precipice_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 80).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._sl_points = self.Param("StopLossPoints", 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(precipice_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
def OnStarted2(self, time):
super(precipice_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
def _get_step(self):
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
return float(self.Security.PriceStep)
return 1.0
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if self._prev_fast == 0 or self._prev_slow == 0:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = candle.ClosePrice
step = self._get_step()
if self.Position > 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close <= self._entry_price - self._sl_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close >= self._entry_price + self._tp_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close >= self._entry_price + self._sl_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close <= self._entry_price - self._tp_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return precipice_strategy()