第 3 章 · 01 五大核心 Handler 接口


文档摘要

第 3 章 · 01 五大核心 Handler 接口 本节摘要:本节是全书第一个高潮(★)的开篇。Lean 把"引擎要做的所有事"切成五大核心 Handler 接口——IDataFeed(数据流)/ISetupHandler(装配)/IResultHandler(结果)/ITransactionHandler(交易)/IRealTimeHandler(定时),加上 IHistoryProvider(历史)与 IBrokerage(券商)。每个接口都标了 ,这是 MEF(Managed Extensibility Framework)的"自注册"标记,实现类只要写好就能被 Composer 扫描到,不用手动登记。

第 3 章 · 01 五大核心 Handler 接口

本节摘要:本节是全书第一个高潮(★)的开篇。Lean 把"引擎要做的所有事"切成五大核心 Handler 接口——IDataFeed(数据流)/ISetupHandler(装配)/IResultHandler(结果)/ITransactionHandler(交易)/IRealTimeHandler(定时),加上 IHistoryProvider(历史)与 IBrokerage(券商)。每个接口都标了 [InheritedExport(typeof(IXxx))],这是 MEF(Managed Extensibility Framework)的"自注册"标记,实现类只要写好就能被 Composer 扫描到,不用手动登记。这套接口设计是"回测实盘统一"的地基——下一节你会看到,每个接口都有 Backtesting* 与 LiveTrading* 两套实现,引擎主体对所有实现一视同仁。

内容来源:原项目源码 Engine/DataFeeds/IDataFeed.csEngine/Setup/ISetupHandler.csEngine/Results/IResultHandler.csEngine/TransactionHandlers/ITransactionHandler.csEngine/RealTime/IRealTimeHandler.csCommon/Interfaces/IHistoryProvider.csCommon/Interfaces/IBrokerage.cs,精读并套用体系化模板。

⚠️ 注意:本节只讲"接口契约",不讲具体实现。五大接口的实现在 Engine 子目录(FileSystemDataFeed/BacktestingSetupHandler/...),IBrokerage/IHistoryProvider 的实现在 Brokerages 项目与 Engine/HistoricalData 子目录。双实现对照是第 02 节的主题。注意 IBrokerage 接口本身没有 [InheritedExport],券商通过 IBrokerageFactory(带 [InheritedExport])装配——这是它与五大 Handler 的细微差别。

学习目标

阅读完本节,你应当能够:

  1. 列举五大核心 Handler 接口的全名与所在文件路径。
  2. 说出每个接口的 4-6 个关键方法签名及职责。
  3. 理解 [InheritedExport(typeof(IXxx))] 的"接口级自注册"语义。
  4. 区分 ITransactionHandler 继承的 IOrderProcessor/IOrderEventProvider 与 IRealTimeHandler 继承的 IEventSchedule
  5. 说清 IHistoryProvider 与 IBrokerage 在装配链中的角色(谁被谁注入)。

一、Handler 插件层的全景图

第 1-2 章我们看到 AlgorithmManager 是回测主循环,它每收到一个 TimeSlice 就调一次算法的 OnData。但 AlgorithmManager 自己不碰磁盘、不碰券商、不写日志——这些活全交给"Handler 插件层"。Lean 把引擎能力切成五块:

接口 文件 职责一句话
IDataFeed Engine/DataFeeds/IDataFeed.cs 喂数据(行情/基本面)给算法
ISetupHandler Engine/Setup/ISetupHandler.cs 装配算法+券商,执行 Initialize
IResultHandler Engine/Results/IResultHandler.cs 输出结果(图表/统计/日志/错误)
ITransactionHandler Engine/TransactionHandlers/ITransactionHandler.cs 处理订单与成交回填
IRealTimeHandler Engine/RealTime/IRealTimeHandler.cs 定时事件(每天收盘、每月报告)

外加两个 Common 层接口(不在 Engine 目录,但同样重要):

接口 文件 职责
IHistoryProvider Common/Interfaces/IHistoryProvider.cs 提供 History() 请求的历史数据
IBrokerage Common/Interfaces/IBrokerage.cs 券商抽象(下单/查仓/查现/连接)

