Python美股量化交易填坑记录——3.盈透(Interactive Brokers)证券API接口

Python美股量化交易填坑记录------3.盈透(Interactive Brokers)证券API接口

ChristopherShen

​​编辑

北京师范大学 心理学硕士

收录于 · 全球网络券商大全 - 盈透证券

74 人赞同了该文章

之前的2篇帖子介绍了德美利(TD Ameritrade)接口:

行情接口:https://zhuanlan.zhihu.com/p/386828885

交易接口:https://zhuanlan.zhihu.com/p/386870269

现在介绍盈透的API接口。

个人认为盈透的优势有:

  • 老牌券商,各方面的体系完善,信誉好。
  • 佣金相对较低,虽然不能跟免佣券商(德美利、第一证券、尊嘉、羊驼、微牛)比,但比起富途和老虎,还是很良心的了。
  • 盘前交易时段从美东时间4:00a.m.开始,这一点大部分免佣券商都做不到(尊嘉除外)。
  • 接受中国大陆的注册。
  • API历史数据全。(我曾成功下载过SPY过去20年的15分钟k线数据)
  • 融资利率低(年化不到2%:https://www.interactivebrokers.com/cn/index.php?f=2365)。
  • 使用盈透的量化交易者很多,方便初学者求助,比如vnpy(其实给我的体验不太好)。

劣势也很明显:

  • 不免佣。
  • 实时行情需付费购买。
  • API接口不能脱离笨重的客户端。
  • 安全机制很繁琐。

1.前期准备

  • **申请一个新用户。**默认情况下,一个人只有一个账号和一个用户身份。但其实一个账号下可以有多个用户。因为我的需求是手机和机器人同时在线查看盈透行情和下单,所以我需要2个用户身份,否则后上线的设备会把先上线的设备踢下线。发起申请的位置在:管理账户→使用者及访问权限→配置→使用者→添加。等待1个工作日后,新用户生效。(备注:新用户的行情权限需单独购买)
  • **购买实时行情。**入口在:使用者设置→交易平台→市场数据订阅→配置→当前GFIS订阅→配置。下图是我目前购买的行情,一共每月7.25刀。API接口的行情权限不用再单独购买(富途需单独购买:60刀/月,很坑!)。

美股各大指数的行情需单独购买,比如CBOE提供标普和道指,3.5刀每月。

Indices - North America

  • 下载客户端 (win/mac/linux)。比较坑的一点是:如果是linux系统,则必须有图形界面才能安装盈透客户端。所以我目前配置了一台Windows Server 2019的服务器(2G内存都卡!真是笨重!)来安装盈透客户端。盈透客户端有2种:一种是功能比较全的TWS(Trader Workstation),另一种是相对轻便的IB网关(IB Gateway)。(备注:客户端若使用中文,则会导致API报错,建议使用默认的英文界面)

  • 安装python包:ib_insync 。和德美利的情况类似,官网的API文档晦涩难懂,所以某些技术大牛制作了简易的python包。此包的论坛:https://groups.io/g/insync,我在这里得到了很多帮助!此包的简介:https://github.com/erdewit/ib_insync,此包的详细说明文档:https://ib-insync.readthedocs.io/readme.html

    pip install ib_insync


2.连接客户端

德美利的第一步是连接服务器,而盈透的客户端已经连接服务器了,所以我们只需要连接客户端就行。

复制代码
 from ib_insync import *
 ib = IB()
 ib.connect('127.0.0.1', 7496, clientId=991)

上面的端口号(7496)和客户ID(991),需要在客户端中设置,位置是:File→Global Configuration→API→settings,第一项(Enable ActiveX and Socket Clients)要勾选,Read-Only API的勾要去掉,Socket port处我填入7496(可以自定义,之后保持一致就好),Master API client ID处我填入999,即上面代码的客户ID不是主客户ID,但也可以使用,我这么做是因为我为不同的机器人分配了不同的客户ID。


3.行情接口

与德美利机器人的思路一样,行情接口依旧分为策略机器人(下单机器人)与询价机器人(行情机器人)两部分,前者与德美利的策略机器人一样(通过csv发送股票代码给询价机器人,然后从csv中读取询价机器人的询价结果)。(备注:两个机器人必须使用不同的clientId,否则会有冲突)

下面重点介绍盈透的询价机器人:

slm_ib_high()负责在盘前查询昨日的最高价,它的参数contract1下面再讲。它核心的ib.reqHistoricalData()在调取历史数据时会很常用。

复制代码
def slm_ib_high(contract1):
    is_error=1
    while is_error==1:
        try:
            x1=ib.reqHistoricalData(
                contract1,
                endDateTime='',
                durationStr='1 D',
                barSizeSetting='1 day',
                whatToShow='TRADES',
                useRTH=True
                )
            x2=x1[0]
            x3=x2.high
        except:
            Log('slm_ib_high出错,等待2秒后重算!@','#FF0000')
            Sleep(2000)
        else:
            is_error=0
    return(x3)

slm_ib_reqMktData()负责对读取的待查询股票进行逐一查询,并将股价导出至csv,供策略机器人使用,即整体思路与德美利机器人一致。这里需要重点说明的是,盈透接口查询股价时,只是提供股票代码是不够的,还必须提供交易所代码(下文主要使用'SMART')和货币币种(下文使用'USD'),三者组合成为一个合约,即contract1 。

复制代码
def slm_ib_reqMktData(stock_list=stock_list):
    x=slm_read_csv()
    date1=x[0]
    stock_list=x[1]
    n0=len(stock_list)
 
    Log('n=',n0,';stock_list=',stock_list)
    for i in range(n0): # 逐个标的地进行订阅和输出
        code_ob=stock_list[i]
        Log('i=',i,',code_ob=',code_ob)
        is_qihuo=1     if '/' in code_ob else 0
        is_option_ob=1 if '@' in code_ob or '_' in code_ob else 0
 
        # 定义合约:
        if '@' in code_ob:
            n = len(code_ob) + 1
            m = code_ob.find('@')
            code_symbol = code_ob[0:m]
            code_date = code_ob[(m + 1):(m + 9)]
            code_callput = code_ob[(m + 9):(m + 10)]
            code_price = code_ob[(m + 10):n]
            contract1 = Option(code_symbol, code_date, code_price, code_callput, 'SMART')
        elif '_' in code_ob:
            slm_create_one_error('期权非尊嘉格式,请更正!')
        elif '/' in code_ob:
            code_ob2=code_ob[1:]
            cds = ib.reqContractDetails(Future(code_ob2,currency='USD',exchange='NYMEX'))
            df0=util.df(cds).sort_values('contractMonth')
            df1=df0.reset_index(drop=True)
            contract1=df1.contract[1]
        else:
            contract1 = Stock(code_ob, 'SMART', 'USD')
 
        # 检查合约:
        ib.qualifyContracts(contract1)
 
        # 提交订阅:
        ib.reqMktData(contract1)
        ib.sleep(1)
 
        # 获取订阅结果:
        ticker1 = ib.ticker(contract1)
        Log(ticker1)
 
        # 处理订阅结果:
        t1=ticker1.time.timestamp()
 
        import numpy as np
        bidPrice=np.nan_to_num(ticker1.bid) # 买一价
        askPrice=np.nan_to_num(ticker1.ask) # 卖一价
        lastPrice=np.nan_to_num(ticker1.last)
        highPrice=np.nan_to_num(ticker1.high)
 
        while highPrice<=0:
            Log('行情异常:highPrice=',highPrice,'!')
            if is_option_ob==0 and is_qihuo==0 :
                Log('使用reqHistoricalData,获取昨日最高价!')
                highPrice=slm_ib_high(contract1=contract1)
            else:
                Log('期权和期货不可用reqHistoricalData,等待开盘,5秒后再用reqMktData......')
                Sleep(5000)
                ib.reqMktData(contract1)
                ib.sleep(1)
                ticker1 = ib.ticker(contract1)
                t1=ticker1.time.timestamp()
                bidPrice=np.nan_to_num(ticker1.bid) # 买一价
                askPrice=np.nan_to_num(ticker1.ask) # 卖一价
                highPrice=np.nan_to_num(ticker1.high)
            
        y={'code': code_ob,'timestamp':t1, 'BID_PRICE': bidPrice, 'ASK_PRICE': askPrice, 'LAST_PRICE': lastPrice, 'HIGH_PRICE': highPrice}
        import pandas as pd
        y1 = pd.DataFrame(y, index=[0])
 
        code_ob2=code_ob[1:] if is_qihuo==1 else code_ob
        Log('code_ob2=', code_ob2)
        y1.to_csv(wd1+'/fmz/price/'+ date1 + '_' + code_ob2 + '.csv', index=False)
        Log(code_ob,'导出到csv的价格:',y)
 
        t2=pd.to_datetime(t1, unit='s', utc=True).tz_convert('US/Eastern')
        t3=str(t2)[:19]
        y1['datetime']=t3
        columns=y1.columns.values.tolist()
        row1=y1[:1].values.tolist()
        if i==0: rows=row1
        else: rows=rows+row1
 
    return([columns,rows])

就我目前的使用感受来说,盈透接口的稳定性还是要强于德美利的,但也不是完美无瑕,还是有一些不稳定的地方,之后我会加一些try进来。


4.交易接口

slm_buy_sell_ib()负责根据用户输入的价格p_ex和数量amount_ex进行买入或卖出的操作action。跟行情接口一样,需要先把标的调整为合约格式,使用Stock()或Option()。而且需要指定订单ID,为避免订单ID重复,我这里简单使用了一个当前时间的"时分秒+毫秒"的十位数字。

复制代码
def slm_buy_sell_ib(action, amount_ex, p_ex):
    # 调整标的格式:
    if is_option == 1:
        if '@' in code_ex:
            n = len(code_ex) + 1
            m = code_ex.find('@')
            code_symbol = code_ex[0:m]
            code_date = code_ex[(m + 1):(m + 9)]
            code_callput = code_ex[(m + 9):(m + 10)]
            code_price = code_ex[(m + 10):n]
            contract1 = Option(code_symbol, code_date, code_price, code_callput, 'SMART')
        else:
            slm_create_one_error('期权非尊嘉格式,请更正!')
    elif is_option == 0: contract1 = Stock(code_ex, 'SMART', 'USD')
    else: slm_create_one_error('盈透code_ex类型错误!')
 
    ib.qualifyContracts(contract1)
 
    # 设置订单:
    order1_id=slm_time_num()
    if p_ex == -1:
        order1=MarketOrder(action, totalQuantity=amount_ex, lmtPrice=p_ex, orderId=order1_id, outsideRth=False, tif='GTC',usePriceMgmtAlgo=True)
    elif p_ex > 0:
        order1=LimitOrder( action, totalQuantity=amount_ex, lmtPrice=p_ex, orderId=order1_id, outsideRth=True,  tif='GTC',usePriceMgmtAlgo=True)
    else:
        slm_create_one_error('盈透p_ex错误!')
 
    # 下单:
    trade1 = ib.placeOrder(contract1, order1)
    Sleep(2000)  # 等2秒再检查订单状态
    ib.sleep(1)
 
    # 查询订单状态:
    order1_status=0
    while order1_status == 0:
        x1 = ib.trades()
        for i in range(len(x1)):
            x2=x1[i]
            order_id_i=x2.orderStatus.orderId
            if order_id_i==order1_id :
                order1_status_str=x2.orderStatus.status
                Log('订单状态为:', order1_status_str)
        if order1_status_str == 'Filled':
            Log('订单已成交!')
            order1_status=1
            break
        Log('订单编号为:', order1_id, ',交易不成功,等5秒再查订单状态......')
        Sleep(5000)
        ib.sleep(1)
 
    if order1_status == 1:
        Log('订单编号为:', order1_id, ',交易成功!', '#00FF00')
    else:
        Log('订单编号为:', order1_id, ',订单异常,请立即查找原因!@', '#FF0000')
 
    return (order1_id)

备注:

除了本文使用的ib_insync包外,盈透还有一款比较有名的python包:IbPy2。但因为已经停止维护和更新了,所以本文并未介绍。感兴趣的同学可以参考如下资料:

blampe/IbPy

量化交易30天 Day14 - 串接券商API做交易(一) IB + IbPy - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

Python股票自动交易从零开始~第十集~交易自动操作~美股_哔哩哔哩_bilibili

下一篇帖子将介绍羊驼的行情接口(https://alpaca.markets/data)。

相关推荐
Madison-No75 小时前
【C++】探秘string的底层实现
开发语言·c++
java1234_小锋6 小时前
TensorFlow2 Python深度学习 - TensorFlow2框架入门 - 神经网络基础原理
python·深度学习·tensorflow·tensorflow2
JJJJ_iii6 小时前
【深度学习03】神经网络基本骨架、卷积、池化、非线性激活、线性层、搭建网络
网络·人工智能·pytorch·笔记·python·深度学习·神经网络
JJJJ_iii6 小时前
【深度学习05】PyTorch:完整的模型训练套路
人工智能·pytorch·python·深度学习
lly2024066 小时前
AJAX JSON 实例
开发语言
QiZhang | UESTC6 小时前
JAVA算法练习题day27
java·开发语言·c++·算法·leetcode·hot100
坚持就完事了6 小时前
2-C语言中的数据类型
c语言·开发语言
ss2737 小时前
手写MyBatis第96弹:异常断点精准捕获MyBatis深层BUG
java·开发语言·bug·mybatis
程序员小远7 小时前
常用的测试用例
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例