使用前面的 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, advancedOrderRejectJson):
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()