在 GitHub 上查看

AOCCI 策略

概述

AOCCI 策略是 MetaTrader 4 “AOCCI” 智能交易系统的直接移植版。该策略通过结合强势指标 Awesome Oscillator (AO)、商品通道指数 (CCI) 以及上一交易日的枢轴点来过滤信号。此版本基于 StockSharp 的高级 API,并保留了原始脚本的风险控制逻辑。

逻辑流程

  1. 数据准备
    • 使用日内 K 线(默认 1 小时)生成信号。
    • 使用日线数据计算上一交易日的枢轴点(最高价 + 最低价 + 收盘价 再除以 3)。
    • 跟踪最近六根日内 K 线的开盘价以识别跳空行情。
  2. 跳空过滤
    • 任意相邻两根 K 线开盘价差超过 Big Jump Filter 阈值时,放弃本次信号。
    • 任意隔一根的两根 K 线开盘价差超过 Double Jump Filter 阈值时,同样放弃本次信号。
  3. 指标判定
    • 当前 K 线要求 AO 大于 0 且 CCI 大于等于 0。
    • 前一根 K 线至少满足下列条件之一:AO 小于 0、CCI 小于等于 0 或价格位于枢轴点之下。
  4. 方向过滤
    • 当前收盘价必须高于枢轴点。
  5. 下单规则
    • 原始 EA 的做空条件与做多条件完全一致,因此实际上只会开多单。本移植版沿用这一特性。
    • 市价单的数量由 Order Volume 参数控制。
  6. 风控设置
    • 初始止损与止盈均以价格步长(point)为单位。
    • 可选的移动止损在价格向有利方向移动至少一定距离后会自动收紧。

参数

名称 说明
CciPeriod CCI 指标周期,默认 55。
SignalCandleOffset 访问历史日线时使用的附加偏移量,默认 0。
StopLossPoints 止损距离,以价格步长表示。
TakeProfitPoints 止盈距离,以价格步长表示。
TrailingStopPoints 移动止损距离,以价格步长表示(0 表示禁用)。
BigJumpPoints 允许的最大相邻跳空距离,以价格步长表示。
DoubleJumpPoints 允许的最大两根隔柱跳空距离,以价格步长表示。
OrderVolume 下单使用的交易量。
CandleType 日内 K 线类型,默认 1 小时。
DailyCandleType 用于枢轴计算的日线类型。

使用说明

  • 策略需要同时订阅日内和日线数据。
  • 将使用所选标的的最小价格步长来换算止损、止盈和过滤阈值。
  • 移动止损在每根完整 K 线收盘后更新,与原始 EA 的运行方式一致。
  • 由于原始 MQL4 版本从未触发做空信号,移植版本也保持相同的交易方向设置。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AOCCI Pivot strategy - CCI crossover with momentum filter.
/// Buys when CCI crosses above 0 with positive momentum.
/// Sells when CCI crosses below 0 with negative momentum.
/// </summary>
public class AocciPivotFilterStrategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevCci;
	private bool _hasPrev;

	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AocciPivotFilterStrategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetDisplay("CCI Period", "CCI period", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetDisplay("Momentum Period", "Momentum period", "Indicators");

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var mom = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(cci, mom, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevCci = cciValue;
			_hasPrev = true;
			return;
		}

		// CCI crosses above 0 with positive momentum
		if (_prevCci <= 0 && cciValue > 0 && momValue > 0 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// CCI crosses below 0 with negative momentum
		else if (_prevCci >= 0 && cciValue < 0 && momValue < 0 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevCci = cciValue;
	}
}