La Estrategia de Orden en Línea dispara una orden de mercado cuando el precio cruza una línea horizontal definida por el usuario. Está diseñada como una conversión simplificada del script MQL original LineOrder.mq4, proporcionando funcionalidad de trading manual por líneas a través de la API de alto nivel de StockSharp.
La estrategia expone parámetros para controlar la dirección, el nivel de entrada y la gestión de riesgos. Después de entrar en una posición, los niveles opcionales de stop-loss, take-profit y trailing stop se monitorean en cada vela completada. La lógica es completamente event-driven y no mantiene colecciones personalizadas.
Parámetros
LinePrice – nivel de precio para colocar la orden.
IsBuy – true para entradas largas, false para entradas cortas.
StopLoss – distancia del stop-loss en unidades de precio (0 lo desactiva).
TakeProfit – distancia del take-profit en unidades de precio (0 lo desactiva).
TrailingStop – distancia del trailing stop en unidades de precio (0 lo desactiva).
Volume – volumen de la orden.
CandleType – tipo de vela utilizado para monitorear el precio.
Reglas de Trading
Entrada: cuando el precio de cierre cruza LinePrice en la dirección elegida.
Stop-loss: cierra la posición cuando la pérdida supera la distancia StopLoss desde la entrada.
Take-profit: cierra la posición cuando la ganancia alcanza la distancia TakeProfit.
Trailing stop: tras la entrada, se ajusta al precio más favorable y cierra cuando el precio se mueve contra la posición en TrailingStop.
Notas
Funciona con cualquier valor soportado por StockSharp.
Diseñado con fines educativos para ilustrar la traducción del trading manual por líneas desde MQL.
La versión en Python está intencionalmente omitida.
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>
/// Executes a market order when price crosses a moving average "line".
/// Manages stop-loss and take-profit via candle monitoring.
/// </summary>
public class LineOrderStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _prevClose;
private decimal _prevMa;
private bool _hasPrev;
public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LineOrderStrategy()
{
_maLength = Param(nameof(MaLength), 20)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period for line", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevClose = 0;
_prevMa = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma = new SimpleMovingAverage { Length = MaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal maVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevClose = close;
_prevMa = maVal;
_hasPrev = true;
return;
}
// Check stop-loss / take-profit for existing positions
if (Position > 0 && _entryPrice > 0)
{
if (close <= _entryPrice * (1 - StopLossPct / 100m) ||
close >= _entryPrice * (1 + TakeProfitPct / 100m))
{
SellMarket();
_entryPrice = 0;
_prevClose = close;
_prevMa = maVal;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (close >= _entryPrice * (1 + StopLossPct / 100m) ||
close <= _entryPrice * (1 - TakeProfitPct / 100m))
{
BuyMarket();
_entryPrice = 0;
_prevClose = close;
_prevMa = maVal;
return;
}
}
// Cross above MA line -> buy
if (_prevClose <= _prevMa && close > maVal)
{
if (Position < 0)
{
BuyMarket();
_entryPrice = 0;
}
if (Position <= 0)
{
BuyMarket();
_entryPrice = close;
}
}
// Cross below MA line -> sell
else if (_prevClose >= _prevMa && close < maVal)
{
if (Position > 0)
{
SellMarket();
_entryPrice = 0;
}
if (Position >= 0)
{
SellMarket();
_entryPrice = close;
}
}
_prevClose = close;
_prevMa = maVal;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class line_order_strategy(Strategy):
def __init__(self):
super(line_order_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 20) \
.SetDisplay("MA Length", "Moving average period for line", "Indicators")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._prev_close = 0.0
self._prev_ma = 0.0
self._has_prev = False
@property
def ma_length(self):
return self._ma_length.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(line_order_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
self._prev_ma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(line_order_strategy, self).OnStarted2(time)
ma = SimpleMovingAverage()
ma.Length = self.ma_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if not self._has_prev:
self._prev_close = close
self._prev_ma = ma_val
self._has_prev = True
return
# Check stop-loss / take-profit for existing positions
if self.Position > 0 and self._entry_price > 0:
if (close <= self._entry_price * (1 - self.stop_loss_pct / 100.0) or
close >= self._entry_price * (1 + self.take_profit_pct / 100.0)):
self.SellMarket()
self._entry_price = 0
self._prev_close = close
self._prev_ma = ma_val
return
elif self.Position < 0 and self._entry_price > 0:
if (close >= self._entry_price * (1 + self.stop_loss_pct / 100.0) or
close <= self._entry_price * (1 - self.take_profit_pct / 100.0)):
self.BuyMarket()
self._entry_price = 0
self._prev_close = close
self._prev_ma = ma_val
return
# Cross above MA line -> buy
if self._prev_close <= self._prev_ma and close > ma_val:
if self.Position < 0:
self.BuyMarket()
self._entry_price = 0
if self.Position <= 0:
self.BuyMarket()
self._entry_price = close
# Cross below MA line -> sell
elif self._prev_close >= self._prev_ma and close < ma_val:
if self.Position > 0:
self.SellMarket()
self._entry_price = 0
if self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._prev_close = close
self._prev_ma = ma_val
def CreateClone(self):
return line_order_strategy()