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?