Тренд по Parabolic SAR
Стратегия основана на индикаторе Parabolic SAR.
Тестирование показывает среднегодичную доходность около 49%. Стратегию лучше запускать на крипторынке.
Parabolic SAR Trend следует за точками индикатора. Перемещение цены с одной стороны SAR на другую указывает на возможное изменение тренда. Если цена возвращается обратно, сделка закрывается.
Поскольку точки SAR следуют за ценой, они естественно дают точку выхода при смене тренда. Метод торгует в обе стороны и не использует дополнительные стопы кроме разворота SAR.
Детали
- Критерии входа: сигналы на основе Parabolic SAR.
- Длинные/короткие: оба направления.
- Критерии выхода: противоположный сигнал.
- Стопы: нет.
- Значения по умолчанию:
AccelerationFactor= 0.02mMaxAccelerationFactor= 0.2mCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Тренд
- Направление: Оба
- Индикаторы: Parabolic SAR
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: Внутридневной (5m)
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy based on Parabolic SAR indicator.
/// It enters long position when price is above SAR and short position when price is below SAR.
/// </summary>
public class ParabolicSarTrendStrategy : Strategy
{
private readonly StrategyParam<decimal> _accelerationFactor;
private readonly StrategyParam<decimal> _maxAccelerationFactor;
private readonly StrategyParam<DataType> _candleType;
// Current state
private decimal _prevSarValue;
private bool _prevIsPriceAboveSar;
/// <summary>
/// Initial acceleration factor for SAR.
/// </summary>
public decimal AccelerationFactor
{
get => _accelerationFactor.Value;
set => _accelerationFactor.Value = value;
}
/// <summary>
/// Maximum acceleration factor for SAR.
/// </summary>
public decimal MaxAccelerationFactor
{
get => _maxAccelerationFactor.Value;
set => _maxAccelerationFactor.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the Parabolic SAR Trend strategy.
/// </summary>
public ParabolicSarTrendStrategy()
{
_accelerationFactor = Param(nameof(AccelerationFactor), 0.003m)
.SetDisplay("Acceleration Factor", "Initial acceleration factor for SAR calculation", "Indicators")
.SetOptimize(0.01m, 0.05m, 0.01m);
_maxAccelerationFactor = Param(nameof(MaxAccelerationFactor), 0.03m)
.SetDisplay("Max Acceleration Factor", "Maximum acceleration factor for SAR calculation", "Indicators")
.SetOptimize(0.1m, 0.5m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSarValue = 0;
_prevIsPriceAboveSar = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create Parabolic SAR indicator
var parabolicSar = new ParabolicSar
{
Acceleration = AccelerationFactor,
AccelerationMax = MaxAccelerationFactor
};
// Create subscription and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(parabolicSar, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, parabolicSar);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal sarValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (sarValue <= 0)
return;
// Check the price position relative to SAR
var isPriceAboveSar = candle.ClosePrice > sarValue;
// Detect signal - crossing of price and SAR
var isEntrySignal = _prevSarValue > 0 && isPriceAboveSar != _prevIsPriceAboveSar;
if (isEntrySignal)
{
var volume = Volume + Math.Abs(Position);
// Long entry - price crosses above SAR
if (isPriceAboveSar && Position <= 0)
{
BuyMarket(volume);
LogInfo($"Buy signal: Price {candle.ClosePrice} crossed above SAR {sarValue}");
}
// Short entry - price crosses below SAR
else if (!isPriceAboveSar && Position >= 0)
{
SellMarket(volume);
LogInfo($"Sell signal: Price {candle.ClosePrice} crossed below SAR {sarValue}");
}
}
// Update previous values
_prevSarValue = sarValue;
_prevIsPriceAboveSar = isPriceAboveSar;
}
}
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
class parabolic_sar_trend_strategy(Strategy):
"""
Strategy based on Parabolic SAR indicator.
Enters long when price crosses above SAR, short when price crosses below SAR.
"""
def __init__(self):
super(parabolic_sar_trend_strategy, self).__init__()
self._acceleration_factor = self.Param("AccelerationFactor", 0.003).SetDisplay("Acceleration Factor", "Initial acceleration factor for SAR calculation", "Indicators")
self._max_acceleration_factor = self.Param("MaxAccelerationFactor", 0.03).SetDisplay("Max Acceleration Factor", "Maximum acceleration factor for SAR calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_sar_value = 0.0
self._prev_is_price_above_sar = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(parabolic_sar_trend_strategy, self).OnReseted()
self._prev_sar_value = 0.0
self._prev_is_price_above_sar = False
def OnStarted2(self, time):
super(parabolic_sar_trend_strategy, self).OnStarted2(time)
sar = ParabolicSar()
sar.Acceleration = self._acceleration_factor.Value
sar.AccelerationMax = self._max_acceleration_factor.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sar, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sar_val):
if candle.State != CandleStates.Finished:
return
sv = float(sar_val)
if sv <= 0:
return
is_price_above_sar = float(candle.ClosePrice) > sv
is_entry_signal = self._prev_sar_value > 0 and is_price_above_sar != self._prev_is_price_above_sar
if is_entry_signal:
vol = self.Volume + abs(self.Position)
if is_price_above_sar and self.Position <= 0:
self.BuyMarket(vol)
elif not is_price_above_sar and self.Position >= 0:
self.SellMarket(vol)
self._prev_sar_value = sv
self._prev_is_price_above_sar = is_price_above_sar
def CreateClone(self):
return parabolic_sar_trend_strategy()