This strategy is a StockSharp port of the MetaTrader "PSAR Trader EA" expert advisor. It observes how price interacts with the Parabolic SAR indicator and reacts only when the dot field flips from one side of the candle body to the other. The conversion preserves the original money-management logic: the strategy can either trade a fixed lot size or dynamically adjust the order volume based on account balance, applies fixed stop-loss and take-profit levels, and activates a trailing stop once a trade accumulates sufficient profit.
Strategy logic
Build a Parabolic SAR indicator with user-defined acceleration and maximum values on the selected candle series (30-minute candles by default).
Detect a bullish flip when the SAR dot moves from above the candle body to below it. If no position is open, submit a market buy order. If a short position exists, close it first and wait for the next signal to re-enter long.
Detect a bearish flip when the SAR dot moves from below the candle body to above it. If flat, open a short position. If a long position is active, close it and defer the entry until the following signal.
Monitor open trades on every finished candle and execute exits whenever any protective level (stop-loss, take-profit, or trailing stop) is reached by the current candle’s high/low.
Risk management
Stop loss – expressed in points (price steps). For long trades the stop is placed below the entry price; for shorts it is placed above.
Take profit – also expressed in points. The target mirrors the stop in the opposite direction and closes the entire position when reached.
Trailing stop – starts after price moves by a configurable number of points in favor of the trade. The trailing stop tightens only in the direction of profit, replicating the “tighten stops only” behavior of the original EA.
Volume management
Fixed lot – when auto-lot is disabled, the strategy submits orders with the configured fixed lot size.
Balance-based lot – when auto-lot is enabled, volume is calculated as (Account Balance / 1000) * LotsPerThousand and aligned to the security’s volume step and minimum volume.
Parameters and defaults
SarStep – Parabolic SAR acceleration factor. Default: 0.02.
SarMaximum – Parabolic SAR maximum acceleration. Default: 0.2.
CandleType – timeframe for the analysis. Default: 30-minute candles.
UseAutoLot – enable dynamic lot sizing. Default: false.
FixedLot – volume used when auto lot sizing is off. Default: 0.1.
LotsPerThousand – multiplier for auto-lot calculations. Default: 0.05.
StopLossPoints – distance to the stop in points. Default: 500.
TakeProfitPoints – distance to the take profit in points. Default: 1000.
TrailingStartPoints – profit threshold that enables trailing. Default: 500.
TrailingDistancePoints – trailing offset once enabled. Default: 100.
Notes
The strategy trades both long and short directions but keeps at most one position open at a time.
Protective orders are simulated on candle data; intra-candle spikes smaller than the selected timeframe may influence fill quality during live trading.
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()