Esta estrategia es un port StockSharp del asesor experto de MetaTrader "PSAR Trader EA". Observa cómo el precio interactúa con el indicador Parabolic SAR y reacciona solo cuando el campo de puntos cambia de un lado del cuerpo de la vela al otro. La conversión conserva la lógica original de gestión monetaria: la estrategia puede operar con lote fijo o ajustar dinámicamente el volumen según el balance, aplica stop-loss y take-profit fijos, y activa trailing stop cuando una operación acumula beneficio suficiente.
Lógica de estrategia
Construir un indicador Parabolic SAR con aceleración y valores máximos definidos por el usuario sobre la serie de velas seleccionada (30 minutos por defecto).
Detectar un giro alcista cuando el punto SAR se mueve de encima del cuerpo de la vela a debajo. Si no hay posición, enviar una compra a mercado. Si existe una posición corta, cerrarla primero y esperar la siguiente señal para reentrar largo.
Detectar un giro bajista cuando el punto SAR se mueve de debajo del cuerpo de la vela a encima. Si está plano, abrir corto. Si hay una posición larga activa, cerrarla y diferir la entrada hasta la siguiente señal.
Monitorizar operaciones abiertas en cada vela terminada y ejecutar salidas cuando cualquier nivel protector (stop-loss, take-profit o trailing stop) sea alcanzado por el máximo/mínimo de la vela actual.
Gestión de riesgo
Stop loss: expresado en puntos (pasos de precio). En largos se coloca por debajo de la entrada; en cortos, por encima.
Take profit: también expresado en puntos. El objetivo refleja el stop en dirección opuesta y cierra toda la posición cuando se alcanza.
Trailing stop: empieza después de que el precio se mueve una cantidad configurable de puntos a favor de la operación. El trailing solo se ajusta en dirección del beneficio, replicando el comportamiento "tighten stops only" del EA original.
Gestión de volumen
Lote fijo: cuando auto-lote está desactivado, la estrategia envía órdenes con el lote fijo configurado.
Lote basado en balance: cuando auto-lote está activado, el volumen se calcula como (Account Balance / 1000) * LotsPerThousand y se alinea al paso de volumen y volumen mínimo del instrumento.
Parámetros y predeterminados
SarStep: factor de aceleración Parabolic SAR. Predeterminado: 0.02.
CandleType: marco temporal para el análisis. Predeterminado: velas de 30 minutos.
UseAutoLot: activa tamaño dinámico de lote. Predeterminado: false.
FixedLot: volumen usado cuando auto-lote está desactivado. Predeterminado: 0.1.
LotsPerThousand: multiplicador para cálculos de auto-lote. Predeterminado: 0.05.
StopLossPoints: distancia al stop en puntos. Predeterminado: 500.
TakeProfitPoints: distancia al take profit en puntos. Predeterminado: 1000.
TrailingStartPoints: umbral de beneficio que activa trailing. Predeterminado: 500.
TrailingDistancePoints: offset de trailing cuando está activado. Predeterminado: 100.
Notas
La estrategia opera direcciones larga y corta, pero mantiene como máximo una posición abierta.
Las órdenes protectoras se simulan con datos de velas; picos intravela menores que el marco elegido pueden influir en la calidad de ejecución en trading real.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR Cross strategy: PSAR crossover.
/// Buys when close crosses above PSAR. Sells when close crosses below PSAR.
/// </summary>
public class ParabolicSarCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ParabolicSarCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sar = new ParabolicSar { Acceleration = 0.02m, AccelerationStep = 0.02m, AccelerationMax = 0.2m };
decimal? prevSar = null;
decimal? prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sar, (candle, sarVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (prevSar.HasValue && prevClose.HasValue)
{
var crossUp = prevClose.Value <= prevSar.Value && close > sarVal;
var crossDown = prevClose.Value >= prevSar.Value && close < sarVal;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && Position >= 0)
SellMarket();
}
prevSar = sarVal;
prevClose = close;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
}
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 ParabolicSar
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class parabolic_sar_cross_strategy(Strategy):
def __init__(self):
super(parabolic_sar_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).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(parabolic_sar_cross_strategy, self).OnReseted()
self._prev_sar = None
self._prev_close = None
def OnStarted2(self, time):
super(parabolic_sar_cross_strategy, self).OnStarted2(time)
self._prev_sar = None
self._prev_close = None
sar = ParabolicSar()
sar.Acceleration = 0.02
sar.AccelerationStep = 0.02
sar.AccelerationMax = 0.2
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sar, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def OnProcess(self, candle, sar_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._prev_sar is not None and self._prev_close is not None:
cross_up = self._prev_close <= self._prev_sar and close > sar_val
cross_down = self._prev_close >= self._prev_sar and close < sar_val
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_sar = sar_val
self._prev_close = close
def CreateClone(self):
return parabolic_sar_cross_strategy()