💡 钻取要点:这七个接口的奇妙之处——Common/Interfaces/ 目录下共 46 个 .cs 文件(其中 43 个是真正的接口定义,另 3 个是 *EventArgs/*Parameters 辅助类)。它们是整个 Lean 项目的"契约中枢"。Engine 的五个 Handler 接口虽然定义在 Engine 目录,但它们的入参大量引用 Common/Interfaces 里的类型(如 IAlgorithm/AlgorithmNodePacket/IBrokerage)。Common 层是"谁都依赖、谁都不依赖 Common"的最底层。

二、IDataFeed:数据流的源头

Engine/DataFeeds/IDataFeed.cs:28-69(精简):

28 [InheritedExport(typeof(IDataFeed))] 29 public interface IDataFeed 30 { 34 bool IsActive { get; } 35 42 void Initialize(IAlgorithm algorithm, 43 AlgorithmNodePacket job, 44 IResultHandler resultHandler, 45 IMapFileProvider mapFileProvider, 46 IFactorFileProvider factorFileProvider, 47 IDataProvider dataProvider, 48 IDataFeedSubscriptionManager subscriptionManager, 49 IDataFeedTimeProvider dataFeedTimeProvider, 50 IDataChannelProvider dataChannelProvider); 51 57 Subscription CreateSubscription(SubscriptionRequest request); 58 63 void RemoveSubscription(Subscription subscription); 64 68 void Exit(); 69 }

四个核心动作:

  1. Initialize:注入算法、任务包、结果处理器、三个数据辅助 provider(地图文件/因子文件/数据通道)。这是所有 Handler 里入参最多的一个——因为数据流需要知道标的清单、复权方式、数据存放位置等。
  2. CreateSubscription:为某个 SubscriptionRequest(标的+分辨率+起止时间)创建一个 Subscription(订阅句柄),返回给 DataManager 管理。返回 null 表示创建失败。
  3. RemoveSubscription:移除订阅,回收资源。
  4. Exit:外部信号"该收尾了",关闭数据线程。

💡 钻取要点:[InheritedExport(typeof(IDataFeed))] 写在接口上而非实现类上。这是 MEF 的"继承式导出"——任何实现 IDataFeed 的类(无论是 FileSystemDataFeed 还是 LiveTradingDataFeed)都会被自动注册为这个契约的导出。这是 Lean 插件化的关键:新增一种数据源,只需 public class XxxDataFeed : IDataFeed,编译进 DLL,Composer 启动时扫描程序集就自动发现它,不用改任何注册代码。

三、ISetupHandler:装配算法与券商

Engine/Setup/ISetupHandler.cs:29-104(精简):

29 [InheritedExport(typeof(ISetupHandler))] 30 public interface ISetupHandler : IDisposable 31 { 43 List<Exception> Errors { get; set; } 52 TimeSpan MaximumRuntime { get; } 60 decimal StartingPortfolioValue { get; } 68 DateTime StartingDate { get; } 76 int MaxOrders { get; } 87 IAlgorithm CreateAlgorithmInstance(AlgorithmNodePacket algorithmNodePacket, string assemblyPath); 96 IBrokerage CreateBrokerage(AlgorithmNodePacket algorithmNodePacket, IAlgorithm uninitializedAlgorithm, out IBrokerageFactory factory); 103 bool Setup(SetupHandlerParameters parameters); 104 }

三个关键方法构成了"装配三段式":

  1. CreateAlgorithmInstance(job, assemblyPath):反射加载用户 DLL,实例化 IAlgorithmassemblyPathconfig.jsonalgorithm-location 指向的 DLL(如 QuantConnect.Algorithm.CSharp.dll)。回测与实盘都走这里,差异只在 DLL 里的用户代码。
  2. CreateBrokerage(job, algorithm, out factory):创建券商实例。回测返回 BacktestingBrokerage(模拟撮合),实盘返回真实券商(Interactive Brokers/盈透等)。out factory 顺带返回工厂,供后续创建消息处理器用。
  3. Setup(parameters):主入口。执行 algorithm.Initialize()(用户写的初始化代码),设置初始现金、起止日期、最大订单数等。返回 true/false 表示是否成功。

💡 钻取要点:ISetupHandler : IDisposable——五个核心接口里只有它继承 IDisposable。因为 Setup 阶段可能持有非托管资源(如反射加载的程序集句柄)。ErrorsList<Exception>,Setup 出错时不立即抛,而是累积到这里,由 Engine.Run 统一汇报(L271-293)。这是"宽容失败"模式——尽量收集所有错误一次性报给用户,而不是在第一个错误处崩溃。

四、IResultHandler:结果输出中枢

Engine/Results/IResultHandler.cs:34-172(精简,挑关键方法):

34 [InheritedExport(typeof(IResultHandler))] 35 public interface IResultHandler : IStatisticsService 36 { 64 void Initialize(ResultHandlerInitializeParams parameters); 70 void DebugMessage(string message); 76 void SystemDebugMessage(string message); 88 void LogMessage(string message); 95 void ErrorMessage(string error, string stacktrace = ""); 102 void RuntimeError(string message, string stacktrace = ""); 115 void Sample(DateTime time); 122 void SetAlgorithm(IAlgorithm algorithm, decimal startingPortfolioValue); 142 void OrderEvent(OrderEvent newEvent); 152 void ProcessSynchronousEvents(bool forceProcess = false); 147 void Exit(); 159 void SaveResults(string name, Result result); 166 void AlgorithmNameUpdated(string name); 171 void AlgorithmTagsUpdated(HashSet<string> tags); 172 }

职责分三类:

  1. 消息类:DebugMessage/SystemDebugMessage/LogMessage/ErrorMessage/RuntimeError/BrokerageMessage。用户代码里 Debug("...")/Log("...") 最终都调到这里。回测实现写到磁盘 JSON,实盘实现推送到云端消息队列。
  2. 采样类:Sample(time)——每天由 AlgorithmManager 调一次,采集组合净值/图表点,用于绘制权益曲线。SetAlgorithm 在装配完成后注入算法引用。
  3. 生命周期:Initialize/ProcessSynchronousEvents/Exit/SaveResultsProcessSynchronousEvents 在算法主循环的同步点调用,处理累积的消息队列;SaveResults 在结束时把统计结果序列化到磁盘。

💡 钻取要点:IResultHandler : IStatisticsService——它还承担"统计服务"。回测时算夏普/最大回撤/胜率等指标都通过这条线。OrderEvent(newEvent) 每次成交都回调,实盘实现用它实时推送订单状态给前端。这是结果处理器最频繁的调用入口。

五、ITransactionHandler:订单与成交回填

Engine/TransactionHandlers/ITransactionHandler.cs:31-83(完整):

31 [InheritedExport(typeof(ITransactionHandler))] 32 public interface ITransactionHandler : IOrderProcessor, IOrderEventProvider 33 { 38 bool IsActive { get; } 46 ConcurrentDictionary<int, Order> Orders { get; } 54 IEnumerable<OrderEvent> OrderEvents { get; } 59 ConcurrentDictionary<int, OrderTicket> OrderTickets { get; } 67 void Initialize(IAlgorithm algorithm, IBrokerage brokerage, IResultHandler resultHandler); 72 void Exit(); 77 void ProcessSynchronousEvents(); 82 void AddOpenOrder(Order order, IAlgorithm algorithm); 83 }

注意它继承了两个接口:

  • IOrderProcessor:提供 Process(OrderRequest)——用户调 MarketOrder(...) 最终走这里。这是"下单入口"。
  • IOrderEventProvider:提供 NewOrderEvent 事件——成交回填时触发,Portfolio 监听它更新持仓。

两个字典是订单的全量存储:

字典 key value 用途
Orders int(orderId) Order 所有订单的永久存储
OrderTickets int(orderId) OrderTicket 订单票据(用户可查/改/撤)

💡 钻取要点:Initialize(algorithm, brokerage, resultHandler) 把券商注入进来。回测时 brokerageBacktestingBrokerage(模拟撮合),实盘时是真实券商。TransactionHandler 充当"用户下单 → 券商执行 → 成交回报"的中介。用户代码只看到 MarketOrder(...) 返回 OrderTicket,完全不知道背后是模拟还是真实券商。这就是"回测实盘统一"在交易侧的体现。

六、IRealTimeHandler:定时事件调度

Engine/RealTime/IRealTimeHandler.cs:30-67(完整):

30 [InheritedExport(typeof(IRealTimeHandler))] 31 public interface IRealTimeHandler : IEventSchedule 32 { 36 bool IsActive { get; } 43 void Setup(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IApi api, IIsolatorLimitResultProvider isolatorLimitProvider); 49 void SetTime(DateTime time); 53 void ScanPastEvents(DateTime time); 56 void Exit(); 61 void OnSecuritiesChanged(SecurityChanges changes); 67 }

注意它继承 IEventSchedule——提供 Add(ScheduledEvent)/Remove(ScheduledEvent),管理用户通过 Schedule.On(...) 注册的定时事件(如每天收盘前 10 分钟平仓)。

三个关键方法体现"时钟"语义:

  1. Setup:装配阶段注入算法,把所有 ScheduledEvent 注册进来。
  2. SetTime(time):设置当前时间。回测实现把模拟时钟推进到数据时间;实盘实现用墙钟(wall clock)。这是回测与实盘在"时间维度"上的核心分叉(第 02 节详述)。
  3. ScanPastEvents(time):补漏——扫描那些因为某个时刻没有数据而"漏触发"的事件。比如用户设了"每天 09:30 触发",但某天 09:30 没有数据点(停牌),实盘靠墙钟能正常触发,回测靠这个方法补。

💡 钻取要点:OnSecuritiesChanged 在动态调仓时被调用——当 universe 选股结果变化(新增/移除标的)时,RealTimeHandler 要把这些标的的定时事件也同步增删。这是五大 Handler 里唯一一个所有接口都实现的"联动回调"(IDataFeed/IResultHandler/IRealTimeHandler 都有同名方法),体现了"标的集变化会波及数据订阅、结果统计、定时事件三处"的设计考量。

七、IHistoryProvider 与 IBrokerage:Common 层两支柱

IHistoryProvider(历史数据)

Common/Interfaces/IHistoryProvider.cs:27-48(精简):

27 [InheritedExport(typeof(IHistoryProvider))] 28 public interface IHistoryProvider : IDataProviderEvents 29 { 33 int DataPointCount { get; } 38 void Initialize(HistoryProviderInitializeParams parameters); 46 IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> requests, DateTimeZone sliceTimeZone); 48 }

用户代码里 History<TradeBar>("SPY", 30, Resolution.Daily) 最终调 GetHistory,返回 IEnumerable<Slice>DataPointCount 记录发射了多少数据点,用于回测结束时的吞吐量统计(Engine.Run L402 算"k data points per second")。回测用 SubscriptionDataReaderHistoryProvider 读磁盘,实盘用 BrokerageHistoryProvider 问券商要。

IBrokerage(券商抽象)

Common/Interfaces/IBrokerage.cs:29-160(精简挑核心):

29 public interface IBrokerage : IBrokerageCashSynchronizer, IDisposable 30 { 34 event EventHandler<BrokerageOrderIdChangedEvent> OrderIdChanged; 39 event EventHandler<List<OrderEvent>> OrdersStatusChanged; 47 event EventHandler<OrderUpdateEvent> OrderUpdated; 73 event EventHandler<AccountEvent> AccountChanged; 78 event EventHandler<BrokerageMessageEvent> Message; 82 string Name { get; } 88 bool IsConnected { get; } 94 List<Order> GetOpenOrders(); 99 List<Holding> GetAccountHoldings(); 106 List<CashAmount> GetCashBalance(); 113 bool PlaceOrder(Order order); 120 bool UpdateOrder(Order order); 127 bool CancelOrder(Order order); 132 void Connect(); 137 void Disconnect(); 154 IEnumerable<BaseData> GetHistory(HistoryRequest request); 160 }

IBrokerage 把券商能力抽象成"连接 + 查询 + 下单 + 事件回调"四类:

  • 连接:Connect/Disconnect/IsConnected
  • 查询:GetCashBalance(查现金)/GetAccountHoldings(查持仓)/GetOpenOrders(查未成交)。
  • 下单:PlaceOrder/UpdateOrder/CancelOrder(下/改/撤),返回 bool 表示请求是否提交成功(不等于成交)。
  • 事件:OrdersStatusChanged(订单状态变化)/OrderUpdated(订单字段更新,如止损价)/AccountChanged(账户变化)/Message(券商文本消息)。

⚠️ 注意:IBrokerage 接口本身没有 [InheritedExport](看 IBrokerage.cs:29 那一行只有继承没有特性)。它的实现类通过 IBrokerageFactory(带 [InheritedExport(typeof(IBrokerageFactory))])装配——config.json 里 live-mode-brokerage 指定的是工厂类名(如 "PaperBrokerage"),工厂再 Create(brokerageName, algorithm, job) 出券商实例。这是 IBrokerage 与五大 Handler 的装配路径差异:Handler 用 Composer.Instance.GetExportedValueByTypeName<IResultHandler>(typeName) 直接实例化,Brokerage 走工厂模式(因为券商创建需要算法引用、订单处理器注入等复杂参数,工厂能封装这些)。

八、接口设计的三个共性

通览七个接口,能提炼三条共性:

1. 都有 Initialize + Exit 生命周期。 装配阶段调 Initialize 注入依赖,收尾阶段调 Exit 关闭线程/连接。Engine.Run 的 try-finally 保证 Exit 一定被调用(L467-471)。

2. 都有 IsActive 标志。 Engine 收尾时(L429-433)轮询各 Handler 的 IsActive,等所有线程都退出才真正结束。这是优雅停机的协调点。

3. 入参都用 DTO 类封装。SetupHandlerParameters/ResultHandlerInitializeParams/HistoryProviderInitializeParams。这是"参数对象"模式——避免方法签名随依赖增多而膨胀,新增依赖只改 DTO 类不改接口签名。

💡 钻取要点:这套接口设计是"开闭原则"的典范——对扩展开放(新增 Handler 实现只需继承接口),对修改关闭(Engine 主体代码不用改)。下一节你会看到,从回测切到实盘,Engine 主体一行代码都不用动,只换 config.json 里五个 handler 的类名字符串。这就是 Lean 引擎被誉为"C# 量化引擎典范"的核心原因。

本节要点回顾

  1. 五大核心 Handler:IDataFeed(数据)/ISetupHandler(装配)/IResultHandler(结果)/ITransactionHandler(交易)/IRealTimeHandler(定时),都在 Engine 子目录,都带 [InheritedExport(typeof(IXxx))]
  2. MEF 自注册:[InheritedExport] 写在接口上,实现类自动被 Composer 发现,不用手动登记。
  3. ISetupHandler 三段式:CreateAlgorithmInstance(反射加载 DLL)→ CreateBrokerage(创建券商)→ Setup(执行 Initialize)。
  4. ITransactionHandler 双继承:IOrderProcessor(下单入口)+ IOrderEventProvider(成交事件),持 Orders/OrderTickets 两个字典。
  5. IRealTimeHandler 时钟语义:SetTime 推进模拟时钟(回测)/墙钟(实盘),ScanPastEvents 补漏触发。
  6. IHistoryProvider:GetHistory 返回 IEnumerable<Slice>,带 DataPointCount 计数。
  7. IBrokerage:连接+查询+下单+事件四类能力,无 [InheritedExport],走 IBrokerageFactory 工厂装配。
  8. 三共性:Initialize/Exit 生命周期、IsActive 停机协调、DTO 参数对象。

下一节,我们看这七个接口的 Backtesting* 与 LiveTrading* 双实现如何对照——这是全书第一个高潮的核心:回测与实盘的统一,本质就是换 handler 实现类。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U