C++连接CTP接口实现简单量化交易(行情、交易、k线、策略)

2023-10-27

对于量化交易来说,量化策略和技术系统缺一不可,为了知其所以然,本文实现了一个C++连接CTP接口进行仿真交易的demo,从接收行情、下订单、数据处理到添加策略、挂载运行交易等多个环节来看一下量化交易的最简单流程,管中窥豹,一探究竟。大笑

 

准备工作

交易所接口

这里使用上期所提供的CTP接口API,通过CTP可以连接交易所进行行情接收交易。下载地址:CTP下载

本文使用的win32版本的,linux版本用法类似。

CTP接口包含以下内容:

 

  • ThostFtdcTraderApi.h:C++头文件,包含交易相关的指令,如报单。
  • ThostFtdcMdApi.h:C++头文件,包含获取行情相关的指令。
  • ThostFtdcUserApiStruct.h:包含了所有用到的数据结构。
  • ThostFtdcUserApiDataType.h:包含了所有用到的数据类型。
  • thosttraderapi.lib、thosttraderapi.dll:交易部分的动态链接库和静态链接库。
  • thostmduserapi.lib、thostmduserapi.dll:行情部分的动态链接库和静态链接库。
  • error.dtd、error.xml:包含所有可能的错误信息。

 

 

整个开发包有2个核心头文件包括4个核心接口
CThostFtdcMdApi接口和CThostFtdcTraderApi两个头文件,一个处理行情,一个处理交易

(1)处理行情的CThostFtdcMdApi接口有两个类,分别是CThostFtdcMdApi和CThostFtdcMdSpi,以Api结尾的是用来下命令的,以Spi结尾的是用来响应命令的回调。

(2)处理交易的CThostFtdcTraderApi接口也有两个类,分别是CThostFtdcTraderApi和CThostFtdcTraderSpi,  通过CThostFtdcTraderApi向CTP发送操作请求,通过CThostFtdcTraderSpi接收CTP的操作响应。

 

期货账户

要连接期货交易所交易,需要开设自己的账户,实现期货交易、银期转账、保证金等功能,由于小白一般不会用实盘资金交易,所以此处推荐用上期所提供的simnow虚拟交易平台simnow申请一个虚拟账户。

SIMNOW提供两类数据前置地址:

(1)交易时段的地址,如09:00-15:00和21:00-02:30,使用第一套地址,这些数据是真实的行情数据,只是时间上比真实的行情会有延迟30秒左右(SIMNOW从交易所接收后转发出来的)。

(2)非交易时段地址,这时的数据是历史行情的播放,比如昨天的数据之类的,可以用来做程序调试。

 

建议选择申请那个7x24行情的账户,便于开发调试。

 

开发步骤

工程总览

 

其中,

 

  • CTP的API文件配置到工程
  • CustomMdSpi.h,CustomMdSpi.cpp是派生的行情回调类
  • CustomTradeSpi.h,CustomTradeSpi.cpp是派生的交易回调类
  • TickToKlineHelper.h,TickToKlineHelper.cpp是处理时序数据,转换成K线的类
  • StrategyTrade.h,StrategyTrade.cpp是策略类
  • main.cpp是程序的入口

一个简单的程序化交易系统需要完成的业务可以划分为:
1.基本操作,比如登录,订阅等;
2.行情操作,比如对行情数据的接收,存储等
3.订单操作,比如报单;对报单,成交状况的查询;报单,成交状况的私有回报等。
4.数据监听和处理操作,比如接收到新数据之后的统计处理,满足统计条件后的报单处理(其实这里就是我们的策略所在)

 

导入CTP接口库

visual studio创建工程后,首先需要将ctp的头文件以及链接库(lib和dll)目录配置到工程

 

 

 


 
 
  1. // 链接库
  2. #pragma comment (lib, "thostmduserapi.lib")
  3. #pragma comment (lib, "thosttraderapi.lib")

 

 

全局参数

连接到交易所,需要配置经纪商代码、帐户名、密码以及订阅合约和买卖合约的相关参数


 
 
  1. // ---- 全局变量 ---- //
  2. // 公共参数
  3. TThostFtdcBrokerIDType gBrokerID = "9999"; // 模拟经纪商代码
  4. TThostFtdcInvestorIDType gInvesterID = ""; // 投资者账户名
  5. TThostFtdcPasswordType gInvesterPassword = ""; // 投资者密码
  6. // 行情参数
  7. CThostFtdcMdApi *g_pMdUserApi = nullptr; // 行情指针
  8. char gMdFrontAddr[] = "tcp://180.168.146.187:10010"; // 模拟行情前置地址
  9. char *g_pInstrumentID[] = { "TF1706", "zn1705", "cs1801", "CF705"}; // 行情合约代码列表,中、上、大、郑交易所各选一种
  10. int instrumentNum = 4; // 行情合约订阅数量
  11. unordered_map< string, TickToKlineHelper> g_KlineHash; // 不同合约的k线存储表
  12. // 交易参数
  13. CThostFtdcTraderApi *g_pTradeUserApi = nullptr; // 交易指针
  14. char gTradeFrontAddr[] = "tcp://180.168.146.187:10001"; // 模拟交易前置地址
  15. TThostFtdcInstrumentIDType g_pTradeInstrumentID = "m1709"; // 所交易的合约代码
  16. TThostFtdcDirectionType gTradeDirection = THOST_FTDC_D_Sell; // 买卖方向
  17. TThostFtdcPriceType gLimitPrice = 2818; // 交易价格


这里只是简单的写一下,真实完整的交易系统中,一般用配置文件,有用户去定制

 

行情回调类

继承CThostFtdcMdSpi实现自己的行情回调类CustomMdSpi,在系统运行时这些重写的函数会被CTP的系统api回调从而实现个性化行情

