VirtualApi诞生的技术背景

现在的量化回测软件和方法有三类,一类是通过文华、TB、MC等商业软件,在商业软件中通过编写交易指标和交易公式,或通过加载用户自己开发的第三方策略库进行交易策略的开发和回测;第二类是直接使用交易所、券商、API软件服务商提供的API或券商等机构提供的行情和交易API直接开发交易策略,或通过一些回测框架调用这些原生API进行回测;第三类是利用聚宽、优矿的网站在线平台进行回测。

若采用第一类商业软件开发量化交易回测系统,虽然对从事量化交易的人来说,开发策略需要的工作量较少,对开发者编程能力要求不高。但缺点也是显而易见的,除了商业软件本身需要收费提高了交易成本以外,采用商业软件开发交易策略不够灵活,使得很多交易策略无法实现。

若采用第二类直接使用API开发策略或采用针对API的回测框架,例如python的各种回测框架、matlaba的各种回测框架、R语言的各种回测框架,PyAlgoTrade、Zipline等、虽然开发策略较为灵活,但缺点是开发交易策略的实盘代码并不能直接进行回测,必须要采用引入回测框架进行回测,待回测完毕,再将回测完成的参数接入实盘策略代码中或删除回测框架部分的代码接入实盘交易的API,使得量化交易回测代码和实盘的代码有较大的改动,增加了策略开发者的工作,也增加了量化交易爱好者时间成本,甚至对很多编程能力有限的量化爱好者来说提搞了研究难度的门槛。

若采用第三类在线回测平台进行回测,由于需要将编写的策略在网站指定的服务器上运行,由于是多用户共享一台服务器,所以回测性能无法得到保证、网站更倾向于采用精度不高的数据进行回测。还由于对策略开发者来说不是使用原生API进行开发策略,所以策略开发的自由度也不够,很多想法也无法实现。更重要的是,选择网站在线平台的方式来开发量化交易策略,就等于默认了网站管理员可随时查看自己辛辛苦苦开发的策略代码,保密性让人担忧,从事量化交易的专业机构几乎不会采用在线网站的回测方式。

近年来,量化交易在金融领域应用的越来越广发,回测系统的设计是量化交易中不可缺失的一部分,但同时也暴露出一些问题,例如商业软件成本高、自己搭建会测框架时间成本高,难度大、采用第三方回测框架难度大、回测到实盘交易的代码改动较大、量化策略保密性不高等等。

为了克服现有技术存在的上述不足,VirtualApi仿真API的回测技术应运而生,它是模拟原生API来实现的。例如通过模拟原生交易API和行情API,例如通过模拟原生API的库方法的定义、头文件的定义等,使得回测和实盘交易代码,简单的将实盘代码替换为仿真API,对底层代码可不作改动或改动较少即可实现回测和参数优化。

支持的编程语言

VirtualApi Api支持多种编程语言,包括C++、Python、Java、C#、Golang、易语言等 。

支持的操作系统

VirtualApi Api支持Windows操作系统,版本要求Windows7、Windows2008及以上。

支持的量化交易框架

VirtualApi 支持各种基于CTP接口的自编程序和框架,例如vn.py、Quicklib、海风等。

CTP实盘程序流程图(C++)

典型CTP实盘程序流程图

VirtualApi回测程序流程图(C++)

VirtualApi For CTP回测程序流程图

CTP库文件

CTP Api是C++库,理论上可以用于包括C++、Python、Java、C#、等在内的多种编程语言的调用。

VirtualApi For CTP一样是采用C++开发,目前只支持Windows操作系统,运行采用VirtualApi Api的计算机和TradeAgent.exe的计算机采用要求Windows7、Windows2008及以上系统,对于Windwo7和Windows Server2008这些较为陈旧的Windows系统安装微软运行时库redist2015补丁。

以最常用的CTP无中继代理模式为例(于2019.6.14实施的穿透式和老的非穿透式),CTP API Windows版本含以下文件:

其中ThostFtdcMdApi.h、ThostFtdcTraderApi.h、ThostFtdcUserApiDataType.h、ThostFtdcUserApiStruct.h 是头文件,thostmduserapi.dll、thosttraderapi.dll、thostmduserapi.lib、thosttraderapi.dll。

VirtualApi库文件

VirtualApi For CTP库文件

包含以下文件:

