简介面向C#量化开发者的股票量化资料包主题是利用C#编写量化程序并结合掘金量化接口获取股票行情、同花顺板块数据适合具备一定编程基础、希望从数据接入到策略决策建立系统认知的交易开发者。内容围绕量化交易完整流程展开先后阐述量化交易模型的基本概念掘金接口进行HTTP请求、获取实时价格/历史K线与成交量等行情信息的方式以及JSON/XML数据的解析与整合同时覆盖了板块指数走势、板块内个股涨跌、成交额等同花顺数据的使用思路。在此基础上资料进一步梳理了策略回测、风险控制与资金分配等量化系统必备模块并强调异常处理与合规维护的重要性能够为后续自行编写C#策略提供清晰的知识框架降低入门试错成本。整套内容以RAR压缩包形式提供大小约326MB已有1140人学习浏览适合需要体系化了解C#量化交易开发流程的中级开发者。1. 用 C# 对接掘金量化行情接口先把需求和边界聊透用C#写股票量化掘金量化接口是绕不开的一环它既给行情也给交易通道比单纯只出数据的行情接口更适合直接跑策略。但第一次上手的人多半会栽在同一处——以为SDK连上服务器就行结果终端没登录、token过期、订阅回调不触发各种玄学问题全冒出来。这篇文章就针对“获取股票行情 同花顺板块数据”这条主线讲清楚桌面终端、SDK、策略程序三者怎么配合初始化和订阅参数怎么设板块数据没有现成接口时怎么补。适合刚拿到掘金C# SDK、准备做选股或行情分析的开发者也适合想从Python切到C#写策略的熟手。读完你会有本地一套能出信号的C#行情工具链。2. 初始化与连接三件事没做对后面全白搭掘金的C# SDK不是一条直连服务器的长连接它依赖本地桌面终端做网关。数据流向是策略程序 - SDK - 本地终端 - 掘金数据服务器。这个模型决定了排错顺序先怀疑终端再怀疑SDK最后才怀疑自己的调用代码。2.1 三层连接模型与C#里SDK的引用掘金量化终端装上之后会在本地监听一个服务端口。你的C#程序通过SDK连接这个本地端口终端负责登录、鉴权、把远程数据转发回来。好处是通信协议不用自己实现坏处是你必须保证终端处在已登录状态。程序放到服务器上跑也一样服务器得装终端并保持登录。引用SDK的方式在C#里一般是“添加引用→选中掘金SDK的dll”或者用NuGet装包。不同小版本的类名不完全一样最典型的写法是分两个接口一个数据接口、一个交易接口。using Quant; // 命名空间以你引入的dll版本为准 // 数据接口负责行情和历史数据 var md new MdApi(); md.SetCallback(new MyMdCallback()); // 交易接口负责下单、持仓和账户资金 var td new TdApi(); td.SetCallback(new MyTdCallback()); // 真正建立连接 md.Init(localhost, 你的token, true); td.Init(localhost, 你的token, true);逻辑说明MdApi和TdApi是掘金SDK里常见的两个门面类前者处理行情后者处理交易。SetCallback把你实现的回调对象注册进去行情到达、成交回报都会通过回调推出来。Init的第一个参数是本地终端地址第二个是账号token第三个是断线自动重连开关。参数说明token在掘金终端个人设置里能看到相当于接口的密钥。不要直接把token硬编码在源码里我一般放到配置文件或者环境变量里避免代码提交后把密钥一起带出去。2.2 Init的时机与顺序很多新手把Init写完之后立刻去拉数据拉不到就开始怀疑SDK有问题。其实SDK是异步模型Init只是把连接请求提交出去连接和登录是后台完成的你甚至要等回调里某个就绪通知链路才算真正可用。常见做法是初始化后先进入事件循环等数据接口的OnReady到位再开始订阅和查数据。伪代码如下class MyMdCallback : MdCallback { public override void OnReady() { Console.WriteLine(数据接口连接成功); // 在这里发起订阅或历史数据请求 } public override void OnError(int code, string msg) { Console.WriteLine($错误码: {code}, 信息: {msg}); } }逻辑说明OnReady触发才表示数据链路可用OnError里能拿到具体错误码。如果OnError弹出一个错误码就按掘金错误码表去查多数是token无效或终端未登录。这里有个常见误用在Main函数里Init后直接Sleep几秒再去取数据本地网络正常时碰巧能跑通但网络波动时就会收到空数据。正确的做法是把“订阅/查询”放在OnReady或连接成功回调里让时序跟着SDK状态走。我还想多说一句配置文件的事。把host、token、重连开关、策略参数都拆到appsettings.json里程序启动时读一次var config JsonSerializer.DeserializeAppConfig( File.ReadAllText(appsettings.json)); md.Init(config.Host, config.Token, config.Reconnect);逻辑说明这段把敏感配置从代码里剥离开切测试账号、切实盘账号不用改代码重新编译的成本一次都省了。后续换服务器部署也只动配置文件。参数说明AppConfig是个自定义类字段和json文件里的键一一对应。这个习惯在涉及第三方接口的项目里特别值得养成因为token一旦泄露改token比改代码再发布快得多。2.3 用一次快照查询验证链路连接打好之后我建议先拉一条最新价快照验证整条链路而不是直接上策略跑。链路不通时问题会混在一起根本分不清是订阅参数写错还是网络问题。var quote md.GetQuote(SHSE.600000); Console.WriteLine($最新价: {quote.LastPrice}, 成交量: {quote.Volume});逻辑说明GetQuote是数据接口里查最新报价的典型方法传入带交易所前缀的股票代码返回当前快照对象。能打出价格说明终端、token、代码格式三者都对了。参数说明这里用的是同步查询适合验证阶段使用。但同步查询在盘中高频调用会被限流正式策略里不要依赖这种写法。返回对象的字段名在不同SDK版本可能有别名以你的dll里智能提示为准。补充一个使用习惯C#程序退出前要关掉接口否则终端会保留残留连接下次再见可能报连接数过多。在finally块里调Release把连接收干净Windows上还能用任务管理器确认没有残留进程占用端口。try { // 策略主流程 } finally { md.Release(); td.Release(); }逻辑说明Release让SDK主动和本地终端断开并释放内部资源。进程崩溃时这个不一定执行得到所以终端侧的自动清理机制也很重要但作为正常退出的路径这个动作必须有。3. 把行情数据拿到手tick、bar、板块快照的订阅与解析行情是量化的口粮。掘金里常见的数据形态有tick、bar、quote三类三者拿到手的难度和适用场景差别很大。3.1 三种行情形态怎么选形态频率适合场景注意点tick每笔成交高频、滑点分析、盘口统计数据量大回调压力大bar分钟/日线聚合均线、动量、版块轮动需指定频率与复权方式quote当前快照监控、风控、画界面只代表当下不适合回测选型上做板块轮动或中低频策略bar足够了看盘口、做拆单才需要tick做实时监控面板用quote。刚开始接触时容易犯的错是上来就订阅tick把链路搞得很重最后只剩几条日志连基本的日线都没落库。3.2 实时订阅的参数与回调实时数据走Subscribe。典型参数是symbol、frequency、type等看完下面这段就明白为什么前面一直强调代码格式。// 订阅上证某只股票的分钟bar md.Subscribe(SHSE.600000, bar, 60s, ); // 订阅深证某只股票的tick md.Subscribe(SZSE.000001, tick, , );逻辑说明Subscribe的第一个参数是标的第二个是数据类型bar/tick/quote第三个是频率tick没有频率参数就传空。订阅成功后数据不是主动返回而是通过回调分发。策略逻辑应该写在OnBar之类的回调事件里。参数说明symbol的格式是固定两段式交易所缩写SHSE/SZSE加上六位代码。60s代表60秒K线也可以用1d代表日线。这个字符串写错一个字母订阅会静默失败没有报错只有等了半天没数据才知道出了问题。3.3 历史bar与复权因子回测前要拉历史数据。掘金的download类方法可以把一段历史bar拉到本地典型参数包括起始时间、结束时间、复权方式、是否包含收盘后修正值。// 拉过去两年的日线用后复权避免除权跳变 var history md.GetHistoryBars( SHSE.600000, 1d, 2023-01-01 00:00:00, 2024-12-31 23:59:59, adjust: backward );逻辑说明GetHistoryBars返回的是bar数组每个bar里有时间、开高低收、成交量。adjust字段有三种常见取值none不复权、forward前复权、backward后复权。参数说明回测我默认用后复权因为后复权的价格序列是连续可比的不会被分红除权打断。实盘信号跟踪则用前复权或不复权因为实盘看到的价格是真实的盘面价用后复权价格算信号虽然历史曲线连续但信号触发时可能与实际盘面价格对不上。收盘后还有一个细节日线最后一根bar在收盘结算后可能被修正。如果策略对最后一根bar的收盘价很敏感建议在T日15:00之后再拉当日数据或者用实时订阅补当天的行情别让历史接口和实时数据混在一起。4. 避坑手册连接、订阅、数据对齐的五个真实踩坑记录这些坑我在不同项目里反复见过有的自己也踩过。按“现象→原因→解决”记下来后面再遇到能少花半天时间。4.1 连接类连不上先按网关模型排查现象1Init调用后OnError一直报错误码或者干脆没有任何回调程序干等日志只有一行“正在连接”。接着查网络、改防火墙折腾一圈还是不行。原因掘金SDK不是直连服务器的终端是本机网关。终端没启动、终端未登录、token失效都会让连接在第一步就卡住。很多人把这个接口当成普通的行情HTTP接口来调方向就错了。解决先打开桌面终端确认登录状态再检查token是否有效最后确认Init的第一个参数是localhost或终端实际监听地址。我习惯在启动程序前先探测本地端口是否可连不通就弹提示不往下走。这个探测动作很小但能省掉90%的初始化排错时间。现象2程序重启后报连接数过多终端上能看到一堆残留连接。原因上次进程没调ReleaseSDK和终端之间的TCP连接没有正常关闭终端侧把旧连接保留了一段时间。解决在finally块里调Release进程退出前把连接收干净。Windows上还可以用任务管理器确认没有残留进程占用端口。如果是崩溃退出等终端侧超时清理即可不用反复重启终端。4.2 数据类回调不触发与代码格式不对现象3Subscribe写好了OnBar就是不触发日志里也没有错误。原因一个原因是订阅时机不对在OnReady之前就发订阅终端还没准备好另一个原因是回调对象没有注册或者回调方法内部抛异常被SDK吞掉了。SDK的C#封装在分发回调时如果遇到未捕获异常可能直接丢弃后续消息表现得非常安静。解决把Subscribe放在OnReady回调里并且回调方法最外层包try/catch把异常打到日志文件。这样即使策略逻辑出错也能看到堆栈而不是永远等不到下一个bar。现象4同花顺板块里的股票代码拿过来查行情返回空。原因掘金的symbol是交易所前缀加6位代码而板块导出文件里通常是纯数字如“600000”、“000001”。直接拿来订阅等于给了一个不存在的代码接口不回数据也不报错很迷惑。解决写一个统一转换函数把纯数字映射成带前缀的symbolpublic static string ToSymbol(string code) { if (code.StartsWith(6)) return SHSE. code; if (code.StartsWith(0) || code.StartsWith(3)) return SZSE. code; if (code.StartsWith(4) || code.StartsWith(8)) return BJSE. code; // 北交所 return code; }逻辑说明这个函数把纯六位代码补齐交易所前缀。6开头是上交所0/3开头是深交所4/8开头是北交所。板块文件里如果带空格或表头先做Trim再转换。参数说明北交所是新坑老代码里容易漏掉。每次从板块文件导入前我还会用一个白名单过滤一遍退市和停牌代码避免把没有行情的标的拉进候选池。现象5收盘后拉的日线bar最后一根K线数据会“变”。昨天跑的候选池今天重跑结果对不上了。原因日线在收盘后要经过结算修正终端缓存的数据在新一天开盘前偶尔会更新最后一根bar。这不是接口坏了而是行情数据的常规修正流程。解决回测尽量用“已收盘确认”的数据把最后一根bar的收盘时间严格放在T日15:00之后拉取。实盘盘中策略不要直接依赖历史接口补当天bar而是用实时订阅和history组合各管一段。5. 同花顺板块数据没有现成接口用分类文件代码对齐来补掘金终端没有内置同花顺板块标签这是一个比较遗憾的地方。板块数据属于第三方整理标准行情接口里没有必须自己补。5.1 数据从哪来客户端导出与公开页面的取舍常见做法有两个。一是从同花顺客户端里导出打开板块页面选“板块成分”把成分股列表和板块指数明细导出为CSV二是用浏览器抓公开行情页的板块数据。前者数据准但更新全要靠手动后者可以定时抓但页面结构一变脚本就废。我一般把两者结合先把客户端导出的板块成分作为基准表每天收盘后只拉涨跌幅和成交额做增量更新。这样既保证成分股名单稳定又让涨跌幅不过夜。导出文件的结构通常包含板块名称、股票代码、股票名称、最新价、涨跌幅、成交额等字段文件里同一板块可能重复出现导入时要按板块代码分组。5.2 代码对齐与停牌过滤导出文件里的股票代码是6位数字先按第4章的ToSymbol转换成掘金symbol再处理三类边缘情况退市整理期的股票代码会带特殊标记或连续停牌需要按白名单过滤停牌股当天没有行情但板块成分里仍在要保留在成分表但标记为停牌B股、基金、债券如果混在板块导出里要按代码段过滤掉只保留A股。下面的C#代码演示了解析CSV并转换的过程public static ListStockInfo ParseBoardCsv(string csvPath) { var list new ListStockInfo(); using var reader new StreamReader( csvPath, Encoding.GetEncoding(GBK)); var isHeader true; string line; while ((line reader.ReadLine()) ! null) { if (isHeader) { isHeader false; continue; } var cols line.Split(,); string board cols[0].Trim(); string rawCode cols[1].Trim(); string symbol ToSymbol(rawCode); double gain double.TryParse(cols[4], out var g) ? g : 0.0; list.Add(new StockInfo { Board board, Symbol symbol, GainPct gain }); } return list; }逻辑说明这段代码把板块导出的CSV解析成结构化列表。首行表头跳过每行取板块名、股票代码和涨跌幅涨跌幅列取不到就给0。板块名里可能带空格或全角字符Trim一下再用于匹配。参数说明CSV的列顺序不固定导出时注意先看表头再对列序号。这里用GBK编码读取能解决中文乱码问题——同花顺导出的文件记事本打开正常但程序默认按UTF-8读就是乱码原因就在编码上。5.3 存到本地SQLite别每次现算板块数据和行情数据要频繁关联查询每次都读CSV不现实。我把它落到本地SQLite表结构就两张表字段说明boardid, name, create_date板块主表board_stockboard_id, symbol, stock_name, gain_pct, trade_date成分与每日涨跌写入逻辑是每日先删掉今天的board_stock再重新插入解析结果。这样查询某板块最近5日涨幅时一条SQL就能得到不需要在前端程序里做内存join。SELECT b.name, bs.symbol, AVG(bs.gain_pct) AS avg_gain_5d FROM board_stock bs JOIN board b ON b.id bs.board_id WHERE bs.trade_date date(now, -5 day) GROUP BY bs.symbol, b.name ORDER BY avg_gain_5d DESC;逻辑说明这段SQL统计每个板块成分股最近5个交易日的平均涨跌幅按均值降序排列可以作为板块强弱排序的依据。参数说明trade_date是字符串用date函数做日期滚动。注意SQLite的date(now)返回的是UTC时间的日期如果你在北京时区跑要手动换算成北京时间再写入日期字符串。这个细节容易漏建议在写入时直接存北京当天的日期而不是靠SQLite现算。6. 进阶玩法把版块轮动想法做成一个可跑的筛选器数据链路通了板块表也有了接下来就是把它变成能用的东西。我最常做的进阶操作是版块轮动筛选先看板块强弱再在强势板块里挑个股两步都有数据支撑。第一步用第5章的SQL按近5日平均涨幅拿到强势板块TopN第二步取每个强势板块的成分股用掘金的历史bar计算个股20日动量第三步把板块涨幅和个股动量交叉过滤输出候选池。一只股票如果它所在的板块整体在走强它自己的动量又能跟上板块强度才有进池子的资格。public static Liststring FilterStocks( ListStockInfo candidates, int topN 3) { var pool new Liststring(); foreach (var group in candidates.GroupBy(c c.Board)) { double boardGain GetBoardGain(group.Key, 5); if (boardGain 0) continue; foreach (var stock in group) { double momentum GetStockMomentum(stock.Symbol, 20); if (momentum boardGain * 0.8) { pool.Add(stock.Symbol); } } if (pool.Count topN) break; } return pool; }逻辑说明这个函数先过滤掉下跌板块再保留个股动量与板块强度匹配的标的。0.8的系数只是经验起点可以调成“个股动量大于板块强度”这种更严格的条件。动量全部用截至当日的收盘价计算不做未来函数。建议把它封装成每日收盘后自动跑一次的后台任务输出候选池存到文件便于盘前人工复核。复核时打开K线确认候选股形态没有明显异常再决定要不要放进当日观察列表。我之前在模拟项目X里图省事直接把板块文件的代码当掘金symbol用结果候选池里一半标的查无行情板块轮动策略的回测收益被“数据缺失”磨平了。从那以后凡是涉及外部数据源的字段我落地前都会强制走一遍“代码转换白名单过滤落库检查”链路跑通再谈信号。这套流程同样希望帮到你。本文还有配套的精品资源点击获取