CustomMdSpi头文件

 


 
 
  1. #pragma once
  2. // ---- 派生的行情类 ---- //
  3. #include <vector>
  4. #include "CTP_API/ThostFtdcMdApi.h"
  5. class CustomMdSpi: public CThostFtdcMdSpi
  6. {
  7. // ---- 继承自CTP父类的回调接口并实现 ---- //
  8. public:
  9. ///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
  10. void OnFrontConnected();
  11. ///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
  12. ///@param nReason 错误原因
  13. /// 0x1001 网络读失败
  14. /// 0x1002 网络写失败
  15. /// 0x2001 接收心跳超时
  16. /// 0x2002 发送心跳失败
  17. /// 0x2003 收到错误报文
  18. void OnFrontDisconnected(int nReason);
  19. ///心跳超时警告。当长时间未收到报文时,该方法被调用。
  20. ///@param nTimeLapse 距离上次接收报文的时间
  21. void OnHeartBeatWarning(int nTimeLapse);
  22. ///登录请求响应
  23. void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  24. ///登出请求响应
  25. void OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  26. ///错误应答
  27. void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  28. ///订阅行情应答
  29. void OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  30. ///取消订阅行情应答
  31. void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  32. ///订阅询价应答
  33. void OnRspSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  34. ///取消订阅询价应答
  35. void OnRspUnSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  36. ///深度行情通知
  37. void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData);
  38. ///询价通知
  39. void OnRtnForQuoteRsp(CThostFtdcForQuoteRspField *pForQuoteRsp);
  40. };

 

都是重写回调函数

连接应答

 


 
 
  1. // 连接成功应答
  2. void CustomMdSpi::OnFrontConnected()
  3. {
  4. std:: cout << "=====建立网络连接成功=====" << std:: endl;
  5. // 开始登录
  6. CThostFtdcReqUserLoginField loginReq;
  7. memset(&loginReq, 0, sizeof(loginReq));
  8. strcpy(loginReq.BrokerID, gBrokerID);
  9. strcpy(loginReq.UserID, gInvesterID);
  10. strcpy(loginReq.Password, gInvesterPassword);
  11. static int requestID = 0; // 请求编号
  12. int rt = g_pMdUserApi->ReqUserLogin(&loginReq, requestID);
  13. if (!rt)
  14. std:: cout << ">>>>>>发送登录请求成功" << std:: endl;
  15. else
  16. std:: cerr << "--->>>发送登录请求失败" << std:: endl;
  17. }

 

登录应答

 


 
 
  1. // 登录应答
  2. void CustomMdSpi::OnRspUserLogin(
  3. CThostFtdcRspUserLoginField *pRspUserLogin,
  4. CThostFtdcRspInfoField *pRspInfo,
  5. int nRequestID,
  6. bool bIsLast)
  7. {
  8. bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
  9. if (!bResult)
  10. {
  11. std:: cout << "=====账户登录成功=====" << std:: endl;
  12. std:: cout << "交易日: " << pRspUserLogin->TradingDay << std:: endl;
  13. std:: cout << "登录时间: " << pRspUserLogin->LoginTime << std:: endl;
  14. std:: cout << "经纪商: " << pRspUserLogin->BrokerID << std:: endl;
  15. std:: cout << "帐户名: " << pRspUserLogin->UserID << std:: endl;
  16. // 开始订阅行情
  17. int rt = g_pMdUserApi->SubscribeMarketData(g_pInstrumentID, instrumentNum);
  18. if (!rt)
  19. std:: cout << ">>>>>>发送订阅行情请求成功" << std:: endl;
  20. else
  21. std:: cerr << "--->>>发送订阅行情请求失败" << std:: endl;
  22. }
  23. else
  24. std:: cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << std:: endl;
  25. }

 

订阅行情应答

 


 
 
  1. // 订阅行情应答
  2. void CustomMdSpi::OnRspSubMarketData(
  3. CThostFtdcSpecificInstrumentField *pSpecificInstrument,
  4. CThostFtdcRspInfoField *pRspInfo,
  5. int nRequestID,
  6. bool bIsLast)
  7. {
  8. bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
  9. if (!bResult)
  10. {
  11. std:: cout << "=====订阅行情成功=====" << std:: endl;
  12. std:: cout << "合约代码: " << pSpecificInstrument->InstrumentID << std:: endl;
  13. // 如果需要存入文件或者数据库,在这里创建表头,不同的合约单独存储
  14. char filePath[ 100] = { '\0'};
  15. sprintf(filePath, "%s_market_data.csv", pSpecificInstrument->InstrumentID);
  16. std::ofstream outFile;
  17. outFile.open(filePath, std::ios::out); // 新开文件
  18. outFile << "合约代码" << ","
  19. << "更新时间" << ","
  20. << "最新价" << ","
  21. << "成交量" << ","
  22. << "买价一" << ","
  23. << "买量一" << ","
  24. << "卖价一" << ","
  25. << "卖量一" << ","
  26. << "持仓量" << ","
  27. << "换手率"
  28. << std:: endl;
  29. outFile.close();
  30. }
  31. else
  32. std:: cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << std:: endl;
  33. }

 

  • 因为是异步接口,这里连接、登录、订阅行情是一步套一步来调用的,在运行过程中,会启动一个行情线程,交易所每500ms会推送一个订阅的行情tick数据,因此,某些接口会被连续间隔调用,直到连接关闭
  • 收到行情后除了存在内存,也可以用文本文件或者数据库等形式存储起来,在这里创建初始文件或者建库

