在 GitHub 上查看

Silver Trend 策略

基于自定义 SilverTrend 指标的趋势跟随策略。该指标利用指定周期内的最高价和最低价以及风险参数构建动态价格通道。当价格突破通道并导致趋势方向改变时产生交易信号。

详情

  • 入场:指标转为上升趋势时买入,转为下降趋势时卖出。
  • 出场:出现相反信号时反向开仓。
  • 指标:Highest、Lowest、SimpleMovingAverage(用于 SilverTrend 计算)。
  • 止损:无。
  • 默认值
    • Ssp = 9 — 计算通道的K线数量。
    • Risk = 3 — 缩小通道宽度的百分比。
    • CandleType = 1 小时K线。
  • 方向:做多和做空。

SilverTrend 指标计算 Ssp + 1 根K线的高低价平均范围,并在 Ssp 根K线内寻找最高点和最低点。通道边界如下:

smin = minLow + (maxHigh - minLow) * (33 - Risk) / 100
smax = maxHigh - (maxHigh - minLow) * (33 - Risk) / 100

若收盘价低于 smin,认为趋势转为空头;若高于 smax,认为趋势转为多头。趋势翻转时生成信号,策略会立即反向持仓。

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 SilverTrend indicator -- trades reversals based on
/// price channel breakouts with a risk-based filter.
/// </summary>
public class SilverTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _ssp;
	private readonly StrategyParam<int> _risk;
	private readonly StrategyParam<DataType> _candleType;

	private Highest _highest;
	private Lowest _lowest;
	private bool? _uptrend;
	private bool? _prevUptrend;

	public int Ssp { get => _ssp.Value; set => _ssp.Value = value; }
	public int Risk { get => _risk.Value; set => _risk.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public SilverTrendStrategy()
	{
		_ssp = Param(nameof(Ssp), 9)
			.SetGreaterThanZero()
			.SetDisplay("SSP", "Lookback length for price channel", "Indicator");

		_risk = Param(nameof(Risk), 3)
			.SetGreaterThanZero()
			.SetDisplay("Risk", "Risk factor used to tighten the channel", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for indicator", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_highest = null;
		_lowest = null;
		_uptrend = null;
		_prevUptrend = null;
	}

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

		_highest = new Highest { Length = Ssp };
		_lowest = new Lowest { Length = Ssp };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_highest, _lowest, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!_highest.IsFormed || !_lowest.IsFormed)
			return;

		var k = 33 - Risk;
		var smin = minLow + (maxHigh - minLow) * k / 100m;
		var smax = maxHigh - (maxHigh - minLow) * k / 100m;

		var uptrend = _uptrend ?? false;

		if (candle.ClosePrice < smin)
			uptrend = false;
		else if (candle.ClosePrice > smax)
			uptrend = true;

		var reversed = _uptrend is not null && uptrend != _uptrend;

		if (IsFormedAndOnlineAndAllowTrading() && reversed)
		{
			if (uptrend && Position <= 0)
				BuyMarket();
			else if (!uptrend && Position >= 0)
				SellMarket();
		}

		_prevUptrend = _uptrend;
		_uptrend = uptrend;
	}
}