Tqsdk.net 1.0.0

dotnet add package Tqsdk.net --version 1.0.0
                    
NuGet\Install-Package Tqsdk.net -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tqsdk.net" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tqsdk.net" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Tqsdk.net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tqsdk.net --version 1.0.0
                    
#r "nuget: Tqsdk.net, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tqsdk.net@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tqsdk.net&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Tqsdk.net&version=1.0.0
                    
Install as a Cake Tool

Tqsdk.net

天勤 TqSdk(tqsdk-python)的 C# 复刻。API 命名、用法与语义对齐 tqsdk: 类名/方法名一一对应(TqApi、TqAuth、GetQuote、WaitUpdate、IsChanging…), 字段名沿用 tqsdk 的 snake_case(last_price、bid_price1…),未收到行情前价格为 NaN、数量为 0。

当前进度:行情 + K 线 + 历史 K 线 + 模拟交易 + 指标算法模块 均已实现并通过真实服务器/数值比对验证。

  • 真实连接天勤行情网关(free-openmd,permessage-deflate 已自行实现);
  • 实时 K 线序列(GetKlineSerial,set_chart/klines 协议);
  • 历史 K 线获取(GetKlineHistory,独立实现,参考 tqsdk-rs 的 kline_history);
  • TqSim 本地模拟账户:撮合、保证金、手续费、浮动/平仓盈亏、持仓/账户计算;
  • insert_order、get_account/get_position/get_order/get_trade、TargetPosTask;
  • 断线指数退避重连(1s → 2s → 4s … 上限 30s);
  • 指标算法模块(TqTa / TqTaFunc):复刻 tqsdk.ta 约 80 个指标 + tqsdk.tafunc 约 60 个底层算子,数值与真实 tqsdk 逐元素比对 87/87 一致。

目录结构

csharp/
├── Tqsdk.slnx                 解决方案
├── Tqsdk.net/                 类库(AssemblyName: Tqsdk.net,命名空间: Tqsdk)
│   ├── TqApi.cs               核心 API:连接、订阅、wait_update/is_changing、diff 合并、重连
│   ├── TqAuth.cs              快期账户认证(登录 + 名称服务)
│   ├── TqEntity.cs            业务对象基类(snake_case 字段存储 + 变更追踪)
│   ├── Quote.cs               盘口行情(字段对齐 tqsdk objs.Quote)
│   ├── Kline.cs / KlineSerial.cs  K 线单根与实时序列(支持 iloc[-1] 读取最新一根)
│   ├── KlineHistory.cs          历史 K 线获取器(独立于实时 K 线,参考 tqsdk-rs)
│   ├── Account.cs / Position.cs / Order.cs / Trade.cs  账户/持仓/委托/成交实体
│   ├── TqSim.cs               本地模拟交易引擎(撮合/保证金/盈亏)
│   ├── TargetPosTask.cs       目标持仓调仓任务(对齐 tqsdk.TargetPosTask)
│   ├── TqWebSocketClient.cs   最小 WebSocket 客户端(支持 permessage-deflate)
│   ├── PermessageDeflateDecoder.cs  permessage-deflate 解压(上下文接管)
│   ├── TqNotify.cs            通知(连接建立/断开/服务器消息)
│   └── TqException.cs         异常类型
└── TqsdkConsole/              控制台测试程序(镜像 Python app.py 用法)

构建与运行

cd csharp
dotnet build

# 设置快期账户凭证(环境变量,不写入代码)
# PowerShell:
$env:TQ_USER="lianshan"; $env:TQ_PASSWORD="你的密码"
# bash:
TQ_USER=lianshan TQ_PASSWORD=你的密码 dotnet run --project TqsdkConsole

控制台输出:订阅 DCE.v2701,last_price 变化时打印时间/最新价/五档/成交量, 并演示 WaitUpdate 超时返回 False、IsChanging 不带字段名等语义。

用法(与 tqsdk 相同)

// 行情 + 本地模拟交易(账户初始资金 100 万)
var sim = new TqSim(1_000_000);
using var api = new TqApi(new TqAuth("快期账户", "密码"), account: sim);

