在 GitHub 上查看
Exp Skyscraper Fix ColorAML 策略
概述
该策略将 MetaTrader 5 专家顾问 Exp_Skyscraper_Fix_ColorAML 移植到 StockSharp 框架。系统包含两个独立的信号模块:
- Skyscraper Fix – 基于 ATR 的通道,按照自适应边界的方向标记多头或空头趋势。
- ColorAML – 自适应市场水平指标,通过比较局部分形区间来识别扩张或收缩阶段。
原始 MQL 版本为两个不同的 magic 值单独管理仓位,并允许同时持有多空。StockSharp 策略维护净头寸,因此相反信号会互相抵消,最终方向由最后一次进场决定。本说明书特别强调这些差异,便于在回测或交易时评估影响。
参数
Skyscraper Fix 模块
- SkyscraperCandleType – 构建 Skyscraper Fix 指标的K线周期,默认为
4h。
- SkyscraperEnableLongEntry / SkyscraperEnableShortEntry – 是否允许开多或开空。
- SkyscraperEnableLongExit / SkyscraperEnableShortExit – 是否允许平掉现有的多头或空头。
- SkyscraperLength – 计算阶梯步长所用的 ATR 样本数量,默认
10 根。
- SkyscraperMultiplier – 乘以 ATR 的系数,默认
0.9。
- SkyscraperPercentage – 中线的额外百分比偏移,设为 0 表示关闭。
- SkyscraperMode – 选择按 High/Low 还是按 Close 构建通道。
- SkyscraperSignalBar – 读取颜色缓冲区时回溯的已完成K线数,至少为
1。
- SkyscraperVolume – 每次入场使用的下单手数。
- SkyscraperStopLoss / SkyscraperTakeProfit – 以价格步长表示的止损/止盈距离。
ColorAML 模块
- ColorAmlCandleType – ColorAML 使用的K线周期,默认
4h。
- ColorAmlEnableLongEntry / ColorAmlEnableShortEntry – 是否允许开多或开空。
- ColorAmlEnableLongExit / ColorAmlEnableShortExit – 是否允许平掉现有仓位。
- ColorAmlFractal – 分形区间长度,默认
6。
- ColorAmlLag – 自适应平滑的滞后参数,默认
7。
- ColorAmlSignalBar – 读取颜色缓冲区时回溯的已完成K线数。
- ColorAmlVolume – ColorAML 模块开仓时使用的手数。
- ColorAmlStopLoss / ColorAmlTakeProfit – 止损与止盈距离,以价格步长表示。
交易逻辑
策略订阅两个模块各自的K线,只处理已经收盘的蜡烛。两个指标均按照原始 MQL 公式用 C# 重写:
- 当 Skyscraper Fix 的颜色变为 0(看涨),模块会在允许的情况下平掉空头,并在颜色变化时准备开多;颜色变为 1(看跌)时,平掉多头并准备开空。
- ColorAML 通过比较分形区间判断趋势。颜色
2 表示上涨扩张,关闭空头并可选择开多;颜色 0 表示下跌扩张,关闭多头并可选择开空;颜色 1 表示保持现状。
每次进场的下单量为 配置手数 + |当前净头寸|,因此在没有对冲功能的情况下也可以一次性反手并完成仓位切换。
风险管理
策略启动时会调用 StartProtection()。当某个模块开仓后,会记录入场价,并根据该模块的参数计算止损和止盈价位。之后若蜡烛的最高价或最低价触及阈值,就会触发市价单离场。将距离设为 0 可以关闭对应的保护。
实现说明
- Skyscraper Fix 与 ColorAML 的计算逻辑直接移植,全部在内部缓冲区完成,无需手动添加额外指标。
- StockSharp 使用净头寸模式,原策略中的多空同时持仓在此会相互抵消,需提前了解。
- 仅处理已完成的K线;
SignalBar 需大于等于 1,未实现逐笔级别的判断。
- 止损和止盈通过检测蜡烛极值实现,而非服务器端委托,这与转换后框架的能力一致。
使用方法
- 将策略绑定到目标证券与投资组合。
- 根据需要配置两个模块的参数,确保相应的K线数据可用。
- 启动策略,系统会自动订阅K线、计算颜色并按信号发送市价单。
- 通过日志或图表监控趋势切换、风险管理事件以及成交记录。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ExpSkyscraperFixColorAmlStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevDm;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public ExpSkyscraperFixColorAmlStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 14).SetGreaterThanZero().SetDisplay("DeMarker Period", "DeMarker lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevDm = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevDm = null;
var dm = new DeMarker { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(dm, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal dmVal)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevDm = dmVal; return; }
if (_prevDm == null) { _prevDm = dmVal; return; }
if (_prevDm.Value < 0.45m && dmVal >= 0.55m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (_prevDm.Value > 0.55m && dmVal <= 0.45m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
_prevDm = dmVal;
}
}
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 DeMarker
from StockSharp.Algo.Strategies import Strategy
class exp_skyscraper_fix_color_aml_strategy(Strategy):
def __init__(self):
super(exp_skyscraper_fix_color_aml_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 14) \
.SetDisplay("DeMarker Period", "DeMarker lookback", "Indicators")
self._prev_dm = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(exp_skyscraper_fix_color_aml_strategy, self).OnReseted()
self._prev_dm = None
def OnStarted2(self, time):
super(exp_skyscraper_fix_color_aml_strategy, self).OnStarted2(time)
self._prev_dm = None
dm = DeMarker()
dm.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(dm, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, dm_value):
if candle.State != CandleStates.Finished:
return
dv = float(dm_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_dm = dv
return
if self._prev_dm is None:
self._prev_dm = dv
return
if self._prev_dm < 0.45 and dv >= 0.55 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_dm > 0.55 and dv <= 0.45 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_dm = dv
def CreateClone(self):
return exp_skyscraper_fix_color_aml_strategy()