Conversión del MetaTrader 4 asesor experto OzFx (carpeta MQL/7994) al API de alto nivel de StockSharp.
Utiliza el oscilador acelerador/desacelerador (AC) junto con la línea %K del oscilador estocástico para detectar inversiones de impulso alrededor de la línea cero.
Replica el comportamiento del experto de acumular cinco órdenes de mercado con toma de ganancias escalonadas y protección de equilibrio después de alcanzar el primer objetivo.
Lógica de trading
Construya el Oscilador Impresionante (5/34) y reste su SMA de 5 períodos para obtener el valor del Oscilador Acelerador de la vela completada anterior y actual.
Suscríbase al oscilador estocástico (%K longitud = StochasticLength, suavizado 3/3) y lea la línea principal al cerrar la vela.
La configuración larga requiere:
%K por encima del nivel medio configurado (predeterminado 50).
Valor AC actual positivo y superior al anterior.
El valor de CA anterior aún está por debajo de cero (el impulso cruza la línea de base).
Configuración corta refleja las reglas en la dirección opuesta.
Cuando aparece una señal en una nueva barra, la estrategia abre cinco órdenes de mercado iguales:
Las capas 1 a 4 reciben tomas de ganancias espaciadas por TakeProfitPips múltiplos.
La capa 5 no tiene objetivo de ganancias y sigue a la zaga del movimiento.
Si aparece la configuración opuesta mientras una pila está abierta, las órdenes restantes se cierran en el mercado, manteniendo la estrategia plana antes de nuevas entradas.
Gestión de Puestos
Cada capa comparte la misma distancia de stop-loss definida por StopLossPips.
Después de que se ejecuta la primera toma de ganancias, las órdenes restantes ajustan sus límites al precio de equilibrio (de entrada), coincidiendo con la lógica "modok" original.
Las salidas protectoras se ejecutan cuando los extremos de las velas perforan el stop almacenado o los niveles objetivo; Las órdenes pendientes del corredor no se utilizan.
La estrategia permite solo una dirección a la vez y espera a que se cierren todas las órdenes antes de restablecer las banderas del bloque de entrada.
Parámetros
Nombre
Descripción
Predeterminado
OrderVolume
Tamaño de lote para cada una de las cinco órdenes de mercado.
0.1
StopLossPips
Distancia entre la entrada y el stop loss, expresada en pips.
100
TakeProfitPips
Incremento entre niveles consecutivos de toma de ganancias (capas 1-4).
50
StochasticLevel
Umbral aplicado al valor estocástico %K.
50
StochasticLength
Período retrospectivo del cálculo estocástico de %K.
5
CandleType
Serie de velas fuente utilizada por la estrategia (el valor predeterminado es velas de 4 horas).
4h time-frame
Notas de implementación
Las señales se evalúan solo en velas terminadas para mantener la coherencia con el experto de MT4 que trabaja en barras nuevas.
La conversión de pips se adapta automáticamente a símbolos forex de 3/5 dígitos multiplicando el paso mínimo del precio por 10 cuando sea necesario.
Las entradas y salidas escalonadas se manejan en la memoria mediante objetos en capas para que la estrategia pueda cerrar adecuadamente partes de la posición.
Todos los comentarios dentro del código C# están escritos en inglés, como lo exigen las pautas del repositorio.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class OzFxSimpleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OzFxSimpleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class oz_fx_simple_strategy(Strategy):
def __init__(self):
super(oz_fx_simple_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 80).SetDisplay("Slow WMA", "Slow WMA period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 100).SetDisplay("Cooldown", "Candles between signals", "General")
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(oz_fx_simple_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(oz_fx_simple_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown_remaining = 0
fast = WeightedMovingAverage()
fast.Length = self._fast_period.Value
slow = WeightedMovingAverage()
slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
def OnProcess(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self._cooldown_candles.Value
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self._cooldown_candles.Value
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return oz_fx_simple_strategy()