var quote = api.GetQuote("DCE.v2701");
Console.WriteLine(quote.LastPrice);        // NaN(未收到行情前,与 tqsdk 一致)

// K 线序列:.Last 等价于 pandas 的 iloc[-1]
var kline = api.GetKlineSerial("DCE.v2701", 60, 10);

// 历史 K 线(独立于实时订阅):一次性获取最近 300 根历史分钟线,参考 tqsdk-rs 的 kline_history
var hist = api.GetKlineHistory("DCE.v2701", 60, 300);
while (api.WaitUpdate())
    if (hist.Ready)
    {
        Console.WriteLine($"历史 {hist.Count} 根,最早 {hist.First?.Datetime} 最新 {hist.Last?.Datetime},还有更早数据: {hist.MoreData}");
        break;
    }
// 也可按 K 线 id 或时间定位更早的窗口:
// var old = api.GetKlineHistory("DCE.v2701", 60, 500, leftKlineId: hist.LeftId - 500);
// var byTime = api.GetKlineHistory("DCE.v2701", 60, 300, focusDateTime: new DateTime(2026, 9, 1));

// 模拟下单:市价开 3 手
var order = api.InsertOrder("DCE.v2701", "BUY", "OPEN", 3);
var pos = api.GetPosition("DCE.v2701");
var acc = api.GetAccount();

while (api.WaitUpdate(TimeSpan.FromSeconds(3)))
{
    if (api.IsChanging(quote, "last_price"))
        Console.WriteLine($"{quote.Datetime} {quote.LastPrice}");
}
api.Close();                                // 或 using 自动释放

API 对照表