可以看到VirtualApi 库在原CTP库基础上增加了list.csv,Price.exe,Graph.exe这3个文件,而对于thostmduserapi.dll、thosttraderapi.dll、thostmduserapi.lib、thosttraderapi.dll这4个文件是VirtualApi提供模拟CTP的实现,而ThostFtdcMdApi.h、ThostFtdcTraderApi.h、ThostFtdcUserApiDataType.h、ThostFtdcUserApiStruct.h 这4个头文件则保持和CTP对应版本一模一样。

list.csv作用:该程序放到回测程序的目录下,用于指定csv格式的数据文件的存放路径,并非自己存放Tick数据,在回测时VirtualApi会从上至下依次读取list.csv种这些文件的Tick数据,并触发CTP方法里的深度行情通知回调函数 virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData),使得和CTP的OnRtnDepthMarketData回调方法一致。 值得注意的是,list.csv指定的数据文件库的字段顺序目前不能更改,将来可能提供字段顺序的自定义设置功能。

localtime (本机写入TICK的时间),
InstrumentID (合约名),
TradingDay (交易日),
ActionDay (业务日期),
UpdateTime (时间),
UpdateMillisec(时间毫秒),
LastPrice (最新价),
Volume(成交量) ,
HighestPrice (最高价),
LowestPrice(最低价) ,
OpenPrice(开盘价) ,
ClosePrice(收盘价),
AveragePrice(均价),
AskPrice1(申卖价一),
AskVolume1(申卖量一),
BidPrice1(申买价一),
BidVolume1(申买量一),
UpperLimitPrice(涨停板价),
LowerLimitPrice(跌停板价),
OpenInterest(持仓量),
Turnover(成交金额),
PreClosePrice (昨收盘),
PreOpenInterest (昨持仓),
PreSettlementPrice (上次结算价),

Graph.exe作用:该程序放到回测程序的目录下。在回测时,回测程序通过API会自动打开Graph.exe,在回测时自动绘制资金曲线分时图;回测完成后,也可以将回测数据文件拖入Graph.exe窗口打开资金曲线分时图;

Price.exe作用:该程序放到回测程序的目录下。在回测的过程中或回测技术后,可以将回测数据文件拖入price.exe窗口,绘制回测时间段内的行情多日分时图,可以和Graph.exe显示的资金曲线进行对照。

C++ Demo

C++ Demo采用Visual Studio2015、Visual Studio2017、Visual Studio2019 编译,运行test.sln打开Demo项目。

微软最新版本的Visual Studio下载: https://visualstudio.microsoft.com/zh-hans/downloads/

AutoTrader.cpp


#include  <stdio.h>
#include  <iostream>
#include  <fstream>
#include  <string>
#include  <windows.h>
#include  <time.h>
#include  <iomanip>
#include  <vector>
#include  <algorithm>
#include  <stdlib.h>
using namespace std;

#include ".\ThostTraderApi\ThostFtdcTraderApi.h"
#include ".\ThostTraderApi\ThostFtdcMdApi.h"
#include "TraderSpi.h"
#include "MdSpi.h"
#include "Common.h"
#include "DataSniffer.h"
#include "MyTrader.h"

#pragma warning(disable : 4996)
// UserApi对象
CThostFtdcTraderApi *pUserApi;
// MdApi对象
CThostFtdcMdApi *pMdApi;

// 配置参数
char  FRONT_ADDR_1A[] = "tcp://180.168.146.187:10000";		// 前置地址1交易:实盘
char  FRONT_ADDR_1B[] = "tcp://180.168.146.187:10010";		// 前置地址2行情:实盘

char  FRONT_ADDR_2A[] = "tcp://180.168.146.187:10000";		// 前置地址1交易:盘后
char  FRONT_ADDR_2B[] = "tcp://180.168.146.187:10010";		// 前置地址2行情:盘后

char  FRONT_ADDR_3A[] = "tcp://180.168.146.187:10000";	    // 前置地址3交易:仿真 17:00开始
char  FRONT_ADDR_3B[] = "tcp://180.168.146.187:10010";		// 前置地址3行情:仿真 17:00开始

TThostFtdcBrokerIDType	BROKER_ID = "9999";					// 实盘:经纪公司代码 国泰君安=7090
TThostFtdcInvestorIDType INVESTOR_ID = "038997";			// 实盘:投资者代码
TThostFtdcPasswordType  PASSWORD = "000000wdg";				// 实盘:用户密码
//TThostFtdcBrokerIDType	BROKER_ID = "2030";				// 经纪公司代码:仿真
//TThostFtdcInvestorIDType INVESTOR_ID = "00092";			// 投资者代码:仿真
//TThostFtdcPasswordType  PASSWORD = "888888";				// 用户密码:仿真

