2 Star 1 Fork 1

云金杞/ibapi

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
client.py 141.91 KB
一键复制 编辑 原始数据 按行查看 历史
云金杞 提交于 2022-01-02 20:09 . 更新client注释
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667
"""
Copyright (C) 2019 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
"""
"""
The main class to use from API user's point of view.
It takes care of almost everything:
- implementing the requests
- creating the answer decoder
- creating the connection to TWS/IBGW
The user just needs to override EWrapper methods to receive the answers.
"""
import logging
import queue
import socket
from ibapi import (decoder, reader, comm)
from ibapi.connection import Connection
from ibapi.message import OUT
from ibapi.common import * # @UnusedWildImport
from ibapi.contract import Contract
from ibapi.order import Order
from ibapi.execution import ExecutionFilter
from ibapi.scanner import ScannerSubscription
from ibapi.comm import (make_field, make_field_handle_empty)
from ibapi.utils import (current_fn_name, BadMessage)
from ibapi.errors import * #@UnusedWildImport
from ibapi.server_versions import * # @UnusedWildImport
from ibapi.utils import ClientException
#TODO: use pylint
logger = logging.getLogger(__name__)
class EClient(object):
(DISCONNECTED, CONNECTING, CONNECTED, REDIRECT) = range(4)
#TODO: support redirect !!
def __init__(self, wrapper):
self.msg_queue = queue.Queue()
self.wrapper = wrapper
self.decoder = None
self.reset()
def reset(self):
self.nKeybIntHard = 0
self.conn = None
self.host = None
self.port = None
self.extraAuth = False
self.clientId = None
self.serverVersion_ = None
self.connTime = None
self.connState = None
self.optCapab = ""
self.asynchronous = False
self.reader = None
self.decode = None
self.setConnState(EClient.DISCONNECTED)
self.connectionOptions = None
def setConnState(self, connState):
_connState = self.connState
self.connState = connState
logger.debug("%s connState: %s -> %s" % (id(self), _connState,
self.connState))
def sendMsg(self, msg):
full_msg = comm.make_msg(msg)
logger.info("%s %s %s", "SENDING", current_fn_name(1), full_msg)
self.conn.sendMsg(full_msg)
def logRequest(self, fnName, fnParams):
if logger.isEnabledFor(logging.INFO):
if 'self' in fnParams:
prms = dict(fnParams)
del prms['self']
else:
prms = fnParams
logger.info("REQUEST %s %s" % (fnName, prms))
def startApi(self):
""" Initiates the message exchange between the client application and
the TWS/IB Gateway. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(),
NOT_CONNECTED.msg())
return
try:
VERSION = 2
msg = make_field(OUT.START_API) \
+ make_field(VERSION) \
+ make_field(self.clientId)
if self.serverVersion() >= MIN_SERVER_VER_OPTIONAL_CAPABILITIES:
msg += make_field(self.optCapab)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def connect(self, host, port, clientId):
"""This function must be called before any other. There is no
feedback for a successful connection, but a subsequent attempt to
connect will return the message \"Already connected.\"
host:str - The host name or IP address of the machine where TWS is
running. Leave blank to connect to the local host.
port:int - Must match the port specified in TWS on the
Configure>API>Socket Port field.
clientId:int - A number used to identify this client connection. All
orders placed/modified from this client will be associated with
this client identifier.
Note: Each client MUST connect with a unique clientId."""
try:
self.host = host
self.port = port
self.clientId = clientId
logger.debug("Connecting to %s:%d w/ id:%d", self.host, self.port, self.clientId)
self.conn = Connection(self.host, self.port)
self.conn.connect()
self.setConnState(EClient.CONNECTING)
#TODO: support async mode
v100prefix = "API\0"
v100version = "v%d..%d" % (MIN_CLIENT_VER, MAX_CLIENT_VER)
if self.connectionOptions:
v100version = v100version + " " + self.connectionOptions
#v100version = "v%d..%d" % (MIN_CLIENT_VER, 101)
msg = comm.make_msg(v100version)
logger.debug("msg %s", msg)
msg2 = str.encode(v100prefix, 'ascii') + msg
logger.debug("REQUEST %s", msg2)
self.conn.sendMsg(msg2)
self.decoder = decoder.Decoder(self.wrapper, self.serverVersion())
fields = []
#sometimes I get news before the server version, thus the loop
while len(fields) != 2:
self.decoder.interpret(fields)
buf = self.conn.recvMsg()
if not self.conn.isConnected():
# recvMsg() triggers disconnect() where there's a socket.error or 0 length buffer
# if we don't then drop out of the while loop it infinitely loops
logger.warning('Disconnected; resetting connection')
self.reset()
return
logger.debug("ANSWER %s", buf)
if len(buf) > 0:
(size, msg, rest) = comm.read_msg(buf)
logger.debug("size:%d msg:%s rest:%s|", size, msg, rest)
fields = comm.read_fields(msg)
logger.debug("fields %s", fields)
else:
fields = []
(server_version, conn_time) = fields
server_version = int(server_version)
logger.debug("ANSWER Version:%d time:%s", server_version, conn_time)
self.connTime = conn_time
self.serverVersion_ = server_version
self.decoder.serverVersion = self.serverVersion()
self.setConnState(EClient.CONNECTED)
self.reader = reader.EReader(self.conn, self.msg_queue)
self.reader.start() # start thread
logger.info("sent startApi")
self.startApi()
self.wrapper.connectAck()
except socket.error:
if self.wrapper:
self.wrapper.error(NO_VALID_ID, CONNECT_FAIL.code(), CONNECT_FAIL.msg())
logger.info("could not connect")
self.disconnect()
def disconnect(self):
"""Call this function to terminate the connections with TWS.
Calling this function does not cancel orders that have already been
sent."""
self.setConnState(EClient.DISCONNECTED)
if self.conn is not None:
logger.info("disconnecting")
self.conn.disconnect()
self.wrapper.connectionClosed()
self.reset()
def isConnected(self):
"""Call this function to check if there is a connection with TWS"""
connConnected = self.conn and self.conn.isConnected()
logger.debug("%s isConn: %s, connConnected: %s" % (id(self),
self.connState, str(connConnected)))
return EClient.CONNECTED == self.connState and connConnected
def keyboardInterrupt(self):
#intended to be overloaded
pass
def keyboardInterruptHard(self):
self.nKeybIntHard += 1
if self.nKeybIntHard > 5:
raise SystemExit()
def setConnectionOptions(self, opts):
self.connectionOptions = opts
def msgLoopTmo( self ):
#intended to be overloaded
pass
def msgLoopRec( self ):
#intended to be overloaded
pass
def run(self):
"""This is the function that has the message loop."""
try:
while self.isConnected() or not self.msg_queue.empty():
try:
try:
text = self.msg_queue.get(block=True, timeout=0.2)
if len(text) > MAX_MSG_LEN:
self.wrapper.error(NO_VALID_ID, BAD_LENGTH.code(),
"%s:%d:%s" % (BAD_LENGTH.msg(), len(text), text))
break
except queue.Empty:
logger.debug("queue.get: empty")
self.msgLoopTmo()
else:
fields = comm.read_fields(text)
logger.debug("fields %s", fields)
self.decoder.interpret(fields)
self.msgLoopRec()
except (KeyboardInterrupt, SystemExit):
logger.info("detected KeyboardInterrupt, SystemExit")
self.keyboardInterrupt()
self.keyboardInterruptHard()
except BadMessage:
logger.info("BadMessage")
logger.debug("conn:%d queue.sz:%d",
self.isConnected(),
self.msg_queue.qsize())
finally:
self.disconnect()
def reqCurrentTime(self):
"""Asks the current system time on the server side."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(),
NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_CURRENT_TIME) \
+ make_field(VERSION)
self.sendMsg(msg)
def serverVersion(self):
"""Returns the version of the TWS instance to which the API
application is connected."""
return self.serverVersion_
def setServerLogLevel(self, logLevel:int):
"""The default detail level is ERROR. For more details, see API
Logging."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(),
NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.SET_SERVER_LOGLEVEL) \
+ make_field(VERSION) \
+ make_field(logLevel)
self.sendMsg(msg)
def twsConnectionTime(self):
"""Returns the time the API application made a connection to TWS."""
return self.connTime
##########################################################################
################## Market Data
##########################################################################
def reqMktData(self, reqId:TickerId, contract:Contract,
genericTickList:str, snapshot:bool, regulatorySnapshot: bool,
mktDataOptions:TagValueList):
"""Call this function to request market data. The market data
will be returned by the tickPrice and tickSize events.
reqId: TickerId - The ticker id. Must be a unique value. When the
market data returns, it will be identified by this tag. This is
also used when canceling the market data.
contract:Contract - This structure contains a description of the
Contractt for which market data is being requested.
genericTickList:str - A commma delimited list of generic tick types.
Tick types can be found in the Generic Tick Types page.
Prefixing w/ 'mdoff' indicates that top mkt data shouldn't tick.
You can specify the news source by postfixing w/ ':<source>.
Example: "mdoff,292:FLY+BRF"
snapshot:bool - Check to return a single snapshot of Market data and
have the market data subscription cancel. Do not enter any
genericTicklist values if you use snapshots.
regulatorySnapshot: bool - With the US Value Snapshot Bundle for stocks,
regulatory snapshots are available for 0.01 USD each.
mktDataOptions:TagValueList - For internal use only.
Use default value XYZ. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(),
NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_DELTA_NEUTRAL:
if contract.deltaNeutralContract:
self.wrapper.error(reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support delta-neutral orders.")
return
if self.serverVersion() < MIN_SERVER_VER_REQ_MKT_DATA_CONID:
if contract.conId > 0:
self.wrapper.error(reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support conId parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support tradingClass parameter in reqMktData.")
return
try:
VERSION = 11
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_MKT_DATA),
make_field(VERSION),
make_field(reqId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_REQ_MKT_DATA_CONID:
flds += [make_field(contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier), # srv v15 and above
make_field(contract.exchange),
make_field(contract.primaryExchange), # srv v14 and above
make_field(contract.currency),
make_field(contract.localSymbol) ] # srv v2 and above
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
# Send combo legs for BAG requests (srv v8 and above)
if contract.secType == "BAG":
comboLegsCount = len(contract.comboLegs) if contract.comboLegs else 0
flds += [make_field(comboLegsCount),]
for comboLeg in contract.comboLegs:
flds += [make_field(comboLeg.conId),
make_field( comboLeg.ratio),
make_field( comboLeg.action),
make_field( comboLeg.exchange)]
if self.serverVersion() >= MIN_SERVER_VER_DELTA_NEUTRAL:
if contract.deltaNeutralContract:
flds += [make_field(True),
make_field(contract.deltaNeutralContract.conId),
make_field(contract.deltaNeutralContract.delta),
make_field(contract.deltaNeutralContract.price)]
else:
flds += [make_field(False),]
flds += [make_field(genericTickList), # srv v31 and above
make_field(snapshot)] # srv v35 and above
if self.serverVersion() >= MIN_SERVER_VER_REQ_SMART_COMPONENTS:
flds += [make_field(regulatorySnapshot),]
# send mktDataOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
#current doc says this part if for "internal use only" -> won't support it
if mktDataOptions:
raise NotImplementedError("not supported")
mktDataOptionsStr = ""
flds += [make_field(mktDataOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelMktData(self, reqId:TickerId):
"""After calling this function, market data for the specified id
will stop flowing.
reqId: TickerId - The ID that was specified in the call to
reqMktData(). """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 2
# send req mkt data msg
flds = []
flds += [make_field(OUT.CANCEL_MKT_DATA),
make_field(VERSION),
make_field(reqId)]
msg = "".join(flds)
self.sendMsg(msg)
def reqMarketDataType(self, marketDataType:int):
"""The API can receive frozen market data from Trader
Workstation. Frozen market data is the last data recorded in our system.
During normal trading hours, the API receives real-time market data. If
you use this function, you are telling TWS to automatically switch to
frozen market data after the close. Then, before the opening of the next
trading day, market data will automatically switch back to real-time
market data.
marketDataType:int - 1 for real-time streaming market data or 2 for
frozen market data"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_MARKET_DATA_TYPE:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support market data type requests.")
return
VERSION = 1
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_MARKET_DATA_TYPE),
make_field(VERSION),
make_field(marketDataType)]
msg = "".join(flds)
self.sendMsg(msg)
def reqSmartComponents(self, reqId: int, bboExchange: str):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_SMART_COMPONENTS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support smart components request.")
return
try:
msg = make_field(OUT.REQ_SMART_COMPONENTS) \
+ make_field(reqId) \
+ make_field(bboExchange)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqMarketRule(self, marketRuleId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_MARKET_RULES:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support market rule requests.")
return
msg = make_field(OUT.REQ_MARKET_RULE) \
+ make_field(marketRuleId)
self.sendMsg(msg)
def reqTickByTickData(self, reqId: int, contract: Contract, tickType: str,
numberOfTicks: int, ignoreSize: bool):
# reqId是请求数据的id号,方便后续操作使用
# contract是IB的合约
# tickType是获取数据的种类,有四种取值:BidAsk,Last,AllLast,MidPoint
# numberOfTicks 是获取的tick的数目
# ignoreSize 是否忽略成交量,只包含价格
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TICK_BY_TICK:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tick-by-tick data requests.")
return
if self.serverVersion() < MIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support ignoreSize and numberOfTicks parameters "
"in tick-by-tick data requests.")
return
try:
msg = make_field(OUT.REQ_TICK_BY_TICK_DATA)\
+ make_field(reqId) \
+ make_field(contract.conId) \
+ make_field(contract.symbol) \
+ make_field(contract.secType) \
+ make_field(contract.lastTradeDateOrContractMonth) \
+ make_field(contract.strike) \
+ make_field(contract.right) \
+ make_field(contract.multiplier) \
+ make_field(contract.exchange) \
+ make_field(contract.primaryExchange) \
+ make_field(contract.currency) \
+ make_field(contract.localSymbol) \
+ make_field(contract.tradingClass) \
+ make_field(tickType)
if self.serverVersion() >= MIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE:
msg += make_field(numberOfTicks) \
+ make_field(ignoreSize)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelTickByTickData(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TICK_BY_TICK:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tick-by-tick data requests.")
return
msg = make_field(OUT.CANCEL_TICK_BY_TICK_DATA) \
+ make_field(reqId)
self.sendMsg(msg)
##########################################################################
################## Options
##########################################################################
def calculateImpliedVolatility(self, reqId:TickerId, contract:Contract,
optionPrice:float, underPrice:float,
implVolOptions:TagValueList):
"""Call this function to calculate volatility for a supplied
option price and underlying price. Result will be delivered
via EWrapper.tickOptionComputation()
reqId:TickerId - The request id.
contract:Contract - Describes the contract.
optionPrice:double - The price of the option.
underPrice:double - Price of the underlying."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support calculateImpliedVolatility req.")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tradingClass parameter in calculateImpliedVolatility.")
return
try:
VERSION = 3
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_CALC_IMPLIED_VOLAT),
make_field(VERSION),
make_field(reqId),
# send contract fields
make_field(contract.conId),
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
flds += [ make_field( optionPrice),
make_field( underPrice)]
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
implVolOptStr = ""
tagValuesCount = len(implVolOptions) if implVolOptions else 0
if implVolOptions:
for implVolOpt in implVolOptions:
implVolOptStr += str(implVolOpt)
flds += [make_field(tagValuesCount),
make_field(implVolOptStr)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelCalculateImpliedVolatility(self, reqId:TickerId):
"""Call this function to cancel a request to calculate
volatility for a supplied option price and underlying price.
reqId:TickerId - The request ID. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support calculateImpliedVolatility req.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_CALC_IMPLIED_VOLAT) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
def calculateOptionPrice(self, reqId:TickerId, contract:Contract,
volatility:float, underPrice:float,
optPrcOptions:TagValueList):
"""Call this function to calculate option price and greek values
for a supplied volatility and underlying price.
reqId:TickerId - The ticker ID.
contract:Contract - Describes the contract.
volatility:double - The volatility.
underPrice:double - Price of the underlying."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support calculateImpliedVolatility req.")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tradingClass parameter in calculateImpliedVolatility.")
return
try:
VERSION = 3
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_CALC_OPTION_PRICE),
make_field(VERSION),
make_field(reqId),
# send contract fields
make_field(contract.conId),
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
flds += [ make_field(volatility),
make_field(underPrice)]
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
optPrcOptStr = ""
tagValuesCount = len(optPrcOptions) if optPrcOptions else 0
if optPrcOptions:
for implVolOpt in optPrcOptions:
optPrcOptStr += str(implVolOpt)
flds += [make_field(tagValuesCount),
make_field(optPrcOptStr)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelCalculateOptionPrice(self, reqId:TickerId):
"""Call this function to cancel a request to calculate the option
price and greek values for a supplied volatility and underlying price.
reqId:TickerId - The request ID. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support calculateImpliedVolatility req.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_CALC_OPTION_PRICE) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
def exerciseOptions(self, reqId:TickerId, contract:Contract,
exerciseAction:int, exerciseQuantity:int,
account:str, override:int):
"""reqId:TickerId - The ticker id. multipleust be a unique value.
contract:Contract - This structure contains a description of the
contract to be exercised
exerciseAction:int - Specifies whether you want the option to lapse
or be exercised.
Values are 1 = exercise, 2 = lapse.
exerciseQuantity:int - The quantity you want to exercise.
account:str - destination account
override:int - Specifies whether your setting will override the system's
natural action. For example, if your action is "exercise" and the
option is not in-the-money, by natural action the option would not
exercise. If you have override set to "yes" the natural action would
be overridden and the out-of-the money option would be exercised.
Values are: 0 = no, 1 = yes."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support conId, multiplier, tradingClass parameter in exerciseOptions.")
return
try:
VERSION = 2
# send req mkt data msg
flds = []
flds += [make_field(OUT.EXERCISE_OPTIONS),
make_field(VERSION),
make_field(reqId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
flds += [make_field(exerciseAction),
make_field(exerciseQuantity),
make_field(account),
make_field(override)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
#########################################################################
################## Orders
########################################################################
def placeOrder(self, orderId:OrderId , contract:Contract, order:Order):
"""Call this function to place an order. The order status will
be returned by the orderStatus event.
orderId:OrderId - The order id. You must specify a unique value. When the
order START_APItus returns, it will be identified by this tag.
This tag is also used when canceling the order.
contract:Contract - This structure contains a description of the
contract which is being traded.
order:Order - This structure contains the details of tradedhe order.
Note: Each client MUST connect with a unique clientId."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(orderId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_DELTA_NEUTRAL:
if contract.deltaNeutralContract:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support delta-neutral orders.")
return
if self.serverVersion() < MIN_SERVER_VER_SCALE_ORDERS2:
if order.scaleSubsLevelSize != UNSET_INTEGER:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support Subsequent Level Size for Scale orders.")
return
if self.serverVersion() < MIN_SERVER_VER_ALGO_ORDERS:
if order.algoStrategy:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support algo orders.")
return
if self.serverVersion() < MIN_SERVER_VER_NOT_HELD:
if order.notHeld:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support notHeld parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_SEC_ID_TYPE:
if contract.secIdType or contract.secId:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support secIdType and secId parameters.")
return
if self.serverVersion() < MIN_SERVER_VER_PLACE_ORDER_CONID:
if contract.conId and contract.conId > 0:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support conId parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_SSHORTX:
if order.exemptCode != -1:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support exemptCode parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_SSHORTX:
if contract.comboLegs:
for comboLeg in contract.comboLegs:
if comboLeg.exemptCode != -1:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support exemptCode parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_HEDGE_ORDERS:
if order.hedgeType:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support hedge orders.")
return
if self.serverVersion() < MIN_SERVER_VER_OPT_OUT_SMART_ROUTING:
if order.optOutSmartRouting:
self.wrapper.error( orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support optOutSmartRouting parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_DELTA_NEUTRAL_CONID:
if order.deltaNeutralConId > 0 \
or order.deltaNeutralSettlingFirm \
or order.deltaNeutralClearingAccount \
or order.deltaNeutralClearingIntent:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support deltaNeutral parameters: ConId, SettlingFirm, ClearingAccount, ClearingIntent.")
return
if self.serverVersion() < MIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE:
if order.deltaNeutralOpenClose \
or order.deltaNeutralShortSale \
or order.deltaNeutralShortSaleSlot > 0 \
or order.deltaNeutralDesignatedLocation:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support deltaNeutral parameters: OpenClose, ShortSale, ShortSaleSlot, DesignatedLocation.")
return
if self.serverVersion() < MIN_SERVER_VER_SCALE_ORDERS3:
if order.scalePriceIncrement > 0 and order.scalePriceIncrement != UNSET_DOUBLE:
if order.scalePriceAdjustValue != UNSET_DOUBLE \
or order.scalePriceAdjustInterval != UNSET_INTEGER \
or order.scaleProfitOffset != UNSET_DOUBLE \
or order.scaleAutoReset \
or order.scaleInitPosition != UNSET_INTEGER \
or order.scaleInitFillQty != UNSET_INTEGER \
or order.scaleRandomPercent:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support Scale order parameters: PriceAdjustValue, PriceAdjustInterval, " +
"ProfitOffset, AutoReset, InitPosition, InitFillQty and RandomPercent")
return
if self.serverVersion() < MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE and contract.secType == "BAG":
if order.orderComboLegs:
for orderComboLeg in order.orderComboLegs:
if orderComboLeg.price != UNSET_DOUBLE:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support per-leg prices for order combo legs.")
return
if self.serverVersion() < MIN_SERVER_VER_TRAILING_PERCENT:
if order.trailingPercent != UNSET_DOUBLE:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support trailing percent parameter")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tradingClass parameter in placeOrder.")
return
if self.serverVersion() < MIN_SERVER_VER_SCALE_TABLE:
if order.scaleTable or order.activeStartTime or order.activeStopTime:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support scaleTable, activeStartTime and activeStopTime parameters")
return
if self.serverVersion() < MIN_SERVER_VER_ALGO_ID:
if order.algoId:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support algoId parameter")
return
if self.serverVersion() < MIN_SERVER_VER_ORDER_SOLICITED:
if order.solicited:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support order solicited parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_MODELS_SUPPORT:
if order.modelCode:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support model code parameter.")
return
if self.serverVersion() < MIN_SERVER_VER_EXT_OPERATOR:
if order.extOperator:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support ext operator parameter")
return
if self.serverVersion() < MIN_SERVER_VER_SOFT_DOLLAR_TIER:
if order.softDollarTier.name or order.softDollarTier.val:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support soft dollar tier")
return
if self.serverVersion() < MIN_SERVER_VER_CASH_QTY:
if order.cashQty:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support cash quantity parameter")
return
if self.serverVersion() < MIN_SERVER_VER_DECISION_MAKER and (order.mifid2DecisionMaker != "" or order.mifid2DecisionAlgo != ""):
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support MIFID II decision maker parameters")
return
if self.serverVersion() < MIN_SERVER_VER_MIFID_EXECUTION and (order.mifid2ExecutionTrader != "" or order.mifid2ExecutionAlgo != ""):
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support MIFID II execution parameters")
return
if self.serverVersion() < MIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE and order.dontUseAutoPriceForHedge:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support dontUseAutoPriceForHedge parameter")
return
if self.serverVersion() < MIN_SERVER_VER_ORDER_CONTAINER and order.isOmsContainer:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support oms container parameter")
return
if self.serverVersion() < MIN_SERVER_VER_PRICE_MGMT_ALGO and order.usePriceMgmtAlgo:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() + " It does not support Use price management algo requests")
return
if self.serverVersion() < MIN_SERVER_VER_DURATION and order.duration != UNSET_INTEGER:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() + " It does not support duration attribute")
return
if self.serverVersion() < MIN_SERVER_VER_POST_TO_ATS and order.postToAts != UNSET_INTEGER:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() + " It does not support postToAts attribute")
return
if self.serverVersion() < MIN_SERVER_VER_AUTO_CANCEL_PARENT and order.autoCancelParent:
self.wrapper.error(orderId, UPDATE_TWS.code(), UPDATE_TWS.msg() + " It does not support autoCancelParent attribute")
return
try:
VERSION = 27 if (self.serverVersion() < MIN_SERVER_VER_NOT_HELD) else 45
# send place order msg
flds = []
flds += [make_field(OUT.PLACE_ORDER)]
if self.serverVersion() < MIN_SERVER_VER_ORDER_CONTAINER:
flds += [make_field(VERSION)]
flds += [make_field(orderId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_PLACE_ORDER_CONID:
flds.append(make_field( contract.conId))
flds += [make_field( contract.symbol),
make_field( contract.secType),
make_field( contract.lastTradeDateOrContractMonth),
make_field( contract.strike),
make_field( contract.right),
make_field( contract.multiplier), # srv v15 and above
make_field( contract.exchange),
make_field( contract.primaryExchange), # srv v14 and above
make_field( contract.currency),
make_field( contract.localSymbol)] # srv v2 and above
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds.append(make_field( contract.tradingClass))
if self.serverVersion() >= MIN_SERVER_VER_SEC_ID_TYPE:
flds += [make_field( contract.secIdType),
make_field( contract.secId)]
# send main order fields
flds.append(make_field( order.action))
if self.serverVersion() >= MIN_SERVER_VER_FRACTIONAL_POSITIONS:
flds.append(make_field(order.totalQuantity))
else:
flds.append(make_field(int(order.totalQuantity)))
flds.append(make_field(order.orderType))
if self.serverVersion() < MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE:
flds.append(make_field(
order.lmtPrice if order.lmtPrice != UNSET_DOUBLE else 0))
else:
flds.append(make_field_handle_empty( order.lmtPrice))
if self.serverVersion() < MIN_SERVER_VER_TRAILING_PERCENT:
flds.append(make_field(
order.auxPrice if order.auxPrice != UNSET_DOUBLE else 0))
else:
flds.append(make_field_handle_empty( order.auxPrice))
# send extended order fields
flds += [make_field( order.tif),
make_field( order.ocaGroup),
make_field( order.account),
make_field( order.openClose),
make_field( order.origin),
make_field( order.orderRef),
make_field( order.transmit),
make_field( order.parentId), # srv v4 and above
make_field( order.blockOrder), # srv v5 and above
make_field( order.sweepToFill), # srv v5 and above
make_field( order.displaySize), # srv v5 and above
make_field( order.triggerMethod), # srv v5 and above
make_field( order.outsideRth), # srv v5 and above
make_field( order.hidden)] # srv v7 and above
# Send combo legs for BAG requests (srv v8 and above)
if contract.secType == "BAG":
comboLegsCount = len(contract.comboLegs) if contract.comboLegs else 0
flds.append(make_field(comboLegsCount))
if comboLegsCount > 0:
for comboLeg in contract.comboLegs:
assert comboLeg
flds += [make_field(comboLeg.conId),
make_field( comboLeg.ratio),
make_field( comboLeg.action),
make_field( comboLeg.exchange),
make_field( comboLeg.openClose),
make_field( comboLeg.shortSaleSlot), #srv v35 and above
make_field( comboLeg.designatedLocation)] # srv v35 and above
if self.serverVersion() >= MIN_SERVER_VER_SSHORTX_OLD:
flds.append(make_field(comboLeg.exemptCode))
# Send order combo legs for BAG requests
if self.serverVersion() >= MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE and contract.secType == "BAG":
orderComboLegsCount = len(order.orderComboLegs) if order.orderComboLegs else 0
flds.append(make_field( orderComboLegsCount))
if orderComboLegsCount:
for orderComboLeg in order.orderComboLegs:
assert orderComboLeg
flds.append(make_field_handle_empty( orderComboLeg.price))
if self.serverVersion() >= MIN_SERVER_VER_SMART_COMBO_ROUTING_PARAMS and contract.secType == "BAG":
smartComboRoutingParamsCount = len(order.smartComboRoutingParams) if order.smartComboRoutingParams else 0
flds.append(make_field( smartComboRoutingParamsCount))
if smartComboRoutingParamsCount > 0:
for tagValue in order.smartComboRoutingParams:
flds += [make_field(tagValue.tag),
make_field(tagValue.value)]
######################################################################
# Send the shares allocation.
#
# This specifies the number of order shares allocated to each Financial
# Advisor managed account. The format of the allocation string is as
# follows:
# <account_code1>/<number_shares1>,<account_code2>/<number_shares2>,...N
# E.g.
# To allocate 20 shares of a 100 share order to account 'U101' and the
# residual 80 to account 'U203' enter the following share allocation string:
# U101/20,U203/80
#####################################################################
# send deprecated sharesAllocation field
flds += [make_field( ""), # srv v9 and above
make_field( order.discretionaryAmt), # srv v10 and above
make_field( order.goodAfterTime), # srv v11 and above
make_field( order.goodTillDate), # srv v12 and above
make_field( order.faGroup), # srv v13 and above
make_field( order.faMethod), # srv v13 and above
make_field( order.faPercentage), # srv v13 and above
make_field( order.faProfile)] # srv v13 and above
if self.serverVersion() >= MIN_SERVER_VER_MODELS_SUPPORT:
flds.append(make_field( order.modelCode))
# institutional short saleslot data (srv v18 and above)
flds += [make_field( order.shortSaleSlot), # 0 for retail, 1 or 2 for institutions
make_field( order.designatedLocation)] # populate only when shortSaleSlot = 2.
if self.serverVersion() >= MIN_SERVER_VER_SSHORTX_OLD:
flds.append(make_field( order.exemptCode))
# not needed anymore
#bool isVolOrder = (order.orderType.CompareNoCase("VOL") == 0)
# srv v19 and above fields
flds.append(make_field( order.ocaType))
#if( self.serverVersion() < 38) {
# will never happen
# send( /* order.rthOnly */ false);
#}
flds += [make_field( order.rule80A),
make_field( order.settlingFirm),
make_field( order.allOrNone),
make_field_handle_empty( order.minQty),
make_field_handle_empty( order.percentOffset),
make_field( False),
make_field( False),
make_field_handle_empty( UNSET_DOUBLE),
make_field( order.auctionStrategy), # AUCTION_MATCH, AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT
make_field_handle_empty( order.startingPrice),
make_field_handle_empty( order.stockRefPrice),
make_field_handle_empty( order.delta),
make_field_handle_empty( order.stockRangeLower),
make_field_handle_empty( order.stockRangeUpper),
make_field( order.overridePercentageConstraints), #srv v22 and above
# Volatility orders (srv v26 and above)
make_field_handle_empty( order.volatility),
make_field_handle_empty( order.volatilityType),
make_field( order.deltaNeutralOrderType), # srv v28 and above
make_field_handle_empty( order.deltaNeutralAuxPrice)] # srv v28 and above
if self.serverVersion() >= MIN_SERVER_VER_DELTA_NEUTRAL_CONID and order.deltaNeutralOrderType:
flds += [make_field( order.deltaNeutralConId),
make_field( order.deltaNeutralSettlingFirm),
make_field( order.deltaNeutralClearingAccount),
make_field( order.deltaNeutralClearingIntent)]
if self.serverVersion() >= MIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE and order.deltaNeutralOrderType:
flds += [make_field( order.deltaNeutralOpenClose),
make_field( order.deltaNeutralShortSale),
make_field( order.deltaNeutralShortSaleSlot),
make_field( order.deltaNeutralDesignatedLocation)]
flds += [make_field( order.continuousUpdate),
make_field_handle_empty( order.referencePriceType),
make_field_handle_empty( order.trailStopPrice)] # srv v30 and above
if self.serverVersion() >= MIN_SERVER_VER_TRAILING_PERCENT:
flds.append(make_field_handle_empty( order.trailingPercent))
# SCALE orders
if self.serverVersion() >= MIN_SERVER_VER_SCALE_ORDERS2:
flds += [make_field_handle_empty( order.scaleInitLevelSize),
make_field_handle_empty( order.scaleSubsLevelSize)]
else:
# srv v35 and above)
flds += [make_field( ""), # for not supported scaleNumComponents
make_field_handle_empty(order.scaleInitLevelSize)] # for scaleComponentSize
flds.append(make_field_handle_empty( order.scalePriceIncrement))
if self.serverVersion() >= MIN_SERVER_VER_SCALE_ORDERS3 \
and order.scalePriceIncrement != UNSET_DOUBLE \
and order.scalePriceIncrement > 0.0:
flds += [make_field_handle_empty( order.scalePriceAdjustValue),
make_field_handle_empty( order.scalePriceAdjustInterval),
make_field_handle_empty( order.scaleProfitOffset),
make_field( order.scaleAutoReset),
make_field_handle_empty( order.scaleInitPosition),
make_field_handle_empty( order.scaleInitFillQty),
make_field( order.scaleRandomPercent)]
if self.serverVersion() >= MIN_SERVER_VER_SCALE_TABLE:
flds += [make_field( order.scaleTable),
make_field( order.activeStartTime),
make_field( order.activeStopTime)]
# HEDGE orders
if self.serverVersion() >= MIN_SERVER_VER_HEDGE_ORDERS:
flds.append(make_field( order.hedgeType))
if order.hedgeType:
flds.append(make_field( order.hedgeParam))
if self.serverVersion() >= MIN_SERVER_VER_OPT_OUT_SMART_ROUTING:
flds.append(make_field( order.optOutSmartRouting))
if self.serverVersion() >= MIN_SERVER_VER_PTA_ORDERS:
flds += [make_field( order.clearingAccount),
make_field( order.clearingIntent)]
if self.serverVersion() >= MIN_SERVER_VER_NOT_HELD:
flds.append(make_field( order.notHeld))
if self.serverVersion() >= MIN_SERVER_VER_DELTA_NEUTRAL:
if contract.deltaNeutralContract:
flds += [make_field(True),
make_field(contract.deltaNeutralContract.conId),
make_field(contract.deltaNeutralContract.delta),
make_field(contract.deltaNeutralContract.price)]
else:
flds.append(make_field(False))
if self.serverVersion() >= MIN_SERVER_VER_ALGO_ORDERS:
flds.append(make_field( order.algoStrategy))
if order.algoStrategy:
algoParamsCount = len(order.algoParams) if order.algoParams else 0
flds.append(make_field(algoParamsCount))
if algoParamsCount > 0:
for algoParam in order.algoParams:
flds += [make_field(algoParam.tag),
make_field(algoParam.value)]
if self.serverVersion() >= MIN_SERVER_VER_ALGO_ID:
flds.append(make_field( order.algoId))
flds.append(make_field( order.whatIf)) # srv v36 and above
# send miscOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
miscOptionsStr = ""
if order.orderMiscOptions:
for tagValue in order.orderMiscOptions:
miscOptionsStr += str(tagValue)
flds.append(make_field( miscOptionsStr))
if self.serverVersion() >= MIN_SERVER_VER_ORDER_SOLICITED:
flds.append(make_field(order.solicited))
if self.serverVersion() >= MIN_SERVER_VER_RANDOMIZE_SIZE_AND_PRICE:
flds += [make_field(order.randomizeSize),
make_field(order.randomizePrice)]
if self.serverVersion() >= MIN_SERVER_VER_PEGGED_TO_BENCHMARK:
if order.orderType == "PEG BENCH":
flds += [make_field(order.referenceContractId),
make_field(order.isPeggedChangeAmountDecrease),
make_field(order.peggedChangeAmount),
make_field(order.referenceChangeAmount),
make_field(order.referenceExchangeId)]
flds.append(make_field(len(order.conditions)))
if len(order.conditions) > 0:
for cond in order.conditions:
flds.append(make_field(cond.type()))
flds += cond.make_fields()
flds += [make_field(order.conditionsIgnoreRth),
make_field(order.conditionsCancelOrder)]
flds += [make_field(order.adjustedOrderType),
make_field(order.triggerPrice),
make_field(order.lmtPriceOffset),
make_field(order.adjustedStopPrice),
make_field(order.adjustedStopLimitPrice),
make_field(order.adjustedTrailingAmount),
make_field(order.adjustableTrailingUnit)]
if self.serverVersion() >= MIN_SERVER_VER_EXT_OPERATOR:
flds.append(make_field( order.extOperator))
if self.serverVersion() >= MIN_SERVER_VER_SOFT_DOLLAR_TIER:
flds += [make_field(order.softDollarTier.name),
make_field(order.softDollarTier.val)]
if self.serverVersion() >= MIN_SERVER_VER_CASH_QTY:
flds.append(make_field( order.cashQty))
if self.serverVersion() >= MIN_SERVER_VER_DECISION_MAKER:
flds.append(make_field( order.mifid2DecisionMaker))
flds.append(make_field( order.mifid2DecisionAlgo))
if self.serverVersion() >= MIN_SERVER_VER_MIFID_EXECUTION:
flds.append(make_field( order.mifid2ExecutionTrader))
flds.append(make_field( order.mifid2ExecutionAlgo))
if self.serverVersion() >= MIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE:
flds.append(make_field(order.dontUseAutoPriceForHedge))
if self.serverVersion() >= MIN_SERVER_VER_ORDER_CONTAINER:
flds.append(make_field(order.isOmsContainer))
if self.serverVersion() >= MIN_SERVER_VER_D_PEG_ORDERS:
flds.append(make_field(order.discretionaryUpToLimitPrice))
if self.serverVersion() >= MIN_SERVER_VER_PRICE_MGMT_ALGO:
flds.append(make_field_handle_empty(UNSET_INTEGER if order.usePriceMgmtAlgo == None else 1 if order.usePriceMgmtAlgo else 0))
if self.serverVersion() >= MIN_SERVER_VER_DURATION:
flds.append(make_field(order.duration))
if self.serverVersion() >= MIN_SERVER_VER_POST_TO_ATS:
flds.append(make_field(order.postToAts))
if self.serverVersion() >= MIN_SERVER_VER_AUTO_CANCEL_PARENT:
flds.append(make_field(order.autoCancelParent))
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(orderId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelOrder(self, orderId:OrderId):
"""Call this function to cancel an order.
orderId:OrderId - The order ID that was specified previously in the call
to placeOrder()"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.CANCEL_ORDER) \
+ make_field(VERSION) \
+ make_field(orderId)
self.sendMsg(msg)
def reqOpenOrders(self):
# 通过调用这个函数可以获取当前的已经下单但是没有完全成交的订单,这些订单是从这个client下的。
# 这些订单信息会通过openOrder() 和 orderStatus()返回。如果clientid设置成0的话,也会接收其他的client下的订单的状态
# 其他client的订单的id会自动改变。即在不同的客户端之间会协同。
"""Call this function to request the open orders that were
placed from this client. Each open order will be fed back through the
openOrder() and orderStatus() functions on the EWrapper.
Note: The client with a clientId of 0 will also receive the TWS-owned
open orders. These orders will be associated with the client and a new
orderId will be generated. This association will persist over multiple
API and TWS sessions. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_OPEN_ORDERS) \
+ make_field(VERSION)
self.sendMsg(msg)
def reqAutoOpenOrders(self, bAutoBind:bool):
# 获取最新的下单,仅仅在clientId为0的时候才能使用
"""Call this function to request that newly created TWS orders
be implicitly associated with the client. When a new TWS order is
created, the order will be associated with the client, and fed back
through the openOrder() and orderStatus() functions on the EWrapper.
Note: This request can only be made from a client with clientId of 0.
bAutoBind: If set to TRUE, newly created TWS orders will be implicitly
associated with the client. If set to FALSE, no association will be
made."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_AUTO_OPEN_ORDERS) \
+ make_field(VERSION) \
+ make_field(bAutoBind)
self.sendMsg(msg)
def reqAllOpenOrders(self):
# 获取所有client下的未完全成交的订单,不同客户端之间的订单不会协同
"""Call this function to request the open orders placed from all
clients and also from TWS. Each open order will be fed back through the
openOrder() and orderStatus() functions on the EWrapper.
Note: No association is made between the returned orders and the
requesting client."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_ALL_OPEN_ORDERS) \
+ make_field(VERSION)
self.sendMsg(msg)
def reqGlobalCancel(self):
"""Use this function to cancel all open orders globally. It
cancels both API and TWS open orders.
If the order was created in TWS, it also gets canceled. If the order
was initiated in the API, it also gets canceled."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_GLOBAL_CANCEL) \
+ make_field(VERSION)
self.sendMsg(msg)
def reqIds(self, numIds:int):
"""Call this function to request from TWS the next valid ID that
can be used when placing an order. After calling this function, the
nextValidId() event will be triggered, and the id returned is that next
valid ID. That ID will reflect any autobinding that has occurred (which
generates new IDs and increments the next valid ID therein).
numIds:int - deprecated"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_IDS) \
+ make_field(VERSION) \
+ make_field(numIds)
self.sendMsg(msg)
#########################################################################
################## Account and Portfolio
########################################################################
def reqAccountUpdates(self, subscribe:bool, acctCode:str):
"""Call this function to start getting account values, portfolio,
and last update time information via EWrapper.updateAccountValue(),
EWrapperi.updatePortfolio() and Wrapper.updateAccountTime().
subscribe:bool - If set to TRUE, the client will start receiving account
and Portfoliolio updates. If set to FALSE, the client will stop
receiving this information.
acctCode:str -The account code for which to receive account and
portfolio updates."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
try:
VERSION = 2
flds = []
flds += [make_field(OUT.REQ_ACCT_DATA),
make_field(VERSION),
make_field(subscribe), # TRUE = subscribe, FALSE = unsubscribe.
make_field(acctCode)] # srv v9 and above, the account code. This will only be used for FA clients
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqAccountSummary(self, reqId:int, groupName:str, tags:str):
# 请求账户的汇总信息
"""Call this method to request and keep up to date the data that appears
on the TWS Account Window Summary tab. The data is returned by
accountSummary().
Note: This request is designed for an FA managed account but can be
used for any multi-account structure.
reqId:int - The ID of the data request. Ensures that responses are matched
to requests If several requests are in process.
groupName:str - Set to All to returnrn account summary data for all
accounts, or set to a specific Advisor Account Group name that has
already been created in TWS Global Configuration.
tags:str - A comma-separated list of account tags. Available tags are:
accountountType
NetLiquidation,
TotalCashValue - Total cash including futures pnl
SettledCash - For cash accounts, this is the same as
TotalCashValue
AccruedCash - Net accrued interest
BuyingPower - The maximum amount of marginable US stocks the
account can buy
EquityWithLoanValue - Cash + stocks + bonds + mutual funds
PreviousDayEquityWithLoanValue,
GrossPositionValue - The sum of the absolute value of all stock
and equity option positions
RegTEquity,
RegTMargin,
SMA - Special Memorandum Account
InitMarginReq,
MaintMarginReq,
AvailableFunds,
ExcessLiquidity,
Cushion - Excess liquidity as a percentage of net liquidation value
FullInitMarginReq,
FullMaintMarginReq,
FullAvailableFunds,
FullExcessLiquidity,
LookAheadNextChange - Time when look-ahead values take effect
LookAheadInitMarginReq,
LookAheadMaintMarginReq,
LookAheadAvailableFunds,
LookAheadExcessLiquidity,
HighestSeverity - A measure of how close the account is to liquidation
DayTradesRemaining - The Number of Open/Close trades a user
could put on before Pattern Day Trading is detected. A value of "-1"
means that the user can put on unlimited day trades.
Leverage - GrossPositionValue / NetLiquidation
$LEDGER - Single flag to relay all cash balance tags*, only in base
currency.
$LEDGER:CURRENCY - Single flag to relay all cash balance tags*, only in
the specified currency.
$LEDGER:ALL - Single flag to relay all cash balance tags* in all
currencies."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
try:
VERSION = 1
msg = make_field(OUT.REQ_ACCOUNT_SUMMARY) \
+ make_field(VERSION) \
+ make_field(reqId) \
+ make_field(groupName) \
+ make_field(tags)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelAccountSummary(self, reqId:int):
# 取消账户汇总信息的更新
"""Cancels the request for Account Window Summary tab data.
reqId:int - The ID of the data request being canceled."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.CANCEL_ACCOUNT_SUMMARY) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
def reqPositions(self):
# 请求所有账户的当前的持仓
"""Requests real-time position data for all accounts."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_POSITIONS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support positions request.")
return
VERSION = 1
msg = make_field(OUT.REQ_POSITIONS) \
+ make_field(VERSION)
self.sendMsg(msg)
def cancelPositions(self):
# 取消实时的持仓信息的更新
"""Cancels real-time position updates."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_POSITIONS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support positions request.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_POSITIONS) \
+ make_field(VERSION)
self.sendMsg(msg)
def reqPositionsMulti(self, reqId:int, account:str, modelCode:str):
# 请求一个账户的持仓
"""Requests positions for account and/or model.
Results are delivered via EWrapper.positionMulti() and
EWrapper.positionMultiEnd() """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_MODELS_SUPPORT:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support positions multi request.")
return
try:
VERSION = 1
msg = make_field(OUT.REQ_POSITIONS_MULTI) \
+ make_field(VERSION) \
+ make_field(reqId) \
+ make_field(account) \
+ make_field(modelCode)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelPositionsMulti(self, reqId:int):
# 取消一个账户的持仓的更新
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_MODELS_SUPPORT:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support cancel positions multi request.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_POSITIONS_MULTI) \
+ make_field(VERSION) \
+ make_field(reqId) \
self.sendMsg(msg)
def reqAccountUpdatesMulti(self, reqId: int, account:str, modelCode:str,
ledgerAndNLV:bool):
"""Requests account updates for account and/or model."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_MODELS_SUPPORT:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support account updates multi request.")
return
try:
VERSION = 1
msg = make_field(OUT.REQ_ACCOUNT_UPDATES_MULTI) \
+ make_field(VERSION) \
+ make_field(reqId) \
+ make_field(account) \
+ make_field(modelCode) \
+ make_field(ledgerAndNLV)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelAccountUpdatesMulti(self, reqId:int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_MODELS_SUPPORT:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support cancel account updates multi request.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_ACCOUNT_UPDATES_MULTI) \
+ make_field(VERSION) \
+ make_field(reqId) \
self.sendMsg(msg)
#########################################################################
################## Daily PnL
#########################################################################
def reqPnL(self, reqId: int, account: str, modelCode: str):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_PNL:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support PnL request.")
return
try:
msg = make_field(OUT.REQ_PNL) \
+ make_field(reqId) \
+ make_field(account) \
+ make_field(modelCode)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelPnL(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_PNL:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support PnL request.")
return
msg = make_field(OUT.CANCEL_PNL) \
+ make_field(reqId)
self.sendMsg(msg)
def reqPnLSingle(self, reqId: int, account: str, modelCode: str, conid: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_PNL:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support PnL request.")
return
try:
msg = make_field(OUT.REQ_PNL_SINGLE) \
+ make_field(reqId) \
+ make_field(account) \
+ make_field(modelCode) \
+ make_field(conid)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelPnLSingle(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_PNL:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support PnL request.")
return
msg = make_field(OUT.CANCEL_PNL_SINGLE) \
+ make_field(reqId)
self.sendMsg(msg)
#########################################################################
################## Executions
#########################################################################
def reqExecutions(self, reqId:int, execFilter:ExecutionFilter):
# 这个函数用于请求过去的交易执行的情况,可以使用execFilter进行过滤
"""When this function is called, the execution reports that meet the
filter criteria are downloaded to the client via the execDetails()
function. To view executions beyond the past 24 hours, open the
Trade Log in TWS and, while the Trade Log is displayed, request
the executions again from the API.
reqId:int - The ID of the data request. Ensures that responses are
matched to requests if several requests are in process.
execFilter:ExecutionFilter - This object contains attributes that
describe the filter criteria used to determine which execution
reports are returned.
NOTE: Time format must be 'yyyymmdd-hh:mm:ss' Eg: '20030702-14:55'"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
try:
VERSION = 3
# send req open orders msg
flds = []
flds += [make_field(OUT.REQ_EXECUTIONS),
make_field(VERSION)]
if self.serverVersion() >= MIN_SERVER_VER_EXECUTION_DATA_CHAIN:
flds += [make_field( reqId),]
# Send the execution rpt filter data (srv v9 and above)
flds += [make_field( execFilter.clientId),
make_field(execFilter.acctCode),
make_field(execFilter.time),
make_field(execFilter.symbol),
make_field(execFilter.secType),
make_field(execFilter.exchange),
make_field(execFilter.side)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
#########################################################################
################## Contract Details
#########################################################################
def reqContractDetails(self, reqId:int , contract:Contract):
"""Call this function to download all details for a particular
underlying. The contract details will be received via the contractDetails()
function on the EWrapper.
reqId:int - The ID of the data request. Ensures that responses are
make_fieldatched to requests if several requests are in process.
contract:Contract - The summary description of the contract being looked
up."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_SEC_ID_TYPE:
if contract.secIdType or contract.secId:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support secIdType and secId parameters.")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support tradingClass parameter in reqContractDetails.")
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
if contract.primaryExchange:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support primaryExchange parameter in reqContractDetails.")
return
try:
VERSION = 8
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_CONTRACT_DATA),
make_field( VERSION)]
if self.serverVersion() >= MIN_SERVER_VER_CONTRACT_DATA_CHAIN:
flds += [make_field( reqId),]
# send contract fields
flds += [make_field(contract.conId), # srv v37 and above
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier)] # srv v15 and above
if self.serverVersion() >= MIN_SERVER_VER_PRIMARYEXCH:
flds += [make_field(contract.exchange),
make_field(contract.primaryExchange)]
elif self.serverVersion() >= MIN_SERVER_VER_LINKING:
if (contract.primaryExchange and
(contract.exchange == "BEST" or contract.exchange == "SMART")):
flds += [make_field(contract.exchange + ":" + contract.primaryExchange),]
else:
flds += [make_field(contract.exchange),]
flds += [make_field( contract.currency),
make_field( contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass), ]
flds += [make_field(contract.includeExpired),] # srv v31 and above
if self.serverVersion() >= MIN_SERVER_VER_SEC_ID_TYPE:
flds += [make_field( contract.secIdType),
make_field( contract.secId)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
#########################################################################
################## Market Depth
#########################################################################
def reqMktDepthExchanges(self):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_MKT_DEPTH_EXCHANGES:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support market depth exchanges request.")
return
msg = make_field(OUT.REQ_MKT_DEPTH_EXCHANGES)
self.sendMsg(msg)
def reqMktDepth(self, reqId:TickerId, contract:Contract,
numRows:int, isSmartDepth:bool, mktDepthOptions:TagValueList):
"""Call this function to request market depth for a specific
contract. The market depth will be returned by the updateMktDepth() and
updateMktDepthL2() events.
Requests the contract's market depth (order book). Note this request must be
direct-routed to an exchange and not smart-routed. The number of simultaneous
market depth requests allowed in an account is calculated based on a formula
that looks at an accounts equity, commissions, and quote booster packs.
reqId:TickerId - The ticker id. Must be a unique value. When the market
depth data returns, it will be identified by this tag. This is
also used when canceling the market depth
contract:Contact - This structure contains a description of the contract
for which market depth data is being requested.
numRows:int - Specifies the numRowsumber of market depth rows to display.
isSmartDepth:bool - specifies SMART depth request
mktDepthOptions:TagValueList - For internal use only. Use default value
XYZ."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass or contract.conId > 0:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support conId and tradingClass parameters in reqMktDepth.")
return
if self.serverVersion() < MIN_SERVER_VER_SMART_DEPTH and isSmartDepth:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support SMART depth request.")
return
if self.serverVersion() < MIN_SERVER_VER_MKT_DEPTH_PRIM_EXCHANGE and contract.primaryExchange:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support primaryExchange parameter in reqMktDepth.")
return
try:
VERSION = 5
# send req mkt depth msg
flds = []
flds += [make_field(OUT.REQ_MKT_DEPTH),
make_field(VERSION),
make_field(reqId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier), # srv v15 and above
make_field(contract.exchange),]
if self.serverVersion() >= MIN_SERVER_VER_MKT_DEPTH_PRIM_EXCHANGE:
flds += [make_field(contract.primaryExchange),]
flds += [make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
flds += [make_field(numRows),] # srv v19 and above
if self.serverVersion() >= MIN_SERVER_VER_SMART_DEPTH:
flds += [make_field(isSmartDepth),]
# send mktDepthOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
#current doc says this part if for "internal use only" -> won't support it
if mktDepthOptions:
raise NotImplementedError("not supported")
mktDataOptionsStr = ""
flds += [make_field(mktDataOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelMktDepth(self, reqId:TickerId, isSmartDepth:bool):
"""After calling this function, market depth data for the specified id
will stop flowing.
reqId:TickerId - The ID that was specified in the call to
reqMktDepth().
isSmartDepth:bool - specifies SMART depth request"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_SMART_DEPTH and isSmartDepth:
self.wrapper.error( reqId, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support SMART depth cancel.")
return
VERSION = 1
# send cancel mkt depth msg
flds = []
flds += [make_field(OUT.CANCEL_MKT_DEPTH),
make_field(VERSION),
make_field(reqId)]
if self.serverVersion() >= MIN_SERVER_VER_SMART_DEPTH:
flds += [make_field(isSmartDepth)]
msg = "".join(flds)
self.sendMsg(msg)
#########################################################################
################## News Bulletins
#########################################################################
def reqNewsBulletins(self, allMsgs:bool):
"""Call this function to start receiving news bulletins. Each bulletin
will be returned by the updateNewsBulletin() event.
allMsgs:bool - If set to TRUE, returns all the existing bulletins for
the currencyent day and any new ones. If set to FALSE, will only
return new bulletins. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_NEWS_BULLETINS) \
+ make_field(VERSION) \
+ make_field(allMsgs)
self.sendMsg(msg)
def cancelNewsBulletins(self):
"""Call this function to stop receiving news bulletins."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.CANCEL_NEWS_BULLETINS) \
+ make_field(VERSION)
self.sendMsg(msg)
#########################################################################
################## Financial Advisors
#########################################################################
def reqManagedAccts(self):
"""Call this function to request the list of managed accounts. The list
will be returned by the managedAccounts() function on the EWrapper.
Note: This request can only be made when connected to a FA managed account."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_MANAGED_ACCTS) \
+ make_field(VERSION)
return self.sendMsg(msg)
def requestFA(self, faData:FaDataType):
"""Call this function to request FA configuration information from TWS.
The data returns in an XML string via a "receiveFA" ActiveX event.
faData:FaDataType - Specifies the type of Financial Advisor
configuration data beingingg requested. Valid values include:
1 = GROUPS
2 = PROFILE
3 = ACCOUNT ALIASES"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_FA) \
+ make_field(VERSION) \
+ make_field(int(faData))
return self.sendMsg(msg)
def replaceFA(self, reqId:TickerId , faData:FaDataType , cxml:str):
"""Call this function to modify FA configuration information from the
API. Note that this can also be done manually in TWS itself.
reqId:TickerId - request id
faData:FaDataType - Specifies the type of Financial Advisor
configuration data beingingg requested. Valid values include:
1 = GROUPS
2 = PROFILE
3 = ACCOUNT ALIASES
cxml: str - The XML string containing the new FA configuration
information. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
try:
VERSION = 1
msg = make_field(OUT.REPLACE_FA) \
+ make_field(VERSION) \
+ make_field(int(faData)) \
+ make_field(cxml) \
if self.serverVersion() >= MIN_SERVER_VER_REPLACE_FA_END:
msg += make_field(reqId)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
return self.sendMsg(msg)
#########################################################################
################## Historical Data
#########################################################################
def reqHistoricalData(self, reqId:TickerId , contract:Contract, endDateTime:str,
durationStr:str, barSizeSetting:str, whatToShow:str,
useRTH:int, formatDate:int, keepUpToDate:bool, chartOptions:TagValueList):
"""Requests contracts' historical data. When requesting historical data, a
finishing time and date is required along with a duration string. The
resulting bars will be returned in EWrapper.historicalData()
reqId:TickerId - The id of the request. Must be a unique value. When the
market data returns, it whatToShowill be identified by this tag. This is also
used when canceling the market data.
contract:Contract - This object contains a description of the contract for which
market data is being requested.
endDateTime:str - Defines a query end date and time at any point during the past 6 mos.
Valid values include any date/time within the past six months in the format:
yyyymmdd HH:mm:ss ttt
where "ttt" is the optional time zone.
durationStr:str - Set the query duration up to one week, using a time unit
of seconds, days or weeks. Valid values include any integer followed by a space
and then S (seconds), D (days) or W (week). If no unit is specified, seconds is used.
barSizeSetting:str - Specifies the size of the bars that will be returned (within IB/TWS listimits).
Valid values include:
1 sec
5 secs
15 secs
30 secs
1 min
2 mins
3 mins
5 mins
15 mins
30 mins
1 hour
1 day
whatToShow:str - Determines the nature of data beinging extracted. Valid values include:
TRADES
MIDPOINT
BID
ASK
BID_ASK
HISTORICAL_VOLATILITY
OPTION_IMPLIED_VOLATILITY
useRTH:int - Determines whether to return all data available during the requested time span,
or only data that falls within regular trading hours. Valid values include:
0 - all data is returned even where the market in question was outside of its
regular trading hours.
1 - only data within the regular trading hours is returned, even if the
requested time span falls partially or completely outside of the RTH.
formatDate: int - Determines the date format applied to returned bars. validd values include:
1 - dates applying to bars returned in the format: yyyymmdd{space}{space}hh:mm:dd
2 - dates are returned as a long integer specifying the number of seconds since
1/1/1970 GMT.
chartOptions:TagValueList - For internal use only. Use default value XYZ. """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(),
NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass or contract.conId > 0:
self.wrapper.error(reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support conId and tradingClass parameters in reqHistoricalData.")
return
try:
VERSION = 6
# send req mkt data msg
flds = []
flds += [make_field(OUT.REQ_HISTORICAL_DATA),]
if self.serverVersion() < MIN_SERVER_VER_SYNT_REALTIME_BARS:
flds += [make_field(VERSION),]
flds += [make_field(reqId),]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field( contract.tradingClass),]
flds += [make_field(contract.includeExpired), # srv v31 and above
make_field(endDateTime), # srv v20 and above
make_field(barSizeSetting), # srv v20 and above
make_field(durationStr),
make_field(useRTH),
make_field(whatToShow),
make_field(formatDate)] # srv v16 and above
# Send combo legs for BAG requests
if contract.secType == "BAG":
flds += [make_field(len(contract.comboLegs)),]
for comboLeg in contract.comboLegs:
flds += [make_field( comboLeg.conId),
make_field( comboLeg.ratio),
make_field( comboLeg.action),
make_field( comboLeg.exchange)]
if self.serverVersion() >= MIN_SERVER_VER_SYNT_REALTIME_BARS:
flds += [make_field(keepUpToDate), ]
# send chartOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
chartOptionsStr = ""
if chartOptions:
for tagValue in chartOptions:
chartOptionsStr += str(tagValue)
flds += [make_field( chartOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelHistoricalData(self, reqId:TickerId):
"""Used if an internet disconnect has occurred or the results of a query
are otherwise delayed and the application is no longer interested in receiving
the data.
reqId:TickerId - The ticker ID. Must be a unique value."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.CANCEL_HISTORICAL_DATA) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
# Note that formatData parameter affects intraday bars only
# 1-day bars always return with date in YYYYMMDD format
def reqHeadTimeStamp(self, reqId:TickerId, contract:Contract,
whatToShow: str, useRTH: int, formatDate: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_HEAD_TIMESTAMP:
self.wrapper.error(reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support head time stamp requests.")
return
try:
flds = []
flds += [make_field(OUT.REQ_HEAD_TIMESTAMP),
make_field(reqId),
make_field(contract.conId),
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol),
make_field(contract.tradingClass),
make_field(contract.includeExpired),
make_field(useRTH),
make_field(whatToShow),
make_field(formatDate) ]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelHeadTimeStamp(self, reqId: TickerId):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_CANCEL_HEADTIMESTAMP:
self.wrapper.error(reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support head time stamp requests.")
return
flds = []
flds += [make_field(OUT.CANCEL_HEAD_TIMESTAMP),
make_field(reqId) ]
msg = "".join(flds)
self.sendMsg(msg)
def reqHistogramData(self, tickerId: int, contract: Contract,
useRTH: bool, timePeriod: str):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_HISTOGRAM:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support histogram requests..")
return
try:
flds = []
flds += [make_field(OUT.REQ_HISTOGRAM_DATA),
make_field(tickerId),
make_field(contract.conId),
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol),
make_field(contract.tradingClass),
make_field(contract.includeExpired),
make_field(useRTH),
make_field(timePeriod)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(tickerId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelHistogramData(self, tickerId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_HISTOGRAM:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support histogram requests..")
return
msg = make_field(OUT.CANCEL_HISTOGRAM_DATA) + make_field(tickerId)
self.sendMsg(msg)
def reqHistoricalTicks(self, reqId: int, contract: Contract, startDateTime: str,
endDateTime: str, numberOfTicks: int, whatToShow: str, useRth: int,
ignoreSize: bool, miscOptions: TagValueList):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_HISTORICAL_TICKS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support historical ticks requests..")
return
try:
flds = []
flds += [make_field(OUT.REQ_HISTORICAL_TICKS),
make_field(reqId),
make_field(contract.conId),
make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol),
make_field(contract.tradingClass),
make_field(contract.includeExpired),
make_field(startDateTime),
make_field(endDateTime),
make_field(numberOfTicks),
make_field(whatToShow),
make_field(useRth),
make_field(ignoreSize)]
miscOptionsString = ""
if miscOptions:
for tagValue in miscOptions:
miscOptionsString += str(tagValue)
flds += [make_field(miscOptionsString),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
#########################################################################
################## Market Scanners
#########################################################################
def reqScannerParameters(self):
# 返回一个xml文档,包含所有可能的扫描仪过滤字段
"""Requests an XML string that describes all possible scanner queries."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.REQ_SCANNER_PARAMETERS) \
+ make_field(VERSION)
self.sendMsg(msg)
def reqScannerSubscription(self, reqId:int,
subscription:ScannerSubscription,
scannerSubscriptionOptions:TagValueList,
scannerSubscriptionFilterOptions:TagValueList):
"""
# 请求获取数据的id
reqId:int - The ticker ID. Must be a unique value.
# 前面定义过的市场扫描仪
scannerSubscription:ScannerSubscription - This structure contains
possible parameters used to filter results.
# 内部使用,忽略
scannerSubscriptionOptions:TagValueList - For internal use only.
Use default value XYZ.
# 增加额外过滤的字段
scannerSubscriptionFilterOptions
"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_SCANNER_GENERIC_OPTS and scannerSubscriptionFilterOptions is not None:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support API scanner subscription generic filter options")
return
try:
VERSION = 4
flds = []
flds += [make_field(OUT.REQ_SCANNER_SUBSCRIPTION)]
if self.serverVersion() < MIN_SERVER_VER_SCANNER_GENERIC_OPTS:
flds += [make_field(VERSION)]
flds +=[make_field(reqId),
make_field_handle_empty(subscription.numberOfRows),
make_field(subscription.instrument),
make_field(subscription.locationCode),
make_field(subscription.scanCode),
make_field_handle_empty(subscription.abovePrice),
make_field_handle_empty(subscription.belowPrice),
make_field_handle_empty(subscription.aboveVolume),
make_field_handle_empty(subscription.marketCapAbove),
make_field_handle_empty(subscription.marketCapBelow),
make_field(subscription.moodyRatingAbove),
make_field(subscription.moodyRatingBelow),
make_field(subscription.spRatingAbove),
make_field(subscription.spRatingBelow),
make_field(subscription.maturityDateAbove),
make_field(subscription.maturityDateBelow),
make_field_handle_empty(subscription.couponRateAbove),
make_field_handle_empty(subscription.couponRateBelow),
make_field(subscription.excludeConvertible),
make_field_handle_empty(subscription.averageOptionVolumeAbove), # srv v25 and above
make_field(subscription.scannerSettingPairs), # srv v25 and above
make_field(subscription.stockTypeFilter)] # srv v27 and above
# send scannerSubscriptionFilterOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_SCANNER_GENERIC_OPTS:
scannerSubscriptionFilterOptionsStr = ""
if scannerSubscriptionFilterOptions:
for tagValueOpt in scannerSubscriptionFilterOptions:
scannerSubscriptionFilterOptionsStr += str(tagValueOpt)
flds += [make_field(scannerSubscriptionFilterOptionsStr)]
# send scannerSubscriptionOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
scannerSubscriptionOptionsStr = ""
if scannerSubscriptionOptions:
for tagValueOpt in scannerSubscriptionOptions:
scannerSubscriptionOptionsStr += str(tagValueOpt)
flds += [make_field(scannerSubscriptionOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelScannerSubscription(self, reqId:int):
"""reqId:int - The ticker ID. Must be a unique value."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
msg = make_field(OUT.CANCEL_SCANNER_SUBSCRIPTION) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
#########################################################################
################## Real Time Bars
#########################################################################
def reqRealTimeBars(self, reqId:TickerId, contract:Contract, barSize:int,
whatToShow:str, useRTH:bool,
realTimeBarsOptions:TagValueList):
"""Call the reqRealTimeBars() function to start receiving real time bar
results through the realtimeBar() EWrapper function.
reqId:TickerId - The Id for the request. Must be a unique value. When the
data is received, it will be identified by this Id. This is also
used when canceling the request.
contract:Contract - This object contains a description of the contract
for which real time bars are being requested
barSize:int - Currently only 5 second bars are supported, if any other
value is used, an exception will be thrown.
whatToShow:str - Determines the nature of the data extracted. Valid
values include:
TRADES
BID
ASK
MIDPOINT
useRTH:bool - Regular Trading Hours only. Valid values include:
0 = all data available during the time span requested is returned,
including time intervals when the market in question was
outside of regular trading hours.
1 = only data within the regular trading hours for the product
requested is returned, even if the time time span falls
partially or completely outside.
realTimeBarOptions:TagValueList - For internal use only. Use default value XYZ."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
if contract.tradingClass:
self.wrapper.error( reqId, UPDATE_TWS.code(),
UPDATE_TWS.msg() + " It does not support conId and tradingClass parameter in reqRealTimeBars.")
return
try:
VERSION = 3
flds = []
flds += [make_field(OUT.REQ_REAL_TIME_BARS),
make_field(VERSION),
make_field(reqId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.lastTradeDateOrContractMonth),
make_field(contract.strike),
make_field(contract.right),
make_field(contract.multiplier),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol)]
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field(contract.tradingClass),]
flds += [make_field(barSize),
make_field(whatToShow),
make_field(useRTH)]
# send realTimeBarsOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
realTimeBarsOptionsStr = ""
if realTimeBarsOptions:
for tagValueOpt in realTimeBarsOptions:
realTimeBarsOptionsStr += str(tagValueOpt)
flds += [make_field(realTimeBarsOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelRealTimeBars(self, reqId:TickerId):
"""Call the cancelRealTimeBars() function to stop receiving real time bar results.
reqId:TickerId - The Id that was specified in the call to reqRealTimeBars(). """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(reqId, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
VERSION = 1
# send req mkt data msg
flds = []
flds += [make_field(OUT.CANCEL_REAL_TIME_BARS),
make_field(VERSION),
make_field(reqId)]
msg = "".join(flds)
self.sendMsg(msg)
#########################################################################
################## Fundamental Data
#########################################################################
def reqFundamentalData(self, reqId:TickerId , contract:Contract,
reportType:str, fundamentalDataOptions:TagValueList):
"""Call this function to receive fundamental data for
stocks. The appropriate market data subscription must be set up in
Account Management before you can receive this data.
Fundamental data will be returned at EWrapper.fundamentalData().
reqFundamentalData() can handle conid specified in the Contract object,
but not tradingClass or multiplier. This is because reqFundamentalData()
is used only for stocks and stocks do not have a multiplier and
trading class.
reqId:tickerId - The ID of the data request. Ensures that responses are
matched to requests if several requests are in process.
contract:Contract - This structure contains a description of the
contract for which fundamental data is being requested.
reportType:str - One of the following XML reports:
ReportSnapshot (company overview)
ReportsFinSummary (financial summary)
ReportRatios (financial ratios)
ReportsFinStatements (financial statements)
RESC (analyst estimates) """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
try:
VERSION = 2
if self.serverVersion() < MIN_SERVER_VER_FUNDAMENTAL_DATA:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support fundamental data request.")
return
if self.serverVersion() < MIN_SERVER_VER_TRADING_CLASS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support conId parameter in reqFundamentalData.")
return
flds = []
flds += [make_field(OUT.REQ_FUNDAMENTAL_DATA),
make_field(VERSION),
make_field(reqId)]
# send contract fields
if self.serverVersion() >= MIN_SERVER_VER_TRADING_CLASS:
flds += [make_field( contract.conId),]
flds += [make_field(contract.symbol),
make_field(contract.secType),
make_field(contract.exchange),
make_field(contract.primaryExchange),
make_field(contract.currency),
make_field(contract.localSymbol),
make_field(reportType)]
if self.serverVersion() >= MIN_SERVER_VER_LINKING:
fundDataOptStr = ""
tagValuesCount = len(fundamentalDataOptions) if fundamentalDataOptions else 0
if fundamentalDataOptions:
for fundDataOption in fundamentalDataOptions:
fundDataOptStr += str(fundDataOption)
flds += [make_field(tagValuesCount),
make_field(fundDataOptStr)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelFundamentalData(self, reqId:TickerId ):
"""Call this function to stop receiving fundamental data.
reqId:TickerId - The ID of the data request."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_FUNDAMENTAL_DATA:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support fundamental data request.")
return
VERSION = 1
msg = make_field(OUT.CANCEL_FUNDAMENTAL_DATA) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
########################################################################
################## News
#########################################################################
def reqNewsProviders(self):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_NEWS_PROVIDERS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support news providers request.")
return
msg = make_field(OUT.REQ_NEWS_PROVIDERS)
self.sendMsg(msg)
def reqNewsArticle(self, reqId: int, providerCode: str, articleId: str, newsArticleOptions: TagValueList):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_NEWS_ARTICLE:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support news article request.")
return
try:
flds = []
flds += [make_field(OUT.REQ_NEWS_ARTICLE),
make_field(reqId),
make_field(providerCode),
make_field(articleId)]
# send newsArticleOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_NEWS_QUERY_ORIGINS:
newsArticleOptionsStr = ""
if newsArticleOptions:
for tagValue in newsArticleOptions:
newsArticleOptionsStr += str(tagValue)
flds += [make_field(newsArticleOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqHistoricalNews(self, reqId: int, conId: int, providerCodes: str,
startDateTime: str, endDateTime: str, totalResults: int, historicalNewsOptions: TagValueList):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_HISTORICAL_NEWS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support historical news request.")
return
try:
flds = []
flds += [make_field(OUT.REQ_HISTORICAL_NEWS),
make_field(reqId),
make_field(conId),
make_field(providerCodes),
make_field(startDateTime),
make_field(endDateTime),
make_field(totalResults)]
# send historicalNewsOptions parameter
if self.serverVersion() >= MIN_SERVER_VER_NEWS_QUERY_ORIGINS:
historicalNewsOptionsStr = ""
if historicalNewsOptions:
for tagValue in historicalNewsOptionsStr:
historicalNewsOptionsStr += str(tagValue)
flds += [make_field(historicalNewsOptionsStr),]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
#########################################################################
################## Display Groups
#########################################################################
def queryDisplayGroups(self, reqId: int):
"""API requests used to integrate with TWS color-grouped windows (display groups).
TWS color-grouped windows are identified by an integer number. Currently that number ranges from 1 to 7 and are mapped to specific colors, as indicated in TWS.
reqId:int - The unique number that will be associated with the
response """
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support queryDisplayGroups request.")
return
VERSION = 1
msg = make_field(OUT.QUERY_DISPLAY_GROUPS) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
def subscribeToGroupEvents(self, reqId:int, groupId:int):
"""reqId:int - The unique number associated with the notification.
groupId:int - The ID of the group, currently it is a number from 1 to 7.
This is the display group subscription request sent by the API to TWS."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support subscribeToGroupEvents request.")
return
VERSION = 1
msg = make_field(OUT.SUBSCRIBE_TO_GROUP_EVENTS) \
+ make_field(VERSION) \
+ make_field(reqId) \
+ make_field(groupId)
self.sendMsg(msg)
def updateDisplayGroup(self, reqId:int, contractInfo:str):
"""reqId:int - The requestId specified in subscribeToGroupEvents().
contractInfo:str - The encoded value that uniquely represents the
contract in IB. Possible values include:
none = empty selection
contractID@exchange - any non-combination contract.
Examples: 8314@SMART for IBM SMART; 8314@ARCA for IBM @ARCA.
combo = if any combo is selected."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support updateDisplayGroup request.")
return
try:
VERSION = 1
msg = make_field(OUT.UPDATE_DISPLAY_GROUP) \
+ make_field(VERSION) \
+ make_field(reqId) \
+ make_field(contractInfo)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def unsubscribeFromGroupEvents(self, reqId:int):
"""reqId:int - The requestId specified in subscribeToGroupEvents()."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support unsubscribeFromGroupEvents request.")
return
VERSION = 1
msg = make_field(OUT.UNSUBSCRIBE_FROM_GROUP_EVENTS) \
+ make_field(VERSION) \
+ make_field(reqId)
self.sendMsg(msg)
def verifyRequest(self, apiName:str, apiVersion:str):
"""For IB's internal purpose. Allows to provide means of verification
between the TWS and third party programs."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support verification request.")
return
if not self.extraAuth:
self.wrapper.error(NO_VALID_ID, BAD_MESSAGE.code(), BAD_MESSAGE.msg() +
" Intent to authenticate needs to be expressed during initial connect request.")
return
try:
VERSION = 1
msg = make_field(OUT.VERIFY_REQUEST) \
+ make_field(VERSION) \
+ make_field(apiName) \
+ make_field(apiVersion)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def verifyMessage(self, apiData:str):
"""For IB's internal purpose. Allows to provide means of verification
between the TWS and third party programs."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support verification request.")
return
try:
VERSION = 1
msg = make_field(OUT.VERIFY_MESSAGE) \
+ make_field(VERSION) \
+ make_field(apiData)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def verifyAndAuthRequest(self, apiName:str, apiVersion:str,
opaqueIsvKey:str):
"""For IB's internal purpose. Allows to provide means of verification
between the TWS and third party programs."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support verification request.")
return
if not self.extraAuth:
self.wrapper.error(NO_VALID_ID, BAD_MESSAGE.code(), BAD_MESSAGE.msg() +
" Intent to authenticate needs to be expressed during initial connect request.")
return
try:
VERSION = 1
msg = make_field(OUT.VERIFY_AND_AUTH_REQUEST) \
+ make_field(VERSION) \
+ make_field(apiName) \
+ make_field(apiVersion) \
+ make_field(opaqueIsvKey)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def verifyAndAuthMessage(self, apiData:str, xyzResponse:str):
"""For IB's internal purpose. Allows to provide means of verification
between the TWS and third party programs."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_LINKING:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support verification request.")
return
try:
VERSION = 1
msg = make_field(OUT.VERIFY_AND_AUTH_MESSAGE) \
+ make_field(VERSION) \
+ make_field(apiData) \
+ make_field(xyzResponse)
except ClientException as ex:
self.wrapper.error(NO_VALID_ID, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqSecDefOptParams(self, reqId:int, underlyingSymbol:str,
futFopExchange:str, underlyingSecType:str,
underlyingConId:int):
"""Requests security definition option parameters for viewing a
contract's option chain reqId the ID chosen for the request
underlyingSymbol futFopExchange The exchange on which the returned
options are trading. Can be set to the empty string "" for all
exchanges. underlyingSecType The type of the underlying security,
i.e. STK underlyingConId the contract ID of the underlying security.
Response comes via EWrapper.securityDefinitionOptionParameter()"""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_SEC_DEF_OPT_PARAMS_REQ:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support security definition option request.")
return
try:
flds = []
flds += [make_field(OUT.REQ_SEC_DEF_OPT_PARAMS),
make_field(reqId),
make_field(underlyingSymbol),
make_field(futFopExchange),
make_field(underlyingSecType),
make_field(underlyingConId)]
msg = "".join(flds)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqSoftDollarTiers(self, reqId:int):
"""Requests pre-defined Soft Dollar Tiers. This is only supported for
registered professional advisors and hedge and mutual funds who have
configured Soft Dollar Tiers in Account Management."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
msg = make_field(OUT.REQ_SOFT_DOLLAR_TIERS) \
+ make_field(reqId)
self.sendMsg(msg)
def reqFamilyCodes(self):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_FAMILY_CODES:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support family codes request.")
return
msg = make_field(OUT.REQ_FAMILY_CODES)
self.sendMsg(msg)
def reqMatchingSymbols(self, reqId:int, pattern:str):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_REQ_MATCHING_SYMBOLS:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support matching symbols request.")
return
try:
msg = make_field(OUT.REQ_MATCHING_SYMBOLS) \
+ make_field(reqId) \
+ make_field(pattern)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def reqCompletedOrders(self, apiOnly:bool):
"""Call this function to request the completed orders. If apiOnly parameter
is true, then only completed orders placed from API are requested.
Each completed order will be fed back through the
completedOrder() function on the EWrapper."""
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
msg = make_field(OUT.REQ_COMPLETED_ORDERS) \
+ make_field(apiOnly)
self.sendMsg(msg)
def reqWshMetaData(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_WSHE_CALENDAR:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support WSHE Calendar API.")
return
try:
msg = make_field(OUT.REQ_WSH_META_DATA) \
+ make_field(reqId)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelWshMetaData(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_WSHE_CALENDAR:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support WSHE Calendar API.")
return
msg = make_field(OUT.CANCEL_WSH_META_DATA) \
+ make_field(reqId)
self.sendMsg(msg)
def reqWshEventData(self, reqId: int, conId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_WSHE_CALENDAR:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support WSHE Calendar API.")
return
try:
msg = make_field(OUT.REQ_WSH_EVENT_DATA) \
+ make_field(reqId) \
+ make_field(conId)
except ClientException as ex:
self.wrapper.error(reqId, ex.code, ex.msg + ex.text)
return
self.sendMsg(msg)
def cancelWshEventData(self, reqId: int):
self.logRequest(current_fn_name(), vars())
if not self.isConnected():
self.wrapper.error(NO_VALID_ID, NOT_CONNECTED.code(), NOT_CONNECTED.msg())
return
if self.serverVersion() < MIN_SERVER_VER_WSHE_CALENDAR:
self.wrapper.error(NO_VALID_ID, UPDATE_TWS.code(), UPDATE_TWS.msg() +
" It does not support WSHE Calendar API.")
return
msg = make_field(OUT.CANCEL_WSH_EVENT_DATA) \
+ make_field(reqId)
self.sendMsg(msg)
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/yunjinqi/ibapi.git
[email protected]:yunjinqi/ibapi.git
yunjinqi
ibapi
ibapi
master

搜索帮助