{"id":37221270,"url":"https://github.com/kkqy/ctp-go","last_synced_at":"2026-01-15T01:25:31.304Z","repository":{"id":61317732,"uuid":"550494876","full_name":"kkqy/ctp-go","owner":"kkqy","description":"上期技术期货交易协议CTP的GO版本，使用方法和C++基本一致，具体接口文档请参考上期CTP官方文档。 支持Windows(x86/x64)、Linux(x64)，虽然上期官方已经更新了MacOS版本，但是由于我没有Mac，所以暂时不支持。","archived":false,"fork":false,"pushed_at":"2025-04-10T11:18:45.000Z","size":15368,"stargazers_count":8,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T12:50:08.239Z","etag":null,"topics":["ctp","futures","go","golang","linux","package","windows"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kkqy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-12T21:36:50.000Z","updated_at":"2025-04-10T11:18:48.000Z","dependencies_parsed_at":"2024-06-21T02:04:20.745Z","dependency_job_id":null,"html_url":"https://github.com/kkqy/ctp-go","commit_stats":{"total_commits":5,"total_committers":2,"mean_commits":2.5,"dds":"0.19999999999999996","last_synced_commit":"77758a64bcee700a3a2ed6b2a8efe36c4dbc617c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kkqy/ctp-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkqy%2Fctp-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkqy%2Fctp-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkqy%2Fctp-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkqy%2Fctp-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kkqy","download_url":"https://codeload.github.com/kkqy/ctp-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkqy%2Fctp-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28441031,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:55:22.719Z","status":"ssl_error","status_checked_at":"2026-01-15T00:55:20.945Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ctp","futures","go","golang","linux","package","windows"],"created_at":"2026-01-15T01:25:30.663Z","updated_at":"2026-01-15T01:25:31.286Z","avatar_url":"https://github.com/kkqy.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# ctp-go\n上期技术期货交易协议CTP的Go版本，使用方法和C++基本一致。\n\n具体接口文档请参考上期CTP官方文档：https://www.simnow.com.cn/static/apiDownload.action （非工作时间可能无法访问）\n\n支持Windows(x86/x64)、Linux(x64)，虽然上期官方已经更新了MacOS版本，但是由于我没有Mac，所以暂时不支持。\nctp-go基于Go 1.18，更早版本请自行修改mod文件测试看看是否可用。\n\n## 安装说明\n### 第一步：安装GCC\n由于ctp-go使用了CGO，所以需要GCC或类GCC编译器，例如LLVM/Clang。\n#### Linux下直接安装GCC或者LLVM/Clang (二选一即可)：\n```\n# Debian系\nsudo apt install gcc\n\n# RedHat系\nsudo yum install gcc\n```\n**建议直接使用Clang，编译速度更快**\n```\n# Debian系\nsudo apt install clang \n\n# RedHat系\nsudo yum install gcc gcc-c++ clang libstdc++-static\n\n# 通过修改环境变量CC指定clang作为编译器\n# export命令只会临时修改当前会话的环境变量，长久修改请自行修改系统或用户的profile文件。\nexport CC=clang\n```\n#### Windows下安装Mingw w64或者使用LLVM (二选一即可)\n\nMingw w64：https://github.com/niXman/mingw-builds-binaries/releases/\n\n**建议使用llvm-mingw，编译速度更快！**\n\nllvm-mingw：https://github.com/mstorsjo/llvm-mingw/releases\n\n**记得将解压后的bin目录添加到系统环境变量。**\n\n### 第二步：下载对应的版本\n由于CTP接口区分不同版本，所以本包采用多个分支对应多个版本.\n\n* 6.3.15\n* 6.6.7\n* 6.7.9\n\n安装的时候需要确定服务器使用的版本。\n\n例如CTP柜台要求是6.3.15版本，那么就安装6.3.15分支:\n```\ngo get github.com/kkqy/ctp-go@6.3.15\n```\n如果柜台要求是6.6.7版本，那么就安装6.6.7：\n```\ngo get github.com/kkqy/ctp-go@6.6.7\n```\n如果柜台要求是6.7.9版本，那么就安装6.7.9：\n```\ngo get github.com/kkqy/ctp-go@6.7.9\n```\n\n一般小版本是能兼容的。\n\n## 使用说明\n这里以交易接口为例，使用API查询合约的价格最小变动(PriceTick)。行情接口用法类似。\n### 第一步：导入ctp-go包\n```\nimport (\n    \"fmt\"\n    \"time\"\n\n    ctp \"github.com/kkqy/ctp-go\"\n)\n```\n### 第二步：创建交易SPI，用于响应回调。\n```\ntype MyTraderSpi struct {\n    ctp.TraderSpi //TraderSpi里定义了交易Spi的所有回调函数，这些回调函数不做任何操作，仅仅用作默认“兜底”。\n}\n\nfunc (p *MyTraderSpi) OnFrontConnected() {\n    fmt.Println(\"交易前置已连接！\")\n}\n\nfunc (p *MyTraderSpi) OnRspAuthenticate(pRspAuthenticateField ctp.CThostFtdcRspAuthenticateField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        fmt.Println(\"客户端认证成功！\")\n    } else {\n        fmt.Println(\"认证失败！错误代码：\", errID)\n    }\n}\nfunc (p *MyTraderSpi) OnRspUserLogin(pRspUserLogin ctp.CThostFtdcRspUserLoginField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        fmt.Println(\"用户登陆\")\n    } else {\n        fmt.Println(\"登陆失败！错误代码：\", errID)\n    }\n}\n\nfunc (p *MyTraderSpi) OnRspQryInstrument(pInstrument ctp.CThostFtdcInstrumentField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    fmt.Println(\"接收到合约：\", pInstrument.GetInstrumentID(), \"价格最小变动：\", pInstrument.GetPriceTick())\n    if bIsLast {\n        fmt.Println(\"合约信息接收完成\")\n    }\n}\n```\n**此处应特别注意：按照CTP接口的设计，SPI是通过纯虚函数实现的回调，而由于Go没有C++中纯虚函数的概念，编译时也无法检查这些回调函数定义是否存在，当回调被调用时，如果Go里面没有定义相应的回调函数，则会产生空指针/引用的panic。**\n\n举个例子：如果服务器断开，则OnFrontDisconnected会被回调，而此时若没有定义OnFrontDisconnected这个回调函数，就会造成空指针/引用的panic。\n\n但是，大多数时候我们其实只关心一小部分接口的回调，如果为了去实现全部的回调，写一大堆空的回调方法，代码就显得冗长了，所以ctp-go提供了“空的”**TraderSpi**和**MarketDataSpi**，里面包含了SPI所有的响应函数实现，只不过它们都不做任何事情。而利用Go语言结构体的“组合”特性，我们能让这个“空的”TraderSpi作为“兜底”，我们只需要“覆盖”我们关心的回调方法即可。\n\n### 第三步：创建交易API，并且查询合约信息。\n```\nfunc main() {\n    flowPath := \"./\"                                 // 流文件存储文件夹，CTP不会自动创建文件夹，所以必须提前创建，否则会报错\n    traderFrontAddr := \"tcp://180.168.146.187:10201\" // 交易前置服务器地址\n    brokerID := \"9999\"                               // BrokerID\n    appID := \"simnow_client_test\"                    // APPID\n    authCode := \"0000000000000000\"                   // 授权码\n    userProductionInfo := \"\"                         // 客户端信息\n    userID := \"888888\"                               // 用户ID\n    password := \"simnowpassword\"                     // 用户密码\n\n    traderApi := ctp.CThostFtdcTraderApiCreateFtdcTraderApi(flowPath)         //创建API\n    traderApi.RegisterSpi(ctp.NewDirectorCThostFtdcTraderSpi(\u0026MyTraderSpi{})) // 注册响应Spi\n    traderApi.RegisterFront(traderFrontAddr)                                  // 注册交易前置\n    traderApi.SubscribePrivateTopic(ctp.THOST_TERT_RESTART)                   // 订阅私有流\n    traderApi.SubscribePublicTopic(ctp.THOST_TERT_RESTART)                    // 订阅公共流\n    traderApi.Init()                                                          // 初始化API\n    time.Sleep(time.Second * 5)                                               // 等待连接完成\n\n    reqAuthenticateField := ctp.NewCThostFtdcReqAuthenticateField() // 创建ReqAuthenticateField\n    reqAuthenticateField.SetBrokerID(brokerID)\n    reqAuthenticateField.SetUserProductInfo(userProductionInfo)\n    reqAuthenticateField.SetAuthCode(authCode)\n    reqAuthenticateField.SetAppID(appID)\n    traderApi.ReqAuthenticate(reqAuthenticateField, 1)             // 请求认证\n    ctp.DeleteCThostFtdcReqAuthenticateField(reqAuthenticateField) // 释放reqAuthenticateField\n    time.Sleep(time.Second * 5)                                    // 等待授权认证完成\n\n    userLoginField := ctp.NewCThostFtdcReqUserLoginField() // 创建UserLoginField\n    userLoginField.SetUserID(userID)\n    userLoginField.SetPassword(password)\n    userLoginField.SetBrokerID(brokerID)\n    traderApi.ReqUserLogin(userLoginField, 2)             // 请求登陆\n    ctp.DeleteCThostFtdcReqUserLoginField(userLoginField) // 释放userLoginField\n    time.Sleep(time.Second * 5)                           // 等待用户登陆完成\n\n    qryInstrumentField := ctp.NewCThostFtdcQryInstrumentField()\n    traderApi.ReqQryInstrument(qryInstrumentField, 3)          // 请求查询合约\n    ctp.DeleteCThostFtdcQryInstrumentField(qryInstrumentField) // 释放qryInstrumentField\n\n    traderApi.Join()\n}\n```\n### 最终代码\n```\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    ctp \"github.com/kkqy/ctp-go\"\n)\n\n\ntype MyTraderSpi struct {\n    ctp.TraderSpi //TraderSpi里定义了交易Spi的所有回调函数，这些回调函数不做任何操作，仅仅用作默认“兜底”。\n}\n\nfunc (p *MyTraderSpi) OnFrontConnected() {\n    fmt.Println(\"交易前置已连接！\")\n}\n\nfunc (p *MyTraderSpi) OnRspAuthenticate(pRspAuthenticateField ctp.CThostFtdcRspAuthenticateField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        fmt.Println(\"客户端认证成功！\")\n    } else {\n        fmt.Println(\"认证失败！错误代码：\", errID)\n    }\n}\nfunc (p *MyTraderSpi) OnRspUserLogin(pRspUserLogin ctp.CThostFtdcRspUserLoginField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        fmt.Println(\"用户登陆\")\n    } else {\n        fmt.Println(\"登陆失败！错误代码：\", errID)\n    }\n}\n\nfunc (p *MyTraderSpi) OnRspQryInstrument(pInstrument ctp.CThostFtdcInstrumentField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    fmt.Println(\"接收到合约：\", pInstrument.GetInstrumentID(), \"价格最小变动：\", pInstrument.GetPriceTick())\n    if bIsLast {\n        fmt.Println(\"合约信息接收完成\")\n    }\n}\n\nfunc main() {\n    flowPath := \"./\"                                 // 流文件存储文件夹，CTP不会自动创建文件夹，所以必须提前创建，否则会报错\n    traderFrontAddr := \"tcp://180.168.146.187:10201\" // 交易前置服务器地址\n    brokerID := \"9999\"                               // BrokerID\n    appID := \"simnow_client_test\"                    // APPID\n    authCode := \"0000000000000000\"                   // 授权码\n    userProductionInfo := \"\"                         // 客户端信息\n    userID := \"888888\"                               // 用户ID\n    password := \"simnowpassword\"                     // 用户密码\n\n    traderApi := ctp.CThostFtdcTraderApiCreateFtdcTraderApi(flowPath)     //创建API\n    traderApi.RegisterSpi(ctp.NewDirectorCThostFtdcTraderSpi(\u0026MyTraderSpi{})) // 注册响应Spi\n    traderApi.RegisterFront(traderFrontAddr)                              // 注册交易前置\n    traderApi.SubscribePrivateTopic(ctp.THOST_TERT_RESTART)               // 订阅私有流\n    traderApi.SubscribePublicTopic(ctp.THOST_TERT_RESTART)                // 订阅公共流\n    traderApi.Init()                                                      // 初始化API\n    time.Sleep(time.Second * 5)                                           // 等待连接完成\n\n    reqAuthenticateField := ctp.NewCThostFtdcReqAuthenticateField() // 创建ReqAuthenticateField\n    reqAuthenticateField.SetBrokerID(brokerID)\n    reqAuthenticateField.SetUserProductInfo(userProductionInfo)\n    reqAuthenticateField.SetAuthCode(authCode)\n    reqAuthenticateField.SetAppID(appID)\n    traderApi.ReqAuthenticate(reqAuthenticateField, 1)             // 请求认证\n    ctp.DeleteCThostFtdcReqAuthenticateField(reqAuthenticateField) // 释放reqAuthenticateField\n    time.Sleep(time.Second * 5)                                    // 等待授权认证完成\n\n    userLoginField := ctp.NewCThostFtdcReqUserLoginField() // 创建UserLoginField\n    userLoginField.SetUserID(userID)\n    userLoginField.SetPassword(password)\n    userLoginField.SetBrokerID(brokerID)\n    traderApi.ReqUserLogin(userLoginField, 2)             // 请求登陆\n    ctp.DeleteCThostFtdcReqUserLoginField(userLoginField) // 释放userLoginField\n    time.Sleep(time.Second * 5)                           // 等待用户登陆完成\n\n    qryInstrumentField := ctp.NewCThostFtdcQryInstrumentField()\n    traderApi.ReqQryInstrument(qryInstrumentField, 3)          // 请求查询合约\n    ctp.DeleteCThostFtdcQryInstrumentField(qryInstrumentField) // 释放qryInstrumentField\n\n    traderApi.Join()\n}\n```\n## 将交易API/SPI、行情API/SPI封装到一起的示例代码\n在CTP接口中，API负责请求，SPI负责响应，而且同时又区分交易接口和行情接口，有一些零散。\n借助Golang的“组合”特性，我们可以巧妙地将CTP接口的API和SPI全部组合到一起。\n在这个示例程序里，交易接口和行情接口的API和SPI都被一起封装到一个名叫Gateway的结构里。\n最终实现通过交易接口查询合约代码，然后再通过行情接口订阅行情，并且输出最新的价格。\n此示例只是一个抛砖引玉的作用，具体项目具体考虑。\n\n```\npackage main\n\nimport (\n    \"fmt\"\n    \"sync\"\n    \"sync/atomic\"\n\n    ctp \"github.com/kkqy/ctp-go\"\n)\ntype Gateway struct {\n    wg *sync.WaitGroup // 使用WaitGroup来同步连接、认证、登陆的顺序\n\n    ctp.CThostFtdcTraderApi // 交易API\n    ctp.CThostFtdcMdApi     // 行情API\n    ctp.TraderSpi           // TraderSpi里定义了交易Spi的所有回调函数，这些回调函数不做任何操作，仅仅用作默认“兜底”。\n    ctp.MarketDataSpi       // MarketDataSpi里定义了行情Spi的所有回调函数，功能同上。\n    requestID               atomic.Uint64\n    instrumentIDs           []string // 用来存储合约列表\n}\n\nfunc (p *Gateway) OnFrontConnected() {\n    p.wg.Done()\n}\n\nfunc (p *Gateway) OnRspAuthenticate(pRspAuthenticateField ctp.CThostFtdcRspAuthenticateField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        p.wg.Done()\n    } else {\n        fmt.Println(\"认证失败！错误代码：\", errID)\n    }\n}\nfunc (p *Gateway) OnRspUserLogin(pRspUserLogin ctp.CThostFtdcRspUserLoginField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    errID := pRspInfo.GetErrorID()\n    if errID == 0 {\n        p.wg.Done()\n    } else {\n        fmt.Println(\"登陆失败！错误代码：\", errID)\n    }\n}\n\nfunc (p *Gateway) OnRspQryInstrument(pInstrument ctp.CThostFtdcInstrumentField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    p.instrumentIDs = append(p.instrumentIDs, pInstrument.GetInstrumentID())\n    if bIsLast {\n        p.wg.Done()\n    }\n}\n\nfunc (p *Gateway) OnRtnDepthMarketData(pDepthMarketData ctp.CThostFtdcDepthMarketDataField) {\n    fmt.Println(\"合约代码：\", pDepthMarketData.GetInstrumentID(), \"最新价格：\", pDepthMarketData.GetLastPrice())\n}\n\nfunc (p *Gateway) Join() {\n    p.CThostFtdcTraderApi.Join()\n    p.CThostFtdcMdApi.Join()\n}\nfunc (p *Gateway) Release() {\n    p.CThostFtdcTraderApi.Release()\n    p.CThostFtdcMdApi.Release()\n}\n\nfunc NewGateway(flowPath, traderFrontAddr, marketDataFrontAddr, brokerID, userID, password, appID, authCode, userProductionInfo string) *Gateway {\n\n    p := \u0026Gateway{\n        wg: \u0026sync.WaitGroup{},\n\n        CThostFtdcTraderApi: ctp.CThostFtdcTraderApiCreateFtdcTraderApi(flowPath),\n        CThostFtdcMdApi:     ctp.CThostFtdcMdApiCreateFtdcMdApi(flowPath),\n        instrumentIDs:       make([]string, 0),\n    }\n\n    p.CThostFtdcTraderApi.RegisterSpi(ctp.NewDirectorCThostFtdcTraderSpi(p))\n    p.CThostFtdcMdApi.RegisterSpi(ctp.NewDirectorCThostFtdcMdSpi(p))\n\n    p.CThostFtdcTraderApi.RegisterFront(traderFrontAddr) //注创建册交易前置\n    p.CThostFtdcMdApi.RegisterFront(marketDataFrontAddr) //注册行情前置\n\n    p.SubscribePrivateTopic(ctp.THOST_TERT_RESTART) //订阅私有流\n    p.SubscribePublicTopic(ctp.THOST_TERT_RESTART)  //订阅公共流\n    fmt.Println(\"网关订阅流完成\")\n\n    p.wg.Add(2) //由于交易和行情接口都会调用OnFrontConnected，所以会Done两次\n    p.CThostFtdcTraderApi.Init()\n    p.CThostFtdcMdApi.Init()\n    p.wg.Wait()\n    fmt.Println(\"网关初始化完成\")\n\n    p.wg.Add(1)\n    reqAuthenticateField := ctp.NewCThostFtdcReqAuthenticateField() //创建认证字段\n    reqAuthenticateField.SetBrokerID(brokerID)\n    reqAuthenticateField.SetUserProductInfo(userProductionInfo)\n    reqAuthenticateField.SetAuthCode(authCode)\n    reqAuthenticateField.SetAppID(appID)\n    p.ReqAuthenticate(reqAuthenticateField, int(p.requestID.Add(1))) // 交易API发送认证请求\n    ctp.DeleteCThostFtdcReqAuthenticateField(reqAuthenticateField)   // 记得释放\n    p.wg.Wait() // 等待认证完成\n    fmt.Println(\"网关已认证\")\n\n    p.wg.Add(2) //由于交易和行情接口都会调用OnRspUserLogin，所以会Done两次\n    userLoginField := ctp.NewCThostFtdcReqUserLoginField()                       //创建登陆字段\n    userLoginField.SetUserID(userID)\n    userLoginField.SetPassword(password)\n    userLoginField.SetBrokerID(brokerID)\n    p.CThostFtdcTraderApi.ReqUserLogin(userLoginField, int(p.requestID.Add(1))) // 交易API发送登陆请求\n    p.CThostFtdcMdApi.ReqUserLogin(userLoginField, 0)                           // 行情API发送登陆请求\n    ctp.DeleteCThostFtdcReqUserLoginField(userLoginField)                       // 记得释放\n    p.wg.Wait() // 等待登陆完成\n    fmt.Println(\"网关已登录\")\n\n    p.wg.Add(1)\n    qryInstrumentField := ctp.NewCThostFtdcQryInstrumentField()\n    p.ReqQryInstrument(qryInstrumentField, int(p.requestID.Add(1)))\n    ctp.DeleteCThostFtdcQryInstrumentField(qryInstrumentField)\n    p.wg.Wait()\n    fmt.Println(\"网关已获取合约列表\")\n\n    p.SubscribeMarketData(p.instrumentIDs)\n    fmt.Println(\"网关已订阅所有合约\")\n    return p\n}\n\nfunc main() {\n    // 替换你自己的登陆信息\n    gateway := NewGateway(\"./\", \"tcp://180.168.146.187:10201\", \"tcp://180.168.146.187:10211\", \"9999\", \"888888\", \"simnowpassword\", \"simnow_client_test\", \"0000000000000000\", \"\")\n    gateway.Join()\n}\n```\n\n## 注意事项（避坑指南）\n* 使用ctp.NewXXX 创建的CTP的任何结构，使用完以后一定要用对应的释放函数ctp.DeleteXXX 进行释放，不然可能会造成内存泄漏。例如:\n```\nuserLoginField := ctp.NewCThostFtdcReqUserLoginField() // 创建UserLoginField\nuserLoginField.SetUserID(userID)\nuserLoginField.SetPassword(password)\nuserLoginField.SetBrokerID(brokerID)\ntraderApi.ReqUserLogin(userLoginField, 2)             // 请求登陆\nctp.DeleteCThostFtdcReqUserLoginField(userLoginField) // 需要手动释放userLoginField，不然会造成内存泄漏！\n\n// 如果担心忘记释放，可以借助Go的defer。\nuserLoginField := ctp.NewCThostFtdcReqUserLoginField() // 创建UserLoginField\ndefer ctp.DeleteCThostFtdcReqUserLoginField(userLoginField) // New完马上通过defer来Delete就不容易忘记。如果就这样了还是记不住，那泄露就泄露吧，如果内存占用没炸，那就“无所谓”，如果内存占用炸了，那肯定会去找原因然后长记性对吧？\n```\n* 回调函数的非GO原生类型的参数不要传递到回调函数外面去，因为这些参数内部的指针指向的内存在回调结束后会被释放掉，那时指向的数据是不确定的。例如：\n```\nfunc (p *Gateway) OnRspUserLogin(pRspUserLogin ctp.CThostFtdcRspUserLoginField, pRspInfo ctp.CThostFtdcRspInfoField, nRequestID int, bIsLast bool) {\n    OuterVar1 = pRspUserLogin // 不可以！pRspUserLogin内部指针指向的内存是CTP接口在C/C++运行时的堆里分配的，不受Go管理，所以回调函数结束以后这些参数指向的内存就会被释放。\n    go func(){\n        _ = pRspUserLogin.GetTradingDay() // 不可以！因为Go并不能保证goroutine执行顺序，也许在调用GetTradingDay之前，OnRspUserLogin就已经执行完，pRspUserLogin内部指针指向的内存已经被释放了。\n    }()\n    OuterVar2 = pRspUserLogin.GetTradingDay() // 可以，因为GetTradingDay()返回的是Go的string类型数据，由Go负责内存管理和回收。\n    OuterVar3 = nRequestID // 可以但没必要，因为int是Go的原生类型，由Go负责内存管理和回收。\n    OuterVar4 = bIsLast // 可以但没必要，因为bool是Go的原生类型，由Go负责内存管理和回收。\n}\n```\n* 使用的包的版本必须和动态链接库严格一致，例如：使用6.6.x的包进行编译就必须用6.6.x的动态链接库，否则虽然程序也许运行得起来，但是会出现稀奇古怪的、不可预料的、捉摸不透的错误。\n* 由于CTP的字符串使用的是GB2312/GBK编码，而Go的字符串使用的是UTF8编码，所以CTP中出现的中文字符串在Go中会乱码。CTP接口众多，考虑到运行效率问题，最佳实践应该是按需转码，所以ctp-go并没有直接集成编码转换，需要自行处理。\n\n### Linux 平台\n* 编译后的可执行文件依赖libthostmduserapi_se.so、libthosttraderapi_se.so这两个动态链接库，这两个文件在包目录下Linux文件夹中可以找到，官方下载的API里这两个动态链接库文件名缺少“lib”前缀，需要手动修改一下文件名。由于编译的时候指定了库的搜索路径，所以在本机上运行的时候能直接运行。但是如果打包发布，则需要附带这两个动态链接库文件一起发布。\n\n### Windows 平台\n* 编译后的可执行文件依赖thostmduserapi_se.dll、thosttraderapi_se.dll、wrap.dll这三个动态链接库，这三个文件在包目录下Windows文件夹中可以找到（需要区分x86和x64版本），需要将这三个文件与可执行文件放在同一目录，才能正常运行，否则会出现“由于找不到 xxx.dll，无法继续执行代码。重新安装程序可能会解决此问题。”的错误。\n\n## 常见问题\n### 为什么使用ctp-go以后我的程序编译那么慢？\n首次构建的时候，ctp-go需要使用CGO进行编译，此时需要耗费较长时间，但仅仅只是首次比较慢，当再次构建的时候，Go的编译器就仅仅只会编译修改过的模块，而不需要再次编译ctp-go。个人实测，如果使用gcc编译器，首次构建最长可能花费10分钟左右，而如果替换成了LLVM/Clang，首次编译时间直接缩短为20秒不到，通过任务管理器可以对比，LLVM/Clang的CPU和内存利用率比gcc高出许多，所以，如果实在在意首次编译时间，不论是Windows还是Linux，都建议直接使用LLVM/Clang替代GCC作为编译器前端。\nLLVM/Clang编译速度比GCC快，且有更友好的错误提示。关于LLVM/Clang与GCC的说明、性能对比以及安装方法可参考网上其它教程，\n\n### 修改GOARCH为386以后，出现undefined: CThostFtdcRspUserLoginField等错误\n由于本包依赖CGO，所以需要将CGO_ENABLE设置为1。默认GOARCH下，CGO_ENABLE默认也是打开状态，但是在切换GOARCH为386，也就是编译32位的时候，CGO_ENABLE默认为关闭，需要手动设置为1。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkqy%2Fctp-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkkqy%2Fctp-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkqy%2Fctp-go/lists"}