I'm trying to use shioaji API to create a Taiwan stock market trading application.
However, I found a strange behavior during the developement.
Here's my code:
import tkinter as tk
import os
import shioaji as sj
from shioaji import BidAskFOPv1, Exchange
from dotenv import load_dotenv
class TouchOrderBuy:
def __init__(self, api, contract):
print(f"{contract.symbol} TouchOrder init ...")
self.api = api
self.contract = contract
self.is_triggered = False
self.api.quote.subscribe(
contract=self.contract,
quote_type=sj.constant.QuoteType.BidAsk,
version=sj.constant.QuoteVersion.v1
)
print(f"self.quote_bidask_callback address: {hex(id(self.quote_bidask_callback))}")
self.api.quote.set_on_bidask_fop_v1_callback(self.quote_bidask_callback)
def quote_bidask_callback(self, exchange: Exchange, bidask: BidAskFOPv1):
print(f"{bidask.code} quote_bidask_callback")
print(f"self.is_triggered: {self.is_triggered}")
print(f"self.is_triggered address: {hex(id(self.is_triggered))}")
if bidask.code == 'TXO17500C2':
print(f"set self.is_triggered as True")
self.is_triggered = True
class TradingApp:
def __init__(self):
self.main_window = tk.Tk()
self.main_window.wm_title('test subscription')
self.initialize()
self.crate_gui()
self.main_window.mainloop()
def initialize(self):
self.api = sj.Shioaji(simulation=False)
login_info = self.api.login(
person_id=os.getenv('SHIOAJI_USERID'),
passwd=os.getenv("SHIOAJI_PASSWORD"),
contracts_timeout=10000,
contracts_cb=print,
fetch_contract=True
)
print('Login Done')
self.api.set_default_account(self.api.list_accounts()[2])
resp = self.api.activate_ca(
ca_path="Sinopac.pfx",
ca_passwd=os.getenv('CA_PASSWORD'),
person_id=os.getenv('SHIOAJI_USERID'),
)
def crate_gui(self):
sub_put_button = tk.Button(self.main_window, text='subs put', command=self.subscribe_put)
sub_put_button.pack()
sub_call_button = tk.Button(self.main_window, text='subs call', command=self.subscribe_call)
sub_call_button.pack()
def subscribe_call(self):
t_call = TouchOrderBuy(self.api, self.api.Contracts.Options.TXO.TXO202203017500C)
def subscribe_put(self):
t_put = TouchOrderBuy(self.api, self.api.Contracts.Options.TXO.TXO202203017500P)
if __name__ == '__main__':
load_dotenv()
app = TradingApp()
As you can see from the above code, I can create 2 TouchOrderBuy objects by first clicking subs put button, then clicking subs call button.
I found that when TouchOrderBuy's self.is_triggered of t_call becomes True, TouchOrderBuy's self.is_triggered of t_put also becomes True.
Why does this happen?
Here's a snippet of the output log:
According to this article, the contents of an instance variable are completely independent from one object instance to the other.
I tried to create a minimum reproducible code without using shioaji, but without success. I feel sorry about that.
Another question I have is why do t_put's and t_call's is_triggered variables refer to the same memory address.
I guess it's because that python has something similar to integer cache for boolean variables.
I also created another version of code to test if self.is_triggered is shared between t_put and t_call:
import tkinter as tk
import os
import shioaji as sj
from shioaji import BidAskFOPv1, Exchange
from dotenv import load_dotenv
class TouchOrderBuy:
def __init__(self, api, contract):
print(f"{contract.symbol} TouchOrder init ...")
self.api = api
self.contract = contract
self.is_triggered = False
def get_is_triggered(self):
print(f"is_triggered address: {hex(id(self.is_triggered))}")
return self.is_triggered
def set_is_triggered(self, value):
self.is_triggered = value
if __name__ == '__main__':
load_dotenv()
api = sj.Shioaji(simulation=False)
login_info = api.login(
person_id=os.getenv('SHIOAJI_USERID'),
passwd=os.getenv("SHIOAJI_PASSWORD"),
contracts_timeout=10000,
contracts_cb=print,
fetch_contract=True
)
print('Login Done')
api.set_default_account(api.list_accounts()[2])
resp = api.activate_ca(
ca_path="Sinopac.pfx",
ca_passwd=os.getenv('CA_PASSWORD'),
person_id=os.getenv('SHIOAJI_USERID'),
)
t_put = TouchOrderBuy(api, api.Contracts.Options.TXO.TXO202203017500P)
t_call = TouchOrderBuy(api, api.Contracts.Options.TXO.TXO202203017500C)
print(f"put is_triggered: {t_put.get_is_triggered()}")
print(f"call is_triggered: {t_call.get_is_triggered()}")
t_call.set_is_triggered(True)
print(f"put is_triggered: {t_put.get_is_triggered()}")
print(f"call is_triggered: {t_call.get_is_triggered()}")
In this version, when t_call's self.is_triggered is chagned, t_put's self.is_triggered does not change.
Here's its output:
TXO202203017500P TouchOrder init ...
TXO202203017500C TouchOrder init ...
is_triggered address: 0x7ffb2a9fa970
put is_triggered: False
is_triggered address: 0x7ffb2a9fa970
call is_triggered: False
is_triggered address: 0x7ffb2a9fa970
put is_triggered: False
is_triggered address: 0x7ffb2a9fa950
call is_triggered: True
Why does this version of code not have a weird behavior?
Related
I wanted to create sunrpc client in python using xdrlib library and sunrpc server is already implemented in C. I have implemented one rpc client in python over udp by referencing following link:
https://svn.python.org/projects/stackless/trunk/Demo/rpc/rpc.py
It is giving timeout error as well as can not unpack none object error.
can anyone guide me on this how it can be done?
there is no information available on this on google.
has anyone implemented such type of code?
please help..I am struggling on this like a week now.
Here is my client code:
import rpc
import rpc_new
from tq_const import *
from tq_type import *
import tq_pack
import socket
import os
class PartialTQClient:
def __init__(self):
pass
def addpackers(self):
self.packer = tq_pack.TQPacker(self)
self.unpacker = tq_pack.TQUnpacker(self, '')
def unpack_month_temperatures(self):
return self.unpacker.unpack_array(self.unpacker.unpack_uint)
def call(self, month):
res = self.make_call(0, month, self.packer.pack_uint, self.unpack_month_temperatures)
return res
class UDPTQClient(PartialTQClient, rpc.RawUDPClient):
def __init__(self, host):
rpc.RawUDPClient.__init__(self, host, TQ_PROGRAM, TQ_VERSION, TQ_PORT)
PartialTQClient.__init__(self)
if __name__ == "__main__":
tqcl = UDPTQClient("127.0.0.1")
print(tqcl)
res = tqcl.call(12)
#print ("Got result", res)
Here is my server code:
import rpc
import rpc_new
from tq_const import *
from tq_type import *
import tq_pack
import socket
import os
class TQServer(rpc.UDPServer):
print("Inside TQServer")
def handle_0(self):
print ("Got request")
m = self.unpacker.unpack_uint()
print ("Arguments was", m)
self.turn_around()
self.packer.pack_array([1, 2, 3], self.packer.pack_int)
#res = PFresults(self, status=TRUE, phone="555-12345")
#res.pack()
if __name__ == "__main__":
s = TQServer("", TQ_PROGRAM, TQ_VERSION, TQ_PORT)
print ("Service started...",s)
try:
print("Trying")
s.loop()
finally:
print ("Service interrupted.")
When I am running client and server on localhost I am getting following error: TypeError: cannot unpack non-iterable NoneType object
The idea I have is to run a flask webapp, encapsulating mitmproxy, so if I use the proxy and perform a get request to specific URL, capture it (data + session), and replay it when initiated by flask
from flask import Flask
from mitmproxy import http
from mitmproxy.options import Options
from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.proxy.server import ProxyServer
from mitmproxy.tools.dump import DumpMaster
import requests
import threading
import asyncio
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
class repeat:
def __init__(self):
self.response=''
def request(self, flow):
if 'http://dummy.com/get_details.html' in flow.request.pretty_url:
'''
STEP 1
Capture authenticated requests for replay later
'''
elif 'repeat_again' in flow.request.pretty_url:
'''
STEP 2
Repeat earlier get_details request
then save data
'''
def response(self, flow: http.HTTPFlow):
'''
return ddynamic details
save the get_details data into class variable self.response
'''
addons=[repeat()]
app = Flask("proxapp")
#app.route('/get_again')
def get_again():
requests.get('repeat_again',proxies=proxies)
return repeat.response
'''
cause a repeat request
'''
def loop_in_thread(loop, m):
asyncio.set_event_loop(loop)
m.run_loop(loop.run_forever)
if __name__ == "__main__":
options = Options(listen_host='0.0.0.0', listen_port=8080, http2=True)
m = DumpMaster(options, with_termlog=True, with_dumper=True)
config = ProxyConfig(options)
m.server = ProxyServer(config)
m.addons.add(addons)
# run mitmproxy in backgroud, especially integrated with other server
loop = asyncio.get_event_loop()
t = threading.Thread( target=loop_in_thread, args=(loop,m) )
t.start()
app.run(debug=True, use_reloader=False, host='0.0.0.0', port=int('28888'))
So here my test browser browsing dummy.com as per normal, but the moment it does get_details.html page, I like to capture the request for repeat replay (can be done with mitmdump easily). the response should be saved in a variable of the class.
So now if I like to replay the earlier request, I can call http://127.0.0.1:2888/get_again to replay the same request.
Any ideas? i can do this manually with mitmdump, but I trying to automate it
Storing replay content must be in response method. In request method, flow returns with replay_response.
from flask import Flask
from mitmproxy.options import Options
from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.proxy.server import ProxyServer
from mitmproxy.tools.dump import DumpMaster
import requests
import threading
import asyncio
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
# Change replay_url -> http://dummy.com/get_details.html
replay_url = 'http://wttr.in/Innsbruck?0'
class Repeat:
def __init__(self):
self.replay_response = None
def request(self, flow):
if 'repeat_again' in flow.request.pretty_url:
flow.response = self.replay_response
def response(self, flow):
if replay_url in flow.request.pretty_url:
self.replay_response = flow.response
app = Flask("proxapp")
repeat = Repeat()
#app.route('/get_again')
def get_again():
resp = requests.get('http://repeat_again', proxies=proxies)
return (resp.text, resp.status_code, resp.headers.items())
def loop_in_thread(loop, m):
asyncio.set_event_loop(loop)
m.run_loop(loop.run_forever)
if __name__ == "__main__":
options = Options(listen_host='0.0.0.0', listen_port=8080, http2=True)
m = DumpMaster(options, with_termlog=True, with_dumper=True)
m.addons.add(repeat)
config = ProxyConfig(options)
m.server = ProxyServer(config)
# run mitmproxy in background, especially integrated with other server
loop = asyncio.get_event_loop()
t = threading.Thread(target=loop_in_thread, args=(loop, m))
t.start()
app.run(debug=True, use_reloader=False, host='0.0.0.0', port=int('28888'))
I have tested with http://wttr.in/Innsbruck?0
Output:
$ curl --proxy http://localhost:8080 "http://wttr.in/Innsbruck?0"
Weather report: Innsbruck
\ / Partly cloudy
_ /"".-. 2..3 °C
\_( ). → 7 km/h
/(___(__) 10 km
0.0 mm
$ curl --proxy http://localhost:8080 "http://repeat_again"
Weather report: Innsbruck
\ / Partly cloudy
_ /"".-. 2..3 °C
\_( ). → 7 km/h
/(___(__) 10 km
0.0 mm
$ curl http://localhost:28888/get_again
Weather report: Innsbruck
\ / Partly cloudy
_ /"".-. 2..3 °C
\_( ). → 7 km/h
/(___(__) 10 km
0.0 mm
New to Python and IB API and stuck on this simple thing. This application works correctly and prints IB server reply. However, I cannot figure out how to get this data into a panda's dataframe or any other variable for that matter. How do you "get the data out?" Thanks!
Nothing on forums, documentation or youtube that I can find with a useful example. I think the answer must be to return accountSummary to pd.Series, but no idea how.
Expected output would be a data series or variable that can be manipulated outside of the application.
from ibapi import wrapper
from ibapi.client import EClient
from ibapi.utils import iswrapper #just for decorator
from ibapi.common import *
import pandas as pd
class TestApp(wrapper.EWrapper, EClient):
def __init__(self):
wrapper.EWrapper.__init__(self)
EClient.__init__(self, wrapper=self)
#iswrapper
def nextValidId(self, orderId:int):
print("setting nextValidOrderId: %d", orderId)
self.nextValidOrderId = orderId
# here is where you start using api
self.reqAccountSummary(9002, "All", "$LEDGER")
#iswrapper
def error(self, reqId:TickerId, errorCode:int, errorString:str):
print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
#iswrapper
def accountSummary(self, reqId:int, account:str, tag:str, value:str, currency:str):
print("Acct Summary. ReqId:" , reqId , "Acct:", account,
"Tag: ", tag, "Value:", value, "Currency:", currency)
#IB API data returns here, how to pass it to a variable or pd.series
#iswrapper
def accountSummaryEnd(self, reqId:int):
print("AccountSummaryEnd. Req Id: ", reqId)
# now we can disconnect
self.disconnect()
def main():
app = TestApp()
app.connect("127.0.0.1", 4001, clientId=123)
test = app.accountSummary
app.run()
if __name__ == "__main__":
main()
Hi had the same problem and collections did it for me. Here is my code for CFDs data. Maybe it will help somebody. You will have your data in app.df. Any suggestion for improvement are more than welcome.
import collections
import datetime as dt
from threading import Timer
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
import pandas as pd
# get yesterday and put it to correct format yyyymmdd{space}{space}hh:mm:dd
yesterday = str(dt.datetime.today() - dt.timedelta(1))
yesterday = yesterday.replace('-','')
IP = '127.0.0.1'
PORT = 7497
class App(EClient, EWrapper):
def __init__(self):
super().__init__(self)
self.data = collections.defaultdict(list)
def error(self, reqId, errorCode, errorString):
print(f'Error {reqId}, {errorCode}, {errorString}')
def historicalData(self, reqId, bar):
self.data['date'].append(bar.date)
self.data['open'].append(bar.open)
self.data['high'].append(bar.high)
self.data['low'].append(bar.low)
self.data['close'].append(bar.close)
self.data['volume'].append(bar.volume)
self.df = pd.DataFrame.from_dict(self.data)
def stop(self):
self.done = True
self.disconnect()
# create App object
app = App()
print('App created...')
app.connect(IP, PORT, 0)
print('App connected...')
# create contract
contract = Contract()
contract.symbol = 'IBDE30'
contract.secType = 'CFD'
contract.exchange = 'SMART'
contract.currency = 'EUR'
print('Contract created...')
# request historical data for contract
app.reqHistoricalData(reqId=1,
contract=contract,
endDateTime=yesterday,
durationStr='1 W',
barSizeSetting='15 mins',
whatToShow='ASK',
useRTH=0,
formatDate=1,
keepUpToDate=False,
chartOptions=[])
Timer(4, app.stop).start()
app.run()
I'd store the data to a dictionary, create a dataframe from the dictionary, and append the new dataframe to the main dataframe using the concat function. Here's an example:
def accountSummary(self, reqId:int, account:str, tag:str, value:str, currency:str):
acct_dict = {"account": account, "value": value, "currency": currency}
acct_df = pd.DataFrame([acct_dict], columns=acct_dict.keys())
main_df = pd.concat([main_df, acct_df], axis=0).reset_index()
For more information, you might like Algorithmic Trading with Interactive Brokers
After deciding to walk away from Ibpy and learn the API IB I am having trouble even placing an order. What am I doing wrong?
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import *
#Def Market Order
class TestApp(EClient, EWrapper):
def __init__(self):
EClient.__init__(self, self)
def nextValidId(self, orderId:int):
self.nextOrderId = orderId
print(orderId)
self.start()
def start(self):
contract = Contract()
contract.symbol = "ES"
contract.secType = "FUT"
contract.exchange = "GLOBEX"
contract.currency = "USD"
contract.lastTradeDateOrContractMonth = "201903"
order = Order()
order.action = "BUY"
order.orderType = "MKT"
order.totalQuantity = 1
order.outsideRth = True
self.placeOrder(self.nextOrderId, contract, order)
def stop(self):
self.done = True
self.disconnect()
def main():
app=TestApp()
app.connect("127.0.0.1", 7496,420)
app.nextOrderId = 0
app.run()
if __name__ == "__main__":
main()
removed more Ibpy and replaced with ibapi
now getting
%Run basic.py
394
ERROR -1 2104 Market data farm connection is OK:usfuture
ERROR -1 2104 Market data farm connection is OK:usfarm
ERROR -1 2106 HMDS data farm connection is OK:ushmds
ERROR 394 200 No security definition has been found for the request
from ib.ext.Contract import Contract
from ib.ext.Order import Order
Those are from IbPy, use only classes that start with ibapi.
I'm looking to mock a set of REST APIs for some tests. The following main() function works fine (i.e. it returns {"some-data": 1234} as json to the browser when I GET localhost:8099). The issue is it blocks the main thread:
from gevent import monkey, sleep, pywsgi
monkey.patch_all()
import flask
from flask_restful import reqparse, abort, Api, Resource
import queue
import sys
import threading
STUFFS = {"some-data": 1234}
class Stuff(Resource):
def get(self):
return flask.jsonify(STUFFS)
class ControlThread(threading.Thread):
def __init__(self, http_server, stop_event):
threading.Thread.__init__(self)
self.stop_event = stop_event
self.http_server = http_server
self.running = False
def run(self):
try:
while not self.stop_event.is_set():
if not self.running:
self.http_server.start()
self.running = True
sleep(0.001)
except (KeyboardInterrupt, SystemExit):
pass
self.http_server.stop()
class StuffMock:
def __init__(self, port, name=None):
if name is None:
name = __name__
self.app = flask.Flask(name)
self.api = Api(self.app)
self.api.add_resource(Stuff, "/stuff/")
self.stop_event = threading.Event()
self.http_server = pywsgi.WSGIServer(('', port), self.app)
self.serving_thread = ControlThread(self.http_server,
self.stop_event)
self.serving_thread.daemon = True
def start(self):
self.serving_thread.start()
def stop(self):
self.stop_event.set()
self.serving_thread.join()
def main():
mocker = StuffMock(8099)
mocker.start()
try:
while True:
sleep(0.01)
except (KeyboardInterrupt, SystemExit):
mocker.stop()
sys.exit()
if __name__ == "__main__":
main()
Without the sleep() call in the while loop above, nothing resolves. Here is a more succinct usage to demonstrate:
import time
from stuff_mock import StuffMock
mocker = StuffMock(8099)
mocker.start()
while True:
user_text = input("let's do some work on the main thread: ")
# will only resolve the GET request after user input
# (i.e. when the main thread executes this sleep call)
time.sleep(0.1)
if user_text == "q":
break
mocker.stop()
The gevent threading module seems to work differently from the core one. Does anyone have any tips or ideas about what's going on under the hood?
Found that if I switch out threading for multiprocessing (and threading.Thread for multiprocessing.Process), everything works as expected, and I can spin up arbitrary numbers of mockers without blocking.