TThostFtdcInstrumentIDType INSTRUMENT_ID = "rb1910";		// 交易合约代码
TThostFtdcDirectionType	DIRECTION;							// 交易买卖方向
TThostFtdcOffsetFlagType MARKETState;						// 开平仓
TThostFtdcPriceType	LIMIT_PRICE;							// 交易价格

//char *ppInstrumentID[] = {"IF1910", "rb1910","ag1910", "ru1910", "cu1910", "j1910", "SR1910", "m1910", "y1910", "p1910"};	// 行情订阅列表
//int iInstrumentID = 10;									// 行情订阅数量

char *ppInstrumentID[] = { "rb1910"};			// 行情订阅列表
int iInstrumentID = 1;							// 行情订阅数量
bool	ReceiveTick = false;

// 请求编号
int iRequestID = 0;
// 交易时间
bool	JustRun = false;	//正在启动标志

TThostFtdcDateExprType	TradingDay;

// User行情数据

extern	char	*InstrumentID_name;	//
extern	string	Q_BarTime_s;		//时间字符串
extern	int		Q_BarTime_1;		//时间采用秒计
extern	double	Q_BarTime_2;		//时间格式0.145100
extern	double	Q_UpperLimit;	
extern	double	Q_LowerLimit;	

extern	double	NewPrice;		
extern	int		FirstVolume;	//前一次成交量数据

extern	double  Mn_open[3];		// 
extern	double  Mn_high[3];		// 
extern	double  Mn_low[3];		// 
extern	double  Mn_close[3];	// 

extern	double  BuyPrice;		//开仓价
extern	double  SellPrice;		//开仓价
extern	int		BNum;			//开仓次数
extern	int		SNum;			//开仓次数

extern	bool	BuySignal;
extern	bool	SellSignal;	

extern	double	BSVolume;		//开仓量

extern	int		TickABS;
extern	double  TickAPrice[4];
extern	int		TickBNum;
extern	double  TickBPrice[4];	

extern	char    LogFilePaths[80];

// 会话参数
extern	TThostFtdcFrontIDType	FRONT_ID;	//前置编号
extern	TThostFtdcSessionIDType	SESSION_ID;	//会话编号
extern	TThostFtdcOrderRefType	ORDER_REF;	//报单引用



void main(void)
{
	void Erasefiles();
	void Sniffer();
	void Trading();
	bool ReadConfiguration(char *filepaths);
	void WriteConfiguration(char *filepaths);
	
	Erasefiles();
	Sleep(2000);

	cerr << "--->>> " << "Welcom MyAutoTrader System!" << endl;
	cerr << "--->>> " << "Version 1.0.0!" << endl;
	// 初始化UserApi
	pUserApi = CThostFtdcTraderApi::CreateFtdcTraderApi("./thosttraderapi.dll");			// 创建UserApi//"./thosttraderapi.dll"
	CTraderSpi* pUserSpi = new CTraderSpi();
	pUserApi->RegisterSpi((CThostFtdcTraderSpi*)pUserSpi);			// 注册事件类
	pUserApi->SubscribePublicTopic(THOST_TERT_RESTART);				// 注册公有流
	pUserApi->SubscribePrivateTopic(THOST_TERT_RESTART);			// 注册私有流
	pUserApi->RegisterFront(FRONT_ADDR_1A);							// connect
	pUserApi->Init();
	cout << pUserApi->GetApiVersion() << endl;
	cout << "--->>> " << "Initialing TradeApi" << endl;

	// 初始化MdApi
	pMdApi = CThostFtdcMdApi::CreateFtdcMdApi("./thostmduserapi.dll");					// 创建MdApi//"./thostmduserapi.dll"
	CThostFtdcMdSpi* pMdSpi = new CMdSpi();
	pMdApi->RegisterSpi(pMdSpi);									// 注册事件类
	pMdApi->RegisterFront(FRONT_ADDR_1B);							// connect		优先行情地址
	pMdApi->RegisterFront(FRONT_ADDR_2B);							// connect		备用行情地址,1B断开,自动连接2B地址
	cout << pMdApi->GetApiVersion() << endl;
	pMdApi->Init();
	cout << "--->>> " << "Initialing MdApi" << endl;
	//pMdApi->Join();
	//pMdApi->Release();
	
	Sleep(6000);
	ReadConfiguration("./AutoTrader.dat");			//自定义数据,如持仓数据等均可
	cout << "--->>> " << "初始化完成!" << endl;
	

	while(1)
	{
		//指标计算,下面只是个简单例子
		//可自建函数,进行复杂处理  见DataSniffer.h
		Sniffer();
		//下单控制
		//可自建函数,单独复杂处理	见MyTrader.h
		Trading();
		Sleep(50);
	}
}

	

