在 GitHub 上查看

PSAR Trader v2 策略

概述

该策略使用抛物线 SAR 指标捕捉趋势反转。当 SAR 值相对于价格发生翻转时开仓,表示可能出现新的趋势。算法仅在指定的时间区间内运行,并可在出现相反信号时关闭当前持仓。

策略逻辑

  • 指标:Parabolic SAR。
  • 当 SAR 从前一根蜡烛上方翻到当前收盘价下方时 做多
  • 当 SAR 从前一根蜡烛下方翻到当前收盘价上方时 做空
  • 仅在 StartHourEndHour 的时间范围内交易。
  • 若启用 CloseOnOppositeSignal,出现相反信号时会先平仓再开新仓。

风险管理

开仓后策略会设定内部止盈和止损价位,当价格触及其中之一时立即平仓。

参数

名称 说明
CandleType 使用的蜡烛时间框架。
Step SAR 的加速步长。
Maximum SAR 的最大加速系数。
TakeProfit 止盈目标,价格单位。
StopLoss 止损限制,价格单位。
StartHour 开始交易的小时(0–23)。
EndHour 结束交易的小时(0–23)。
CloseOnOppositeSignal 出现相反信号时是否先平仓。

注意

该示例展示了如何使用高级 API 结合常见的趋势反转指标。请根据交易品种和个人偏好调整参数及风险控制。

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>
/// Parabolic SAR trader v2 with reversal logic.
/// </summary>
public class PsarTraderV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _step;
	private readonly StrategyParam<decimal> _maximum;

	private decimal _prevSar;
	private bool _prevPriceAboveSar;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal Step { get => _step.Value; set => _step.Value = value; }
	public decimal Maximum { get => _maximum.Value; set => _maximum.Value = value; }

	public PsarTraderV2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Trading timeframe", "General");

		_step = Param(nameof(Step), 0.001m)
			.SetDisplay("PSAR Step", "Acceleration step for PSAR", "Indicators");

		_maximum = Param(nameof(Maximum), 0.2m)
			.SetDisplay("PSAR Maximum", "Maximum acceleration for PSAR", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevSar = 0;
		_prevPriceAboveSar = false;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var psar = new ParabolicSar
		{
			AccelerationStep = Step,
			AccelerationMax = Maximum
		};

		SubscribeCandles(CandleType).Bind(psar, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal sar)
	{
		if (candle.State != CandleStates.Finished) return;

		var priceAboveSar = candle.ClosePrice > sar;

		if (!_hasPrev)
		{
			_prevSar = sar;
			_prevPriceAboveSar = priceAboveSar;
			_hasPrev = true;
			return;
		}

		if (priceAboveSar != _prevPriceAboveSar)
		{
			if (priceAboveSar && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
			}
			else if (!priceAboveSar && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
			}
		}

		_prevSar = sar;
		_prevPriceAboveSar = priceAboveSar;
	}
}