在 GitHub 上查看
SilverTrend ColorJFatl Digit 策略
概述
SilverTrend ColorJFatl Digit 策略将两个经典的 MQL 指标系统封装成一个 StockSharp 高级策略。SilverTrend 模块通过短周期高低价通道来识别突破方向;ColorJFatl Digit 模块则使用 Jurik 移动平均 (JMA) 对价格进行平滑处理,并分析其斜率的正负号。只有当两个模块同时指向同一方向时,策略才会开仓或持仓;一旦信号出现分歧,仓位立即平仓。
该实现遵循 AGENTS.md 的要求:使用 SubscribeCandles().Bind(...) 来连接指标,不直接访问内部缓冲区;延迟逻辑通过轻量队列完成;代码中提供了详细的英文注释,便于继续研究和二次开发。
策略逻辑
1. SilverTrend 突破模块
- 通过
Highest 和 Lowest 指标(窗口长度为 SilverTrendLength + 1)构建最近的价格通道。
SilverTrendRisk 参数对通道进行收缩(原始公式 33 - risk),值越大,通道越窄,信号越敏感。
- 收盘价突破上阈值时认为出现多头趋势 (
+1),跌破下阈值时认为出现空头趋势 (-1)。
SilverTrendSignalBar 设置信号确认所需的闭合 K 线数量,忠实还原原策略的 SignalBar 行为。
2. ColorJFatl Digit 确认模块
- 使用
JurikMovingAverage 对由 JmaPriceType 指定的价格进行平滑,支持 MetaTrader 的全部 applied price 选项(收盘价、开盘价、中值、典型价、趋势跟随价、Demark 等)。
- JMA 输出根据
JmaRoundDigits 四舍五入,模拟原始数字化输出。
- 圆整后的 JMA 斜率为正则输出
+1,为负输出 -1,若斜率为零则沿用上一状态,避免频繁抖动。
JmaSignalBar 指定在动作前需要等待的闭合 K 线数量,实现与原指标相同的延迟确认。
3. 交易执行
- 入场:
- 当两个模块同时给出
+1 且当前没有多头仓位时买入。
- 当两个模块同时给出
-1 且当前没有空头仓位时卖出。
- 离场:
- 两个信号出现分歧(包含返回
0)时立即平仓。
- 方向反转时先平掉原有仓位,再开立新仓,避免加仓或摊平。
- 在方向切换前取消所有挂单,保持委托簿整洁。
参数说明
| 参数 |
说明 |
SilverTrendCandleType |
SilverTrend 通道使用的 K 线类型,默认等价于 H4。 |
SilverTrendLength |
通道回溯长度(原策略中的 SSP)。 |
SilverTrendRisk |
通道收缩因子 (33 - risk),越大越灵敏但也更易假突破。 |
SilverTrendSignalBar |
信号确认所需的闭合 K 线数量。 |
ColorJfatlCandleType |
JMA 模块使用的 K 线类型,可与 SilverTrend 不同。 |
JmaLength |
Jurik 移动平均的长度。 |
JmaSignalBar |
在采纳 JMA 斜率前需要等待的闭合 K 线数。 |
JmaPriceType |
JMA 输入价格类型(close、open、median、TrendFollow 等)。 |
JmaRoundDigits |
对 JMA 输出进行四舍五入时保留的小数位数。 |
实现细节
- 延迟逻辑通过固定长度的 FIFO 队列完成,不需要保存完整历史数据,符合高效内存使用的要求。
- 交易信号完全由高层 API 触发,未直接访问任何指标缓冲;这样更容易维护与测试。
- 代码对每一步计算都有英文注释,解释阈值调整、斜率判断、队列管理和仓位控制逻辑。
- 若运行环境提供图表,策略会绘制价格、SilverTrend 通道以及实际成交,方便监控与复盘。
使用建议
- 适用市场与周期: 原策略面向 H4 外汇图表。对于节奏较慢的商品或加密货币同样有效;若用于更快的周期,请谨慎降低
SilverTrendLength 与 JmaLength。
- 联合优化: 需要同时调整突破窗口与确认窗口(
SilverTrendLength 与 JmaLength),单独修改某一侧容易造成信号不一致。
- Applied price 试验: 在 Heikin-Ashi、Renko 等平滑图表上,TrendFollow 或 Demark 价格常常表现更稳定,可重点测试。
- 风险控制: 虽然双重确认能过滤不少噪声,但极端行情仍可能突破通道。建议结合账户级止损或基于 ATR 的
StartProtection。
- 头寸管理: 策略默认使用
Strategy.Volume 作为下单数量。若需动态调仓,可结合 StockSharp 的资金管理组件。
拓展方向
- 在确认模块中引入更高周期(如日线)以构建多周期趋势过滤。
- 结合成交量或情绪指标(如 OBV、VWAP 偏离)进一步减少震荡区交易。
- 在累计测试后加入
StartProtection,设置 ATR 或百分比形式的止损/止盈。
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>
/// SilverTrend ColorJFatl Digit strategy (simplified). Uses Highest/Lowest channel
/// breakout combined with EMA slope for trend confirmation.
/// </summary>
public class SilverTrendColorJFatlDigitStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _channelLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _riskLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int RiskLevel
{
get => _riskLevel.Value;
set => _riskLevel.Value = value;
}
public SilverTrendColorJFatlDigitStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_channelLength = Param(nameof(ChannelLength), 21)
.SetGreaterThanZero()
.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators");
_emaLength = Param(nameof(EmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period for trend confirmation", "Indicators");
_riskLevel = Param(nameof(RiskLevel), 3)
.SetDisplay("Risk Level", "Channel threshold tightness", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = ChannelLength + 1 };
var lowest = new Lowest { Length = ChannelLength + 1 };
var lastTrend = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, (ICandleMessage candle, decimal highVal, decimal lowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var range = highVal - lowVal;
if (range <= 0)
return;
var riskModifier = 33m - RiskLevel;
if (riskModifier < 0m) riskModifier = 0m;
if (riskModifier > 33m) riskModifier = 33m;
var thresholdPercent = riskModifier / 100m;
var lowerThreshold = lowVal + range * thresholdPercent;
var upperThreshold = highVal - range * thresholdPercent;
var close = candle.ClosePrice;
// SilverTrend breakout logic
if (close < lowerThreshold)
lastTrend = -1;
else if (close > upperThreshold)
lastTrend = 1;
// Simple EMA slope confirmation using close vs channel midpoint
var midpoint = (highVal + lowVal) / 2m;
var emaConfirmUp = close > midpoint;
var emaConfirmDown = close < midpoint;
if (lastTrend > 0 && emaConfirmUp && Position <= 0)
BuyMarket();
else if (lastTrend < 0 && emaConfirmDown && Position >= 0)
SellMarket();
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class silver_trend_color_j_fatl_digit_strategy(Strategy):
def __init__(self):
super(silver_trend_color_j_fatl_digit_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._channel_length = self.Param("ChannelLength", 21) \
.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators")
self._risk_level = self.Param("RiskLevel", 3) \
.SetDisplay("Risk Level", "Channel threshold tightness", "Logic")
self._last_trend = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def ChannelLength(self):
return self._channel_length.Value
@property
def RiskLevel(self):
return self._risk_level.Value
def OnReseted(self):
super(silver_trend_color_j_fatl_digit_strategy, self).OnReseted()
self._last_trend = 0
def OnStarted2(self, time):
super(silver_trend_color_j_fatl_digit_strategy, self).OnStarted2(time)
self._last_trend = 0
highest = Highest()
highest.Length = self.ChannelLength + 1
lowest = Lowest()
lowest.Length = self.ChannelLength + 1
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
rng = hv - lv
if rng <= 0:
return
risk_modifier = 33.0 - self.RiskLevel
if risk_modifier < 0:
risk_modifier = 0.0
if risk_modifier > 33:
risk_modifier = 33.0
threshold_pct = risk_modifier / 100.0
lower_threshold = lv + rng * threshold_pct
upper_threshold = hv - rng * threshold_pct
close = float(candle.ClosePrice)
if close < lower_threshold:
self._last_trend = -1
elif close > upper_threshold:
self._last_trend = 1
midpoint = (hv + lv) / 2.0
if self._last_trend > 0 and close > midpoint and self.Position <= 0:
self.BuyMarket()
elif self._last_trend < 0 and close < midpoint and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return silver_trend_color_j_fatl_digit_strategy()