深度行情通知


 
 
  1. // 行情详情通知
  2. void CustomMdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData)
  3. {
  4. // 打印行情,字段较多,截取部分
  5. std:: cout << "=====获得深度行情=====" << std:: endl;
  6. std:: cout << "交易日: " << pDepthMarketData->TradingDay << std:: endl;
  7. std:: cout << "交易所代码: " << pDepthMarketData->ExchangeID << std:: endl;
  8. std:: cout << "合约代码: " << pDepthMarketData->InstrumentID << std:: endl;
  9. std:: cout << "合约在交易所的代码: " << pDepthMarketData->ExchangeInstID << std:: endl;
  10. std:: cout << "最新价: " << pDepthMarketData->LastPrice << std:: endl;
  11. std:: cout << "数量: " << pDepthMarketData->Volume << std:: endl;
  12. // 如果只获取某一个合约行情,可以逐tick地存入文件或数据库
  13. char filePath[ 100] = { '\0'};
  14. sprintf(filePath, "%s_market_data.csv", pDepthMarketData->InstrumentID);
  15. std::ofstream outFile;
  16. outFile.open(filePath, std::ios::app); // 文件追加写入
  17. outFile << pDepthMarketData->InstrumentID << ","
  18. << pDepthMarketData->UpdateTime << "." << pDepthMarketData->UpdateMillisec << ","
  19. << pDepthMarketData->LastPrice << ","
  20. << pDepthMarketData->Volume << ","
  21. << pDepthMarketData->BidPrice1 << ","
  22. << pDepthMarketData->BidVolume1 << ","
  23. << pDepthMarketData->AskPrice1 << ","
  24. << pDepthMarketData->AskVolume1 << ","
  25. << pDepthMarketData->OpenInterest << ","
  26. << pDepthMarketData->Turnover << std:: endl;
  27. outFile.close();
  28. // 计算实时k线
  29. std:: string instrumentKey = std:: string(pDepthMarketData->InstrumentID);
  30. if (g_KlineHash.find(instrumentKey) == g_KlineHash.end())
  31. g_KlineHash[instrumentKey] = TickToKlineHelper();
  32. g_KlineHash[instrumentKey].KLineFromRealtimeData(pDepthMarketData);
  33. // 取消订阅行情
  34. //int rt = g_pMdUserApi->UnSubscribeMarketData(g_pInstrumentID, instrumentNum);
  35. //if (!rt)
  36. // std::cout << ">>>>>>发送取消订阅行情请求成功" << std::endl;
  37. //else
  38. // std::cerr << "--->>>发送取消订阅行情请求失败" << std::endl;
  39. }
  • 每个tick世间节点系统都会调用这个函数,推送具体的行情截面数据
  • 可以在此处将行情写到本地,或者做一些数据处理(例如实时K线计算,判断是否触发策略等)

 

交易回调类

同理,也需要继承CThostFtdcTraderSpi来实现自己的CustomTradeSpi类,用于交易下单、报单等操作的回调

