A Estratégia de Ordem em Linha dispara uma ordem de mercado quando o preço cruza uma linha horizontal definida pelo usuário. Foi concebida como uma conversão simplificada do script MQL original LineOrder.mq4, fornecendo funcionalidade de negociação manual por linhas através da API de alto nível do StockSharp.
A estratégia expõe parâmetros para controlar direção, nível de entrada e gerenciamento de risco. Após entrar em uma posição, níveis opcionais de stop-loss, take-profit e trailing stop são monitorados em cada vela concluída. A lógica é totalmente orientada a eventos e não mantém coleções personalizadas.
Parâmetros
LinePrice – nível de preço para colocar a ordem.
IsBuy – true para entradas compradas, false para entradas vendidas.
StopLoss – distância do stop-loss em unidades de preço (0 desativa).
TakeProfit – distância do take-profit em unidades de preço (0 desativa).
TrailingStop – distância do trailing stop em unidades de preço (0 desativa).
Volume – volume da ordem.
CandleType – tipo de vela utilizado para monitorar o preço.
Regras de Negociação
Entrada: quando o preço de fechamento cruza LinePrice na direção escolhida.
Stop-loss: fecha a posição quando a perda supera a distância StopLoss a partir da entrada.
Take-profit: fecha a posição quando o lucro atinge a distância TakeProfit.
Trailing stop: após a entrada, ajusta-se ao preço mais favorável e fecha quando o preço se move contra a posição em TrailingStop.
Notas
Funciona com qualquer ativo suportado pelo StockSharp.
Projetado para fins educacionais para ilustrar a tradução de negociação manual por linhas do MQL.
A versão em Python foi 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()