AlgoPlus 2.0是使用c++语言开发的全市场量化交易SDK,旨在提高量化交易开发效率、促进量化交易技术交流。
本文将通过一个范例介绍如何使用AlgoPlus 2.0实现股票模拟交易。
首先,先给大家介绍AlgoPlus 2.0的关键方法。
AlgoPlus关键方法
初始化方法
/// pLoginField中存放了登录所需要的信息;
/// pMarketDataCallback是行情事件回调函数;
/// pOrderCallback是订单事件回调函数;
/// pEventCallback是其他事件回调函数指针;
/// 初始化完成后,pLoopCallback在一个独立线程中被反复执行;
CBaseTrader* init(short nRunID, CAPLoginField* pLoginField, FAPOnMarketDataFunctionType pMarketDataCallback, FAPOnOrderFunctionType pOrderCallback, FAPOnEventFunctionType pEventCallback, FAPOnLoopFunctionType pLoopCallback)
初始化时需要提供必要的登录信息,以及事件回调函数。这些回调函数都是由用户定义,由AlgoPlus在事件发生时调用。用户在回调函数中实现事件处理逻辑。
订阅方法
/// 省略交易所,优先匹配A股标的;
/// 如果交易代码不重复,可以省略交易所,且一次订阅不同交易所的标的;
/// 如果交易代码存在重复情况,需指定交易所,且订阅标的必须属于该交易所。
TAPErrorIDType subscribe(CBaseTrader* pAPTrader, char** ppStandardID, int nCount, const char* szExchangeID)
报单方法
/// 买入指定的数量;
CAPOrderField* buy(CBaseTrader* pAPTrader, const char* szStandardID, int nOrderVolume, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 先根据订单类型确定报单价格,再计算金额对应的符合交易所规则的报单数量;
CAPOrderField* buyAmount(CBaseTrader* pAPTrader, const char* szStandardID, double dAmount, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 以可用资金为基数计算买入金额,最终换算成买入数量
CAPOrderField* buyRatio(CBaseTrader* pAPTrader, const char* szStandardID, double dRatio, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 卖出指定的数量,超过可用持仓则卖出全部可用持仓;
CAPOrderField* sell(CBaseTrader* pAPTrader, const char* szStandardID, int nOrderVolume, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 先根据订单类型确定报单价格,再计算金额对应的符合交易所规则的报单数量;
CAPOrderField* sellAmount(CBaseTrader* pAPTrader, const char* szStandardID, double dAmount, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 以可用持仓为基数计算卖出数量;
CAPOrderField* sellRatio(CBaseTrader* pAPTrader, const char* szStandardID, double dRatio, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 调仓指令,将持仓调整至目标数量。
CAPOrderField* balanceToLongValue(CBaseTrader* pAPTrader, const char* szStandardID, double dTargetValue, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
/// 调仓指令,将持仓调整至目标金额。
CAPOrderField* balanceToLongVolume(CBaseTrader* pAPTrader, const char* szStandardID, int nTargetVolume, char chOrderType, double dOrderPrice, short nOrderMark, const char* szExchangeID);
撤单方法
/// 根据OrderID撤销某个特定的订单
TAPErrorIDType cancelOrder(CBaseTrader* pAPTrader, int nOrderID);
/// 撤销某只股票所有的未成成交订单
TAPErrorIDType cancelOrderByStandardID(CBaseTrader* pAPTrader, const char* szStandardID, const char* szExchangeID);
/// 撤销某交易所所有未成交订单
TAPErrorIDType cancelOrderByExchangeID(CBaseTrader* pAPTrader, const char* szExchangeID);
/// 撤销账号所有未成交订单
TAPErrorIDType cancelOrderAll(CBaseTrader* pAPTrader);
我们还需要一个交易环境,这里用的是N视界(
http://www.n-sight.com.cn
)提供的股票模拟交易环境。
N视界股票模拟交易环境
N视界有三套股票模拟交易环境,其中一套的交易时间与实盘一致:
交易前置 tcp://210.14.72.11:4400
行情交易 tcp://210.14.72.11:4402
另外两套是7*24小时环境:
A:
7*24股票模拟环境交易前置 tcp://210.14.72.15:4400
7*24股票模拟环境行情前置 tcp://210.14.72.15:4402
B:
7*24股票模拟环境交易前置 tcp://210.14.72.16:9500
7*24股票模拟环境行情前置 tcp://210.14.72.16:9402
范例与解析
import AlgoPlus as ap
def stock_on_event(event_id, field, islast, error_id, error_msg):
field = ap.decode_ctp_event_field(event_id, field)
print(f"-------")
print(f"on_event -> EventID:{event_id},LastFlag:{islast},ErrorID:{error_id},ErrorMsg:{error_msg}")
print(f"on_event -> Content: {field}")
print(f"-------")
@ap.ap_marketdata_callback_wraps
def stock_on_marketdata_event(data):
print(f"-------")
print(f"on_marketdata -> {data}")
print(f"-------")
@ap.ap_order_callback_wraps
def stock_on_order_event(event_id, order, position, cash):
print(f"-------")
print(f"on_order -> EventID:{event_id},UsableCash:{cash}")
print(f"on_order -> {order}")
print(f"on_order -> {position}")
print(f"-------")
def stock_on_loop():
global counter
global standard_list
global trader
global last_order
counter += 1
if counter == 1:
ap.cancelOrderAll(trader)
print(f"-------")
print(f"loop:{counter} -> cancelOrderAll")
print(f"-------")
elif counter == 100:
print(f"-------")
for item in standard_list:
order = ap.buy(trader, item, 100, ap.ENUM_OrderType_FrontierLimitAndWait, 0)
if order is not None:
print(f"loop:{counter} -> buy UpperLimitPrice -> ErrorID:{order.ErrorID},StandardID:{item},Volume:{100},Price:{order.OriginalPrice}")
print(f"-------")
elif counter == 150:
print(f"-------")
for item in standard_list:
order = ap.buy(trader, item, 100, ap.ENUM_OrderType_HomeBestLimitAndWait, 0)
if order is not None:
print(f"loop:{counter} -> buy HomeBestPrice -> ErrorID:{order.ErrorID},StandardID:{item},Volume:{100},Price:{order.OriginalPrice}")
if order.ErrorID == 0:
last_order = order
print(f"-------")
elif counter == 200:
if last_order is not None:
ap.cancelOrder(trader, last_order.OrderID)
print(f"-------")
print(f"loop:{counter} -> cancelOrder last_order -> StandardID:{last_order.StandardID}")
print(f"-------")
elif counter == 250:
ap.cancelOrderAll(trader)
print(f"-------")
print(f"loop:{counter} -> cancelOrderAll")
print(f"-------")
if __name__ == '__main__':
login = ap.CAPLoginField()
login.License = ""
login.UserType = ap.ENUM_UserType_NSIGHTStock
login.UserID = ""
login.InvestorID = ""
login.Password = ""
login.TraderFront = "tcp://210.14.72.15:4400"
login.MdFront = "tcp://210.14.72.15:4402"
login.ConnectTimeout = 0
login.TaskExecuteGap = ap.MICROSECONDS_IN_SECOND * 2
login.BasePath = "./"
last_order = None
counter = 0
standard_list = ["600000", "000001", "300001"]
trader = ap.init(0, login, stock_on_marketdata_event, stock_on_order_event, stock_on_event, stock_on_loop)
if trader is not None:
ap.subscribe(trader, standard_list)
ap.loop()
else:
init_error_id = ap.getInitError()
print(f"init_error -> InitErrorID:{init_error_id}")
-
stock_on_event
是我们定义的其他事件回调函数。ap.decode_ctp_event_field函数根据event_id将c++结构体转成pyhon对象。
如图所示,初始化阶段分别触发ENUM_EventID_DayRolling(10008)事件和ENUM_EventID_Ready(10009)事件。
-
stock_on_marketdata_event
是我们定义的行情通知事件的回调函数。@ap.ap_marketdata_callback_wraps是一个装饰器,可以将c++行情结构体转成python对象。
-
初始化完成之后,
stock_on_loop
在一个独立线程中被反复执行。
如图所示在第100次执行时,以涨停板限价分别买入100股600000、000001、300001。使用的报单指令是
buy
,订单类型型是
ENUM_OrderType_FrontierLimitAndWait
。
在第150次执行时,以本方最优限价分别买入100股600000、000001、300001。使用的报单指令是
buy
,订单类型型是
ENUM_OrderType_HomeBestLimitAndWait
。
其中,
stock_on_order_event
负责实时推送订单状态变化、成交金额变化。@ap.ap_order_callback_wraps是一个装饰器,将c++订单结构体、持仓结构体装成python对象。
客户端
在N视界官网下载页面(
http://n-sight.com.cn/u/w/download/download.html
)定制服务专区下载剑鱼星终端。
登录前选择N视界仿真交易地址:
登录后可监测账户交易状态:
如图所示,这是范例报单的情况,前三笔涨停板限价单全都成交了,后三笔本方最优限价单成交了两笔。第一次撤单使用的时cancelOrder,撤单对象是300001,这笔订单已经成交,所以未执行。第二次撤单使用的指令时cancelOrderAll,撤单对象是所有未成交单,所以600000此时被撤销。
项目地址
-
官网:
http://algo.plus
-
码云(Gitee):
https://gitee.com/algoplus/AlgoPlus