快期上怎么修改密码:快期登录的时候 动态密码怎么填
我记得快速登录没有动态密码验证的吧 那个是手机令牌的动态密码、去安全中心看看你是用哪个手机绑定的手机令牌 然后用那个手机打开那个令牌软件就可以看到了。
快期上怎么修改密码:快期交易系统怎么登录啊?用户代码是什么 ,也没有看到有注册的按钮?试了用开户的帐户也登录不了。
用户代码:就是期货公司给你的资金账号,
交易密码:就是开户时设置账号交易密码;注意和银行转账密码区分。
动态密码:空着就行。
快期上怎么修改密码:我申请了模拟商品期货,有账户和密码,下载了模拟软件,登陆时还要求填写动态密码,动态密码在哪里?
您好,针对您的问题,国泰君安上海分公司给予如下解答
您好,不知道您用的是哪个公司的软件,一般要点一下动态密码,会随机发送您手机上的。
希望我们国泰君安证券上海分公司的回答可以让您满意!
回答人员:国泰君安证券客户经理屠经理
国泰君安证券——百度知道企业平台乐意为您服务!
如仍有疑问,欢迎向国泰君安证券上海分公司官网或企业知道平台提问。
如仍有疑问,欢迎向国泰君安证券上海分公司官网或企业知道平台提问。
快期上怎么修改密码:我申请了国泰君安的模拟期货,有账户和密码,下载了模拟软件,登陆时还要求填写动态密码,动态密码在哪里
要下载恒生版的那个模拟软件
快期上怎么修改密码:C++连接CTP实现简单量化交易(行情/交易/k线/策略)
本文章和相关代码已不再更新,在行业合规的范围内,进一步的量化金融技术交流,可以扫码咨询 完整原文:C++连接CTP接口实现简单量化交易(行情、交易、k线、策略) - CSDN博客对于量化交易来说,量化策略和技术系统缺一不可,为了知其所以然,本文实现了一个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)目录配置到工程
// 链接库n #pragma comment (lib, "thostmduserapi.lib")n #pragma comment (lib, "thosttraderapi.lib")n
全局参数
连接到交易所,需要配置经纪商代码、帐户名、密码以及订阅合约和买卖合约的相关参数
// ---- 全局变量 ---- //n // 公共参数n TThostFtdcBrokerIDType gBrokerID = "9999" ; // 模拟经纪商代码n TThostFtdcInvestorIDType gInvesterID = "" ; // 投资者账户名n TThostFtdcPasswordType gInvesterPassword = "" ; // 投资者密码n n// 行情参数n CThostFtdcMdApi * g_pMdUserApi = nullptr ; // 行情指针n char gMdFrontAddr [] = "tcp://180.168.146.187:10010" ; // 模拟行情前置地址n char * g_pInstrumentID [] = { "TF1706" , "zn1705" , "cs1801" , "CF705" }; // 行情合约代码列表,中、上、大、郑交易所各选一种n int instrumentNum = 4 ; // 行情合约订阅数量n unordered_map < string , TickToKlineHelper > g_KlineHash ; // 不同合约的k线存储表n n// 交易参数n CThostFtdcTraderApi * g_pTradeUserApi = nullptr ; // 交易指针n char gTradeFrontAddr [] = "tcp://180.168.146.187:10001" ; // 模拟交易前置地址n TThostFtdcInstrumentIDType g_pTradeInstrumentID = "m1709" ; // 所交易的合约代码n TThostFtdcDirectionType gTradeDirection = THOST_FTDC_D_Sell ; // 买卖方向n TThostFtdcPriceType gLimitPrice = 2818 ; // 交易价格n
这里只是简单的写一下,真实完整的交易系统中,一般用配置文件,有用户去定制
行情回调类
继承CThostFtdcMdSpi实现自己的行情回调类CustomMdSpi,在系统运行时这些重写的函数会被CTP的系统api回调从而实现个性化行情
CustomMdSpi头文件
#pragma oncen // ---- 派生的行情类 ---- //n #include n #include "CTP_API/ThostFtdcMdApi.h" n nclass CustomMdSpi : public CThostFtdcMdSpi n{ nt// ---- 继承自CTP父类的回调接口并实现 ---- //n public : nt///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。n tvoid OnFrontConnected (); n nt///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。n t///@param nReason 错误原因n t/// 0x1001 网络读失败n t/// 0x1002 网络写失败n t/// 0x2001 接收心跳超时n t/// 0x2002 发送心跳失败n t/// 0x2003 收到错误报文n tvoid OnFrontDisconnected ( int nReason ); n nt///心跳超时警告。当长时间未收到报文时,该方法被调用。n t///@param nTimeLapse 距离上次接收报文的时间n tvoid OnHeartBeatWarning ( int nTimeLapse ); n nt///登录请求响应n tvoid OnRspUserLogin ( CThostFtdcRspUserLoginField * pRspUserLogin , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///登出请求响应n tvoid OnRspUserLogout ( CThostFtdcUserLogoutField * pUserLogout , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///错误应答n tvoid OnRspError ( CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///订阅行情应答n tvoid OnRspSubMarketData ( CThostFtdcSpecificInstrumentField * pSpecificInstrument , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///取消订阅行情应答n tvoid OnRspUnSubMarketData ( CThostFtdcSpecificInstrumentField * pSpecificInstrument , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///订阅询价应答n tvoid OnRspSubForQuoteRsp ( CThostFtdcSpecificInstrumentField * pSpecificInstrument , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///取消订阅询价应答n tvoid OnRspUnSubForQuoteRsp ( CThostFtdcSpecificInstrumentField * pSpecificInstrument , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///深度行情通知n tvoid OnRtnDepthMarketData ( CThostFtdcDepthMarketDataField * pDepthMarketData ); n nt///询价通知n tvoid OnRtnForQuoteRsp ( CThostFtdcForQuoteRspField * pForQuoteRsp ); n}; n
都是重写回调函数
连接应答
// 连接成功应答n void CustomMdSpi :: OnFrontConnected () n{ ntstd :: cout << "=====建立网络连接成功=====" << std :: endl ; nt// 开始登录n tCThostFtdcReqUserLoginField loginReq ; ntmemset ( & loginReq , 0 , sizeof ( loginReq )); ntstrcpy ( loginReq . BrokerID , gBrokerID ); ntstrcpy ( loginReq . UserID , gInvesterID ); ntstrcpy ( loginReq . Password , gInvesterPassword ); ntstatic int requestID = 0 ; // 请求编号n tint rt = g_pMdUserApi -> ReqUserLogin ( & loginReq , requestID ); ntif ( ! rt ) nttstd :: cout << ">>>>>>发送登录请求成功" << std :: endl ; ntelse nttstd :: cerr << "--->>>发送登录请求失败" << std :: endl ; n} n
登录应答
// 登录应答n void CustomMdSpi :: OnRspUserLogin ( ntCThostFtdcRspUserLoginField * pRspUserLogin , ntCThostFtdcRspInfoField * pRspInfo , ntint nRequestID , ntbool bIsLast ) n{ ntbool bResult = pRspInfo && ( pRspInfo -> ErrorID != 0 ); ntif ( ! bResult ) nt{ nttstd :: cout << "=====账户登录成功=====" << std :: endl ; nttstd :: cout << "交易日: " << pRspUserLogin -> TradingDay << std :: endl ; nttstd :: cout << "登录时间: " << pRspUserLogin -> LoginTime << std :: endl ; nttstd :: cout << "经纪商: " << pRspUserLogin -> BrokerID << std :: endl ; nttstd :: cout << "帐户名: " << pRspUserLogin -> UserID << std :: endl ; ntt// 开始订阅行情n ttint rt = g_pMdUserApi -> SubscribeMarketData ( g_pInstrumentID , instrumentNum ); nttif ( ! rt ) ntttstd :: cout << ">>>>>>发送订阅行情请求成功" << std :: endl ; nttelse ntttstd :: cerr << "--->>>发送订阅行情请求失败" << std :: endl ; nt} ntelse nttstd :: cerr << "返回错误--->>> ErrorID=" << pRspInfo -> ErrorID << ", ErrorMsg=" << pRspInfo -> ErrorMsg << std :: endl ; n} n
订阅行情应答
// 订阅行情应答n void CustomMdSpi :: OnRspSubMarketData ( ntCThostFtdcSpecificInstrumentField * pSpecificInstrument , ntCThostFtdcRspInfoField * pRspInfo , ntint nRequestID , ntbool bIsLast ) n{ ntbool bResult = pRspInfo && ( pRspInfo -> ErrorID != 0 ); ntif ( ! bResult ) nt{ nttstd :: cout << "=====订阅行情成功=====" << std :: endl ; nttstd :: cout << "合约代码: " << pSpecificInstrument -> InstrumentID << std :: endl ; ntt// 如果需要存入文件或者数据库,在这里创建表头,不同的合约单独存储n ttchar filePath [ 100 ] = { '0' }; nttsprintf ( filePath , "%s_market_data.csv" , pSpecificInstrument -> InstrumentID ); nttstd :: ofstream outFile ; nttoutFile . open ( filePath , std :: ios :: out ); // 新开文件n ttoutFile << "合约代码" << "," nttt<< "更新时间" << "," nttt<< "最新价" << "," nttt<< "成交量" << "," nttt<< "买价一" << "," nttt<< "买量一" << "," nttt<< "卖价一" << "," nttt<< "卖量一" << "," nttt<< "持仓量" << "," nttt<< "换手率" nttt<< std :: endl ; nttoutFile . close (); nt} ntelse nttstd :: cerr << "返回错误--->>> ErrorID=" << pRspInfo -> ErrorID << ", ErrorMsg=" << pRspInfo -> ErrorMsg << std :: endl ; n} n
因为是异步接口,这里连接、登录、订阅行情是一步套一步来调用的,在运行过程中,会启动一个行情线程,交易所每500ms会推送一个订阅的行情tick数据,因此,某些接口会被连续间隔调用,直到连接关闭 收到行情后除了存在内存,也可以用文本文件或者数据库等形式存储起来,在这里创建初始文件或者建库 深度行情通知
// 行情详情通知n void CustomMdSpi :: OnRtnDepthMarketData ( CThostFtdcDepthMarketDataField * pDepthMarketData ) n{ nt// 打印行情,字段较多,截取部分n tstd :: cout << "=====获得深度行情=====" << std :: endl ; ntstd :: cout << "交易日: " << pDepthMarketData -> TradingDay << std :: endl ; ntstd :: cout << "交易所代码: " << pDepthMarketData -> ExchangeID << std :: endl ; ntstd :: cout << "合约代码: " << pDepthMarketData -> InstrumentID << std :: endl ; ntstd :: cout << "合约在交易所的代码: " << pDepthMarketData -> ExchangeInstID << std :: endl ; ntstd :: cout << "最新价: " << pDepthMarketData -> LastPrice << std :: endl ; ntstd :: cout << "数量: " << pDepthMarketData -> Volume << std :: endl ; nt// 如果只获取某一个合约行情,可以逐tick地存入文件或数据库n tchar filePath [ 100 ] = { '0' }; ntsprintf ( filePath , "%s_market_data.csv" , pDepthMarketData -> InstrumentID ); ntstd :: ofstream outFile ; ntoutFile . open ( filePath , std :: ios :: app ); // 文件追加写入 n toutFile << pDepthMarketData -> InstrumentID << "," ntt<< pDepthMarketData -> UpdateTime << "." << pDepthMarketData -> UpdateMillisec << "," ntt<< pDepthMarketData -> LastPrice << "," ntt<< pDepthMarketData -> Volume << "," ntt<< pDepthMarketData -> BidPrice1 << "," ntt<< pDepthMarketData -> BidVolume1 << "," ntt<< pDepthMarketData -> AskPrice1 << "," ntt<< pDepthMarketData -> AskVolume1 << "," ntt<< pDepthMarketData -> OpenInterest << "," ntt<< pDepthMarketData -> Turnover << std :: endl ; ntoutFile . close (); n nt// 计算实时k线n tstd :: string instrumentKey = std :: string ( pDepthMarketData -> InstrumentID ); ntif ( g_KlineHash . find ( instrumentKey ) == g_KlineHash . end ()) nttg_KlineHash [ instrumentKey ] = TickToKlineHelper (); ntg_KlineHash [ instrumentKey ]. KLineFromRealtimeData ( pDepthMarketData ); n n nt// 取消订阅行情n t//int rt = g_pMdUserApi->UnSubscribeMarketData(g_pInstrumentID, instrumentNum);n t//if (!rt)n t//tstd::cout << ">>>>>>发送取消订阅行情请求成功" << std::endl;n t//elsen t//tstd::cerr << "--->>>发送取消订阅行情请求失败" << std::endl;n } n
每个tick世间节点系统都会调用这个函数,推送具体的行情截面数据 可以在此处将行情写到本地,或者做一些数据处理(例如实时K线计算,判断是否触发策略等) 交易回调类
同理,也需要继承CThostFtdcTraderSpi来实现自己的CustomTradeSpi类,用于交易下单、报单等操作的回调
CustomTradeSpi头文件
#pragma oncen // ---- 派生的交易类 ---- //n #include "CTP_API/ThostFtdcTraderApi.h" n nclass CustomTradeSpi : public CThostFtdcTraderSpi n{ n// ---- ctp_api部分回调接口 ---- //n public : nt///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。n tvoid OnFrontConnected (); n nt///登录请求响应n tvoid OnRspUserLogin ( CThostFtdcRspUserLoginField * pRspUserLogin , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///错误应答n tvoid OnRspError ( CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。n tvoid OnFrontDisconnected ( int nReason ); n nt///心跳超时警告。当长时间未收到报文时,该方法被调用。n tvoid OnHeartBeatWarning ( int nTimeLapse ); n nt///登出请求响应n tvoid OnRspUserLogout ( CThostFtdcUserLogoutField * pUserLogout , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///投资者结算结果确认响应n tvoid OnRspSettlementInfoConfirm ( CThostFtdcSettlementInfoConfirmField * pSettlementInfoConfirm , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///请求查询合约响应n tvoid OnRspQryInstrument ( CThostFtdcInstrumentField * pInstrument , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///请求查询资金账户响应n tvoid OnRspQryTradingAccount ( CThostFtdcTradingAccountField * pTradingAccount , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///请求查询投资者持仓响应n tvoid OnRspQryInvestorPosition ( CThostFtdcInvestorPositionField * pInvestorPosition , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///报单录入请求响应n tvoid OnRspOrderInsert ( CThostFtdcInputOrderField * pInputOrder , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///报单操作请求响应n tvoid OnRspOrderAction ( CThostFtdcInputOrderActionField * pInputOrderAction , CThostFtdcRspInfoField * pRspInfo , int nRequestID , bool bIsLast ); n nt///报单通知n tvoid OnRtnOrder ( CThostFtdcOrderField * pOrder ); n nt///成交通知n tvoid OnRtnTrade ( CThostFtdcTradeField * pTrade ); ntn// ---- 自定义函数 ---- //n public : ntbool loginFlag ; // 登陆成功的标识n tvoid reqOrderInsert ( nttTThostFtdcInstrumentIDType instrumentID , nttTThostFtdcPriceType price , nttTThostFtdcVolumeType volume , nttTThostFtdcDirectionType direction ); // 个性化报单录入,外部调用n private : ntvoid reqUserLogin (); // 登录请求n tvoid reqUserLogout (); // 登出请求n tvoid reqSettlementInfoConfirm (); // 投资者结果确认n tvoid reqQueryInstrument (); // 请求查询合约n tvoid reqQueryTradingAccount (); // 请求查询资金帐户n tvoid reqQueryInvestorPosition (); // 请求查询投资者持仓n tvoid reqOrderInsert (); // 请求报单录入n tntvoid reqOrderAction ( CThostFtdcOrderField * pOrder ); // 请求报单操作n tbool isErrorRspInfo ( CThostFtdcRspInfoField * pRspInfo ); // 是否收到错误信息n tbool isMyOrder ( CThostFtdcOrderField * pOrder ); // 是否我的报单回报n tbool isTradingOrder ( CThostFtdcOrderField * pOrder ); // 是否正在交易的报单n }; n
除了重写的基类函数,还自己封装一些主动调用的操作函数,比如登入登出、下单报单、查询报单、查询投资者结算结果应答、查询投资者资金帐户应答、查询投资者持仓应答等,这里只写几个跟交易密切相关的
下单操作
void CustomTradeSpi :: reqOrderInsert ( ntTThostFtdcInstrumentIDType instrumentID , ntTThostFtdcPriceType price , ntTThostFtdcVolumeType volume , ntTThostFtdcDirectionType direction ) n{ ntCThostFtdcInputOrderField orderInsertReq ; ntmemset ( & orderInsertReq , 0 , sizeof ( orderInsertReq )); nt///经纪公司代码n tstrcpy ( orderInsertReq . BrokerID , gBrokerID ); nt///投资者代码n tstrcpy ( orderInsertReq . InvestorID , gInvesterID ); nt///合约代码n tstrcpy ( orderInsertReq . InstrumentID , instrumentID ); nt///报单引用n tstrcpy ( orderInsertReq . OrderRef , order_ref ); nt///报单价格条件: 限价n torderInsertReq . OrderPriceType = THOST_FTDC_OPT_LimitPrice ; nt///买卖方向: n torderInsertReq . Direction = direction ; nt///组合开平标志: 开仓n torderInsertReq . CombOffsetFlag [ 0 ] = THOST_FTDC_OF_Open ; nt///组合投机套保标志n torderInsertReq . CombHedgeFlag [ 0 ] = THOST_FTDC_HF_Speculation ; nt///价格n torderInsertReq . LimitPrice = price ; nt///数量:1n torderInsertReq . VolumeTotalOriginal = volume ; nt///有效期类型: 当日有效n torderInsertReq . TimeCondition = THOST_FTDC_TC_GFD ; nt///成交量类型: 任何数量n torderInsertReq . VolumeCondition = THOST_FTDC_VC_AV ; nt///最小成交量: 1n torderInsertReq . MinVolume = 1 ; nt///触发条件: 立即n torderInsertReq . ContingentCondition = THOST_FTDC_CC_Immediately ; nt///强平原因: 非强平n torderInsertReq . ForceCloseReason = THOST_FTDC_FCC_NotForceClose ; nt///自动挂起标志: 否n torderInsertReq . IsAutoSuspend = 0 ; nt///用户强评标志: 否n torderInsertReq . UserForceClose = 0 ; n ntstatic int requestID = 0 ; // 请求编号n tint rt = g_pTradeUserApi -> ReqOrderInsert ( & orderInsertReq , ++ requestID ); ntif ( ! rt ) nttstd :: cout << ">>>>>>发送报单录入请求成功" << std :: endl ; ntelse nttstd :: cerr << "--->>>发送报单录入请求失败" << std :: endl ; n} n
下单操作可以用默认参数下单,可以传参下单,比如设定合约代码、价格、数量等,报单操作主要是对于未成交的订单进行编辑或者撤销操作
报单应答
void CustomTradeSpi :: OnRtnOrder ( CThostFtdcOrderField * pOrder ) n{ ntchar str [ 10 ]; ntsprintf ( str , "%d" , pOrder -> OrderSubmitStatus ); ntint orderState = atoi ( str ) - 48 ; t//报单状态0=已经提交,3=已经接受n ntstd :: cout << "=====收到报单应答=====" << std :: endl ; n ntif ( isMyOrder ( pOrder )) nt{ nttif ( isTradingOrder ( pOrder )) ntt{ ntttstd :: cout << "--->>> 等待成交中!" << std :: endl ; nttt//reqOrderAction(pOrder); // 这里可以撤单n ttt//reqUserLogout(); // 登出测试n tt} nttelse if ( pOrder -> OrderStatus == THOST_FTDC_OST_Canceled ) ntttstd :: cout << "--->>> 撤单成功!" << std :: endl ; nt} n} n nvoid CustomTradeSpi :: OnRtnTrade ( CThostFtdcTradeField * pTrade ) n{ ntstd :: cout << "=====报单成功成交=====" << std :: endl ; ntstd :: cout << "成交时间: " << pTrade -> TradeTime << std :: endl ; ntstd :: cout << "合约代码: " << pTrade -> InstrumentID << std :: endl ; ntstd :: cout << "成交价格: " << pTrade -> Price << std :: endl ; ntstd :: cout << "成交量: " << pTrade -> Volume << std :: endl ; ntstd :: cout << "开平仓方向: " << pTrade -> Direction << std :: endl ; n} n
等待成交进行轮询可以选择报单操作,成交完成后的应答
时间序列转K线
从交易拿到的tick数据是时间序列数据,在证券交易中其实还需要根据时间序列算出一些技术指标数据,例如MACD,KDJ、K线等,这里简单地对数据做一下处理,写一个TickToKlineHelper将时间序列专程K线
K线数据结构
// k线数据结构n struct KLineDataType n{ ntdouble open_price ; // 开n tdouble high_price ; // 高n tdouble low_price ; // 低n tdouble close_price ; // 收n tint volume ; // 量n }; n
转换函数
void TickToKlineHelper :: KLineFromLocalData ( const std :: string & sFilePath , const std :: string & dFilePath ) n{ nt// 先清理残留数据n tm_priceVec . clear (); ntm_volumeVec . clear (); ntm_KLineDataArray . clear (); n ntstd :: cout << "开始转换tick到k线..." << std :: endl ; nt// 默认读取的tick数据表有4个字段:合约代码、更新时间、最新价、成交量n tstd :: ifstream srcInFile ; ntstd :: ofstream dstOutFile ; ntsrcInFile . open ( sFilePath , std :: ios :: in ); ntdstOutFile . open ( dFilePath , std :: ios :: out ); ntdstOutFile << "开盘价" << ',' ntt<< "最高价" << ',' ntt<< "最低价" << ',' ntt<< "收盘价" << ',' ntt<< "成交量" << std :: endl ; n nt// 一遍解析文件一边计算k线数据,1分钟k线每次读取60 * 2 = 120行数据n tstd :: string lineStr ; ntbool isFirstLine = true ; ntwhile ( std :: getline ( srcInFile , lineStr )) nt{ nttif ( isFirstLine ) ntt{ nttt// 跳过第一行表头n tttisFirstLine = false ; ntttcontinue ; ntt} nttstd :: istringstream ss ( lineStr ); nttstd :: string fieldStr ; nttint count = 4 ; nttwhile ( std :: getline ( ss , fieldStr , ',' )) ntt{ ntttcount -- ; ntttif ( count == 1 ) nttttm_priceVec . push_back ( std :: atof ( fieldStr . c_str ())); ntttelse if ( count == 0 ) nttt{ nttttm_volumeVec . push_back ( std :: atoi ( fieldStr . c_str ())); nttttbreak ; nttt} ntt} n ntt// 计算k线n nttif ( m_priceVec . size () == kDataLineNum ) ntt{ ntttKLineDataType k_line_data ; ntttk_line_data . open_price = m_priceVec . front (); ntttk_line_data . high_price = * std :: max_element ( m_priceVec . cbegin (), m_priceVec . cend ()); ntttk_line_data . low_price = * std :: min_element ( m_priceVec . cbegin (), m_priceVec . cend ()); ntttk_line_data . close_price = m_priceVec . back (); nttt// 成交量的真实的算法是当前区间最后一个成交量减去上去一个区间最后一个成交量n tttk_line_data . volume = m_volumeVec . back () - m_volumeVec . front (); nttt//m_KLineDataArray.push_back(k_line_data); // 此处可以存到内存n tttntttdstOutFile << k_line_data . open_price << ',' ntttt<< k_line_data . high_price << ',' ntttt<< k_line_data . low_price << ',' ntttt<< k_line_data . close_price << ',' ntttt<< k_line_data . volume << std :: endl ; n ntttm_priceVec . clear (); ntttm_volumeVec . clear (); ntt} nt} n ntsrcInFile . close (); ntdstOutFile . close (); n ntstd :: cout << "k线生成成功" << std :: endl ; n} n nvoid TickToKlineHelper :: KLineFromRealtimeData ( CThostFtdcDepthMarketDataField * pDepthMarketData ) n{ ntm_priceVec . push_back ( pDepthMarketData -> LastPrice ); ntm_volumeVec . push_back ( pDepthMarketData -> Volume ); ntif ( m_priceVec . size () == kDataLineNum ) nt{ nttKLineDataType k_line_data ; nttk_line_data . open_price = m_priceVec . front (); nttk_line_data . high_price = * std :: max_element ( m_priceVec . cbegin (), m_priceVec . cend ()); nttk_line_data . low_price = * std :: min_element ( m_priceVec . cbegin (), m_priceVec . cend ()); nttk_line_data . close_price = m_priceVec . back (); ntt// 成交量的真实的算法是当前区间最后一个成交量减去上去一个区间最后一个成交量n ttk_line_data . volume = m_volumeVec . back () - m_volumeVec . front (); nttm_KLineDataArray . push_back ( k_line_data ); // 此处可以存到内存n nttm_priceVec . clear (); nttm_volumeVec . clear (); nt} n} n
可以从本地文件中读取行情数据,进行离线转换,也可以在接受到行情时进行实时计算 基本思想是,针对每个合约代码,建立字典,维持一个行情数组,当时间间隔达到要求(例如分钟、分时、分日)时计算该时段的开、高、低、收、成交量等数据存入K线数组 最低时间单位的K线计算出来之后,高时间间隔的K线数据可以根据低时间间隔的K线计算出来(例如,算出了分钟K,那么分时K就根据分钟K来算) 本例子中只是实现了一个大概的原理,非常不精确,仅供参考 策略交易
量化交易系统最终是需要将编写的策略代码挂载到系统中进行策略交易的,本文只做了一个简单的实现,实际的中低频策略都是通过事件驱动去调用接口下单的,并不会像这样裸写C++耦合在CTP中
入口
main.cpp
CThostFtdcMdApi跟CustomMdSpi要建立关联,CThostFtdcTraderApi跟CustomTradeSpi建立关联,其实就是类似于函数注册 配置行情和交易地址 行情和交易分别是不同的线程,注意线程同步 记得内存回收 运行结果 行情
应答日志
存成csv表格
交易
应答日志
K线数据
报单情况
用上期所的快期软件,登录上自己的账号之后,从过程序下单,在这个界面里能看到实时的报单成交状况
源码下载 github:demo
结语 本文旨在为刚接触CTP的小白们抛砖引玉,各交易接口的深度运用还需要看官方开发文档。
另外,对于完整的量化交易系统来说,不仅要具备行情、交易、策略模块,事件驱动、风控、回测模块以及底层的数据存储、网络并发都是需要深入钻研的方面,金融工程的Quant Researcher可以只专注于数据的分析、策略的研发,但是对于程序员Quant Developer来说,如何设计和开发一个高并发、低延迟、功能完善与策略结合紧密的量化交易系统的确是一项需要不断完善的工程。
支持是知识分享的动力,有问题可扫码哦
快期上怎么修改密码:定期更换密码真的更安全吗?
为什么需要定期修改密码?按照附文的解释,适用场景是这样的:
你的密码已经被人盗取,获得你密码的人会时常登入你的帐号(同时想办法不让你察觉),监控你的活动。
如果你每隔一段时间就修改密码,那么盗取你密码的人顶多只能在一段时间内监控你的帐户活动,不能一直监控。
现实中这种情形似乎不常见。如果你不认为你的密码过去曾经有被盗取的可能性,那就没必要经常修改。
但如果你的密码有曾经被盗的可能性,或者曾经在安全性不高的网站使用过相同的密码,那么隔一段时间修改是有好处的。
比如QQ密码,可能很多人都点击过模仿QQ空间登录的钓鱼链接,甚至在里面输入过密码。有可能盗取密码的人并不会立即使用这些密码,但可能以后会用。如果自己及时修改,那就避免了以后被人登录进你的QQ帐号的可能性。
如果强制让人隔一段时间就修改密码,会诱导人使用弱密码(方便构造、记忆和输入),这样反而不安全。
参考: HTG Explains: Should You Change Your Passwords Regularly?
快期上怎么修改密码:0经济学基础学习量化投资[5]用CTP接口仿真交易
通过上面几次学习一些基础知识,现在打算小试牛刀一把。
今天的小目标就是通过CTP接口实现一次模拟的交易。
坑巨多。疯狂扫雷。看到这篇文章你们应该很开心。
我想先尝试着把examples里面的CtaTrading跑起来。里面主要是用三种策略分别管理三大股指合约,IF,IC和IH。
第一步,修改CTP_connect.json
这个json长这个样子:
{nn "brokerID": "9999", n "mdAddress": "tcp://180.168.146.187:10011", n "tdAddress": "tcp://180.168.146.187:10001", n "userID": "simnow申请",n "password": "simnow申请" n}
根据simnow的描述:
8、问:我是量化交易投资者,如何登录SimNow? 答:可以 BrokerID统一为:9999 标准CTP: 第一组:Trade Front:180.168.146.187:10000,Market Front:180.168.146.187:10010;【电信】 第二组:Trade Front:180.168.146.187:10001,Market Front:180.168.146.187:10011;【电信】 第三组:Trade Front:218.202.237.33 :10002,Market Front:218.202.237.33 :10012;【移动】 CTPMini1: 第一组:Trade Front:180.168.146.187:10003,Market Front:180.168.146.187:10013;【电信】 BrokerID是9999,是对的。mdAddress和tdAddress也是可以的。
然而!
这个json里的地址完全Ping不通啊,机房都关门了啊,Facepalm。果断换成了第三组(唯一能Ping通的那个)
其次,按照vnpy的wiki上的描述,本人注册了一个simnow的账号。注意UserID对应的是你的InvertorID,而不是你的账号,切记。
密码没什么好说的。然而,你以为就完了吗?
当你非常愉快地打算python runCtaTrading.py的时候,他告诉你连接ctp失败了,因为第一次登录需要修改密码。
好吧,那就上simnow改密码。完了吗?
他告诉你改不了。
终于在论坛找到了解决方案,下载一个他们家的软件,快期新一代,然后用软件登录就可以修改密码了。
同时发现一个好处,用这个软件可以当一个很好的图形化工具辅助观察策略运行的情况。
OK,都改好之后就可以开始运行了。另外补充一点,同样的方法VnTrader的图形化界面也可以跑起来了。
第二步,读懂runCtaTrading.py
runCtaTrading的核心是runChildProcess,至于runParentProcess实际上是利用多线程在规定时间自动调用ChildProcess,本质上是一样的。runChildProcess的代码:
def runChildProcess (): n """子进程运行函数""" n print '-' * 20 nn # 创建日志引擎 n le = LogEngine () n le . setLogLevel ( le . LEVEL_INFO ) n le . addConsoleHandler () n le . addFileHandler () nn le . info ( u '启动CTA策略运行子进程' ) nn ee = EventEngine2 () n le . info ( u '事件引擎创建成功' ) nn me = MainEngine ( ee ) n me . addGateway ( ctpGateway ) n me . addApp ( ctaStrategy ) n le . info ( u '主引擎创建成功' ) nn ee . register ( EVENT_LOG , le . processLogEvent ) n ee . register ( EVENT_CTA_LOG , le . processLogEvent ) n ee . register ( EVENT_ERROR , processErrorEvent ) n le . info ( u '注册日志事件监听' ) nn me . connect ( 'CTP' ) n le . info ( u '连接CTP接口' ) nn sleep ( 10 ) # 等待CTP接口初始化 nn cta = me . getApp ( ctaStrategy . appName ) nn cta . loadSetting () n le . info ( u 'CTA策略载入成功' ) nn cta . initAll () n le . info ( u 'CTA策略初始化成功' ) nn cta . startAll () n le . info ( u 'CTA策略启动成功' ) nn while True : n sleep ( 1 )
大概解释下运作的基本流程,首先是日志引擎启动,和我们关系不密切。然后是主引擎启动,同样不是我们特别关心的,除非后期魔改。链接ctp接口通过调用ctp的api实现,我们在第一个步骤当中已经完成了配置。接下来比较重要的就是ctaEngine实例cta的声明了。这个按照模板来就行,不管。然后cta做了三个操作,loadSetting,initAll和startAll。其实大致就分别对应了Strategy文件内的参数设置,init和start部分,区别在于runCtaTrading可以同时多策略多品种的运行。注意loadSetting是通过当前目录下的CTA_setting.json实现的。
[n {n "name": "double ema",n "className": "EmaDemoStrategy",n "vtSymbol": "IF1706"n },nn {n "name": "atr rsi",n "className": "AtrRsiStrategy",n "vtSymbol": "IC1706"n },nn {n "name": "king keltner",n "className": "KkStrategy",n "vtSymbol": "IH1706"n }n]
如果按照这样设置的话,大概意思就是用EmaDemoStrategy运行在IF1706上。(当然vn1.8版本内并没有这个策略2333)
另外这里有个小坑就是,IF1706早就交割了,所以为了测试,查询现在期货交易信息,要改成IF1804,同理下方也是。
如果想修改策略的参数怎么办,其实只需要修改这个json就行了(通过读代码推理的结果,还未验证)。如下:
[n {n "name": "king keltner",n "className": "KkStrategy",n "vtSymbol": "IH1706",n "size": 10n }n]
第三步,实际测试
2019/04/19/9:30
等到上午9点半,运行runCtaTrading,同时打开robo 3T和上期新一代观察仓位变化。为了简单验证,我只选取了IF1804以及KK策略。
2019/04/19/9:40
开始跑了十分钟,啥动静也没有,委托合约也没见到,我在猜想是不是那个需要55分钟初始化的问题。
2019/04/19/10:30
仍然看不到任何仓位和委托,不会是上期新一代冲突导致的?我关了上期,重启了CtaTrading再等待试试。要不然就要开始debug了。
妈蛋终于搞清楚为什么了,因为KK策略需要有历史数据计算均线,而我的数据库里没有IF1804的历史数据,因此需要使用dataService或者历史行情进行下载。
群里和大神们交流了一番,打算下载开拓者导出数据来实现。
下篇的主题就是填历史行情数据的坑了,本篇暂时更新到这里。
数据库修改密码风险高,如何保证业务持续,这几种密码双活方案可以参考
这是学习笔记的第 2147 篇文章
今天下午的时候,有个朋友(感谢Crane.Chen氵)提了一个Oracle的问题,引起了我的好奇,问题的描述如下:
出于安全需要,这边的数据库密码必须90天更改一次,否则账号过期自动锁定。目前我们改密码都需要停掉应用,然后改密码后,启动应用。这样业务就会有中断。有什么方案可以保障即能更改密码,又能热更新应用里连接池的账号密码。现在若不停机,不管先更改连接池还是先改库,都可能因为密码不匹配而锁定账号。
乍一看是个“不合理”的需求,数据库层面我们遵从安全规范来修改密码,我们照做就是了,但是显然做这个事情的成本实在太高,而且存在极高的风险,因为防御导致业务不可用带来的损失其实是很大的。 从一个层面来说,其实是孤立的看待了这个问题,只是考虑了自己,而没有考虑到应用层和全盘。做完自我批评,我来给出这个问题的一些解决之道。
首先我们把这个密码修改的过程和应用层结合起来,应用层通常是使用连接池,而且主要是长连接的方式,如果修改了密码,那么会在数据库的字典配置里面修改密码信息,密码会在新的会话中立刻生效,但是已有的连接还是会保持,通常在业务层也是做了密码的配置文件,很可能看到的密码是一个加密串。
所以最严重的的情况下无非是下面几种:
1)数据库先改密码,应用层后改,已有的长连接依旧可用,但是新连接失败,数据库密码错误超过3次,数据库账号就会被锁定,导致业务不可用。
2)应用层先改密码,数据库层后改,新连接会全部失败,错误密码超过3次,导致账号被锁定,业务不可用。
小结 :从以上看出来,不管是什么样的顺序都会导致同样严重的后果,所以也就无所谓先后顺序了。
而回到这个问题本身,我们可以做一些补救措施,我又了解了一些背景信息,这是使用weblogic中间件,通常这些配置都是启动时加载的,连接池目前我知道的还不支持动态热更新,所以要解决的重要问题是防止账号被锁定,数据库这边可以写一个脚本去实时检测账号的状态,如果被锁定,可以快速解锁, 这是一种不够优雅但是快速实用的解决方法。
如果我们更近一步,从整个应用架构的层面来考虑,这个问题是否有解呢。
我们来看下面的这个图:
密码信息在应用层是配置形式,而且是启动自加载的模式,数据库密码是数据字典的基础信息,数据库层面是可以实时变化的,我们要保证业务的持续连接,一种方案就是建立影子账户,这个账户没有实际的数据,只有对等的权限,它平时处于锁定状态,即不可用。而应用层来建立连接的部分可以加一个动态的逻辑,如果连接失败,可以使用影子账户,这个影子账户的密码信息是约定好的,不会轻易发生改变,当需要修改密码的时候,可以按照如下的流程来操作:
1)DBA解锁影子账户
2)DBA修改账户密码
3)应用层修改用户密码配置信息
4)应用层分批次启动应用服务,使得配置生效
5)DBA锁定影子账户
在这个过程中如果连接检测失败,会启用影子账户的来建立连接,在应用服务重启完成之后,就可以将影子账户锁定,应用层来再次动态适配,这样对于业务层来说就是一个动态平衡的过程。
其实对于很多公司来说,随着业务的快速变化,安全问题是挥之不去的隐患。所以密码修改也是一种合情合理的解决方式,当然在这个过程中会有一些可用性的影响,整体来说,我们要保证的是可控。
可能到了这里,我们才进行了一半,上面的方案应用层改造其实还是比较大的,而且对于权限的维护来说需要保证影子账户的权限也要完全一致,否则产生业务影响就很被动了。
我们有没有更好的方案呢,其实最透明的方案是数据库层面来做好这种双层校验,这时我想到了MySQL 8.0的新特性。
在MySQL 8.0.14的release note中是这么低调的一段描述。
我们通俗些可以理解为双密码,retain current password这个语法只在修改密码的场景中使用,在create user中是不能用的。意思是修改密码的时候原来的密码依旧可用,这样对于业务是最友好的方式。
正确理解这个语法的姿势是查看官方文档的解读:
不演示一下不尽兴,我们来看看这个特性的效果。
首先创建一个用户:
mysql> create user test_pwd identified by 'test_pwd1';
Query OK, 0 rows affected (0.01 sec)
然后赋予一些基本的权限
mysql> grant usage on *.* to test_pwd ;
Query OK, 0 rows affected (0.01 sec)
这个时候通过客户端连接是OK的。
# mysql -utest_pwd -ptest_pwd1 -P33061 -h127.0.0.1
mysql>
我们开始关键部分,修改用户密码,使用retain current password语法:
mysql> alter user test_pwd identified by 'test_pwd1' retain current password;
ERROR 1227 (42000): Access denied; you need (at least one of) the CREATE USER or APPLICATION_PASSWORD_ADMIN privilege(s) for this operation
很遗憾失败了,因为这个操作是需要基本的权限的,根据提示需要APPLICATION_PASSWORD_ADMIN 或者CREATE USER的权限。
我们使用管理员账户修正下权限。
mysql> grant APPLICATION_PASSWORD_ADMIN on *.* to test_pwd;
Query OK, 0 rows affected (0.01 sec)
再次修改就可以了。
mysql> alter user test_pwd identified by 'test_pwd2' retain current password;
Query OK, 0 rows affected (0.01 sec)
这个时候就达到了密码双活的状态,两个密码都可以用,而且校验始终都可用,幸福感大大增强。
当然我不建议对于业务无限制开放,毕竟这种方式会让双方都很难持续维护密码,所以在业务修改密码,批次启动应用服务后,我们可以回收原来的密码,使用discard old password子句。
mysql> alter user test_pwd DISCARD OLD PASSWORD;
Query OK, 0 rows affected (0.01 sec)
这个时候连接会再次失败。
# mysql -utest_pwd -ptest_pwd1 -P33061 -h127.0.0.1
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'test_pwd'@'localhost' (using password: YES)
这应该算是我们最希望看到的错误场景了,满满的完成任务的使命感。
期待MySQL 8.0更多更好的新特性。。。