第 4 章 · 02 BasicTemplateAlgorithm 最简示例(C# + Pyt...


文档摘要

第 4 章 · 02 BasicTemplateAlgorithm 最简示例(C# + Python) 本节摘要:本节精读 Lean 仓库的"Hello World"—— 。C# 版与 Python 版各 40 行不到,完整展示了用户写策略的最小骨架:继承 ,override 两个方法 (配置回测期/资金/订阅)与 (策略逻辑)。C# 版多实现一个 接口,提供回归测试用的元数据( 等)。本节还会讲清 这个百分比再平衡高层 API 的语义,以及 Python 版通过 配置切换的机制。 内容来源:原项目源码 (共 126 行)与 (共 44 行),精读并对照。 ⚠️ 注意:这是用户视角的"最简示例",不涉及引擎内部。看完本节你应该能独立写出第一个 Lean 策略。

第 4 章 · 02 BasicTemplateAlgorithm 最简示例(C# + Python)

本节摘要:本节精读 Lean 仓库的"Hello World"——BasicTemplateAlgorithm。C# 版与 Python 版各 40 行不到,完整展示了用户写策略的最小骨架:继承 QCAlgorithm,override 两个方法 Initialize(配置回测期/资金/订阅)与 OnData(Slice)(策略逻辑)。C# 版多实现一个 IRegressionAlgorithmDefinition 接口,提供回归测试用的元数据(ExpectedStatistics 等)。本节还会讲清 SetHoldings(symbol, 1) 这个百分比再平衡高层 API 的语义,以及 Python 版通过 algorithm-language 配置切换的机制。

内容来源:原项目源码 Algorithm.CSharp/BasicTemplateAlgorithm.cs(共 126 行)与 Algorithm.Python/BasicTemplateAlgorithm.py(共 44 行),精读并对照。

⚠️ 注意:这是用户视角的"最简示例",不涉及引擎内部。看完本节你应该能独立写出第一个 Lean 策略。具体下单/订阅/历史 API 的所有重载见 03 节。

学习目标

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

  1. 用 C# 和 Python 各写出一个最简 Lean 策略(Initialize + OnData)。
  2. 说清 SetStartDate/SetEndDate/SetCash/AddEquity 这"初始化四件套"。
  3. 理解 SetHoldings(symbol, 1) 的百分比再平衡语义(不是"买 1 股")。
  4. 认识 IRegressionAlgorithmDefinitionExpectedStatistics/DataPoints 回归契约。
  5. 知道 Python 版怎么通过配置切换(algorithm-language=Python)。

一、C# 版类头与 _spy 字段

BasicTemplateAlgorithm.cs:29-31:

29 public class BasicTemplateAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition 30 { 31 private Symbol _spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);

类头继承 QCAlgorithm(上一节讲的主类),额外实现 IRegressionAlgorithmDefinition——这是 Lean 回归测试系统用的接口,提供 CanRunLocally/Languages/DataPoints/ExpectedStatistics 等元数据,普通用户策略不需要实现它,只有官方回归算法才标。

_spy 是预定义的 Symbol 对象。Symbol.Create(ticker, securityType, market) 三参工厂方法,返回一个不可变的 Symbol 实例。Symbol 是 Lean 全程用来标识标的的核心类型(第 6 章详讲),这里提前缓存到字段,后续 OnData 里下单用。

💡 钻取要点:为什么用 Symbol.Create 而不是直接字符串 "SPY"?因为 Lean 支持多市场(USA/Oanda/CME 等),同一个 ticker 在不同市场是不同标的。Symbol 内部带 SecurityTypeMarket 信息,身份唯一。Lean 也允许直接传字符串(会自动解析),但缓存 Symbol 对象更高效、更安全。

二、Initialize 配置回测

BasicTemplateAlgorithm.cs:36-50:

36 public override void Initialize() 37 { 38 SetStartDate(2013, 10, 07); //Set Start Date 39 SetEndDate(2013, 10, 11); //Set End Date 40 SetCash(100000); //Set Strategy Cash 41 42 // Find more symbols here: http://quantconnect.com/data 43 // Forex, CFD, Equities Resolutions: Tick, Second, Minute, Hour, Daily. 44 // Futures Resolution: Tick, Second, Minute 45 // Options Resolution: Minute Only. 46 AddEquity("SPY", Resolution.Minute); 47 }

四个调用就是"初始化四件套":

  1. SetStartDate(2013, 10, 07):回测起点。上一节看过,内部存到 _startDate,并做 _locked 检查。
  2. SetEndDate(2013, 10, 11):回测终点。注意是"日期"不是"时刻",内部会取该日 23:59:59.9999999(RoundDown(1天).AddDays(1).AddTicks(-1))。
  3. SetCash(100000):起始资金,默认币种 USD。也有重载 SetCash("EUR", 10000, 1.1) 设定非主币种并指定汇率。
  4. AddEquity("SPY", Resolution.Minute):订阅 SPY 股票,分钟频率。AddEquityQCAlgorithm.Universe.cs 里的方法(03 节),返回一个 Equity 对象,内部把订阅塞到 UniverseManager

Initialize 是用户唯一必须 override 的方法。基类默认实现是 throw new NotImplementedException("Please override the Initialize() method")(QCAlgorithm.cs:773),强制用户写。

三、OnData 策略逻辑

BasicTemplateAlgorithm.cs:56-63:

56 public override void OnData(Slice slice) 57 { 58 if (!Portfolio.Invested) 59 { 60 SetHoldings(_spy, 1); 61 Debug("Purchased Stock"); 62 } 63 }

OnData(Slice) 是策略主入口,引擎每个时间步把数据包 Slice 喂进来(第 5 章详讲 Slice 怎么来)。这个最简策略的逻辑是:

  • Portfolio.Invested:组合是否已持有任何仓位(布尔)。
  • 第一次 OnData 进来时 Portfolio.Invested == false,所以执行 SetHoldings(_spy, 1)
  • SetHoldings(symbol, weight)百分比再平衡——1 表示把总资产的 100% 投到 SPY。它内部算出目标持仓数 = 总资产 * 1 / 当前价,与现有持仓求差,发市价单。
  • Debug("Purchased Stock") 把消息塞进上一节讲的 _debugMessages 队列。
  • 之后所有 OnData 进来时 Portfolio.Invested == true,什么都不做,直到回测结束。

⚠️ 重要说明:SetHoldings(_spy, 1) 不是"买 1 股 SPY",是"把 100% 仓位放到 SPY"。如果总资产是 100000 美元,SPY 价 168,它会买约 595 股(100000/168)。weight 范围 0 到 1(可加杠杆时 >1,但有上限)。这是 Lean 的高层 API,避免用户手算股数。

OnData 默认实现有一段反射 hack(QCAlgorithm.cs:1079-1108),自动找用户漏写 override 关键字时的同名方法。这是 Python 用户(没有 override 概念)和 C# 新手的兜底。

四、IRegressionAlgorithmDefinition 回归契约

BasicTemplateAlgorithm.cs:68-123:

68 public bool CanRunLocally { get; } = true; 73 public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python }; 78 public long DataPoints => 3943; 83 public int AlgorithmHistoryDataPoints => 0; 88 public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; 93 public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string> 94 { 95 {"Total Orders", "1"}, 96 {"Average Win", "0%"}, ... 101 {"Start Equity", "100000"}, 102 {"End Equity", "101691.92"}, 103 {"Net Profit", "1.692%"}, 104 {"Sharpe Ratio", "8.854"}, ... 122 {"OrderListHash", "3da9fa60bf95b9ed148b95e02e0cfc9e"} 123 };

这是 Lean 回归测试系统的核心:

  • CanRunLocally:本地仓库是否有数据跑这个算法。
  • Languages:支持哪些语言(C#/Python)。这个算法两种语言都跑。
  • DataPoints:整个回测会消耗多少数据点(3943),用于 CI 资源预估。
  • AlgorithmStatus:期望的结束状态(Completed)。
  • ExpectedStatistics:期望统计字典——回测跑完后实际算出的统计(Total Orders/Sharpe/Net Profit 等)必须与这个字典精确匹配,任何一项偏差都判回归失败。OrderListHash 是订单列表的哈希,连订单顺序都校验。

💡 钻取要点:这套机制是 Lean 仓库 CI 的基石——100+ 回归算法每个都有期望统计,每次提交代码自动跑一遍,统计变了立刻报警。这保证引擎行为不漂移,用户策略跨版本可复现。普通用户策略不需要实现这个接口。

五、Python 版对照

BasicTemplateAlgorithm.py:23-44:

23 class BasicTemplateAlgorithm(QCAlgorithm): 24 '''Basic template algorithm simply initializes the date range and cash''' 25 26 def initialize(self): 27 '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.''' 28 29 self.set_start_date(2013,10, 7) #Set Start Date 30 self.set_end_date(2013,10,11) #Set End Date 31 self.set_cash(100000) #Set Strategy Cash 32 # Find more symbols here: http://quantconnect.com/data 33 self.add_equity("SPY", Resolution.MINUTE) 34 self.debug("numpy test >>> print numpy.pi: " + str(np.pi)) 35 36 def on_data(self, data): 37 '''OnData event is the primary entry point for your algorithm. 38 Each new data point will be pumped in here.''' 39 if not self.portfolio.invested: 40 self.set_holdings("SPY", 1)

结构与 C# 版完全对应,只是命名风格转 Python:

  • 类名相同 BasicTemplateAlgorithm,继承 QCAlgorithm(通过 PythonNet 桥接 C# 类)。
  • Initializeinitialize,C# 的 PascalCase 转 Python 的 snake_case。Lean 用 PythonNet 自动做这个映射,所以 C# 的 SetStartDate 在 Python 里是 self.set_start_date
  • OnData(Slice)on_data(data),slice 参数名改成 data
  • Slice/Portfolio/Symbol 等 C# 类型在 Python 里直接可用,无需导入。
  • 第 34 行 self.debug(...) 在 initialize 里直接调,证明框架在 initialize 阶段已经初始化好消息队列。
  • 第 40 行 self.set_holdings("SPY", 1) 直接传字符串,Lean 自动用 SymbolCache 解析成 Symbol。

语言切换:同一个 BasicTemplateAlgorithm 名字,C# 和 Python 各一份。在配置文件 config.json 里设:

"algorithm-language": "Python", "algorithm-type-name": "BasicTemplateAlgorithm", "algorithm-location": "Algorithm.Python/BasicTemplateAlgorithm.py"

引擎据此选择用 PythonInitializer 还是 C# Loader 加载。Lean 整个引擎是 C#,Python 算法通过 PythonNet 互操作(见 QCAlgorithm.Python.csQCAlgorithm.Framework.Python.cs)。

本节要点回顾

  1. 最简骨架:继承 QCAlgorithm,override Initialize(配置)+ OnData(Slice)(逻辑)。
  2. 初始化四件套:SetStartDate/SetEndDate/SetCash/AddEquity
  3. SetHoldings(symbol, 1) 是百分比再平衡(100% 仓位),不是买 1 股。
  4. IRegressionAlgorithmDefinition 提供 ExpectedStatistics/DataPoints 给回归测试比对,是 Lean CI 基石。
  5. Python 版 结构对应,通过 algorithm-language 配置切换,PythonNet 自动做 PascalCase → snake_case 映射。

下一节,我们钻取主类之外的几个关键 partial 文件——Trading(下单)/Universe(订阅)/History(历史)/Indicators(指标)/Plotting(绘图)/Framework(框架五段)的 API 全貌。


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