Estrategia Omni Tendencia
Descripción general
La estrategia Omni Tendencia es un port directo del experto MetaTrader "Exp_Omni_Trend". Combina una media móvil con un canal basado en ATR para detectar la tendencia dominante y cambiar entre exposición larga y corta. La versión StockSharp mantiene el comportamiento original, incluyendo el retraso entre la detección de señales y la ejecución de órdenes, así como la capacidad de deshabilitar tramos individuales de entrada o salida.
La estrategia se suscribe a la serie de velas configurada y alimenta cada barra finalizada a la lógica Omni Tendencia. La media móvil sirve como estimación de la tendencia central, mientras que los multiplicadores ATR construyen sobres de volatilidad. Los sobres se comportan como stops trailing: el precio que cierra más allá del límite del sobre anterior cambia la tendencia, genera una nueva señal de entrada en la nueva dirección, y cierra inmediatamente cualquier exposición contraria.
Si los umbrales opcionales de stop-loss y take-profit están habilitados, actúan del lado del broker en pasos de precio, complementando las salidas basadas en indicadores. El tamaño de posición se controla a través de la propiedad integrada Volume de la estrategia (por defecto 1).
Lógica de Trading
- Calcular la media móvil elegida (
MaType, MaLength, AppliedPrice) en el flujo de velas.
- Calcular ATR (
AtrLength) y derivar dos bandas adaptativas usando VolatilityFactor y MoneyRisk. La banda superior protege las posiciones cortas, la banda inferior protege las posiciones largas.
- Cuando el precio supera la banda protectora de la barra anterior, la tendencia cambia:
- Un breakout alcista (
HighPrice por encima de la banda superior anterior) convierte la tendencia en "arriba", cierra cualquier posición corta si se permite, y abre una posición larga después de SignalBar velas completadas.
- Un breakout bajista (
LowPrice por debajo de la banda inferior anterior) convierte la tendencia en "abajo", cierra cualquier posición larga si se permite, y abre una posición corta después del retraso configurado.
- Mientras la tendencia se mantiene alcista, la estrategia continúa solicitando salidas cortas; la regla simétrica se aplica para una tendencia bajista y salidas largas. Esto refleja el comportamiento del experto MetaTrader, donde la banda opuesta fuerza constantemente exposición plana contra la dirección dominante.
- La gestión de riesgo opcional monitorea cada vela finalizada. Si la barra actual alcanza el precio de stop u objetivo (expresado en pasos de precio), la posición se cierra inmediatamente, reiniciando el precio de entrada almacenado.
Las señales se programan a través de una cola FIFO. Cuando SignalBar es cero, se ejecutan al cierre de la misma vela. De lo contrario, se activan en la apertura de la vela que completa el retraso, lo que replica el estilo de ejecución de "barra anterior" del experto fuente.
Parámetros
| Nombre |
Descripción |
Por defecto |
CandleType |
Tipo de vela (marco temporal) utilizado para los cálculos. |
Marco temporal de 4 horas |
MaLength |
Período de la media móvil. |
13 |
MaType |
Método de media móvil: simple, exponencial, suavizada o ponderada linealmente. |
Exponencial |
AppliedPrice |
Campo de precio pasado a la media móvil (cierre, apertura, alto, bajo, mediano, típico, ponderado). |
Cierre |
AtrLength |
Período ATR utilizado por el canal de volatilidad. |
11 |
VolatilityFactor |
Multiplicador aplicado al ATR al construir el canal bruto. |
1.3 |
MoneyRisk |
Factor de desplazamiento que aleja el canal de la media móvil, idéntico a la entrada MQL. |
0.15 |
SignalBar |
Número de velas completadas a esperar antes de actuar sobre una señal. |
1 |
EnableBuyOpen |
Permitir abrir posiciones largas. |
true |
EnableSellOpen |
Permitir abrir posiciones cortas. |
true |
EnableBuyClose |
Permitir cerrar posiciones largas cuando se detecta una tendencia bajista. |
true |
EnableSellClose |
Permitir cerrar posiciones cortas cuando se detecta una tendencia alcista. |
true |
StopLossPoints |
Distancia de stop protector opcional en pasos de precio. Establecer en 0 para deshabilitar. |
1000 |
TakeProfitPoints |
Distancia del objetivo de beneficio opcional en pasos de precio. Establecer en 0 para deshabilitar. |
2000 |
Volume |
Propiedad de la estrategia que controla el tamaño de la operación. |
1 |
Notas y Recomendaciones
- La implementación StockSharp alimenta los mismos valores de indicadores que el original y reproduce sus cambios de tendencia. Sin embargo, las ejecuciones precisas dependen de la fuente de datos y la latencia de ejecución.
- Establezca
SignalBar = 1 para imitar el valor predeterminado del asesor experto, donde las órdenes se ejecutan en la apertura de la siguiente vela después de que una señal esté disponible. Valores más grandes retrasan más la ejecución; establecer 0 ejecuta en el cierre actual.
- Los umbrales de stop-loss y take-profit se expresan en puntos (pasos de precio). Asegúrese de que el valor conectado exponga un
PriceStep válido.
- El gráfico integrado dibuja la serie de velas, la media móvil seleccionada y las operaciones propias de la estrategia para validación visual rápida.
- Deshabilite tramos específicos de entrada o salida para restringir la estrategia a operación unilateral o para manejar salidas manualmente.
- La estrategia no crea órdenes pendientes; emite órdenes de mercado usando
BuyMarket y SellMarket exactamente como el placement de órdenes directo del experto fuente.
Archivos
CS/OmniTrendStrategy.cs — Implementación en C# de la estrategia.
README.md, README_ru.md, README_zh.md — Documentación en inglés, ruso y chino.
El soporte de Python se omite intencionalmente según lo solicitado.
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>
/// Trend-following strategy that replicates the Omni Trend MetaTrader expert.
/// </summary>
public class OmniTrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<MovingAverageMethods> _maType;
private readonly StrategyParam<AppliedPriceTypes> _appliedPrice;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _volatilityFactor;
private readonly StrategyParam<decimal> _moneyRisk;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<bool> _enableBuyOpen;
private readonly StrategyParam<bool> _enableSellOpen;
private readonly StrategyParam<bool> _enableBuyClose;
private readonly StrategyParam<bool> _enableSellClose;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly List<SignalInfo> _pendingSignals = new();
private IIndicator _ma;
private AverageTrueRange _atr;
private decimal _previousSmin;
private decimal _previousSmax;
private decimal _previousTrendUp;
private decimal _previousTrendDown;
private int _previousTrend;
private bool _isInitialized;
private decimal? _longEntryPrice;
private decimal? _shortEntryPrice;
public OmniTrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used to build Omni Trend signals", "General")
;
_maLength = Param(nameof(MaLength), 13)
.SetDisplay("MA Length", "Moving average period", "Indicators")
.SetGreaterThanZero()
;
_maType = Param(nameof(MaType), MovingAverageMethods.Exponential)
.SetDisplay("MA Type", "Moving average calculation method", "Indicators")
;
_appliedPrice = Param(nameof(AppliedPrice), AppliedPriceTypes.Close)
.SetDisplay("Applied Price", "Price field used by the moving average", "Indicators")
;
_atrLength = Param(nameof(AtrLength), 11)
.SetDisplay("ATR Length", "ATR period for volatility bands", "Indicators")
.SetGreaterThanZero()
;
_volatilityFactor = Param(nameof(VolatilityFactor), 1.3m)
.SetDisplay("Volatility Factor", "Multiplier applied to ATR", "Indicators")
.SetGreaterThanZero()
;
_moneyRisk = Param(nameof(MoneyRisk), 0.15m)
.SetDisplay("Money Risk", "Offset factor used to position trend bands", "Indicators")
.SetGreaterThanZero()
;
_signalBar = Param(nameof(SignalBar), 0)
.SetDisplay("Signal Bar", "Delay in bars before acting on a signal", "Trading")
;
_enableBuyOpen = Param(nameof(EnableBuyOpen), true)
.SetDisplay("Enable Long Entries", "Allow opening long positions", "Trading");
_enableSellOpen = Param(nameof(EnableSellOpen), true)
.SetDisplay("Enable Short Entries", "Allow opening short positions", "Trading");
_enableBuyClose = Param(nameof(EnableBuyClose), true)
.SetDisplay("Enable Long Exits", "Allow closing long positions", "Trading");
_enableSellClose = Param(nameof(EnableSellClose), true)
.SetDisplay("Enable Short Exits", "Allow closing short positions", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 0)
.SetDisplay("Stop Loss (points)", "Protective stop distance expressed in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 0)
.SetDisplay("Take Profit (points)", "Profit target distance expressed in price steps", "Risk");
Volume = 1m;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = Math.Max(1, value);
}
public MovingAverageMethods MaType
{
get => _maType.Value;
set => _maType.Value = value;
}
public AppliedPriceTypes AppliedPrice
{
get => _appliedPrice.Value;
set => _appliedPrice.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = Math.Max(1, value);
}
public decimal VolatilityFactor
{
get => _volatilityFactor.Value;
set => _volatilityFactor.Value = value;
}
public decimal MoneyRisk
{
get => _moneyRisk.Value;
set => _moneyRisk.Value = value;
}
public int SignalBar
{
get => Math.Max(0, _signalBar.Value);
set => _signalBar.Value = Math.Max(0, value);
}
public bool EnableBuyOpen
{
get => _enableBuyOpen.Value;
set => _enableBuyOpen.Value = value;
}
public bool EnableSellOpen
{
get => _enableSellOpen.Value;
set => _enableSellOpen.Value = value;
}
public bool EnableBuyClose
{
get => _enableBuyClose.Value;
set => _enableBuyClose.Value = value;
}
public bool EnableSellClose
{
get => _enableSellClose.Value;
set => _enableSellClose.Value = value;
}
public int StopLossPoints
{
get => Math.Max(0, _stopLossPoints.Value);
set => _stopLossPoints.Value = Math.Max(0, value);
}
public int TakeProfitPoints
{
get => Math.Max(0, _takeProfitPoints.Value);
set => _takeProfitPoints.Value = Math.Max(0, value);
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_pendingSignals.Clear();
_ma = null;
_atr = null;
_previousSmin = 0m;
_previousSmax = 0m;
_previousTrendUp = 0m;
_previousTrendDown = 0m;
_previousTrend = 0;
_isInitialized = false;
_longEntryPrice = null;
_shortEntryPrice = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma = CreateMovingAverage(MaType, MaLength);
_atr = new AverageTrueRange
{
Length = AtrLength,
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
if (_ma is not null)
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_ma is null || _atr is null)
return;
var atrValue = _atr.Process(new CandleIndicatorValue(_atr, candle));
var appliedPrice = GetAppliedPrice(candle, AppliedPrice);
var maValue = _ma.Process(new DecimalIndicatorValue(_ma, appliedPrice, candle.OpenTime) { IsFinal = true });
if (!atrValue.IsFinal || !maValue.IsFinal)
return;
CheckRiskManagement(candle);
var atr = atrValue.GetValue<decimal>();
var ma = maValue.GetValue<decimal>();
var signal = CalculateSignal(candle, ma, atr);
_pendingSignals.Add(signal);
while (_pendingSignals.Count > SignalBar)
{
var pending = _pendingSignals[0];
try { _pendingSignals.RemoveAt(0); } catch { break; }
ExecuteSignal(candle, pending);
}
}
private SignalInfo CalculateSignal(ICandleMessage candle, decimal ma, decimal atr)
{
var smax = ma + VolatilityFactor * atr;
var smin = ma - VolatilityFactor * atr;
if (!_isInitialized)
{
_previousSmax = smax;
_previousSmin = smin;
_previousTrendUp = 0m;
_previousTrendDown = 0m;
_previousTrend = 0;
_isInitialized = true;
return SignalInfo.Empty;
}
var trend = _previousTrend;
if (candle.HighPrice > _previousSmax)
trend = 1;
else if (candle.LowPrice < _previousSmin)
trend = -1;
decimal? trendUp = null;
decimal? trendDown = null;
if (trend > 0)
{
if (smin < _previousSmin)
smin = _previousSmin;
var candidate = smin - (MoneyRisk - 1m) * atr;
if (_previousTrend > 0 && _previousTrendUp > 0m && candidate < _previousTrendUp)
candidate = _previousTrendUp;
trendUp = candidate;
}
else if (trend < 0)
{
if (smax > _previousSmax)
smax = _previousSmax;
var candidate = smax + (MoneyRisk - 1m) * atr;
if (_previousTrend < 0 && _previousTrendDown > 0m && candidate > _previousTrendDown)
candidate = _previousTrendDown;
trendDown = candidate;
}
var signal = SignalInfo.Empty;
if (trend > 0)
{
if (_previousTrend <= 0 && trendUp.HasValue && EnableBuyOpen)
signal.BuyOpen = true;
if (trendUp.HasValue && EnableSellClose)
signal.SellClose = true;
}
else if (trend < 0)
{
if (_previousTrend >= 0 && trendDown.HasValue && EnableSellOpen)
signal.SellOpen = true;
if (trendDown.HasValue && EnableBuyClose)
signal.BuyClose = true;
}
_previousTrend = trend;
_previousSmax = smax;
_previousSmin = smin;
_previousTrendUp = trendUp ?? 0m;
_previousTrendDown = trendDown ?? 0m;
return signal;
}
private void ExecuteSignal(ICandleMessage candle, SignalInfo signal)
{
if (signal.BuyClose && Position > 0)
{
var volume = Math.Abs(Position);
if (volume > 0)
SellMarket();
_longEntryPrice = null;
}
if (signal.SellClose && Position < 0)
{
var volume = Math.Abs(Position);
if (volume > 0)
BuyMarket();
_shortEntryPrice = null;
}
var executionPrice = SignalBar == 0 ? candle.ClosePrice : candle.OpenPrice;
if (signal.BuyOpen && Position <= 0)
{
if (Position < 0)
{
var volume = Math.Abs(Position);
BuyMarket();
_shortEntryPrice = null;
}
BuyMarket();
_longEntryPrice = executionPrice;
}
if (signal.SellOpen && Position >= 0)
{
if (Position > 0)
{
var volume = Math.Abs(Position);
SellMarket();
_longEntryPrice = null;
}
SellMarket();
_shortEntryPrice = executionPrice;
}
}
private void CheckRiskManagement(ICandleMessage candle)
{
if (Security is null)
return;
var step = Security?.PriceStep ?? 0.01m;
if (step <= 0m)
return;
if (Position > 0)
{
if (StopLossPoints > 0 && _longEntryPrice.HasValue)
{
var stopPrice = _longEntryPrice.Value - StopLossPoints * step;
if (candle.LowPrice <= stopPrice || candle.ClosePrice <= stopPrice)
{
SellMarket();
_longEntryPrice = null;
return;
}
}
if (TakeProfitPoints > 0 && _longEntryPrice.HasValue)
{
var targetPrice = _longEntryPrice.Value + TakeProfitPoints * step;
if (candle.HighPrice >= targetPrice || candle.ClosePrice >= targetPrice)
{
SellMarket();
_longEntryPrice = null;
return;
}
}
}
else if (Position < 0)
{
if (StopLossPoints > 0 && _shortEntryPrice.HasValue)
{
var stopPrice = _shortEntryPrice.Value + StopLossPoints * step;
if (candle.HighPrice >= stopPrice || candle.ClosePrice >= stopPrice)
{
BuyMarket();
_shortEntryPrice = null;
return;
}
}
if (TakeProfitPoints > 0 && _shortEntryPrice.HasValue)
{
var targetPrice = _shortEntryPrice.Value - TakeProfitPoints * step;
if (candle.LowPrice <= targetPrice || candle.ClosePrice <= targetPrice)
{
BuyMarket();
_shortEntryPrice = null;
return;
}
}
}
}
private static decimal GetAppliedPrice(ICandleMessage candle, AppliedPriceTypes type)
{
return type switch
{
AppliedPriceTypes.Open => candle.OpenPrice,
AppliedPriceTypes.High => candle.HighPrice,
AppliedPriceTypes.Low => candle.LowPrice,
AppliedPriceTypes.Median => (candle.HighPrice + candle.LowPrice) / 2m,
AppliedPriceTypes.Typical => (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m,
AppliedPriceTypes.Weighted => (candle.HighPrice + candle.LowPrice + 2m * candle.ClosePrice) / 4m,
_ => candle.ClosePrice
};
}
private static IIndicator CreateMovingAverage(MovingAverageMethods type, int length)
{
return type switch
{
MovingAverageMethods.Simple => new SMA { Length = length },
MovingAverageMethods.Exponential => new EMA { Length = length },
MovingAverageMethods.Smoothed => new EMA { Length = length },
MovingAverageMethods.LinearWeighted => new SMA { Length = length },
_ => new EMA { Length = length }
};
}
private struct SignalInfo
{
public static readonly SignalInfo Empty = new();
public bool BuyOpen;
public bool BuyClose;
public bool SellOpen;
public bool SellClose;
}
public enum MovingAverageMethods
{
Simple,
Exponential,
Smoothed,
LinearWeighted
}
public enum AppliedPriceTypes
{
Close,
Open,
High,
Low,
Median,
Typical,
Weighted
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class omni_trend_strategy(Strategy):
"""Trend-following strategy replicating the Omni Trend MetaTrader expert."""
def __init__(self):
super(omni_trend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe used to build Omni Trend signals", "General")
self._ma_length = self.Param("MaLength", 13) \
.SetGreaterThanZero() \
.SetDisplay("MA Length", "Moving average period", "Indicators")
self._atr_length = self.Param("AtrLength", 11) \
.SetGreaterThanZero() \
.SetDisplay("ATR Length", "ATR period for volatility bands", "Indicators")
self._volatility_factor = self.Param("VolatilityFactor", 1.3) \
.SetGreaterThanZero() \
.SetDisplay("Volatility Factor", "Multiplier applied to ATR", "Indicators")
self._money_risk = self.Param("MoneyRisk", 0.15) \
.SetGreaterThanZero() \
.SetDisplay("Money Risk", "Offset factor used to position trend bands", "Indicators")
self._signal_bar = self.Param("SignalBar", 0) \
.SetDisplay("Signal Bar", "Delay in bars before acting on a signal", "Trading")
self._enable_buy_open = self.Param("EnableBuyOpen", True) \
.SetDisplay("Enable Long Entries", "Allow opening long positions", "Trading")
self._enable_sell_open = self.Param("EnableSellOpen", True) \
.SetDisplay("Enable Short Entries", "Allow opening short positions", "Trading")
self._enable_buy_close = self.Param("EnableBuyClose", True) \
.SetDisplay("Enable Long Exits", "Allow closing long positions", "Trading")
self._enable_sell_close = self.Param("EnableSellClose", True) \
.SetDisplay("Enable Short Exits", "Allow closing short positions", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 0) \
.SetDisplay("Stop Loss (points)", "Protective stop distance expressed in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 0) \
.SetDisplay("Take Profit (points)", "Profit target distance expressed in price steps", "Risk")
self.Volume = 1
self._ma = None
self._atr = None
self._prev_smin = 0.0
self._prev_smax = 0.0
self._prev_trend_up = 0.0
self._prev_trend_down = 0.0
self._prev_trend = 0
self._is_initialized = False
self._long_entry_price = None
self._short_entry_price = None
self._pending_signals = []
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaLength(self):
return self._ma_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def VolatilityFactor(self):
return self._volatility_factor.Value
@property
def MoneyRisk(self):
return self._money_risk.Value
@property
def SignalBar(self):
return max(0, int(self._signal_bar.Value))
@property
def EnableBuyOpen(self):
return self._enable_buy_open.Value
@property
def EnableSellOpen(self):
return self._enable_sell_open.Value
@property
def EnableBuyClose(self):
return self._enable_buy_close.Value
@property
def EnableSellClose(self):
return self._enable_sell_close.Value
@property
def StopLossPoints(self):
return max(0, int(self._stop_loss_points.Value))
@property
def TakeProfitPoints(self):
return max(0, int(self._take_profit_points.Value))
def OnStarted2(self, time):
super(omni_trend_strategy, self).OnStarted2(time)
self._prev_smin = 0.0
self._prev_smax = 0.0
self._prev_trend_up = 0.0
self._prev_trend_down = 0.0
self._prev_trend = 0
self._is_initialized = False
self._long_entry_price = None
self._short_entry_price = None
self._pending_signals = []
self._ma = ExponentialMovingAverage()
self._ma.Length = self.MaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._ma is None or self._atr is None:
return
atr_result = self._atr.Process(CandleIndicatorValue(self._atr, candle))
applied_price = Decimal(float(candle.ClosePrice))
ma_result = process_float(self._ma, applied_price, candle.OpenTime, True)
if not atr_result.IsFinal or not ma_result.IsFinal:
return
self._check_risk(candle)
atr_v = float(atr_result)
ma_v = float(ma_result)
sig = self._calculate_signal(candle, ma_v, atr_v)
self._pending_signals.append(sig)
while len(self._pending_signals) > self.SignalBar:
pending = self._pending_signals.pop(0)
self._execute_signal(candle, pending)
def _calculate_signal(self, candle, ma_v, atr_v):
vf = float(self.VolatilityFactor)
mr = float(self.MoneyRisk)
smax = ma_v + vf * atr_v
smin = ma_v - vf * atr_v
if not self._is_initialized:
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend_up = 0.0
self._prev_trend_down = 0.0
self._prev_trend = 0
self._is_initialized = True
return [False, False, False, False]
trend = self._prev_trend
if float(candle.HighPrice) > self._prev_smax:
trend = 1
elif float(candle.LowPrice) < self._prev_smin:
trend = -1
trend_up = None
trend_down = None
if trend > 0:
if smin < self._prev_smin:
smin = self._prev_smin
candidate = smin - (mr - 1.0) * atr_v
if self._prev_trend > 0 and self._prev_trend_up > 0.0 and candidate < self._prev_trend_up:
candidate = self._prev_trend_up
trend_up = candidate
elif trend < 0:
if smax > self._prev_smax:
smax = self._prev_smax
candidate = smax + (mr - 1.0) * atr_v
if self._prev_trend < 0 and self._prev_trend_down > 0.0 and candidate > self._prev_trend_down:
candidate = self._prev_trend_down
trend_down = candidate
sig = [False, False, False, False] # buy_open, buy_close, sell_open, sell_close
if trend > 0:
if self._prev_trend <= 0 and trend_up is not None and self.EnableBuyOpen:
sig[0] = True
if trend_up is not None and self.EnableSellClose:
sig[3] = True
elif trend < 0:
if self._prev_trend >= 0 and trend_down is not None and self.EnableSellOpen:
sig[2] = True
if trend_down is not None and self.EnableBuyClose:
sig[1] = True
self._prev_trend = trend
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend_up = trend_up if trend_up is not None else 0.0
self._prev_trend_down = trend_down if trend_down is not None else 0.0
return sig
def _execute_signal(self, candle, sig):
buy_open, buy_close, sell_open, sell_close = sig
if buy_close and self.Position > 0:
self.SellMarket()
self._long_entry_price = None
if sell_close and self.Position < 0:
self.BuyMarket()
self._short_entry_price = None
exec_price = float(candle.ClosePrice) if self.SignalBar == 0 else float(candle.OpenPrice)
if buy_open and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self._short_entry_price = None
self.BuyMarket()
self._long_entry_price = exec_price
if sell_open and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self._long_entry_price = None
self.SellMarket()
self._short_entry_price = exec_price
def _check_risk(self, candle):
sec = self.Security
if sec is None:
return
step = float(sec.PriceStep) if sec.PriceStep is not None else 0.01
if step <= 0:
return
if self.Position > 0:
if self.StopLossPoints > 0 and self._long_entry_price is not None:
stop_price = self._long_entry_price - self.StopLossPoints * step
if float(candle.LowPrice) <= stop_price or float(candle.ClosePrice) <= stop_price:
self.SellMarket()
self._long_entry_price = None
return
if self.TakeProfitPoints > 0 and self._long_entry_price is not None:
target = self._long_entry_price + self.TakeProfitPoints * step
if float(candle.HighPrice) >= target or float(candle.ClosePrice) >= target:
self.SellMarket()
self._long_entry_price = None
return
elif self.Position < 0:
if self.StopLossPoints > 0 and self._short_entry_price is not None:
stop_price = self._short_entry_price + self.StopLossPoints * step
if float(candle.HighPrice) >= stop_price or float(candle.ClosePrice) >= stop_price:
self.BuyMarket()
self._short_entry_price = None
return
if self.TakeProfitPoints > 0 and self._short_entry_price is not None:
target = self._short_entry_price - self.TakeProfitPoints * step
if float(candle.LowPrice) <= target or float(candle.ClosePrice) <= target:
self.BuyMarket()
self._short_entry_price = None
return
def OnReseted(self):
super(omni_trend_strategy, self).OnReseted()
self._pending_signals = []
self._ma = None
self._atr = None
self._prev_smin = 0.0
self._prev_smax = 0.0
self._prev_trend_up = 0.0
self._prev_trend_down = 0.0
self._prev_trend = 0
self._is_initialized = False
self._long_entry_price = None
self._short_entry_price = None
def CreateClone(self):
return omni_trend_strategy()