专栏首页Crossin的编程教室Python做量化|使用AlgoPlus接收期货实时行情

Python做量化|使用AlgoPlus接收期货实时行情

金融领域也是 Python 的重要方向之一,我知道有一些读者就是冲着做量化交易才接触 Python 的。今天给大家分享一个使用 Python 的期货交易API。


量化交易在国内发展方兴未艾。因为T+0且允许做空的交易制度、交易所的大力推动、信息技术红利带来的赚钱效应培养了一大批拥趸等原因,量化交易在期货行业起步比较早,而且发展比较成熟。

虽然各期货交易所都开发了自己的交易后台,但是上期所的CTP仍是目前使用最广泛的。所以,对于想从事量化研究、交易的同学们而言,学习CTP开发是一门必修课。本文就带大家了解一下CTP,并给大家介绍一个python版CTP交易接口——AlgoPlus

关于CTP

CTP是Comprehensive Transaction Platform的简称。CTP有MdApi和TraderApi两个独立的开放接口。

MdApi负责行情相关操作(订阅、接收)。

TraderApi负责交易相关的操作(买、卖、撤、查)。

MdApi与TraderApi方法的执行过程都是异步的,每一个请求都对应一个或多个负责接收执行结果的回调函数。例如,通过ReqOrderInsert方法向交易所发出买开仓指令,对应的回调方法OnRtnOrder可以实时接收交易所服务器发回来的执行通知。

关于AlgoPlus

上期所CTP的官方API只支持C++语言,除非有开发经验,否则不推荐直接使用C++语言开发。

python语言在许多领域被非常广泛的应用,量化交易也不例外。本文给大家介绍的AlgoPlus就是对官方CTP封装的python版量化投资接口。相比较其他Python版CTP,AlgoPlus具有以下特点:

  1. 忠实于CTP官方特性。简单的说,AlgoPlus就是官方CTP的python翻译版,没有过度的封装,让交易者根据自己的实际情况选择官方设计的功能。
  2. 使用Cython、ctypes技术封装,即能实现了低延时的要求,又能兼容python语言的易用性。经过严格测试,AlgoPlus从策略触发交易信号到调用C++方法,延时只有40微秒左右。
  3. 从实战的角度为同学们展示量化策略的开发过程,例如趋势策略、套利策略、风控策略、执行算法等教程。

安装AlgoPlus

第一步:安装Anaconda。

第二步:使用pip install algoplus命令安装。

当你看到以下内容时:

Processing dependencies for AlgoPlus==1.5
Finished processing dependencies for AlgoPlus==1.5
请按任意键继续. . .

安装成功!

注册模拟账号

1、Simnow是上海期货交易所旗下技术公司维护的一套模拟交易系统,只需注册账号即可免费使用:http://www.simnow.com.cn/

2、在常用下载页面下载一个客户端,方便实时查看模拟交易情况:http://www.simnow.com.cn/static/softwareDownload.action

3、记录个人主页中的InvestrorID,以及产品与服务页面中的服务器地址。配置账户参数时需要使用这些信息。

配置账户信息

FutureAccountInfo是一个期货账户类,包括broker_id(所属期货公司的标识),server_dict(行情与交易服务器地址),reserve_server_dict(备用服务器地址)、investor_id(账户)、password(密码)、app_id(客户端ID,与auth_code对应,监管要求)、auth_code(客户端ID对应的授权码,监管要求)、instrument_id_list(订阅合约列表)、md_page_dir(行情相关文件存放地址)、td_page_dir(交易相关文件存放地址)。

将所有的账户信息存放入my_future_account_info_dict字典中。创建交易接口实例时找到相应的账户信息作为参数。

# -*- coding: utf-8 -*-
 
BASE_LOCATION = "."  # 根目录地址
MD_LOCATION = BASE_LOCATION + "\\MarketData"  # 行情数据地址
TD_LOCATION = BASE_LOCATION + "\\TradingData"  # 交易数据地址
SD_LOCATION = BASE_LOCATION + "\\StrategyData"  # 策略数据地址
 