stdafx.h


#pragma once
#ifndef _WIN32_WINNT		// 允许使用特定于 Windows XP 或更高版本的功能。
#define _WIN32_WINNT 0x0501	// 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif						
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <windows.h>
#include <iostream>
using namespace std;

TraderSpi.h


#pragma once
#include "stdafx.h"
#include "..\\..\\Library(C++)\\ThostFtdcTraderApi.h"

class CTraderSpi : public CThostFtdcTraderSpi
{
public:
	///当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	virtual void OnFrontConnected();

	///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
	///@param nReason 错误原因
	///        0x1001 网络读失败
	///        0x1002 网络写失败
	///        0x2001 接收心跳超时
	///        0x2002 发送心跳失败
	///        0x2003 收到错误报文
	virtual  void OnFrontDisconnected(int nReason);

	///登录请求响应
	//	virtual  void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast) ;
	virtual  void OnRspUserLogin(char *username, char *password);

	///登出请求响应
	virtual  void OnRspUserLogout(CThostFtdcVirtualApiLogoutField *pUserLogout, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	///错误应答
	virtual  void OnRspError(CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	///报单录入请求响应
	virtual  void OnRspOrderInsert(COrderField *pInputOrder, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	///请求查询投资者持仓响应
	virtual  void OnRspQryInvestorPosition(CThostFtdcInvestorPositionField *pInvestorPosition, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	virtual  void OnRtnOrder(CThostFtdcTradeOrderField *pOrder);

public:
	///请求查询资金账户
	void ReqQryTradingAccount();

	///请求查询投资者持仓
	void ReqQryInvestorPosition();

	///报单录入请求
	void ReqOrderInsert(int Sid, char *Instrument, int Volume, double Price, double LastPrice, int Direction, char *Remark);

	///报单操作请求
	void ReqOrderAction(CThostFtdcTradeOrderField *pOrder);

	// 是否收到成功的响应
	bool IsErrorRspInfo(CThostFtdcVirtualApiRspInfoField *pRspInfo);

	// 是否我的报单回报
	bool IsMyOrder(CThostFtdcTradeOrderField *pOrder);

	// 是否正在交易的报单
	bool IsTradingOrder(CThostFtdcTradeOrderField *pOrder);
};

TraderSpi.h


#include 
#include 
#include 
#include 
using namespace std;
#include "stdafx.h"
#include "..\\..\\Library(C++)\\ThostFtdcTraderApi.h"
#include "TraderSpi.h"

#pragma warning(disable : 4996)

// USER_API参数
extern CThostFtdcTraderApi* pTdApi;

// 配置参数
extern char FRONT_ADDR[];		// 前置地址
extern char BROKER_ID[];		// 经纪公司代码
extern char INVESTOR_ID[];		// 投资者代码
extern char PASSWORD[];			// 用户密码
extern char* ppInstrumentID[];
extern int iInstrumentID;
extern char INSTRUMENT_ID[];	// 合约代码
extern TThostFtdcDirectionType	DIRECTION;	// 买卖方向
extern TThostFtdcOffsetFlagType MARKETState;//开平仓
extern TThostFtdcPriceType	LIMIT_PRICE;	// 价格
#define TYPE_NUM 20
extern	double	Q_UpperLimit;
extern	double	Q_LowerLimit;

extern	bool	JustRun;		//正在启动标志

								// 会话参数
TThostFtdcFrontIDType	FRONT_ID;	//前置编号
TThostFtdcSessionIDType	SESSION_ID;	//会话编号
TThostFtdcOrderRefType	ORDER_REF;	//报单引用

extern int needstate;
void CTraderSpi::OnFrontConnected()
{
	cerr << "--->>> " << __FUNCTION__ << endl;
	char username[51] = { 0 };
	char password[51] = { 0 };
	pTdApi->GetLoginInfoUsername(username);
	pTdApi->GetLoginInfoPassword(password);
	pTdApi->ReqUserLogin(username, password);
	printf("[%s] are logging into the TradeAgent server...\n", username);
	///用户登录请求
	//ReqUserLogin();
	Sleep(500);
}
 
void CTraderSpi::OnRspUserLogin(char *username, char *password)
{
	cerr << "--->>> " << __FUNCTION__ << endl;

	/*
	if (pRspUserLogin == NULL)
	{
	cout << "--->>>指针错误OnRspUserLogin" << endl;			  //指针检查
	//WirteTradeRecordToFileMainThread(0, "OnRspUserLogin指针错误");
	Sleep(5000);
	ReqUserLogin();// 自己添加
	return;
	}

	if (IsErrorRspInfo(pRspInfo))
	{
	cerr << "--->>> 交易登录错误: " << pRspInfo->ErrorID << pRspInfo->ErrorMsg << endl;
	//WirteTradeRecordToFileMainThread(0, "交易登录错误");

	Sleep(5000);
	ReqUserLogin();// 自己添加

	}

	if (bIsLast && !IsErrorRspInfo(pRspInfo))
	{
	// 保存会话参数
	FRONT_ID = pRspUserLogin->FrontID;
	SESSION_ID = pRspUserLogin->SessionID;
	int iNextOrderRef = atoi(pRspUserLogin->MaxOrderRef);
	iNextOrderRef++;
	sprintf(ORDER_REF, "%d", iNextOrderRef);
	//cerr << "--->>> 报单引用 = " << ORDER_REF << endl;
	///获取当前交易日

	char TradingDay[9] = { "0" };
	strcpy(TradingDay, pTdApi->GetTradingDay());
	cerr << "--->>> 获取当前交易日 = " << pTdApi->GetTradingDay() << endl;

	}
	*/
}


void CTraderSpi::OnRspUserLogout(CThostFtdcVirtualApiLogoutField *pUserLogout, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
}




extern  double YestayAllAmount;
extern  double TodayAllAmount;
//extern  double UserAmount;

int  JsReqQryTradingAccountFailer = 0;
void CTraderSpi::ReqQryTradingAccount()
{
	/*
	CThostFtdcQryTradingAccountField req;
	memset(&req, 0, sizeof(req));
	strcpy(req.BrokerID, BROKER_ID);
	strcpy(req.InvestorID, INVESTOR_ID);
	int iResult = pTdApi->ReqQryTradingAccount(&req, ++iRequestID);
	//cerr << "--->>> 请求查询资金账户: " << ((iResult == 0) ? "成功" : "失败") << endl;


	if (iResult == 0)
	{
	JsReqQryTradingAccountFailer=  0;
	}
	else
	{
	JsReqQryTradingAccountFailer++;
	if (JsReqQryTradingAccountFailer > 20)
	{
	printf("请求查询资金账户资金X失败");
	}
	}
	*/

}

double UserAmount = 0;


void CTraderSpi::ReqQryInvestorPosition()
{
	/*
	CThostFtdcQryInvestorPositionField req;
	memset(&req, 0, sizeof(req));
	strcpy(req.BrokerID, BROKER_ID);
	strcpy(req.InvestorID, INVESTOR_ID);
	strcpy(req.InstrumentID, InstrumentID_n[0]);
	int iResult = pTdApi->ReqQryInvestorPosition(&req, ++iRequestID);
	//cerr << "--->>> 请求查询投资者持仓: " << ((iResult == 0) ? "成功" : "失败") << endl;

	if (iResult == 0)
	{
	JsReqQryInvestorPositionFailer=0;
	}
	else
	{
	JsReqQryInvestorPositionFailer++;
	if (JsReqQryInvestorPositionFailer > 20)
	{
	printf("请求查询投资者持仓X失败");
	}
	}
	*/

}







bool FindStr(int id, char * str)
{

	//char * pdest1 = strstr(InstrumentID_n[id], str);
	//int  result1 = pdest1 - InstrumentID_n[id] + 1;
	//printf("%s  %s\n", InstrumentID_n[id], str);

	//if (stricmp(InstrumentID_n[id], str) == 0)
	//if (pdest1 != NULL)
	//{	//printf("在%s发现%s\n", InstrumentID_n[id],str );
	return true;
	//}
	//else
	//{
	//printf("%s 没有在%s发现\n", str, InstrumentID_n[id]);
	//	return false;
	//}
}

int SaveInstrumentID = { 0 };
bool  checkstate = false;
bool  TypeCheckState_B_Today[TYPE_NUM] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
bool  TypeCheckState_S_Today[TYPE_NUM] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };

bool  TypeCheckState_B_History[TYPE_NUM] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
bool  TypeCheckState_S_History[TYPE_NUM] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };

int	Trade_dataA_Amount_S_History[TYPE_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		//空头持仓
int	Trade_dataA_Amount_S_Today[TYPE_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		//空头持仓


int	Trade_dataA_Amount_B_History[TYPE_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		//多头持仓
int	Trade_dataA_Amount_B_Today[TYPE_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		//多头持仓

																												//int	Trade_dataA_Amount_B_Today = 0;		//多头持仓
																												//int	Trade_dataA_Amount_S_Today = 0;		//多头持仓

bool orderstate = false;


 

///请求查询投资者持仓响应
void CTraderSpi::OnRspQryInvestorPosition(CThostFtdcInvestorPositionField *pInvestorPosition, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{

}

 
extern  int iRequestID;
extern int  MAX_TRADENUM;
extern double totlemoney;

 
void CTraderSpi::ReqOrderInsert(int Sid, char *Instrument, int Volume, double Price, double LastPrice, int Direction, char *Remark)
{
	COrderField req;
	memset(&req, 0, sizeof(req));
	req.Sid = Sid;                        //策略ID编号
	strcpy(req.InstrumentID, Instrument); //股票代码
	_snprintf_s(req.Remark, sizeof(req.Remark), sizeof(req.Remark) - 1, "%s", Remark); //备注,也可以输出策略程序的变量等

	req.Volume = Volume;                     //下单数量
	req.Price = Price;                    //委托价格
	req.LastPrice = LastPrice;                    //委托价格

	req.Direction = Direction;            //买卖方向
	int iRequestID = 1;
	int iResult = pTdApi->ReqOrderInsert(&req, ++iRequestID);
	cerr << "--->>> 报单录入请求: " << ((iResult == 0) ? "成功" : "失败") << endl;
}

void CTraderSpi::ReqOrderAction(CThostFtdcTradeOrderField *pOrder)
{


}



void CTraderSpi::OnRspOrderInsert(COrderField *pInputOrder, CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	cerr << "--->>> " << __FUNCTION__ << endl;

	SYSTEMTIME sys_time;
	GetLocalTime(&sys_time);
	double system_times;
	system_times = (double)((sys_time.wHour) / 10e1) + (double)((sys_time.wMinute) / 10e3) + (double)((sys_time.wSecond) / 10e5);	//格式时间0.145100

    //cerr << "--->>> 报单: " <>> " << __FUNCTION__ << endl;
	if (IsMyOrder(pOrder))
	{
		if (IsTradingOrder(pOrder))
			ReqOrderAction(pOrder);
		else if (pOrder->OrderStatus == THOST_FTDC_OST_Canceled)
			cout << "--->>> 撤单成功" << endl;
	}
}



void CTraderSpi::OnFrontDisconnected(int nReason)
{
	cerr << "--->>> " << "OnFrontDisconnected" << endl;
	cerr << "--->>> Reason = " << nReason << endl;
}



void CTraderSpi::OnRspError(CThostFtdcVirtualApiRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	cerr << "--->>> " << "OnRspError" << endl;
	IsErrorRspInfo(pRspInfo);
}

bool CTraderSpi::IsErrorRspInfo(CThostFtdcVirtualApiRspInfoField *pRspInfo)
{
	// 如果ErrorID != 0, 说明收到了错误的响应
	bool bResult = ((pRspInfo) && (pRspInfo->ErrorID != 0));
	if (bResult)
		cerr << "--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << endl;
	return bResult;
}

bool CTraderSpi::IsMyOrder(CThostFtdcTradeOrderField *pOrder)
{
	//return ((pOrder->FrontID == FRONT_ID) &&
	//(pOrder->SessionID == SESSION_ID) &&
	//(strcmp(pOrder->OrderRef, ORDER_REF) == 0));

	return (strcmp(pOrder->OrderRef, ORDER_REF) == 0);
}

bool CTraderSpi::IsTradingOrder(CThostFtdcTradeOrderField *pOrder)
{
	return ((pOrder->OrderStatus != THOST_FTDC_OST_PartTradedNotQueueing) &&
		(pOrder->OrderStatus != THOST_FTDC_OST_Canceled) &&
		(pOrder->OrderStatus != THOST_FTDC_OST_AllTraded));
}