tqsdk (Python) Tqsdk.net (C#) 状态
TqApi(auth=TqAuth(user, pwd)) new TqApi(new TqAuth(user, pwd), account: sim) ✅
api.get_quote(symbol) api.GetQuote(symbol) ✅
api.get_quote_list(symbols) api.GetQuoteList(symbols) ✅
api.wait_update() / wait_update(deadline) api.WaitUpdate() / WaitUpdate(TimeSpan) ✅
api.is_changing(obj, "key") api.IsChanging(obj, "key") ✅
api.close() api.Close() / using ✅
TqAuth(user, pwd) new TqAuth(user, pwd) ✅
Quote(全部字段,初值 NaN/0/"") Quote(同名字段) ✅
api.get_kline_serial(symbol, dur, length) api.GetKlineSerial(symbol, dur, length) ✅
tqsdk-rs: kline_history(...) / kline_history_with_focus(...) api.GetKlineHistory(symbol, dur, count, leftKlineId?, focusDateTime?, focusPosition?) ✅
api.get_account() / TqSim api.GetAccount() / new TqSim(...) ✅
api.get_position(symbol) api.GetPosition(symbol) ✅
api.insert_order(...) api.InsertOrder(...) ✅
TargetPosTask(api, symbol, target) new TargetPosTask(api, symbol, target, sim) ✅
api.get_order / get_trade api.GetOrder / GetTrade ✅
api.get_instrument_list() api.GetInstrumentList() ✅(free 网关返回已订阅合约)
Quote(全部字段,初值 NaN/0/"") Tick 实体(逐笔行情,字段对齐 tqsdk objs.Tick) ✅
api.get_tick_serial(symbol, data_length) api.GetTickSerial(symbol, data_length) ✅(免费账户可用,盘中返回最近若干笔)
api.get_kline_data_series(symbol, dur, start, end) api.GetKlineDataSeries(symbol, dur, startDt, endDt) ✅(免费账户取近期区间;更深历史需专业版)
api.get_tick_data_series(symbol, start, end) api.GetTickDataSeries(symbol, startDt, endDt) ⚠️ 仅限专业版(免费网关不提供历史 Tick,只返回近期留存)
api.cancel_order(order_id) api.CancelOrder(orderId) ✅(需 TqSim)

新增:Tick 序列与按时间段序列

  • GetTickSerial(symbol, dataLength=200)(对应 tqsdk get_tick_serial):通过 set_chart(duration=0)订阅逐笔行情, 数据落在 ticks.<symbol>.data.<id>,维护最近 dataLength 笔的滚动窗口。返回 TickSerial,用 .Last 读最新一笔、支持负索引 [-1]。 免费账户可用(盘中返回最近若干笔;收盘后返回当日最后会话的留存 Tick)。
  • GetKlineDataSeries(symbol, durationSeconds, startDt, endDt)(对应 tqsdk get_kline_data_series,官方标“专业版”): 底层走 set_chart(focus_datetime=起点, focus_position=0, view_width 由时间跨度推算),结果按 [start, end] 过滤。 免费账户可获取近期区间(分钟线约 2 个月、日线约 9 个月,见 GetKlineHistory 实测);更深历史需专业版账户。 返回 KlineDataSeries,Ready/Count/Bars/First/Last 均只在 [start, end] 内统计。
  • GetTickDataSeries(symbol, startDt, endDt)(对应 tqsdk get_tick_data_series,⚠️ 仅限专业版): 协议同 GetTickDataSeries(set_chart duration=0 + focus_datetime),数据落在 ticks.<symbol>.data 并按 [start, end] 过滤。 免费行情网关不提供历史 Tick,免费账户调用只会返回近期留存 Tick(无法取到请求的跨历史区间),必须购买天勤专业版才能获取跨历史 Tick。

    说明:tqsdk 官方把 get_kline_data_series / get_tick_data_series 用 tq_dl 特征硬门槛限制为专业版(客户端检查,免费账户直接抛“仅限专业版”; 本库不复制该客户端门槛,直接走底层 set_chart,因此在免费账户上也能拉到网关“愿意给”的近期数据,更深历史仍取决于账户权限。

  • CancelOrder(orderId)(对应 tqsdk cancel_order):需构造 TqApi 时挂接 TqSim;仅对仍 ALIVE 的委托生效,撤单后状态置 FINISHED 并触发账户重算。

指标算法模块(TqTa / TqTaFunc)

复刻 tqsdk.ta(约 80 个常用技术指标)与 tqsdk.tafunc(约 60 个底层系列算子)。 输入 K 线序列(IReadOnlyList<Kline>,通常来自 KlineSerial.Bars),输出 TaDataFrame(按列名取序列)或 double[]。

var bars = api.GetKlineSerial("DCE.v2701", 60, 300).Bars;   // IReadOnlyList<Kline>

// 底层算子(TqTaFunc):单/双序列运算,参数与返回值均为 double[];先用列提取器取序列
double[] close = TqTa.C(bars);
double[] high  = TqTa.H(bars);
double[] ma5   = TqTaFunc.Ma(close, 5);
double[] hh9   = TqTaFunc.Hhv(high, 9);

// 指标(TqTa):返回 TaDataFrame,按列名取值(NaN 语义与 tqsdk/pandas 一致)
var macd = TqTa.MACD(bars, 12, 26, 9);
double[] dif  = macd["dif"];
double[] dea  = macd["dea"];
double[] bar  = macd["bar"];

var kdj  = TqTa.KDJ(bars, 9, 3, 3);
double[] k = kdj["k"], d = kdj["d"], j = kdj["j"];

string[] ccl = TqTa.CCL(bars);                              // 持仓异动:返回文字标记数组

覆盖范围

  • 趋势/均线:MA EMA SMA EMA2 TRMA BOLL ENV HCL EXPMA DMA BBI BBIBOLL DKX PUBU MIKE B3612
  • 摆动/动量:MACD KDJ KD SLOWKD WR LWR RSI BIAS CCI ROC PRICEOSC PSY MI MTM MICD RC RCCD DPO DBCD MASS ZDZB SRMI
  • 能量/成交量:ATR DMI SAR CR VR ARBR MFI ADTM DDI SRDM OBV AD PVT WVAD WAD CJL OPI CCL VOSC VROC VRSI MV LON SHORT CDP QHLSR ASI
  • 底层算子 TqTaFunc:Ref Ma Sma Ema Ema2 Std Trma Hhv Llv Median Avedev Harmean NumPow Abs Min Max CrossUp CrossDown Count Exist Every BarLast

NaN 语义(与 pandas 严格对齐)

  • rolling(n) 系列(Ma/Std/Hhv/Llv/Median/Avedev/Harmean/…)采用 min_periods=n:前 n−1 个为 NaN。
  • Sma/Ema/Ewm 均为 ewm(..., adjust=False):首个非 NaN 作种子递推;前置 NaN 输出 NaN;中间 NaN 保持上一有效输出(不向下游传播)。
  • CumSum 与 pandas.cumsum() 一致:运行和跳过 NaN(不累加),但 NaN 位置本身输出 NaN。
  • Ref(x, n) 越界为 NaN;故 CrossUp/CrossDown 首位置恒为 0,BarLast 从未成立处为 -1。

数值验证:用确定性合成 OHLCV(种子 20261023,150 根)经真实 tqsdk 生成参考值(tools/gen_ta_ref.py → tools/ta_ref.json), TqTaVerify 逐元素比对(NaN=NaN,相对容差 1e-6)——22 个底层算子 + 65 个指标共 87 项全部一致。

cd csharp && dotnet run --project TqTaVerify -- ../tools/ta_ref.json

未移植:期权 BS 定价类(BS_VALUE / OPTION_GREEKS / OPTION_VALUE / OPTION_IMPV / VOLATILITY_CURVE)—— 依赖 scipy 且需要 quote 列,不属于常规 K 线指标,暂未复刻。

通信协议说明(调试结论)

  1. 认证:POST https://auth.shinnytech.com/.../token(client_id=shinny_tq)获取 JWT access_token; 再 GET https://api.shinnytech.com/ns?stock=false&backtest=false 获取行情网关地址 (免费账户:wss://free-openmd.shinnytech.com/t/md/front/mobile)。
  2. 连接:WebSocket 请求头带 Authorization: Bearer <token>,无需登录报文; 服务器连接后首先回复 {"aid":"rsp_login"}。
  3. 订阅/拉取:发送 {"aid":"subscribe_quote","ins_list":"DCE.v2701"}, 之后每次 wait_update 前发送 {"aid":"peek_message"}。 服务器是 peek 驱动的:每次 peek 必有响应,价格有变化时随响应推送。
  4. permessage-deflate 强制要求:天勤行情服务器对不带 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits 的握手直接返回 400, 且所有服务器帧均压缩(RSV1=1)、启用了 deflate 上下文接管。 .NET 内置 ClientWebSocket 不支持该扩展,因此本项目自行实现了 TqWebSocketClient(TcpClient + SslStream + 帧编解码 + permessage-deflate 解压)。
  5. diff 合并:rtn_data 报文中的 data 数组逐项合并进实体; 字段 null 表示删除(对应 tqsdk 的 None),"-" 字符串按 NaN 处理。
  6. K 线:set_chart 订阅后,数据落在 klines.<symbol>.<duration_ns>.data.<id>, charts.<chart_id> 携带 ready/left_id/right_id/state;本库按 id 有序插入并支持 Last 读取最新一根。 注意 klines.<symbol>.<duration> 是该合约+周期全局共享的节点,实时订阅与历史获取都从它读数据。 Tick 同理:set_chart(duration=0)订阅后数据落在 ticks.<symbol>.data.<id>,charts.<chart_id>.ready 标记就绪; ticks 节点无 duration 子层级,按 symbol 路由——GetTickSerial 维护最近 data_length 笔滚动窗口,GetTickDataSeries 按 [start,end] 过滤。
  7. 历史 K 线(GetKlineHistory,参考 tqsdk-rs):通过 set_chart 的 left_kline_id(按 K 线 id 定位, 向右取 view_width 根)或 focus_datetime + focus_position(0-100,按时间定位)请求某个历史窗口; 依据 charts.<chart_id> 的 ready / more_data(即 mdhis_more_data) 判断窗口是否加载完成、是否还有更早数据, 必要时按 left_id - view_width 继续向前回退(walk-back,步数上限 64,单块宽度定位窗口时收紧到 2000 以避开免费网关单块投递上限)拼接更早历史。 view_width 按 tqsdk-rs 规则 clamp(锚定最新 [200,10000];按 left_kline_id 定位历史窗口时 [200,2000])。
    • 历史深度由行情网关决定,不是客户端限制:more_data 是网关告诉客户端"还有更早数据吗"的信号,一旦为 false 任何客户端都必须停。 免费 free-openmd 网关实测保留期:分钟线约 2 个月(一次取满 10000 根回到 ~2026-09-03)、日线约 9 个月(~2026-01-19,more_data=False)。 故免费账户下分钟线拿不到半年/一年;日线可覆盖近半年(含在 9 个月范围内),完整近一年差约 3 个月。
    • tqsdk-rs 之所以"能取很深",是因为它通常配合专业/付费行情源(其 TqConfig 可自定义 md_url 行情网关地址, 历史保留期年级别且持续 more_data=true),而非它绕过限制。其 kline_history 本身不实现自动 walk-back, 需调用者用更小 left_kline_id 反复订阅拼接——本库已复刻该机制(OnChartUpdate 自动向前回退)。
    • 若要分钟级半年/一年或完整日线一年:须接入天勤专业版/极速行情(付费,历史网关 nfmd 才提供深历史; 免费账户连 nfmd 会"连上即断",已用官方 downloader 实测印证)。
  8. 断线重连:指数退避(1s → 2s → 4s … 上限 30s),达到上限次数(MaxReconnectAttempts)后放弃。

模拟交易(TqSim)实现要点

  • 撮合:与 tqsdk 一致——市价单以对手盘价全部成交,限价单达到对手盘价即全部成交(无部分成交); 支持 OPEN / CLOSE / CLOSETODAY,上期所/能源中心区分今老仓。
  • 保证金:最新价 × 合约乘数 × MarginRatio(默认 10%),MarginRatio 可改。
  • 手续费:CommissionPerLot(每手,默认 0),SetCommission(rate) 设置。
  • 盈亏:浮动盈亏 (最新价 − 持仓均价) × 手数 × 合约乘数;平仓盈亏在平仓时计入 close_profit。
  • 合约乘数来源:优先取实时行情 quote.volume_multiple;若行情未下发(如免费行情网关),回退到持仓创建时的记录值, 再回退到 TqSim.VolumeMultipleFallback(默认 1,可针对合约设置,例如 PVC/聚丙烯=5、豆粕/鸡蛋=10)。 ins_query 静态字段请求已在 GetQuote/GetQuoteList 时自动发送,真实账户可据此拿到真实乘数。
  • 健壮性:撮合/重算对未初始化数值字段做了 NaN 防御(累加字段初始化为 0,持仓为 0 的一侧不参与 NaN×0 计算), 避免 NaN + x = NaN 污染账户权益。

已知限制

  • 历史 K 线深度:GetKlineHistory 取“最近 N 根”在免费网关可用(实测请求 1000 根正常返回,约覆盖 3 个交易日; 社区经验免费账户单序列上限约 8000 根)。免费网关对已返回的完整窗口置 more_data=false,不会触发向前回退; 通过 left_kline_id / focus_datetime 定位更早窗口能否命中取决于网关数据范围。历史为快照式,加载中途断线需重新调用以恢复。
  • 免费行情网关(free-openmd)对部分合约不保证下发 volume_multiple / instrument_name 等静态字段, 此时 TqSim 的保证金/盈亏按乘数 1(或回退 5)计算,可能与真实交易所乘数有偏差;接入实盘/极速行情可消除此偏差。
  • 期权(OPTION)模拟交易暂不支持(与 tqsdk TqSim 限制一致)。
  • 真实期货公司账户(非 TqSim)交易链路未实现。
  • Tick 历史:GetTickDataSeries(按时间段 Tick)为专业版能力;免费网关不提供历史 Tick,免费账户调用只返回近期留存、取不到跨历史区间。 另 get_kline_data_series/get_tick_data_series 在官方 tqsdk 由 tq_dl 特征门槛限制为专业版,本库直接走底层 set_chart,故免费账户也能拉到近期数据,更深历史仍取决于账户权限。
  • 回测/复盘(TqBacktest / TqReplay) 与 EDB/宏观数据(api.query) 等尚未迁移(属连接模式 / 独立数据服务,非单纯方法复刻)。
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 94 10/26/2026