盈透证券(IB) TWS API使用教程(python版本)及一个简单的程序化交易策略
作者:yunjinqi 类别:
日期:2022-01-03 21:16:04
阅读:3136 次 消耗积分:0 分
使用前面的TWS API写成的simpleClient做了一个简单的策略,供大家参考。不要用于实盘,大概率会亏损。
TWS API相关的教程
【TWS API使用教程1】—如何在自己创建的client和TWS之间创建一个连接,并请求当前的时间
【TWS API使用教程2】—如何使用 TWS API在ubuntu和windows上分别设置contract、获取contract详细信息、设置order、下单、获取持仓信息、获取账户信息
【TWS API使用教程3】—如何使用TWS API从盈透证券中设置contract及获取contract的信息?
【TWS API使用教程4】—如何使用TWS API在盈透证券中设置order?
【TWS API使用教程5】—如何使用TWS API在盈透证券中下单(place order)、获取订单信息、获取持仓、获取账户汇总信息?
【TWS API使用教程6】—如何使用TWS API在盈透证券中获取数据?
【TWS API 使用教程7】如何使用TWS API 从盈透证券中筛选满足一定条件的contract?
【TWS API 使用教程8】一个基于TWS API的简单的程序化策略
TWS API 官方API的翻译
1、TWS API的相关配置
2、TWS API接口的使用说明
3、第三方软件中使用TWS API的相关问题
4、TWS API在Excel中的使用
5、TWS API的故障排除和支持
6、TWS API的体系结构和连接
7、TWS API和IB中的金融工具介绍
8、IB和TWS API中的一些基本order
9、IB和TWS API中的一些高级order
10、IB和TWS API中一些常见的关于订单的算法
11、TWS和IB中的streaming市场数据
12、TWS API和IB中的订单管理
13、TWS API和IB中的历史数据
14、TWS API和IB中的账户和投资组合数据
15、TWS API和IB中的期权相关的操作
16、TWS API和IB中关于数字货币的操作
17、TWS API和IB中的财务顾问
18、TWS和IB中的错误处理信息
19、TWS API和IB中的市场扫描仪
20、TWS API和IB中的显示组
21、TWS API和IB中的新闻和公告
TWS 使用系列
【TWS使用系列1】如何从TWS的自选列表中添加/删除自选股?
【TWS使用系列2】如何通过TWS下单及查看账户盈亏
【TWS使用系列3】如何使用市场扫描仪找到美国的小市值股票?
from datetime import datetime
from threading import Thread
import time
import sys
from ibapi.client import EClient, Contract
from ibapi.order import Order
from ibapi.wrapper import EWrapper
from ibapi.utils import iswrapper
from ibapi.scanner import ScannerSubscription
from ibapi.tag_value import TagValue
import pandas as pd
import numpy as np
class SimpleStrategy(EWrapper, EClient):
''' Serves as the client and the wrapper '''
def __init__(self, addr, port, client_id):
EWrapper.__init__(self)
EClient.__init__(self, self)
# 订单号
self.order_id = 0
# 初始化账户
self.funds = 0.0
# 保存价格
self.price_list=[]
# 保存仓位
self.position_size = 0.0
self.avg_cost = 0.0
# 连接到TWS
self.connect(addr, port, client_id)
thread = Thread(target=self.run)
thread.start()
@iswrapper
def currentTime(self, cur_time):
t = datetime.fromtimestamp(cur_time)
print('Current time: {}'.format(t))
@iswrapper
def scannerData(self, reqId: int, rank: int, contractDetails,
distance: str, benchmark: str, projection: str, legsStr: str):
# 处理市场扫描仪数据
super().scannerData(reqId, rank, contractDetails, distance, benchmark,
projection, legsStr)
print("ScannerData. ReqId:", reqId, "Rank:", rank, "Symbol:", contractDetails.contract.symbol,
"SecType:", contractDetails.contract.secType,
"Currency:", contractDetails.contract.currency,
"Distance:", distance, "Benchmark:", benchmark,
"Projection:", projection, "Legs String:", legsStr)
# print("ScannerData. ReqId:", reqId, ScanData(contractDetails.contract, rank, distance, benchmark, projection, legsStr))
@iswrapper
def scannerDataEnd(self, reqId: int):
# 市场扫描仪获取数据结束
super().scannerDataEnd(reqId)
print("ScannerDataEnd. ReqId:", reqId)
@iswrapper
def scannerParameters(self, xml: str):
# 处理获取市场扫描仪的参数
super().scannerParameters(xml)
# df = pd.read_xml(xml)
# print(df)
# df.to_csv("scanner.csv")
open('scanner.txt', 'w',encoding="utf-8").write(xml)
print("ScannerParameters received.")
@iswrapper
def contractDetails(self, reqId, details):
print('Long name: {}'.format(details.longName))
print('Category: {}'.format(details.category))
print('Subcategory: {}'.format(details.subcategory))
print('Contract ID: {}\n'.format(details.contract.conId))
@iswrapper
def contractDetailsEnd(self, reqId):
print('The End')
@iswrapper
def nextValidId(self, order_id):
''' Provides the next order ID '''
self.order_id = order_id print('Order ID: {}'.format(order_id))
@iswrapper
def openOrder(self,order_id, contract, order, state):
''' Called in response to the submitted order '''
print('Order status: '.format(state.status))
print('Commission charged: '.format(state.commission))
@iswrapper
def orderStatus(self,order_id, status, filled, remaining, avgFillPrice, \
permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
''' Check the status of the subnitted order '''
print('Number of filled positions: {}'.format(filled))
print('Average fill price: {}'.format(avgFillPrice))
@iswrapper
def position(self, account: str, contract: Contract, position,
avgCost: float):
super().position(account, contract, position, avgCost)
print("Position.", "Account:", account, "Symbol:", contract.symbol, "SecType:",
contract.secType, "Currency:", contract.currency,
"Position:", position, "Avg cost:", avgCost)
if contract.symbol=="EUR" and contract.secType=="CASH" and contract.currency=="USD":
self.position_size = float(position)
self.avg_cost = avgCost @iswrapper
def positionEnd(self):
super().positionEnd()
print("PositionEnd")
@iswrapper
def accountSummary(self, req_id, account, tag, value, currency):
''' Read information about the account '''
print('req_id : {} Account {}: {} = {}, currency = {}'.format(req_id, account, tag, value , currency))
if tag == 'AvailableFunds':
print('Account {}: available funds = {}'.format(account, value))
self.funds = float(value)
@iswrapper
def tickByTickMidPoint(self, reqId: int, time: int, midPoint: float):
super().tickByTickMidPoint(reqId, time, midPoint)
print("Midpoint. ReqId:", reqId,"Time:", datetime.fromtimestamp(time),"MidPoint:", midPoint)
@iswrapper
def tickByTickBidAsk(self, reqId: int, time: int, bidPrice: float, askPrice: float, bidSize, askSize, tickAttribBidAsk):
super().tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize,askSize, tickAttribBidAsk)
print("BidAsk. ReqId:", reqId,"Time:", datetime.fromtimestamp(time),
"BidPrice:", bidPrice, "AskPrice:", askPrice, "BidSize:", bidSize,"AskSize:", askSize, "BidPastLow:",
tickAttribBidAsk.bidPastLow,"AskPastHigh:", tickAttribBidAsk.askPastHigh)
@iswrapper
def tickByTickAllLast(self, reqId: int, tickType: int, time: int, price: float,
size, tickAtrribLast, exchange: str,specialConditions: str):
super().tickByTickAllLast(reqId, tickType, time, price, size, tickAtrribLast,
exchange, specialConditions)
if tickType == 1:
print("Last.", end='')
else:
print("AllLast.", end='')
print(" ReqId:", reqId,
"Time:", datetime.fromtimestamp(time),
"Price:", price, "Size:", size, "Exch:" , exchange,
"Spec Cond:", specialConditions, "PastLimit:", tickAtrribLast.pastLimit,
"Unreported:",tickAtrribLast.unreported)
@iswrapper
def tickPrice(self, reqId, tickType, price: float,attrib):
super().tickPrice(reqId, tickType, price, attrib)
print("TickPrice. TickerId:", reqId, "tickType:", tickType,
"Price:", price, "CanAutoExecute:", attrib.canAutoExecute,
"PastLimit:", attrib.pastLimit, end=' ')
@iswrapper
def tickSize(self, reqId, tickType, size):
super().tickSize(reqId, tickType, size)
print("TickSize. TickerId:", reqId, "TickType:", tickType, "Size: ", size)
@iswrapper
def tickGeneric(self, reqId, tickType, value: float):
super().tickGeneric(reqId, tickType, value)
print("TickGeneric. TickerId:", reqId, "TickType:", tickType, "Value:", value)
@iswrapper
def realtimeBar(self, reqId, time, open, high, low, close, volume, WAP, count):
''' Called in response to reqRealTimeBars '''
print('realtimeBar:{},time:{} - Opening : {},high :{},low :{},close :{},volume :{},WAP :{},count :{}'.format(reqId,datetime.fromtimestamp(time),open,high,low,close,volume,WAP,count))
self.price_list.append(close)
@iswrapper
def historicalData(self, reqId:int, bar):
print("HistoricalData. ReqId:", reqId, "BarData.", bar)
@iswrapper
def historicalDataEnd(self, reqId: int, start: str, end: str):
super().historicalDataEnd(reqId, start, end)
print("HistoricalDataEnd. ReqId:", reqId, "from", start, "to", end)
@iswrapper
def historicalDataUpdate(self, reqId: int, bar):
print("HistoricalDataUpdate. ReqId:", reqId, "BarData.", bar)
@iswrapper
def histogramData(self, reqId:int, items):
print("HistogramData. ReqId:", reqId, "HistogramDataList:", "[%s]" % "; ".join(map(str, items)))
@iswrapper
def historicalTicks(self, reqId: int, ticks, done: bool):
for tick in ticks:
print("HistoricalTick. ReqId:", reqId, tick)
@iswrapper
def historicalTicksBidAsk(self, reqId: int, ticks,done: bool):
for tick in ticks:
print("HistoricalTickBidAsk. ReqId:", reqId, tick)
@iswrapper
def historicalTicksLast(self, reqId: int, ticks,done: bool):
for tick in ticks:
print("HistoricalTickLast. ReqId:", reqId, tick)
@iswrapper
def fundamentalData(self, reqId, data):
''' Called in response to reqFundamentalData '''
print('Fundamental data: ' + data)
@iswrapper
def error(self, req_id, code, msg):
print('Error {}: {}'.format(code, msg))
def main():
# Create the client and connect to TWS
client = SimpleStrategy('127.0.0.1', 7497, 0)
client.reqCurrentTime()
# Sleep while the request is processed
time.sleep(0.5)
# client.reqContractDetails(1, contract)
# print("self.conn",client.conn)
# print("self.isConnected()",client.isConnected())
time.sleep(2)
# # 设置一个限价单
# order = Order()
# order.action = 'SELL'
# order.totalQuantity = 20000
# order.orderType = 'MKT'
# # 给order获取一个有效的id
# client.reqIds(1)
# time.sleep(0.5)
# # 下单
# client.placeOrder(order_id, contract, order)
# # 获取持仓的信息
# client.reqPositions()
# time.sleep(2)
# # 获取账户的信息
# client.reqAccountSummary(0, 'All', 'AccountType,AvailableFunds')
# time.sleep(2)
# 请求tick数据
# print("获取bidask的数据")
# client.reqTickByTickData(1, contract, 'BidAsk', 1, True)
# time.sleep(5)
# print("获取last的数据")
# client.reqTickByTickData(2, contract, 'Last', 1, False)
# time.sleep(5)
# print("获取alllast的数据")
# client.reqTickByTickData(3, contract, 'AllLast', 1, True)
# time.sleep(10)
# print("获取midpoint的数据")
# client.reqTickByTickData(0, contract, 'MidPoint', 1, True)
# time.sleep(5)
# 请求市场数据
# client.reqMktData(4, contract, '', False, False, [])
# 请求bar数据
# client.reqRealTimeBars(5, contract, 10, 'MIDPOINT', True, [])
# 请求历史数据
# now = datetime.now().strftime("%Y%m%d, %H:%M:%S")
# client.reqHistoricalData(6, contract, now, '2 w', '1 day',
# 'MIDPOINT', False, 1, False, [])
# 请求历史直方图数据
# client.reqHistogramData(7,contract,1,"3 days")
# 请求历史tick数据
# client.reqHistoricalTicks(8,contract,"20211230 21:39:33", "", 10, "TRADES", 1, True, [])
# 请求基础数据
# con = Contract()
# con.symbol = 'IBM'
# con.secType = 'STK'
# con.exchange = 'SMART'
# con.currency = 'USD'
# client.reqFundamentalData(9, con, 'ReportSnapshot', [])
# # 创建一个市场扫描仪
# ss = ScannerSubscription()
# ss.instrument = 'STK'
# ss.locationCode = 'STK.US.MAJOR'
# ss.scanCode = 'HOT_BY_VOLUME'
# # 增加额外的筛选标准
# tagvalues = []
# tagvalues.append(TagValue('avgVolumeAbove', '500000'))
# tagvalues.append(TagValue('marketCapAbove1e6', '10'))
# # 请求过滤后的contract
# client.reqScannerSubscription(0, ss, [], tagvalues)
# # 请求市场扫描仪的过滤参数
# # client.reqScannerParameters()
# 写一个简单的策略,当持仓为0的时候,当价格大于20周期的5秒钟的均线的时候,开多;持仓大于0的时候,当价格小于20周期的5秒钟的均线的时候平多
# 设置交易的contract
contract = Contract()
contract.symbol = "EUR"
contract.secType = "CASH"
contract.currency = "USD"
contract.exchange = "IDEALPRO"
# 设置一个买入市价单
buy_order = Order()
buy_order.action = 'BUY'
buy_order.totalQuantity = 20000
buy_order.orderType = 'MKT'
# 设置一个卖出市价单
sell_order = Order()
sell_order.action = 'SELL'
sell_order.totalQuantity = 20000
sell_order.orderType = 'MKT'
client.reqRealTimeBars(5, contract, 10, 'MIDPOINT', True, [])
# 获取账户持仓
client.reqPositions()
time.sleep(1)
# 获取账户的信息
client.reqAccountSummary(0, 'All', 'AccountType,AvailableFunds')
time.sleep(1)
# 设置运行1000个bar
count = 1000
pre_bar_num = len(client.price_list)
while count>0:
now_bar_num = len(client.price_list)
client.reqCurrentTime()
if now_bar_num>=5 and now_bar_num > pre_bar_num:
count = count-1
now_price = client.price_list[-1]
now_avg_price = sum(client.price_list[-5:])/5
if client.position_size<=0 and now_price>now_avg_price:
print('now_price',now_price,"now_avg_price",now_avg_price)
print("当前账户持仓",client.position_size,"当前bar数目",now_bar_num,"前一个bar数目",pre_bar_num,"count",count)
print("此时应该平空开多")
client.reqIds(1)
client.placeOrder(client.order_id,contract,buy_order)
print("下多单成功")
if client.position_size>0 and now_price<now_avg_price:
print('now_price',now_price,"now_avg_price",now_avg_price)
print("当前账户持仓",client.position_size,"当前bar数目",now_bar_num,"前一个bar数目",pre_bar_num,"count",count)
print("此时应该平空开多")
client.reqIds(1)
client.placeOrder(client.order_id,contract,sell_order)
print("下空单成功")
pre_bar_num = now_bar_num
time.sleep(1)
# # 休息5秒钟,等待数据返回
# time.sleep(5)
# 断开连接
client.disconnect()
if __name__ == '__main__':
main()