CustomTradeSpi头文件

 


 
 
  1. #pragma once
  2. // ---- 派生的交易类 ---- //
  3. #include "CTP_API/ThostFtdcTraderApi.h"
  4. class CustomTradeSpi : public CThostFtdcTraderSpi
  5. {
  6. // ---- ctp_api部分回调接口 ---- //
  7. public:
  8. ///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
  9. void OnFrontConnected();
  10. ///登录请求响应
  11. void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  12. ///错误应答
  13. void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  14. ///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
  15. void OnFrontDisconnected(int nReason);
  16. ///心跳超时警告。当长时间未收到报文时,该方法被调用。
  17. void OnHeartBeatWarning(int nTimeLapse);
  18. ///登出请求响应
  19. void OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  20. ///投资者结算结果确认响应
  21. void OnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  22. ///请求查询合约响应
  23. void OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  24. ///请求查询资金账户响应
  25. void OnRspQryTradingAccount(CThostFtdcTradingAccountField *pTradingAccount, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  26. ///请求查询投资者持仓响应
  27. void OnRspQryInvestorPosition(CThostFtdcInvestorPositionField *pInvestorPosition, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  28. ///报单录入请求响应
  29. void OnRspOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  30. ///报单操作请求响应
  31. void OnRspOrderAction(CThostFtdcInputOrderActionField *pInputOrderAction, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
  32. ///报单通知
  33. void OnRtnOrder(CThostFtdcOrderField *pOrder);
  34. ///成交通知
  35. void OnRtnTrade(CThostFtdcTradeField *pTrade);
  36. // ---- 自定义函数 ---- //
  37. public:
  38. bool loginFlag; // 登陆成功的标识
  39. void reqOrderInsert(
  40. TThostFtdcInstrumentIDType instrumentID,
  41. TThostFtdcPriceType price,
  42. TThostFtdcVolumeType volume,
  43. TThostFtdcDirectionType direction); // 个性化报单录入,外部调用
  44. private:
  45. void reqUserLogin(); // 登录请求
  46. void reqUserLogout(); // 登出请求
  47. void reqSettlementInfoConfirm(); // 投资者结果确认
  48. void reqQueryInstrument(); // 请求查询合约
  49. void reqQueryTradingAccount(); // 请求查询资金帐户
  50. void reqQueryInvestorPosition(); // 请求查询投资者持仓
  51. void reqOrderInsert(); // 请求报单录入
  52. void reqOrderAction(CThostFtdcOrderField *pOrder); // 请求报单操作
  53. bool isErrorRspInfo(CThostFtdcRspInfoField *pRspInfo); // 是否收到错误信息
  54. bool isMyOrder(CThostFtdcOrderField *pOrder); // 是否我的报单回报
  55. bool isTradingOrder(CThostFtdcOrderField *pOrder); // 是否正在交易的报单
  56. };

 

 

除了重写的基类函数,还自己封装一些主动调用的操作函数,比如登入登出、下单报单、查询报单等

 

 

 

登录应答

 


 
 
  1. void CustomTradeSpi::OnRspUserLogin(
  2. CThostFtdcRspUserLoginField *pRspUserLogin,
  3. CThostFtdcRspInfoField *pRspInfo,
  4. int nRequestID,
  5. bool bIsLast)
  6. {
  7. if (!isErrorRspInfo(pRspInfo))
  8. {
  9. std:: cout << "=====账户登录成功=====" << std:: endl;
  10. loginFlag = true;
  11. std:: cout << "交易日: " << pRspUserLogin->TradingDay << std:: endl;
  12. std:: cout << "登录时间: " << pRspUserLogin->LoginTime << std:: endl;
  13. std:: cout << "经纪商: " << pRspUserLogin->BrokerID << std:: endl;
  14. std:: cout << "帐户名: " << pRspUserLogin->UserID << std:: endl;
  15. // 保存会话参数
  16. trade_front_id = pRspUserLogin->FrontID;
  17. session_id = pRspUserLogin->SessionID;
  18. strcpy(order_ref, pRspUserLogin->MaxOrderRef);
  19. // 投资者结算结果确认
  20. reqSettlementInfoConfirm();
  21. }
  22. }

查询投资者结算结果应答

 

 


 
 
  1. void CustomTradeSpi::OnRspSettlementInfoConfirm(
  2. CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm,
  3. CThostFtdcRspInfoField *pRspInfo,
  4. int nRequestID,
  5. bool bIsLast)
  6. {
  7. if (!isErrorRspInfo(pRspInfo))
  8. {
  9. std:: cout << "=====投资者结算结果确认成功=====" << std:: endl;
  10. std:: cout << "确认日期: " << pSettlementInfoConfirm->ConfirmDate << std:: endl;
  11. std:: cout << "确认时间: " << pSettlementInfoConfirm->ConfirmTime << std:: endl;
  12. // 请求查询合约
  13. reqQueryInstrument();
  14. }
  15. }

查询合约应答

 

 


 
 
  1. void CustomTradeSpi::OnRspQryInstrument(
  2. CThostFtdcInstrumentField *pInstrument,
  3. CThostFtdcRspInfoField *pRspInfo,
  4. int nRequestID,
  5. bool bIsLast)
  6. {
  7. if (!isErrorRspInfo(pRspInfo))
  8. {
  9. std:: cout << "=====查询合约结果成功=====" << std:: endl;
  10. std:: cout << "交易所代码: " << pInstrument->ExchangeID << std:: endl;
  11. std:: cout << "合约代码: " << pInstrument->InstrumentID << std:: endl;
  12. std:: cout << "合约在交易所的代码: " << pInstrument->ExchangeInstID << std:: endl;
  13. std:: cout << "执行价: " << pInstrument->StrikePrice << std:: endl;
  14. std:: cout << "到期日: " << pInstrument->EndDelivDate << std:: endl;
  15. std:: cout << "当前交易状态: " << pInstrument->IsTrading << std:: endl;
  16. // 请求查询投资者资金账户
  17. reqQueryTradingAccount();
  18. }
  19. }

查询投资者资金帐户应答

 

 


 
 
  1. void CustomTradeSpi::OnRspQryTradingAccount(
  2. CThostFtdcTradingAccountField *pTradingAccount,
  3. CThostFtdcRspInfoField *pRspInfo,
  4. int nRequestID,
  5. bool bIsLast)
  6. {
  7. if (!isErrorRspInfo(pRspInfo))
  8. {
  9. std:: cout << "=====查询投资者资金账户成功=====" << std:: endl;
  10. std:: cout << "投资者账号: " << pTradingAccount->AccountID << std:: endl;
  11. std:: cout << "可用资金: " << pTradingAccount->Available << std:: endl;
  12. std:: cout << "可取资金: " << pTradingAccount->WithdrawQuota << std:: endl;
  13. std:: cout << "当前保证金: " << pTradingAccount->CurrMargin << std:: endl;
  14. std:: cout << "平仓盈亏: " << pTradingAccount->CloseProfit << std:: endl;
  15. // 请求查询投资者持仓
  16. reqQueryInvestorPosition();
  17. }
  18. }

查询投资者持仓应答

 

 


 
 
  1. void CustomTradeSpi::OnRspQryInvestorPosition(
  2. CThostFtdcInvestorPositionField *pInvestorPosition,
  3. CThostFtdcRspInfoField *pRspInfo,
  4. int nRequestID,
  5. bool bIsLast)
  6. {
  7. if (!isErrorRspInfo(pRspInfo))
  8. {
  9. std:: cout << "=====查询投资者持仓成功=====" << std:: endl;
  10. if (pInvestorPosition)
  11. {
  12. std:: cout << "合约代码: " << pInvestorPosition->InstrumentID << std:: endl;
  13. std:: cout << "开仓价格: " << pInvestorPosition->OpenAmount << std:: endl;
  14. std:: cout << "开仓量: " << pInvestorPosition->OpenVolume << std:: endl;
  15. std:: cout << "开仓方向: " << pInvestorPosition->PosiDirection << std:: endl;
  16. std:: cout << "占用保证金:" << pInvestorPosition->UseMargin << std:: endl;
  17. }
  18. else
  19. std:: cout << "----->该合约未持仓" << std:: endl;
  20. // 报单录入请求(这里是一部接口,此处是按顺序执行)
  21. /*if (loginFlag)
  22. reqOrderInsert();*/
  23. if (loginFlag)
  24. reqOrderInsert(g_pTradeInstrumentID, gLimitPrice, 1, gTradeDirection); // 自定义一笔交易
  25. // 策略交易
  26. /*std::cout << "=====开始进入策略交易=====" << std::endl;
  27. while (loginFlag)
  28. StrategyCheckAndTrade(g_pTradeInstrumentID, this);*/
  29. }
  30. }

这里把下单录入的操作放在了持仓结果出来之后的回调里面,策略交易也简单的放在了这里,真实的情况下,应该是由行情触发某个策略条件开一个线程进行策略交易

 

下单操作

 

 


 
 
  1. void CustomTradeSpi::reqOrderInsert(
  2. TThostFtdcInstrumentIDType instrumentID,
  3. TThostFtdcPriceType price,
  4. TThostFtdcVolumeType volume,
  5. TThostFtdcDirectionType direction)
  6. {
  7. CThostFtdcInputOrderField orderInsertReq;
  8. memset(&orderInsertReq, 0, sizeof(orderInsertReq));
  9. ///经纪公司代码
  10. strcpy(orderInsertReq.BrokerID, gBrokerID);
  11. ///投资者代码
  12. strcpy(orderInsertReq.InvestorID, gInvesterID);
  13. ///合约代码
  14. strcpy(orderInsertReq.InstrumentID, instrumentID);
  15. ///报单引用
  16. strcpy(orderInsertReq.OrderRef, order_ref);
  17. ///报单价格条件: 限价
  18. orderInsertReq.OrderPriceType = THOST_FTDC_OPT_LimitPrice;
  19. ///买卖方向:
  20. orderInsertReq.Direction = direction;
  21. ///组合开平标志: 开仓
  22. orderInsertReq.CombOffsetFlag[ 0] = THOST_FTDC_OF_Open;
  23. ///组合投机套保标志
  24. orderInsertReq.CombHedgeFlag[ 0] = THOST_FTDC_HF_Speculation;
  25. ///价格
  26. orderInsertReq.LimitPrice = price;
  27. ///数量:1
  28. orderInsertReq.VolumeTotalOriginal = volume;
  29. ///有效期类型: 当日有效
  30. orderInsertReq.TimeCondition = THOST_FTDC_TC_GFD;
  31. ///成交量类型: 任何数量
  32. orderInsertReq.VolumeCondition = THOST_FTDC_VC_AV;
  33. ///最小成交量: 1
  34. orderInsertReq.MinVolume = 1;
  35. ///触发条件: 立即
  36. orderInsertReq.ContingentCondition = THOST_FTDC_CC_Immediately;
  37. ///强平原因: 非强平
  38. orderInsertReq.ForceCloseReason = THOST_FTDC_FCC_NotForceClose;
  39. ///自动挂起标志: 否
  40. orderInsertReq.IsAutoSuspend = 0;
  41. ///用户强评标志: 否
  42. orderInsertReq.UserForceClose = 0;
  43. static int requestID = 0; // 请求编号
  44. int rt = g_pTradeUserApi->ReqOrderInsert(&orderInsertReq, ++requestID);
  45. if (!rt)
  46. std:: cout << ">>>>>>发送报单录入请求成功" << std:: endl;
  47. else
  48. std:: cerr << "--->>>发送报单录入请求失败" << std:: endl;
  49. }

通过重载写了两个函数,一个是用默认参数下单,一个可以传参下单,比如设定合约代码、价格、数量等

 

报单操作

 


 
 
  1. void CustomTradeSpi::reqOrderAction(CThostFtdcOrderField *pOrder)
  2. {
  3. static bool orderActionSentFlag = false; // 是否发送了报单
  4. if (orderActionSentFlag)
  5. return;
  6. CThostFtdcInputOrderActionField orderActionReq;
  7. memset(&orderActionReq, 0, sizeof(orderActionReq));
  8. ///经纪公司代码
  9. strcpy(orderActionReq.BrokerID, pOrder->BrokerID);
  10. ///投资者代码
  11. strcpy(orderActionReq.InvestorID, pOrder->InvestorID);
  12. ///报单操作引用
  13. // TThostFtdcOrderActionRefType OrderActionRef;
  14. ///报单引用
  15. strcpy(orderActionReq.OrderRef, pOrder->OrderRef);
  16. ///请求编号
  17. // TThostFtdcRequestIDType RequestID;
  18. ///前置编号
  19. orderActionReq.FrontID = trade_front_id;
  20. ///会话编号
  21. orderActionReq.SessionID = session_id;
  22. ///交易所代码
  23. // TThostFtdcExchangeIDType ExchangeID;
  24. ///报单编号
  25. // TThostFtdcOrderSysIDType OrderSysID;
  26. ///操作标志
  27. orderActionReq.ActionFlag = THOST_FTDC_AF_Delete;
  28. ///价格
  29. // TThostFtdcPriceType LimitPrice;
  30. ///数量变化
  31. // TThostFtdcVolumeType VolumeChange;
  32. ///用户代码
  33. // TThostFtdcUserIDType UserID;
  34. ///合约代码
  35. strcpy(orderActionReq.InstrumentID, pOrder->InstrumentID);
  36. static int requestID = 0; // 请求编号
  37. int rt = g_pTradeUserApi->ReqOrderAction(&orderActionReq, ++requestID);
  38. if (!rt)
  39. std:: cout << ">>>>>>发送报单操作请求成功" << std:: endl;
  40. else
  41. std:: cerr << "--->>>发送报单操作请求失败" << std:: endl;
  42. orderActionSentFlag = true;
  43. }

 

主要是对于未成交的订单进行编辑或者撤销操作

 

 

报单应答

 


 
 
  1. void CustomTradeSpi::OnRtnOrder(CThostFtdcOrderField *pOrder)
  2. {
  3. char str[ 10];
  4. sprintf(str, "%d", pOrder->OrderSubmitStatus);
  5. int orderState = atoi(str) - 48; //报单状态0=已经提交,3=已经接受
  6. std:: cout << "=====收到报单应答=====" << std:: endl;
  7. if (isMyOrder(pOrder))
  8. {
  9. if (isTradingOrder(pOrder))
  10. {
  11. std:: cout << "--->>> 等待成交中!" << std:: endl;
  12. //reqOrderAction(pOrder); // 这里可以撤单
  13. //reqUserLogout(); // 登出测试
  14. }
  15. else if (pOrder->OrderStatus == THOST_FTDC_OST_Canceled)
  16. std:: cout << "--->>> 撤单成功!" << std:: endl;
  17. }
  18. }
  19. void CustomTradeSpi::OnRtnTrade(CThostFtdcTradeField *pTrade)
  20. {
  21. std:: cout << "=====报单成功成交=====" << std:: endl;
  22. std:: cout << "成交时间: " << pTrade->TradeTime << std:: endl;
  23. std:: cout << "合约代码: " << pTrade->InstrumentID << std:: endl;
  24. std:: cout << "成交价格: " << pTrade->Price << std:: endl;
  25. std:: cout << "成交量: " << pTrade->Volume << std:: endl;
  26. std:: cout << "开平仓方向: " << pTrade->Direction << std:: endl;
  27. }

等待成交进行轮询可以选择报单操作,成交完成后的应答

 

时间序列转K线

从交易拿到的tick数据是时间序列数据,在证券交易中其实还需要根据时间序列算出一些技术指标数据,例如MACD,KDJ、K线等,这里简单地对数据做一下处理,写一个TickToKlineHelper将时间序列专程K线

 

 

K线数据结构

 


 
 
  1. // k线数据结构
  2. struct KLineDataType
  3. {
  4. double open_price; // 开
  5. double high_price; // 高
  6. double low_price; // 低
  7. double close_price; // 收
  8. int volume; // 量
  9. };


转换函数

 

 


 
 
  1. void TickToKlineHelper::KLineFromLocalData( const std:: string &sFilePath, const std:: string &dFilePath)
  2. {
  3. // 先清理残留数据
  4. m_priceVec.clear();
  5. m_volumeVec.clear();
  6. m_KLineDataArray.clear();
  7. std:: cout << "开始转换tick到k线..." << std:: endl;
  8. // 默认读取的tick数据表有4个字段:合约代码、更新时间、最新价、成交量
  9. std::ifstream srcInFile;
  10. std::ofstream dstOutFile;
  11. srcInFile.open(sFilePath, std::ios::in);
  12. dstOutFile.open(dFilePath, std::ios::out);
  13. dstOutFile << "开盘价" << ','
  14. << "最高价" << ','
  15. << "最低价" << ','
  16. << "收盘价" << ','
  17. << "成交量" << std:: endl;
  18. // 一遍解析文件一边计算k线数据,1分钟k线每次读取60 * 2 = 120行数据
  19. std:: string lineStr;
  20. bool isFirstLine = true;
  21. while ( std::getline(srcInFile, lineStr))
  22. {
  23. if (isFirstLine)
  24. {
  25. // 跳过第一行表头
  26. isFirstLine = false;
  27. continue;
  28. }
  29. std:: istringstream ss(lineStr);
  30. std:: string fieldStr;
  31. int count = 4;
  32. while ( std::getline(ss, fieldStr, ','))
  33. {
  34. count--;
  35. if (count == 1)
  36. m_priceVec.push_back( std::atof(fieldStr.c_str()));
  37. else if (count == 0)
  38. {
  39. m_volumeVec.push_back( std::atoi(fieldStr.c_str()));
  40. break;
  41. }
  42. }
  43. // 计算k线
  44. if (m_priceVec.size() == kDataLineNum)
  45. {
  46. KLineDataType k_line_data;
  47. k_line_data.open_price = m_priceVec.front();
  48. k_line_data.high_price = * std::max_element(m_priceVec.cbegin(), m_priceVec.cend());
  49. k_line_data.low_price = * std::min_element(m_priceVec.cbegin(), m_priceVec.cend());
  50. k_line_data.close_price = m_priceVec.back();
  51. // 成交量的真实的算法是当前区间最后一个成交量减去上去一个区间最后一个成交量
  52. k_line_data.volume = m_volumeVec.back() - m_volumeVec.front();
  53. //m_KLineDataArray.push_back(k_line_data); // 此处可以存到内存
  54. dstOutFile << k_line_data.open_price << ','
  55. << k_line_data.high_price << ','
  56. << k_line_data.low_price << ','
  57. << k_line_data.close_price << ','
  58. << k_line_data.volume << std:: endl;
  59. m_priceVec.clear();
  60. m_volumeVec.clear();
  61. }
  62. }
  63. srcInFile.close();
  64. dstOutFile.close();
  65. std:: cout << "k线生成成功" << std:: endl;
  66. }
  67. void TickToKlineHelper::KLineFromRealtimeData(CThostFtdcDepthMarketDataField *pDepthMarketData)
  68. {
  69. m_priceVec.push_back(pDepthMarketData->LastPrice);
  70. m_volumeVec.push_back(pDepthMarketData->Volume);
  71. if (m_priceVec.size() == kDataLineNum)
  72. {
  73. KLineDataType k_line_data;
  74. k_line_data.open_price = m_priceVec.front();
  75. k_line_data.high_price = * std::max_element(m_priceVec.cbegin(), m_priceVec.cend());
  76. k_line_data.low_price = * std::min_element(m_priceVec.cbegin(), m_priceVec.cend());
  77. k_line_data.close_price = m_priceVec.back();
  78. // 成交量的真实的算法是当前区间最后一个成交量减去上去一个区间最后一个成交量
  79. k_line_data.volume = m_volumeVec.back() - m_volumeVec.front();
  80. m_KLineDataArray.push_back(k_line_data); // 此处可以存到内存
  81. m_priceVec.clear();
  82. m_volumeVec.clear();
  83. }
  84. }

 

  • 可以从本地文件中读取行情数据,进行离线转换,也可以在接受到行情时进行实时计算
  • 基本思想是,针对每个合约代码,建立字典,维持一个行情数组,当时间间隔达到要求(例如分钟、分时、分日)时计算该时段的开、高、低、收、成交量等数据存入K线数组
  • 最低时间单位的K线计算出来之后,高时间间隔的K线数据可以根据低时间间隔的K线计算出来(例如,算出了分钟K,那么分时K就根据分钟K来算)
  • 本例子中只是实现了一个大概的原理,非常不精确,仅供参考

策略交易

量化交易系统最终是需要将编写的策略代码挂载到系统中进行策略交易的,这里做了一个简单的实现

StrategyTrade.h


 
 
  1. #pragma once
  2. // ---- 简单策略交易的类 ---- //
  3. #include <functional>
  4. #include "CTP_API/ThostFtdcUserApiStruct.h"
  5. #include "TickToKlineHelper.h"
  6. #include "CustomTradeSpi.h"
  7. typedef void(*reqOrderInsertFun)(
  8. TThostFtdcInstrumentIDType instrumentID,
  9. TThostFtdcPriceType price,
  10. TThostFtdcVolumeType volume,
  11. TThostFtdcDirectionType direction);
  12. using ReqOrderInsertFunctionType = std::function<
  13. void(TThostFtdcInstrumentIDType instrumentID,
  14. TThostFtdcPriceType price,
  15. TThostFtdcVolumeType volume,
  16. TThostFtdcDirectionType direction)>;
  17. void StrategyCheckAndTrade(TThostFtdcInstrumentIDType instrumentID, CustomTradeSpi *customTradeSpi);


StrategyTrade.cpp


 
 
  1. #include <vector>
  2. #include <string>
  3. #include <unordered_map>
  4. #include <thread>
  5. #include <mutex>
  6. #include "StrategyTrade.h"
  7. #include "CustomTradeSpi.h"
  8. extern std:: unordered_map< std:: string, TickToKlineHelper> g_KlineHash;
  9. // 线程互斥量
  10. std::mutex marketDataMutex;
  11. void StrategyCheckAndTrade(TThostFtdcInstrumentIDType instrumentID, CustomTradeSpi *customTradeSpi)
  12. {
  13. // 加锁
  14. std::lock_guard< std::mutex> lk(marketDataMutex);
  15. TickToKlineHelper tickToKlineObject = g_KlineHash.at( std:: string(instrumentID));
  16. // 策略
  17. std:: vector< double> priceVec = tickToKlineObject.m_priceVec;
  18. if (priceVec.size() >= 3)
  19. {
  20. int len = priceVec.size();
  21. // 最后连续三个上涨就买开仓,反之就卖开仓,这里暂时用最后一个价格下单
  22. if (priceVec[len - 1] > priceVec[len - 2] && priceVec[len - 2] > priceVec[len - 3])
  23. customTradeSpi->reqOrderInsert(instrumentID, priceVec[len - 1], 1, THOST_FTDC_D_Buy);
  24. else if (priceVec[len - 1] < priceVec[len - 2] && priceVec[len - 2] < priceVec[len - 3])
  25. customTradeSpi->reqOrderInsert(instrumentID, priceVec[len - 1], 1, THOST_FTDC_D_Buy);
  26. }
  27. }
  • 基本思想,针对指定合约,判断如果连续三个上涨就买开仓,连续三个下跌就卖开仓,价格都是用最新价
  • 因为行情和交易是分开的线程,涉及到线程竞争,所以在实际下单时需要加入互斥锁,线程同步
  • 策略如何被行情触发然后交易其实需要用事件驱动来做的,这里没有实现T_T

入口

main.cpp


 
 
  1. int main()
  2. {
  3. // 账号密码
  4. cout << "请输入账号: ";
  5. scanf( "%s", gInvesterID);
  6. cout << "请输入密码: ";
  7. scanf( "%s", gInvesterPassword);
  8. // 初始化行情线程
  9. cout << "初始化行情..." << endl;
  10. g_pMdUserApi = CThostFtdcMdApi::CreateFtdcMdApi(); // 创建行情实例
  11. CThostFtdcMdSpi *pMdUserSpi = new CustomMdSpi; // 创建行情回调实例
  12. g_pMdUserApi->RegisterSpi(pMdUserSpi); // 注册事件类
  13. g_pMdUserApi->RegisterFront(gMdFrontAddr); // 设置行情前置地址
  14. g_pMdUserApi->Init(); // 连接运行
  15. // 初始化交易线程
  16. cout << "初始化交易..." << endl;
  17. g_pTradeUserApi = CThostFtdcTraderApi::CreateFtdcTraderApi(); // 创建交易实例
  18. //CThostFtdcTraderSpi *pTradeSpi = new CustomTradeSpi;
  19. CustomTradeSpi *pTradeSpi = new CustomTradeSpi; // 创建交易回调实例
  20. g_pTradeUserApi->RegisterSpi(pTradeSpi); // 注册事件类
  21. g_pTradeUserApi->SubscribePublicTopic(THOST_TERT_RESTART); // 订阅公共流
  22. g_pTradeUserApi->SubscribePrivateTopic(THOST_TERT_RESTART); // 订阅私有流
  23. g_pTradeUserApi->RegisterFront(gTradeFrontAddr); // 设置交易前置地址
  24. g_pTradeUserApi->Init(); // 连接运行
  25. // 等到线程退出
  26. g_pMdUserApi->Join();
  27. delete pMdUserSpi;
  28. g_pMdUserApi->Release();
  29. g_pTradeUserApi->Join();
  30. delete pTradeSpi;
  31. g_pTradeUserApi->Release();
  32. // 转换本地k线数据
  33. //TickToKlineHelper tickToKlineHelper;
  34. //tickToKlineHelper.KLineFromLocalData("market_data.csv", "K_line_data.csv");
  35. getchar();
  36. return 0;
  37. }
  • CThostFtdcMdApi跟CustomMdSpi要建立关联,CThostFtdcTraderApi跟CustomTradeSpi建立关联,其实就是类似于函数注册
  • 配置行情和交易地址
  • 行情和交易分别是不同的线程,注意线程同步
  • 记得内存回收

运行结果

行情

应答日志

 

存成csv表格

 

交易

应答日志

 

 

K线数据

 

 

 

 

报单情况

用上期所的快期软件,登录上自己的账号之后,从过程序下单,在这个界面里能看到实时的报单成交状况

 

 

源码下载

 

csdn:demo

 

github:demo

 

结语

 

本文旨在为刚接触CTP的小白们抛砖引玉,各交易接口的深度运用还需要看官方开发文档。

另外,对于完整的量化交易系统来说,不仅要具备行情、交易、策略模块,事件驱动、风控、回测模块以及底层的数据存储、网络并发都是需要深入钻研的方面,金融工程的Quant Researcher可以只专注于数据的分析、策略的研发,但是对于程序员Quant Developer来说,如何设计和开发一个高并发、低延迟、功能完善与策略结合紧密的量化交易系统的确是一项需要不断完善的工程。

 

ps:如果需要更高级和细致甚至可以用于实盘的功能,比如完整的开源交易系统,数据系统,算法交易,数据和交易接口等完备的解决方案,由于博客回复不现实,只能私信联系啦~

支持是知识分享的动力,有问题可扫码哦

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++连接CTP接口实现简单量化交易(行情、交易、k线、策略) 的相关文章

  • 如何在 Python 中创建元组字典

    本演练是关于在 Python 中创建元组字典的全部内容 此数据结构存储键值对 通过组合字典和元组 可以创建元组字典 好处是以结构化格式组织且可访问的数据 可以轻松表示每个键的多个值 例如学生成绩或联系信息 让我们看看它如何有效地存储和检索复
  • 抖音生活小妙招类短视频创作技巧分享,几个方面带你了解整个流程

    想做抖音 又不想真人出镜 该选择什么项目做呢 更多精彩干货请关注共众号 萤火宠 免费领取108个抖音小项目 我们的学员中有宝妈 有大学生 也有不少职场人员 他们大多数都非常普通 没有什么很强的职业技能 也没有什么丰富的专业知识 但是他们有人
  • 找实习、工作的一点浅见

    一 实习的必要性 为什么需要去实习 1 实习能帮助自己增进对于具体职场的认识 包括具体工作的职责 内容 工作氛围 是否有较大压力等等 2 通过一段时间的实习经历 能帮助自己作出未来是否能胜任类似的工作的判断 如果有留用 是否考虑留下 如果没
  • 阿里的iOS协程库 coobjc 源码解析(一)——元组和协程

    Coobjc中的元组 底层主要依赖NSPointerArray进行实现 因为NSPointerArray支持插入nil指针 能配合元组中有对象为nil的特性 比较引人入胜的设计 主要是co tuple 这个宏定义 co tuple COTu
  • 学习笔记:SpringCloud 微服务技术栈_实用篇②_黑马旅游案例

    若文章内容或图片失效 请留言反馈 部分素材来自网络 若不小心影响到您的利益 请联系博主删除 前言 学习视频链接 SpringCloud RabbitMQ Docker Redis 搜索 分布式 史上最全面的 SpringCloud 微服务技
  • CSS水平垂直居中

    1 利用定位 margin auto 2 flex布局 3 grid布局 一 利用position margin auto

随机推荐

  • 深入剖析 Python 函数参数传递机制及高级应用

    前言 在本篇文章中 笔者将带你深入探讨 Python 函数传参的进阶主题 通过阅读本篇文章 你可以深入了解 Python 函数传参的进阶主题 掌握更多高级的函数技巧 提升你的 Python 编程能力 前面分享了Python 函数传参基础篇
  • linux 安装Elasticsearchhe和kibana以及启动遇到的错误解决(已成功运行)

    linux安装es和kibana 参考博文 https blog csdn net han12398766 article details 88373869 启动报错1 Exception elasticsearch keystore 这个
  • ELK+Wazuh搭建笔记

    本文借鉴https www cnblogs com backlion p 10394369 html 在此谢谢大佬指明方向 本人又总结了wazuh界面上opencat Vulnerabilities的后台配置情况 以及agent版本升级情况
  • 页面刷新 Vuex 数据丢失

    用 Vuex 的时候发现一个问题 在页面刷新的时候 vuex 的 state 里面存储的数据会丢失 问题产生原因 因为 store 里的数据是保存在运行内存中的 当页面刷新时 页面会重新加载 vue 实例 store 里面的数据就会被重新赋
  • 子类覆盖父类方法时参数以及方法的访问权限问题

    一 子类覆盖父类可继承方法时子类同名方法的访问权限必须大等于父类 父类private不被子类继承 也无覆盖一说 报错 Fatal error Access level to Sun3 a must be protected as in cl
  • 高精度高精度乘法(C++)

    高精度加法以及高精度单精度乘法这里就不过多赘述了 今天咱们的主角是高精度高精度乘法 咱们先回顾一下竖式乘法 我们先不急着进位 先来看看 对应位置上的数字都是这么来的 对于不足位我们补充零后 不难发现 对应位置的最后答案 是由该位置起以后的全
  • Timing Borrow的理解

    在集成电路设计中 静态时序分析 Static Timing Analysis STA 是一种常用的验证方法 用于确保芯片在运行时的时序约束得到满足 在STA分析过程中 Timing Borrow是一种时序收敛技术 即在某些情况下 可以借用下
  • PowerMock--Mock静态方法

    1 PowerMock静态方法 写单元测试时 经常会遇到测试方法体内调用了某些工具类的静态方法的情况 而这些静态方法一般是读取配置中心里的文件数据 或者是一些其他涉及到需要启动项目的操作 往往这些操作会造成Mock单元测试的不彻底 有些流水
  • 排序算法之快速排序

    高快省的排序算法 有没有既不浪费空间又可以快一点的排序算法呢 那就是 快速排序 啦 光听这个名字是不是就觉得很高端呢 假设我们现在对 6 1 2 7 9 3 4 5 10 8 这个10个数进行排序 首先在这个序列中随便找一个数作为基准数 不
  • 【福利】南昌大学NCU主题电子笔记本,免费获取!

    转载请注明出处 小锋学长生活大爆炸 xfxuezhang cn 禁止转卖 恰烂钱可耻 下载链接 https pan baidu com s 1RWVrQgbmth CwoTcY 1yVw pwd 5gu8
  • MATLAB——基于自回归积分移动平均线 (ARIMA) 模型对数据进行预测

    通过1985至20222年各年的参加测试人数为实际数据 通过ARIMA模型对未来十年参加测试人数进行预测 clc clear Y data 2 N length Y t 1985 2022 figure 1 plot t Y title 高
  • 数据库性能优化的误区!

    常见的数据库系统优化中的一些观点 系统性能出现问题进行优化 一定要深入了解数据库内部参数 等待事件 Latch 缓冲池 trace文件 查询 优化引擎等底层细节 这种观点往往出自数据库 高手 这部分人以了解数据库底层实现细节而感到非常骄傲
  • Python文件指针

    实验文件data txt如下 This file is data txt hello world My name is mark 实验一 文件指针初始化 f open data txt r print f tell f write nice
  • matlab读取.dat数据

    任务 手上有一个dat后缀名文件 需要导入matlab 并对其中的每一列数据进行处理 解决方案 导入数据 S1 点击 导入数据 按钮 弹出导入数据窗口 S2 选择 范围 因为第一行是标题 所以我不想导入 导入范围从A2开始 到G436结束
  • Notes Twenty one days-渗透攻击-红队-权限提升

    Notes Twenty one days 渗透攻击 红队 权限提升 dayu 作者 大余 时间 2020 10 7 请注意 对于所有笔记中复现的这些终端或者服务器 都是自行搭建的环境进行渗透的 我将使用Kali Linux作为此次学习的攻
  • 获取系统磁盘类型信息以及判断是否是U盘

    使用WIN API函数实现 include
  • Web渗透测试实战——(1)Web渗透测试简介

    一 什么是渗透测试 渗透测试 penetration testing 是对计算机系统的一种授权攻击 旨在评估系统 网络的安全性 执行测试以识别漏洞及其带来的风险 一般而言 渗透测试过程分为五个阶段 包括识别目标系统 检测存在的漏洞以及每个漏
  • Java基础系列30-单列 Collection集合

    文章目录 一 集合的概述 1 1 为什么会出现集合类 1 2 集合类体系结构图 二 Collection集合 2 1 Collection集合入门 2 2 Collection集合的成员方法 2 3 Collection集合的遍历 2 4
  • vue使用vue-amap 高德地图进行选点和搜索

    vue使用vue amap进行地图点的搜索和点击选点 npm install vue amap save 下载npm 包 在main js主文件中进行引用 import VueAMap from vue amap Vue use VueAM
  • C++连接CTP接口实现简单量化交易(行情、交易、k线、策略)

    对于量化交易来说 量化策略和技术系统缺一不可 为了知其所以然 本文实现了一个C 连接CTP接口进行仿真交易的demo 从接收行情 下订单 数据处理到添加策略 挂载运行交易等多个环节来看一下量化交易的最简单流程 管中窥豹 一探究竟 准备工作