在 GitHub 上查看

Maybeawo222 策略

概述

Maybeawo222 策略是对 MetaTrader 专家顾问 “maybeawo222” 的 StockSharp 高级 API 版本。策略基于单一品种的简单移动平均线(SMA)穿越信号,并只在设定的交易时段内开新仓,同时保留了原版的分阶段保本逻辑。

交易逻辑

  1. 订阅 CandleType 指定的主蜡烛序列,并按 MovingPeriod 计算简单移动平均线。指标值在使用前按照 MovingShift 参数向前回溯指定的已完成K线数量,对应原脚本中的 iMA 函数。
  2. 仅当已完成蜡烛的收盘时间处于 [StartHour, EndHour) 区间时才评估开仓信号。超出时段虽然不会开新仓,但持仓仍会被监控。
  3. 若上一根蜡烛开盘价低于移位后的 SMA、收盘价高于 SMA,则判定为买入信号;反向条件触发卖出信号。策略会根据需要反手,从而始终只保持单向持仓。
  4. 每根蜡烛收盘后都会检查当根最高价与最低价,以确认是否触及止损或止盈价位。一旦命中,即刻通过市价平仓。
  5. 当浮盈超过 BreakevenPips1BreakevenPips2 时,止损价会按照对应的 DesiredBreakevenDistancePips1DesiredBreakevenDistancePips2 向入场价移动,实现两级保本锁盈。

风险控制

  • 初始止损与止盈以“点(pip)”为单位,通过合约的 PriceStep 进行折算;若 PriceStep 小于 0.01,则自动乘以 10 以模拟 MetaTrader 对三位与五位报价的处理方式。
  • 每次开仓都会重置保本状态标记,确保同一仓位在一生中至多触发两次止损前移。
  • 出场均使用市价单,即便经纪商端未挂出保护单,也能在逻辑上实现强制平仓。

参数

名称 默认值 取值范围 / 说明 描述
MovingPeriod 14 正整数 用于判断的 SMA 周期。
MovingShift 0 建议 010 在读取 SMA 值前回溯的已完成蜡烛数量。
StopLossPips 100 0 表示关闭 入场价到止损价的距离(点)。
TakeProfitPips 800 0 表示关闭 入场价到止盈价的距离(点)。
BreakevenPips1 180 0 表示关闭 触发第一次保本的浮盈阈值(点)。
DesiredBreakevenDistancePips1 60 非负值 第一次保本触发后,止损距入场价的目标距离。
BreakevenPips2 500 0 表示关闭 触发第二次保本的浮盈阈值(点)。
DesiredBreakevenDistancePips2 350 非负值 第二次保本触发后,止损距入场价的目标距离。
StartHour 3 023 交易时段开始小时(含)。
EndHour 22 023 交易时段结束小时(不含)。
OrderVolume 0.5 大于 0 每次市价开仓的基础手数。
CandleType H1 任意蜡烛类型 参与信号计算的主蜡烛序列。

使用提示

  • 请确认所选证券提供有效的 PriceStep。若返回值为 0,转换函数会退化为 1,需要相应调整所有以点为单位的参数。
  • 策略按照单品种设计,应在连接器中预先添加目标交易品种后再启动。
  • 实盘运行时可视需要结合经纪商扩展功能添加滑点或外部保护单,以适应更严格的风控要求。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Moving average crossover strategy with price crossing the SMA line.
/// Buys when candle opens below MA and closes above it, sells vice versa.
/// </summary>
public class Maybeawo222Strategy : Strategy
{
	private readonly StrategyParam<int> _movingPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private ExponentialMovingAverage _ema;
	private decimal? _prevClose;
	private decimal? _prevMa;

	public int MovingPeriod
	{
		get => _movingPeriod.Value;
		set => _movingPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public Maybeawo222Strategy()
	{
		_movingPeriod = Param(nameof(MovingPeriod), 20)
			.SetDisplay("MA Period", "Simple moving average period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series", "General");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_ema = new ExponentialMovingAverage { Length = MovingPeriod };
		_prevClose = null;
		_prevMa = null;

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

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

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

		if (!_ema.IsFormed)
		{
			_prevClose = candle.ClosePrice;
			_prevMa = maValue;
			return;
		}

		var close = candle.ClosePrice;
		if (_prevClose is null || _prevMa is null)
		{
			_prevClose = close;
			_prevMa = maValue;
			return;
		}

		// Buy signal: candle crosses MA from below to above
		var buySignal = _prevClose <= _prevMa && close > maValue;
		// Sell signal: candle crosses MA from above to below
		var sellSignal = _prevClose >= _prevMa && close < maValue;

		if (buySignal && Position <= 0)
		{
			BuyMarket(Position < 0 ? Math.Abs(Position) + 1 : 1);
		}
		else if (sellSignal && Position >= 0)
		{
			SellMarket(Position > 0 ? Math.Abs(Position) + 1 : 1);
		}

		_prevClose = close;
		_prevMa = maValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_ema = null;
		_prevClose = null;
		_prevMa = null;

		base.OnReseted();
	}
}