class FutureAccountInfo:
    def __init__(self, broker_id, server_dict, reserve_server_dict, investor_id, password, app_id, auth_code, instrument_id_list, md_page_dir=MD_LOCATION, td_page_dir=TD_LOCATION):
        self.broker_id = broker_id  # 期货公司BrokerID
        self.server_dict = server_dict  # 服务器地址。TDServer为交易服务器,MDServer为行情服务器。服务器地址格式为"ip:port"
        self.reserve_server_dict = reserve_server_dict  # 备用服务器地址
        self.investor_id = investor_id  # 账户
        self.password = password  # 密码
        self.app_id = app_id  # 认证使用AppID
        self.auth_code = auth_code  # 认证使用授权码
        self.instrument_id_list = instrument_id_list  # 订阅合约列表[]
        self.md_page_dir = md_page_dir  # MdApi流文件存储地址,默认MD_LOCATION
        self.td_page_dir = td_page_dir  # TraderApi流文件存储地址,默认TD_LOCATION
 
my_future_account_info_dict = {
    # 交易时间测试
    'SimNow': FutureAccountInfo(
        broker_id='9999'  # 期货公司BrokerID
        # TDServer为交易服务器,MDServer为行情服务器。服务器地址格式为"ip:port"
        , server_dict={'TDServer': "180.168.146.187:10100", 'MDServer': '180.168.146.187:10110'}
        # 备用服务器地址
        , reserve_server_dict={'电信1': {'TDServer': "180.168.146.187:10100", 'MDServer': '180.168.146.187:10110'},
                               '电信2': {'TDServer': "180.168.146.187:10101", 'MDServer': '180.168.146.187:10111'},
 
                               '其他1': {'TDServer': "180.168.146.187:10130", 'MDServer': '180.168.146.187:10131'},  # 7*24
                               '其他2': {'TDServer': "218.202.237.33:10102", 'MDServer': '218.202.237.33:10112'},  # 移动
                               }
        , investor_id=''  # 账户
        , password=''  # 密码
        , app_id='simnow_client_test'  # 认证使用AppID
        , auth_code='0000000000000000'  # 认证使用授权码
        # 订阅合约列表
        , instrument_id_list=[b'rb2001', b'fu2001'] # 订阅螺纹2001合约和燃料油2001合约
    ),
}

AlgoPlus创建行情接口

MdApi是行情接口,使用时只需要传递账户参数创建一个实例就可以了。下面是一个完整的例子:

from AlgoPlus.CTP.MdApi import MdApi
 
class TickEngine(MdApi):
    # ///深度行情通知
    def OnRtnDepthMarketData(self, pDepthMarketData):
        print(pDepthMarketData)
        # print(f"{pDepthMarketData.InstrumentID}当前最新价:{pDepthMarketData.LastPrice}")
 
if __name__ == '__main__':
    from account_info import my_future_account_info_dict
    future_account = my_future_account_info_dict['SimNow']
    tick_engine = TickEngine(future_account.server_dict['MDServer']
                             , future_account.broker_id
                             , future_account.investor_id
                             , future_account.password
                             , future_account.app_id
                             , future_account.auth_code
                             , future_account.instrument_id_list
                             , None
                             , future_account.md_page_dir)
    tick_engine.Join()

1、从AlgoPlus.CTP.MdApi文件中导入MdApi类。MdApi已对工作流程的前六步进行了封装。

2、TickEngine是MdApi的子类。TickEngine类主要实现收到行情的数据处理算法,示例只将收到的行情打印出来。

3、创建行情接口实例前,需要导入账户信息。示例的账户信息存放在同一个目录下的account_info.py文件中。

4、交易时间运行以上代码就可以将接收到的实时期货行情打印出来。

5、回调函数OnRtnDepthMarketData接收到的pDepthMarketData行情是DepthMarketDataField结构体的实例,在AlgoPlus.CTP.ApiStruct中被定义。以调用属性的方式可以获取行情任意字段的数值,例如pDepthMarketData.LastPrice表示最新价。DepthMarketDataField包括以下字段:

class DepthMarketDataField(BaseField):
    """深度行情"""
    _fields_ = [
        ('TradingDay', c_char * 9)  # ///交易日
        , ('InstrumentID', c_char * 31)  # 合约代码
        , ('ExchangeID', c_char * 9)  # 交易所代码
        , ('ExchangeInstID', c_char * 31)  # 合约在交易所的代码
        , ('LastPrice', c_double)  # 最新价
        , ('PreSettlementPrice', c_double)  # 上次结算价
        , ('PreClosePrice', c_double)  # 昨收盘
        , ('PreOpenInterest', c_double)  # 昨持仓量
        , ('OpenPrice', c_double)  # 今开盘
        , ('HighestPrice', c_double)  # 最高价
        , ('LowestPrice', c_double)  # 最低价
        , ('Volume', c_int)  # 数量
        , ('Turnover', c_double)  # 成交金额
        , ('OpenInterest', c_double)  # 持仓量
        , ('ClosePrice', c_double)  # 今收盘
        , ('SettlementPrice', c_double)  # 本次结算价
        , ('UpperLimitPrice', c_double)  # 涨停板价
        , ('LowerLimitPrice', c_double)  # 跌停板价
        , ('PreDelta', c_double)  # 昨虚实度
        , ('CurrDelta', c_double)  # 今虚实度
        , ('UpdateTime', c_char * 9)  # 最后修改时间
        , ('UpdateMillisec', c_int)  # 最后修改毫秒
        , ('BidPrice1', c_double)  # 申买价一
        , ('BidVolume1', c_int)  # 申买量一
        , ('AskPrice1', c_double)  # 申卖价一
        , ('AskVolume1', c_int)  # 申卖量一
        , ('BidPrice2', c_double)  # 申买价二
        , ('BidVolume2', c_int)  # 申买量二
        , ('AskPrice2', c_double)  # 申卖价二
        , ('AskVolume2', c_int)  # 申卖量二
        , ('BidPrice3', c_double)  # 申买价三
        , ('BidVolume3', c_int)  # 申买量三
        , ('AskPrice3', c_double)  # 申卖价三
        , ('AskVolume3', c_int)  # 申卖量三
        , ('BidPrice4', c_double)  # 申买价四
        , ('BidVolume4', c_int)  # 申买量四
        , ('AskPrice4', c_double)  # 申卖价四
        , ('AskVolume4', c_int)  # 申卖量四
        , ('BidPrice5', c_double)  # 申买价五
        , ('BidVolume5', c_int)  # 申买量五
        , ('AskPrice5', c_double)  # 申卖价五
        , ('AskVolume5', c_int)  # 申卖量五
        , ('AveragePrice', c_double)  # 当日均价
        , ('ActionDay', c_char * 9)  # 业务日期
    ]

其他

项目已在 Github 和码云上开源。

本文分享自微信公众号 - Crossin的编程教室(crossincode)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 干货 | 27 个问题,告诉你 Python 为什么如此设计?

    https://docs.python.org/zh-cn/3.7/faq/design.html

    小小詹同学
  • Pandas中文官档~基础用法2

    Series 与 DataFrame 支持大量计算描述性统计的方法与操作。这些方法大部分都是 sum()、mean()、quantile() 等聚合函数,其输出...

    小小詹同学
  • 面试官:new一个对象有哪两个过程?

    Java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。加载并初始化类完成后,再进行对象的创建工作。...

    Java技术栈
  • 静默安装Oracle Database 18c

    系统环境:Oracle Linux 7(OL7) 一、首先设置主机名和ip,修改/etc/hosts (很简单,不赘述) 二、Oracle安装先决条件 执行自...

    孙杰
  • C++打造迷宫游戏,直接上代码

    题目: 通过让游戏角色自动寻找迷宫出口,走出迷宫,来练习C++面向对象之封装的基础知识。迷宫图如下所示,其中X表示墙。

    诸葛青云
  • linux下的 lib文件的学习思考

    某日开发说,一台测试用虚机可以PING通SSH不能连了。运维同学就赶紧去查,SSHD_CONFIG配置文件都正确啊,一点错误都没有,那为什么呢?

    孙杰
  • 解析centos7.3的hostnamectl命令

        hostnamectl 是在 centos7以上版本 中新增加的命令,它是用来修改主机名称的,centos7 修改主机名称会比以往容易许多。

    孙杰
  • Linux防火墙iptables中mark模块分析及编写

    在linux系统中为了更好的实现网络流量的管理,使用了内核的mark来标识网络流量。这样造成了用户层再使用mark来标记多线负载,两种mark会互相覆盖,达不到...

    孙杰
  • 动态规划问题-LeetCode 120(动态内存的传递,函数指针,DP)

    在C++中,函数声明形式为:返回值 函数名称(参数类型 参数名称, 参数类型 参数名称) 其中参数名称可以省略不写,记得最后加分号!

    算法工程师之路
  • Java 自定义 ClassLoader 实现 JVM 类加载

    为了能够实现类加载,并展示效果,定义一个Hello类,再为其定义一个sayHello()方法,加载Hello类之后,调用它的sayHello()方法。

    芋道源码

扫码关注云+社区

领取腾讯云代金券