自定义K线类型
S# 支持通过自定义K线类型扩展K线构建功能。当需要使用 S# 尚未内置支持的K线时,此功能非常有用。下面以 Delta K线(根据买入量与卖出量之差形成的K线)为例,介绍创建自定义K线类型的完整过程。
实现 Delta K线
首先创建自定义K线消息类型。该类型必须继承 CandleMessage:
/// <summary> /// 基于买入和卖出成交量差值形成的 K线。 /// </summary> public class DeltaCandleMessage : CandleMessage { // 从辅助方法获取消息类型标识符 // 以便在 RegisterCandleType 中使用相同值 /// <summary> /// 初始化 <see cref="DeltaCandleMessage"/> 的新实例。 /// </summary> public DeltaCandleMessage() : base(DeltaCandleHelper.DeltaCandleType) { } /// <summary> /// K线形成的 delta 阈值。 /// </summary> public decimal DeltaThreshold { get; set; } /// <summary> /// 当前 delta 值。 /// </summary> public decimal CurrentDelta { get; set; } /// <summary> /// 创建 <see cref="DeltaCandleMessage"/> 的副本。 /// </summary> /// <returns>副本。</returns> public override Message Clone() { return CopyTo(new DeltaCandleMessage { DeltaThreshold = DeltaThreshold, CurrentDelta = CurrentDelta }); } /// <summary> /// K线参数。 /// </summary> public override object Arg { get => DeltaThreshold; set => DeltaThreshold = (decimal)value; } /// <summary> /// K线参数类型。 /// </summary> public override Type ArgType => typeof(decimal); }然后在 DataType 中创建自定义数据类型:
public static class DeltaCandleHelper { /// <summary> /// 为 delta-K线定义唯一的 MessageType。 /// </summary> public const MessageTypes DeltaCandleType = (MessageTypes)10001; /// <summary> /// <see cref="DeltaCandleMessage"/> 数据类型。 /// </summary> public static readonly DataType CandleDelta = DataType.Create(typeof(DeltaCandleMessage)).Immutable(); /// <summary> /// 创建 delta-K线的数据类型。 /// </summary> /// <param name="threshold">delta 阈值。</param> /// <returns>数据类型。</returns> public static DataType Delta(this decimal threshold) { return DataType.Create(typeof(DeltaCandleMessage), threshold); } /// <summary> /// 在系统中注册 delta-K线类型。 /// </summary> public static void RegisterDeltaCandleType() { // 在 StockSharp 中注册新的 K线类型 Extensions.RegisterCandleType<decimal>( typeof(DeltaCandleMessage), // Candle message type DeltaCandleType, // Message type "delta", // File name for storage str => str.To<decimal>(), // Converter from string to parameter arg => arg.ToString(), // Converter from parameter to string a => a > 0, // Parameter validator false // Whether such candles can be obtained from the source (not only built) ); } }接下来为新类型创建K线构建器,即实现 CandleBuilder<TCandleMessage>:
/// <summary> /// <see cref="DeltaCandleMessage"/> 类型的 K线构建器。 /// </summary> public class DeltaCandleBuilder : CandleBuilder<DeltaCandleMessage> { /// <summary> /// 初始化 <see cref="DeltaCandleBuilder"/> 的新实例。 /// </summary> /// <param name="exchangeInfoProvider">交易所信息提供者。</param> public DeltaCandleBuilder(IExchangeInfoProvider exchangeInfoProvider) : base(exchangeInfoProvider) { } /// <inheritdoc /> protected override DeltaCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform) { var time = transform.Time; return FirstInitCandle(subscription, new DeltaCandleMessage { DeltaThreshold = subscription.Message.GetArg<decimal>(), OpenTime = time, CloseTime = time, HighTime = time, LowTime = time, CurrentDelta = 0 }, transform); } /// <inheritdoc /> protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, DeltaCandleMessage candle, ICandleBuilderValueTransform transform) { // 当 delta 的绝对值超过阈值时 K线 收盘 return Math.Abs(candle.CurrentDelta) >= candle.DeltaThreshold; } /// <inheritdoc /> protected override void UpdateCandle(ICandleBuilderSubscription subscription, DeltaCandleMessage candle, ICandleBuilderValueTransform transform) { base.UpdateCandle(subscription, candle, transform); // 根据成交方向更新 delta if (transform.Side == Sides.Buy) candle.CurrentDelta += transform.Volume ?? 0; else if (transform.Side == Sides.Sell) candle.CurrentDelta -= transform.Volume ?? 0; } }在 CandleBuilderProvider 中注册K线构建器:
private Connector _connector; ... // 在系统中注册 delta-K线类型 DeltaCandleHelper.RegisterDeltaCandleType(); // 注册 delta-K线构建器 _connector.Adapter.CandleBuilderProvider.Register(new DeltaCandleBuilder(_connector.ExchangeInfoProvider));为
DeltaCandleMessage类型的K线创建订阅并请求数据:// delta 阈值 decimal deltaThreshold = 1000m; // 创建 delta-K线订阅 var subscription = new Subscription( // 使用我们的扩展方法创建数据类型 deltaThreshold.Delta(), security) { MarketData = { // 指定 K线将从 tick 构建 BuildMode = MarketDataBuildModes.Build, BuildFrom = DataType.Ticks } }; // 订阅 K线接收事件 _connector.CandleReceived += (sub, candle) => { if (sub != subscription) return; var deltaCandle = (DeltaCandleMessage)candle; // 处理 delta-K线 Console.WriteLine($"Delta K线 {candle.OpenTime}: O:{candle.OpenPrice} H:{candle.HighPrice} " + $"L:{candle.LowPrice} C:{candle.ClosePrice} V:{candle.TotalVolume} Delta:{deltaCandle.CurrentDelta}"); }; // 订阅切换到在线模式的事件 _connector.SubscriptionOnline += sub => { if (sub == subscription) Console.WriteLine("Delta K线订阅已切换到在线模式"); }; // 启动订阅 _connector.Subscribe(subscription);
在交易策略中使用 Delta K线
以下是使用 Delta K线的简单策略示例:
public class DeltaCandleStrategy : Strategy
{
private readonly StrategyParam<decimal> _deltaThreshold;
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<decimal> _signalDelta;
private IChart _chart;
private IChartCandleElement _chartCandleElement;
private IChartIndicatorElement _deltaIndicatorElement;
public decimal DeltaThreshold
{
get => _deltaThreshold.Value;
set => _deltaThreshold.Value = value;
}
public decimal Volume
{
get => _volume.Value;
set => _volume.Value = value;
}
public decimal SignalDelta
{
get => _signalDelta.Value;
set => _signalDelta.Value = value;
}
public DeltaCandleStrategy()
{
// 策略参数
_deltaThreshold = Param(nameof(DeltaThreshold), 1000m)
.SetDisplay("Delta 阈值", "用于K线形成的成交量差值", "主要设置")
.SetGreaterThanZero()
.SetCanOptimize(true)
.SetOptimize(500m, 2000m, 100m);
_volume = Param(nameof(Volume), 1m)
.SetDisplay("订单数量", "交易操作的数量", "主要设置")
.SetGreaterThanZero();
_signalDelta = Param(nameof(SignalDelta), 500m)
.SetDisplay("信号最小 Delta", "用于信号生成的最小差值", "主要设置")
.SetGreaterThanZero()
.SetCanOptimize(true);
Name = "DeltaCandleStrategy";
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// 如果可用,则初始化图表
_chart = GetChart();
if (_chart != null)
{
var area = _chart.AddArea();
_chartCandleElement = area.AddCandles();
_deltaIndicatorElement = area.AddIndicator();
_deltaIndicatorElement.DrawStyle = DrawStyles.Histogram;
_deltaIndicatorElement.Color = System.Drawing.Color.Purple;
}
// 创建 delta-K线订阅
var subscription = new Subscription(DeltaThreshold.Delta(), Security)
{
MarketData =
{
BuildMode = MarketDataBuildModes.Build,
BuildFrom = DataType.Ticks
}
};
// 创建处理 delta-K线的规则
this
.WhenCandleReceived(subscription)
.Do(ProcessDeltaCandle)
.Apply(this);
// 启动订阅
Subscribe(subscription);
}
private void ProcessDeltaCandle(ICandleMessage candle)
{
// 如果图表可用,则绘制到图表上
if (_chart != null)
{
var deltaCandle = (DeltaCandleMessage)candle;
var data = _chart.CreateData();
data.Group(candle.OpenTime)
.Add(_chartCandleElement, candle)
.Add(_deltaIndicatorElement, deltaCandle.CurrentDelta);
_chart.Draw(data);
}
// 只处理已完成的 K线
if (candle.State != CandleStates.Finished)
return;
var deltaCandle = (DeltaCandleMessage)candle;
// 检查 delta 是否足以形成信号
if (Math.Abs(deltaCandle.CurrentDelta) < SignalDelta)
{
this.AddInfoLog($"Delta {deltaCandle.CurrentDelta} 小于阈值 {SignalDelta}。不生成信号。");
return;
}
// 操作方向取决于 delta 符号
var direction = deltaCandle.CurrentDelta > 0 ? Sides.Buy : Sides.Sell;
this.AddInfoLog($"Delta K线已完成。Delta: {deltaCandle.CurrentDelta}。方向: {direction}");
// 使用 K线收盘价确定价格
var price = deltaCandle.ClosePrice;
var volume = Volume;
// 如果 已经有反向仓位,
// 增加数量以平掉现有仓位
if ((Position < 0 && direction == Sides.Buy) ||
(Position > 0 && direction == Sides.Sell))
{
volume = Math.Max(volume, Math.Abs(Position) + volume);
}
// 注册订单
RegisterOrder(this.CreateOrder(direction, price, volume));
}
}
创建自定义K线类型时的注意事项
MessageTypes 的唯一性 — 确保所选
MessageTypes标识符不与 StockSharp 中的现有类型冲突。建议自定义类型使用大于 10000 的值。注册K线类型 — 必须通过
Extensions.RegisterCandleType注册,才能正确集成 StockSharp 图形控件和数据存储。未注册的K线类型只能在代码中使用,不会出现在用户界面中。K线参数 — 实现返回K线参数类型的
ArgType属性,以便在图形界面中正确显示参数。文件系统 — 使用 StockSharp 数据存储时,
RegisterCandleType方法的fileName参数用于在文件系统中保存K线。参数验证 — StockSharp 会在创建订阅前调用参数验证方法,检查参数值是否有效。
至此,自定义K线类型已经创建完成。它能够与整个 StockSharp 生态系统(包括用户界面和数据存储)集成,并可用于构建基于成交量 Delta 分析的交易策略。