summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2021-03-29 18:27:39 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2021-03-29 18:27:39 +0200
commit534930b298a35798cd884b661bc151c0e47ad7c0 (patch)
tree1cc71c9909e1b87a38938432a18bf85f2cce3866
parentbbbd4fb3da4c7989846c221f8c0ba16b21b4d978 (diff)
downloadtwstest-master.tar.gz
twstest-master.tar.bz2
added Python testHEADmaster
-rw-r--r--python/4tut.py33
-rw-r--r--python/client_gui.py560
-rw-r--r--python/config_demo.py80
-rw-r--r--python/get_account_data_demo2.py211
-rw-r--r--python/gistfile1.py43
5 files changed, 927 insertions, 0 deletions
diff --git a/python/4tut.py b/python/4tut.py
new file mode 100644
index 0000000..e46bef3
--- /dev/null
+++ b/python/4tut.py
@@ -0,0 +1,33 @@
+import random
+import string
+
+import cherrypy
+
+
+class StringGenerator(object):
+ @cherrypy.expose
+ def index(self):
+ return """<html>
+ <head></head>
+ <body>
+ <form method="get" action="action1">
+ <input type="text" value="8" name="length" />
+ <button type="submit">action1!</button>
+ </form>
+ <form method="get" action="action2">
+ <input type="text" value="8" name="length" />
+ <button type="submit">action2!</button>
+ </form>
+ </body>
+ </html>"""
+
+ @cherrypy.expose
+ def action1(self, length=8):
+ return 'action1'
+
+ @cherrypy.expose
+ def action2(self, length=8):
+ return 'action2'
+
+if __name__ == '__main__':
+ cherrypy.quickstart(StringGenerator())
diff --git a/python/client_gui.py b/python/client_gui.py
new file mode 100644
index 0000000..7579acc
--- /dev/null
+++ b/python/client_gui.py
@@ -0,0 +1,560 @@
+import tkinter as tk
+from tkinter import ttk
+from tkinter import scrolledtext
+from tkinter import messagebox
+import time
+import webbrowser
+import threading
+import queue
+
+DEF_window_name = "TWS GUI M. B."
+DEF_threading_time_GUI = 0.1 # seconds
+
+class GUI_tab_test:
+
+ def __init__(self, root):
+
+ self.chk_state = [0 for columns in range(10)]
+ self.label = tk.Label(root, text="This is our first GUI!")
+ self.label.grid(row=1, column=1, columnspan=2)
+
+ self.greet_button = tk.Button(root, text="Greet", command=self.greet)
+ self.greet_button.grid(row=2, column=1)
+
+ self.chk_state[0] = tk.BooleanVar()
+ self.chk_state[0].set(True) #set check state
+ self.chk = tk.Checkbutton(root, text='Choose', var=self.chk_state[0])
+ self.chk.grid(row=2, column=2)
+
+ self.chk_state[1] = tk.BooleanVar()
+ self.chk_state[1].set(True) #set check state
+ self.chk = tk.Checkbutton(root, text='Choose', var=self.chk_state[1])
+ self.chk.grid(row=3, column=2)
+
+ lf = ttk.Labelframe(root, text='Label')
+ lf.grid(row=4, column=1, sticky="w", columnspan=4)
+ lbl = tk.Label(lf, text= 'lbl with no dynamic text') # lbl with no dynamic text
+ lbl.grid(row=4, column=1, sticky="w")
+
+ def greet(self):
+ self.label.configure(state='disable')
+ self.label["text"] = self.chk_state[0].get()+self.chk_state[1].get()
+ i=0
+ while i<2:
+ print(int(self.chk_state[i].get()))
+ i += 1
+
+class reqIdCode(): #for input / output
+
+ def __init__(self):
+ self.GET_ACCOUNTS = 1,
+ self.GET_ACCOUNT_MARGIN = 2,
+ self.GET_ACCOUNT_ALL_DETAILS = 3,
+ self.GET_ACCOUNT_POSITIONS = 4,
+ self.GET_TWS_STATUS = 98,
+ self.GET_ERROR_MSG = 90,
+ self.TERMINATE = 99,
+
+class MyGUI(threading.Thread):
+
+ #! GUI init and handling
+
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self.reqIdCode = reqIdCode()
+ self.requests = queue.Queue() # request queue for IBApp
+ self.receives = queue.Queue() # receive queue for GUI
+ self.receiveOutput() # start searching for output to display
+ self.GUIisReady = False
+ self.start()
+
+ def run(self):
+ self.root = tk.Tk()
+ self.root.protocol("WM_DELETE_WINDOW", self.mainClose)
+ self.buildGui()
+
+ def mainOpen(self):
+ self.root.mainloop()
+ return self.root
+
+ def mainClose(self):
+ msgBox = messagebox.askquestion ('Close '+DEF_window_name,'Are you sure you want to exit?',icon = 'warning')
+ if msgBox == 'yes':
+ self.queueRequest(self.reqIdCode.TERMINATE)
+ time.sleep(1)
+ self.root.destroy()
+ else:
+ pass
+
+ def queueRequest(self,reqId,val=""): #request data from outside
+ self.requests.put([reqId,val])
+ print("GUI: queue request sent to IB:",reqId)
+
+ #! receive output data from main app
+ def receiveOutput(self):
+ threading.Timer(DEF_threading_time_GUI, self.receiveOutput).start() # check all DEF_threading_time_GUI seconds
+ while not self.receives.empty():
+ (reqId,val) = self.gui.requests.get()
+ print("receiveOutput",reqId,val)
+ if reqId == self.reqIdCode.GET_ERROR_MSG:
+ self.tm_msgTxt.insert(tk.INSERT," \n"+val)
+ self.tp_txtStatus.delete(0,tk.END)
+ self.tp_txtStatus.insert(0,val)
+ self.tr_txtStatus.delete(0,tk.END)
+ self.tr_txtStatus.insert(0,val)
+ elif reqId == self.reqIdCode.GET_TWS_STATUS:
+ self.tm_msgTxt.insert(tk.INSERT," \n"+str(val)) # for now just in messages
+ elif reqId == self.reqIdCode.GET_ACCOUNTS:
+ print("account*",val[0])
+ self.tp_cmbAccountSelect['values'] = val[0] # e.g. ('U633914', 'U633915'),('U633915')
+ self.tp_cmbAccountSelect.set(val[1])
+
+ #! receive output data from main app END
+
+ #! GUI init and handling END
+
+
+ #! methods called from internal to request data from outside
+
+ #! methods called from internal to request data from outside END
+
+ #! methods called from external to put some text
+
+ def tp_AccountUpdate(self):
+ print("account update:"+self.tp_cmbAccountSelect.get())
+ self.queueRequest(self.reqIdCode.GET_ACCOUNT_POSITIONS,self.tp_cmbAccountSelect.get())
+
+ def tp_gets_focus(self, event):
+ print("tp_gets_focus")
+ self.queueRequest(self.reqIdCode.GET_ACCOUNTS)
+
+ def tp_setAccountValues(self,val): # val list of three values
+ self.tp_lblAccountVal1.set(val[1])
+ self.tp_lblAccountVal2.set(val[2])
+ self.tp_lblAccountVal3.set(val[3])
+
+ def tr_symbol_click(self):
+ print("Symbol",self.txt_symbol.get())
+ print("Strategy:",self.selected.get())
+
+ def tr_select_click(self):
+ print("Symbol",self.tr_txtSymbol.get())
+ print("Strategy:",self.tr_selStrategy.get())
+
+ #! methods internal for GUI
+ def openBrowser(self, event):
+ webbrowser.open_new_tab(event.widget.cget("text"))
+
+ def tp_postp_tree_OnDoubleClick(self, event):
+ item = self.tp_posTree.identify('item',event.x,event.y)
+ print("you clicked on", self.tp_posTree.item(item,"text"))
+
+ def tp_posTree_OnDoubleClick(self, event):
+ item = self.tp_posTree.identify('item',event.x,event.y)
+ print("you clicked on", self.tp_posTree.item(item,"text"))
+
+ def tp_posTree_RightClick(self, event):
+ # display the tp_popup menu
+ try:
+ self.tp_popup.selection = self.tp_posTree.set(self.tp_posTree.identify_row(event.y))
+ self.tp_popup.post(event.x_root, event.y_root)
+ finally:
+ # make sure to release the grab (Tk 8.0a1 only)
+ self.tp_popup.grab_release()
+
+ def edit_position(self):
+ print ("edit_position",self.tp_popup.selection)
+
+ def categorize(self):
+ print ("categorize",self.tp_popup.selection)
+
+ def edit_position(self):
+ print ("edit_position",self.tp_popup.selection)
+
+ def categorize(self):
+ print ("categorize",self.tp_popup.selection)
+
+
+
+
+ #! end of methods called from external to put some text
+
+ #! GUI init and handling
+ def buildGui(self):
+
+ # build gui now
+ self.root.title(DEF_window_name)
+
+ # formats definition
+
+ # main window dimensions
+ master_x = 0
+ master_y = 0
+ master_width = 1200
+ master_height = 1000
+ self.root.geometry('%dx%d+%d+%d'%(master_width,master_height,master_x,master_y))
+
+ # tab positions
+
+ # tree setup general
+ L = "w" # LEFT
+ R = "e" # RIGHT
+ tag_heading_background='AntiqueWhite1'
+ tag_evenrow_background='white'
+ tag_oddrow_background='white smoke'
+
+ # tree setup specific
+ master_MarginLeft = 30
+ master_MarginTop = 15
+ master_DEFAlign = tk.W+tk.S
+ tp_tree_y = 120
+ tp_tree_height = 500
+ tp_scrollbar_add_x = 190
+ tp_scrollbar_add_y = 0
+ tp_scrollbar_add_height = 0
+ DEF_tp_combosize = 10
+
+ tr_tree_y = 120
+ tr_tree_height = 500
+ tr_scrollbar_add_x = 190
+ tr_scrollbar_add_y = 0
+ tr_scrollbar_add_height = 0
+ DEF_tr_colWidthAlign = [(0,0),(0,0),(50,L),(50,L),(40,L),(20,L),(35,R),(50,L),(45,R),(45,R),(40,R),(40,R),(30,L),(50,L),(45,R),(150,L)]
+
+ DEF_tr_combosize = 4
+
+
+ # formats definition end
+
+ # build tabs
+
+ tab_control = ttk.Notebook(self.root)
+
+ tab_test = ttk.Frame(tab_control)
+ tp = ttk.Frame(tab_control) # Tab Positions
+ tp.bind("<Visibility>", self.tp_gets_focus)
+
+ tr = ttk.Frame(tab_control)
+ tab_executions = ttk.Frame(tab_control)
+ tm = ttk.Frame(tab_control)
+
+ tab_control.add(tp, text='Positions')
+ tab_control.add(tr, text='Trading')
+ tab_control.add(tab_test, text='Test')
+ tab_control.add(tab_executions, text='Executions')
+ tab_control.add(tm, text='Messages')
+
+
+ #! tab 4: Messages
+
+ self.tm_msgTxt = scrolledtext.ScrolledText(tm,width=120,height=30)
+ self.tm_msgTxt.grid(column=0,row=0)
+
+ #! tab 4: Messages END
+
+ #! Tab Test Start
+
+ # tab 1: tab_test
+ GUI_tab_test(tab_test)
+
+ #! Tab Test End
+
+ #! tab 2: tab_positions
+
+ #style = ttk.Style()
+ #current_theme =style.theme_use()
+ #style.theme_settings(current_theme, {tp: {"configure": {"padding": [50, 50, 50, 50]}}})
+
+ #tab_control.geometry('%dx%d+%d+%d'%(master_width,master_height-100,master_x,master_y))
+
+ lbl = tk.Label(tp, text= 'Account:') # lbl with no dynamic text
+ lbl.grid(row=1, column=1, sticky="w", padx=(master_MarginLeft,0), pady=(master_MarginTop,0))
+
+ self.tp_cmbAccountSelect = ttk.Combobox(tp, width= DEF_tp_combosize)
+ self.tp_cmbAccountSelect.grid(row=1, column=2, sticky= master_DEFAlign)
+ self.queueRequest(self.reqIdCode.GET_ACCOUNTS)
+
+ lbl = tk.Label(tp, text= 'Netliq:') # lbl with no dynamic text
+ lbl.grid(row=1, column=3, sticky= master_DEFAlign)
+ lbl = tk.Label(tp, text= 'Initial Margin:') # lbl with no dynamic text
+ lbl.grid(row=2, column=3, sticky= master_DEFAlign)
+ lbl = tk.Label(tp, text= 'Maint. Margin:') # lbl with no dynamic text
+ lbl.grid(row=3, column=3, sticky= master_DEFAlign)
+
+ self.tp_lbl = {}
+ self.tp_lbl["NetLiquidation_val"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["NetLiquidation_val"]) # lbl with dynamic text
+ lbl.grid(row=1,column=4, sticky= master_DEFAlign)
+ self.tp_lbl["NetLiquidation_percent"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["NetLiquidation_percent"]) # lbl with dynamic text
+ lbl.grid(row=1,column=5, sticky= master_DEFAlign)
+ lbl = tk.Label(tp, text= '%')
+ lbl.grid(row=1,column=6, sticky= master_DEFAlign)
+
+
+ self.tp_lbl["FullInitMarginReq_val"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["FullInitMarginReq_val"]) # lbl with dynamic text
+ lbl.grid(row=2,column=4, sticky= master_DEFAlign)
+ self.tp_lbl["NetLiquidation_percent"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["NetLiquidation_percent"]) # lbl with dynamic text
+ lbl.grid(row=2,column=5, sticky= master_DEFAlign)
+ lbl = tk.Label(tp, text= '%')
+ lbl.grid(row=2,column=6, sticky= master_DEFAlign)
+
+ self.tp_lbl["FullMaintMarginReq_val"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["FullMaintMarginReq_val"]) # lbl with dynamic text
+ lbl.grid(row=3,column=4, sticky= master_DEFAlign)
+ self.tp_lbl["FullMaintMarginReq_percent"] = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tp_lbl["FullMaintMarginReq_percent"]) # lbl with dynamic text
+ lbl.grid(row=3,column=5, sticky= master_DEFAlign)
+ lbl = tk.Label(tp, text= '%')
+ lbl.grid(row=3,column=6, sticky= master_DEFAlign)
+
+
+ lbl = tk.Label(tp, text= "Filter")
+ lbl.grid(row=3, column=9)
+ self.tp_txtfilter = tk.Entry(tp,width=10)
+ self.tp_txtfilter.grid(row=3, column=2)
+
+ btn = tk.Button(tp, text="Update", command=self.tp_AccountUpdate)
+ btn.grid(row=3,column=10)
+
+ lbl = tk.Label(tp, text=r"https://www.google.com", fg="blue", cursor="hand2")
+ lbl.bind("<Button-1>", self.openBrowser)
+ lbl.grid(row=4,column=1)
+
+ # Create Tree
+ self.tp_posTree = ttk.Treeview(tp, selectmode='browse', show="headings")
+ self.tp_posTree.place(x= master_MarginLeft, y= tp_tree_y, height=tp_tree_height)
+
+ vsb = ttk.Scrollbar(tp, orient="vertical", command=self.tp_posTree.yview)
+ vsb.place(x=master_MarginLeft +tp_tree_height+2+tp_scrollbar_add_x, y= tp_tree_y+tp_scrollbar_add_y, height=tp_tree_height+tp_scrollbar_add_height)
+
+ self.tp_posTree.configure(yscrollcommand=vsb.set)
+
+ DEF_tp_header = ["-AccountName","-conId","Expiry","Symbol","Type","Exch","Pos","Strategy","Price","AvgPrice", "Value","PNL","MaxWin","Profitable","Stock","Remark"]
+ DEF_tp_colWidthAlign = [(0,0),(0,0),(50,L),(50,L),(40,L),(20,L),(35,R),(50,L),(45,R),(45,R),(40,R),(40,R),(30,L),(50,L),(45,R),(150,L)]
+ positions = [['U633914', "Group1", 'UR', '', '', '', '', '', '', '', '', '', '', '', '', 'Undefined Risk'],
+ ['U633914', 211651685, '', 'URA', 'STK', '', 1300.0, 'Strategy', 12.56758405, 'OptionPrice', 16337.86, -4429.64, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 12087820, '', 'USD', 'CASH', '', 187416.0, 'Strategy', 0.97008995, 'OptionPrice', 181810.38, -322.27, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 321623106, 'Sep-21', 'VONN', 'OPT', '', -3.0, 'Strategy', 0.6067744, 'OptionPrice', -182.03, 82.87, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 289313012, 'Sep-21', 'VONN', 'OPT', '', -3.0, 'Strategy', 2.1291783, 'OptionPrice', -638.75, -163.85, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 330623057, 'Sep-21', 'WDAY', 'OPT', '', -1.0, 'Strategy', 2.9383762, 'OptionPrice', -293.84, -59.08, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 326998242, 'Sep-21', 'WSM', 'OPT', '', -2.0, 'Strategy', 1.08746315, 'OptionPrice', -217.49, 22.63, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 326998454, 'Sep-21', 'WSM', 'OPT', '', -2.0, 'Strategy', 1.48435305, 'OptionPrice', -296.87, -16.75, 'MaxWin', 'Profitable', 'Stock', 'Remark'], ['U633914', 88819736, '', 'YNDX', 'STK', '', 400.0, 'Strategy', 30.4699993, 'OptionPrice', 12188.0, -3812.0, 'MaxWin', 'Profitable', 'Stock', 'Remark']
+ ]
+
+ # create header
+ headers = {}
+ cellValues = []
+ nr_of_groups = 0
+ column_nr = 1
+ #create declaration
+ cellId = []
+ for cell in DEF_tp_header:
+ if cell[0] != "-":
+ if cellId == []: cellId = ["c"+str(column_nr)]
+ else: cellId.append("c"+str(column_nr))
+ column_nr += 1
+ self.tp_posTree["columns"] = cellId
+ column_nr = 1
+ #create headings
+ for cell in DEF_tp_header:
+ headers[column_nr] = cell
+ if cell[0] != "-": # skip headers and values with _ in name
+ (col_width,align) = DEF_tp_colWidthAlign[column_nr-1]
+ self.tp_posTree.column("c"+str(column_nr), minwidth=0, width= col_width, stretch=tk.NO, anchor=align)
+ self.tp_posTree.heading("c"+str(column_nr), text= cell)
+ pass
+ column_nr += 1
+
+
+ # create cells
+ row_nr = 2
+ cellValues = []
+ row = {}
+ nr_of_groups = 0
+ for row_pos in positions:
+ column_nr = 1
+ cellValues = []
+ for cell in row_pos:
+ if headers[column_nr][0] != "-":
+ if cellValues == []: cellValues = [cell]
+ else: cellValues.append(cell)
+ column_nr += 1
+
+ #calc tag
+ if "Group" in str(row_pos[1]):
+ nr_of_groups += 1
+ tag = "tag_heading"
+ elif (row_nr+nr_of_groups)%2 == 0:
+ tag = "tag_evenrow"
+ else: tag = "tag_oddrow"
+
+ # create row
+ row[row_nr-1] = self.tp_posTree.insert("",'end',text= str(row_nr),values= cellValues, tags = (tag,))
+ row_nr += 1
+
+ #last adjustments
+ self.tp_posTree.selection_set(row[2])
+
+ self.tp_posTree.tag_configure('tag_heading', background= tag_heading_background)
+ self.tp_posTree.tag_configure('tag_evenrow', background= tag_evenrow_background)
+ self.tp_posTree.tag_configure('tag_oddrow', background= tag_oddrow_background)
+
+ self.tp_posTree.bind("<Double-1>", self.tp_postp_tree_OnDoubleClick)
+
+ #Create right mouse menu
+ self.tp_popup = tk.Menu(tp, tearoff=0)
+ self.tp_popup.add_command(label="Edit", command=self.edit_position)
+ self.tp_popup.add_command(label="Categorize", command=self.categorize)
+ #self.tp_popup.add_separator()
+ self.tp_posTree.bind("<Button-3>", self.tp_posTree_RightClick)
+
+ btn = tk.Button(tp, text="Update", command=self.tp_AccountUpdate)
+ btn.place(x=master_MarginLeft, y=tp_tree_height+tp_tree_y+20, anchor="w")
+
+ self.tp_txtStatus = tk.Entry(tp,width=120, text="Status")
+ self.tp_txtStatus.place(x=master_MarginLeft, y=tp_tree_height+tp_tree_y+60, anchor="w")
+
+
+
+ #! tab 2: tab_positions end
+
+ #! tab 3: tab_trading
+
+ lbl = tk.Label(tr, text= "Text")
+ lbl.grid(row=1, column=1)
+ lbl = tk.Label(tr, text= "Account:")
+ lbl.grid(row=1, column=2)
+
+ self.tr_lblAccountSelect = tk.StringVar()
+ lbl.grid(row=1,column=3)
+ lbl = tk.Label(tp, textvariable= self.tr_lblAccountSelect) # lbl with dynamic text
+ lbl.grid(row=1,column=4)
+ lbl = tk.Label(tr, text= "Anker trade:")
+ lbl.grid(row=1, column=5)
+ self.tr_lblAnkertrade = tk.StringVar()
+ lbl.grid(row=1,column=6)
+
+ lbl = tk.Label(tr, text= "Symbol")
+ lbl.grid(row=2, column=1)
+ self.tr_txtSymbol = tk.Entry(tr,width=10)
+ self.tr_txtSymbol.grid(row=2, column=2)
+
+ tr_simple = ["Buy/Stock or Future","Sell/Stock or Future"]
+ tr_strategy = ["Strangle/Strangle","Put/Naked Put","Call/Naked Call","Straddle/Straddle",\
+ "IC/Iron Condor","VP/Vertical Put Spread","VC/Vertical Call Spread"]
+
+ self.tr_selStrategy = tk.StringVar()
+ colstart = 4
+ col = 1
+ for simple in tr_simple:
+ str1 = simple.split("/")
+ val = str1[0]
+ rad = tk.Radiobutton(tr,text=val, value=val, variable=self.tr_selStrategy)
+ rad.grid(row=3, column= colstart+col)
+ col += 1
+ simple_len = col
+ col = 1
+ for strategy in tr_strategy:
+ str1 = strategy.split("/")
+ val = str1[0]
+ rad = tk.Radiobutton(tr,text=val, value=val, variable=self.tr_selStrategy)
+ rad.grid(row=3, column= colstart+simple_len+col)
+ col += 1
+ strategy_len = col
+
+ lbl = tk.Label(tr, text= "Simple")
+ lbl.grid(row=2, column=colstart+simple_len-3, columnspan=simple_len)
+ lbl = tk.Label(tr, text= "Strategy")
+ lbl.grid(row=2, column=colstart+simple_len+int(strategy_len/2)-4, columnspan=strategy_len)
+
+ self.tr_selStrategy.set("Strangle") # move somewhere else
+
+ lbl = tk.Label(tr, text= "Delta:")
+ lbl.grid(row=4, column=1)
+ self.tr_txtDelta = tk.Entry(tr,width=10)
+ self.tr_txtDelta.grid(row=4, column=2)
+
+ lbl = tk.Label(tr, text= "Min. Premium:")
+ lbl.grid(row=4, column=3, columnspan=2)
+ self.tr_txtPremium = tk.Entry(tr,width=10)
+ self.tr_txtPremium.grid(row=4, column=5)
+
+ lbl = tk.Label(tr, text= "Expiry: ")
+ lbl.grid(row=4, column=7)
+ lbl = tk.Label(tr, text= "20180927")
+ lbl.grid(row=4, column=8)
+ lbl = tk.Label(tr, text= "(30d - monhtly)")
+ lbl.grid(row=4, column=9)
+
+ lbl = tk.Label(tr, text= "Quantity:")
+ lbl.grid(row=5, column=1, columnspan=1)
+ self.tr_txtPremium = tk.Entry(tr,width=10)
+ self.tr_txtPremium.grid(row=5, column=2)
+
+ lbl = tk.Label(tr, text= "Price:")
+ lbl.grid(row=5, column=3, columnspan=1)
+ self.tr_txtPremium = tk.Entry(tr,width=10)
+ self.tr_txtPremium.grid(row=5, column=4)
+
+ self.tr_cmbOrderType = ttk.Combobox(tr, width= DEF_tr_combosize)
+ self.tr_cmbOrderType['values'] = ('LMT', 'MKT')
+ self.tr_cmbOrderType.set('LMT')
+ self.tr_cmbOrderType.grid(row=5, column=7)
+
+ self.tr_cmbOrderTime = ttk.Combobox(tr, width= DEF_tr_combosize)
+ self.tr_cmbOrderTime['values'] = ('DAY', 'GTC')
+ self.tr_cmbOrderTime.set('DAY')
+ self.tr_cmbOrderTime.grid(row=5, column=8)
+
+ btn = tk.Button(tr, text="Calc", command=self.tr_symbol_click)
+ btn.grid(row=4, column=11)
+
+ self.tr_AddProfitTaker = tk.BooleanVar()
+ self.tr_AddProfitTaker.set(True) #set check state
+ chk = tk.Checkbutton(tr, text='Attach Profit Taker', var=self.tr_AddProfitTaker)
+ chk.grid(row=6, column=2, columnspan=3)
+
+ self.tr_txtProfitTakerPercent = tk.Entry(tr,width=2)
+ self.tr_txtProfitTakerPercent.grid(row=6, column=7)
+ lbl = tk.Label(tr, text= "% = ")
+ lbl.grid(row=6, column=8)
+ self.tr_txtProfitTakerPrice = tk.Entry(tr,width=4)
+ self.tr_txtProfitTakerPrice.grid(row=6, column=9)
+ self.tr_lblCurrency = tk.StringVar()
+ lbl = tk.Label(tp, textvariable= self.tr_lblCurrency)
+ lbl.grid(row=6,column=10)
+ self.tr_lblCurrency.set("USD") # temp
+
+ lbl = tk.Label(tr, text= "Remark:")
+ lbl.grid(row=8, column=1)
+ self.tr_txtRemark = tk.Entry(tr,width=50)
+ self.tr_txtRemark.grid(row=8, column=2, columnspan=5)
+
+ # create buttons
+ btn = tk.Button(tr, text="Add", command=self.tr_select_click)
+ btn.grid(row= 4, column=13)
+
+ self.tr_txtStatus = tk.Entry(tr,width=120, text="Status")
+ self.tr_txtStatus.place(x=master_MarginLeft, y=tp_tree_height+tp_tree_y+60, anchor="w")
+
+ #! tab 3: tab_trading END
+
+
+
+ # tab 2: tab_executions
+ lbl2 = tk.Label(tab_executions, text= 'label2')
+ lbl2.grid(column=0, row=0)
+
+ #! tab 4: Messages END
+
+ # show tabs
+ tab_control.pack(expand=1, fill='both')
+
+ self.GUIisReady = True
+ self.mainOpen()
+
+ #! GUI init end
+
+
+if __name__ == "__main__":
+
+ app = MyGUI()
+ print('Now we can continue running code while mainloop runs!')
+
diff --git a/python/config_demo.py b/python/config_demo.py
new file mode 100644
index 0000000..27585e6
--- /dev/null
+++ b/python/config_demo.py
@@ -0,0 +1,80 @@
+# Config
+
+# CONNECTION
+PORT = 7497
+CLIENT_ID = 799
+
+# SQL
+
+# ACCOUNTS
+class myAccounts():
+
+ def __init__(self):
+ self.accounts = [ {"portfolio_ref": 99, "accountIB": "DU1165506", "accountDesc": "Account IB 99"} ]
+ self.mainAccount = 1
+
+ def getMainAccountRef(self):
+ return self.mainAccount
+
+ def getAccountNameIB(self,accountRef):
+ for account in self.accounts:
+ if account["portfolio_ref"] == accountRef: return account["accountIB"]
+ return None
+
+ def getAccountRef(self,accountIB):
+ for account in self.accounts:
+ if account["accountIB"] == accountIB: return account["portfolio_ref"]
+ return None
+
+# TRADING
+DELTA_PREFERENCES = {
+ "PUT": -30,
+ "CALL": 30,
+ "STRANGLE": (15,15),
+ "STRADDLE": (0,0),
+}
+EXPIRY_PREFERENCES = (30,60) #days
+
+# ERROR MESSAGES that should be ignored
+ERR_MSG_NO_SHOW = (
+ 'Market data farm connection is OK',
+ 'HMDS data farm connection is OK',
+ 'API client has been unsubscribed from account data'
+ )
+
+DEF_SecurityTable = { # get definitions for SQL
+
+ "currencyDefault": { "currency": "USD", },
+ "stockUSDefault": { "currency": "USD", "exchange": "SMART", },
+
+ "/ES": { "exchange": "GLOBEX", },
+ "/NQ": { "exchange": "GLOBEX", },
+ "/RTY": { "exchange": "GLOBEX", },
+
+ "/CL": { "exchange": "NYMEX", },
+ "/NG": { "exchange": "NYMEX", },
+ "/GC": { "exchange": "NYMEX", },
+ "/SI": { "exchange": "NYMEX", },
+
+ "/CAD": { "exchange": "GLOBEX", "multiplier": 100000, },
+ "/AUD": { "exchange": "GLOBEX", "multiplier": 100000, },
+ "/EUR": { "exchange": "GLOBEX", "multiplier": 125000, },
+ "/JPY": { "exchange": "GLOBEX", "multiplier": 12500000, },
+ "/ZB": { "exchange": "ECBOT", },
+ "/ZC": { "exchange": "ECBOT", "multiplier": 5000, },
+ "/ZS": { "exchange": "ECBOT", "multiplier": 5000, },
+ "/ZW": { "exchange": "ECBOT", "multiplier": 5000, },
+ "/ZM": { "exchange": "ECBOT", "multiplier": 5000, },
+
+ "/CC": { "exchange": "NYBOT", "multiplier": 10},
+ "/KC": { "exchange": "NYBOT", "multiplier": 37500},
+
+ "^DAX": { "exchange": "DTB", "currency": "EUR", },
+ "^SPX": { "exchange": "CBOE", },
+
+ ".SW": { "exchange": "EBS", "currency": "CHF", },
+ ".VX": { "exchange": "VIRTX", "currency": "CHF", },
+ ".DE": { "exchange": "IBIS", "currency": "EUR", },
+
+}
+
diff --git a/python/get_account_data_demo2.py b/python/get_account_data_demo2.py
new file mode 100644
index 0000000..b31a717
--- /dev/null
+++ b/python/get_account_data_demo2.py
@@ -0,0 +1,211 @@
+#ib
+
+from ibapi import wrapper
+from ibapi.client import EClient
+from ibapi.utils import iswrapper #just for decorator
+
+# types
+from ibapi.common import *
+from ibapi.order_condition import *
+from ibapi.contract import *
+from ibapi.order import *
+from ibapi.order_state import *
+from ibapi.execution import Execution
+from ibapi.execution import ExecutionFilter
+from ibapi.commission_report import CommissionReport
+from ibapi.scanner import ScannerSubscription
+from ibapi.ticktype import *
+from ibapi.account_summary_tags import *
+
+# import threading
+# import queue
+from datetime import datetime
+import time
+import mysql.connector
+import itertools
+import copy
+import pandas as pd
+
+from config_demo import *
+
+#! lists used for portfolio management
+
+myLabels = [ {"code":1, "title":"Account Summaries", "last update": ""},
+ {"code":2, "title":"Account Details", "last update": ""},
+ {"code":3, "title":"Account Positions", "last update": "", "#Positions":int},
+ ]
+
+myAccountsValues = []
+# {"code":1 , "portfolio_ref": int, "tag": str, "value":float, "currency" :str, "percent":float} , 3 values per portfolio
+
+#! lists used for portfolio management END
+
+
+#! internal data structures
+
+myConIdMarketPrices = {} # e.g. myConIdMarketPrices[234563453] = 24.12
+
+myMessage = {
+ "Title": "", # "Error, Message"
+ "values": {} # "Tag":{}
+ }
+
+#! internal data structures END
+
+class reqIdCodes(): #for input / output
+
+ def __init__(self):
+ self.GET_ACCOUNTS = 1
+ self.GET_ERROR_MSG = 90
+ self.TERMINATE = 99
+
+class errorCodes():
+
+ def __init__(self):
+ self.DEF_errorCodeConnected = 1
+ self.DEF_NoAccountPositions = 2
+
+#! functions
+
+def setLastUpdate(code:int):
+ global myLabels
+ i = 0
+ for label in myLabels:
+ if label["code"] == 1: myLabels[i]["last update"] = time.strftime("%H:%M:%S")
+ i += 1
+
+#! end of functions
+
+class IBApp(wrapper.EWrapper, EClient):
+
+ #! Initialization
+ def __init__(self):
+ # Start IB
+ wrapper.EWrapper.__init__(self)
+ EClient.__init__(self, wrapper=self)
+
+ self.reqIdCodes = reqIdCodes()
+ self.errorCodes = errorCodes()
+ self.myAccounts = myAccounts()
+
+ # init pandas
+ pd.set_option('display.expand_frame_repr', False)
+ pd.options.display.max_rows = 999
+
+ def terminate(self):
+ # now we can disconnect
+ print("terminate")
+ self.disconnect()
+
+ def menu_overview(self,orderId):
+
+ print("menu_overview")
+ i = 1
+ options = vars(reqIdCodes())
+ for (label) in options:
+ print(options[label],":", label)
+ reqId = int(input('reqId:'))
+ if reqId == self.reqIdCodes.GET_ACCOUNTS:
+ self.getAccountsSummary(reqId)
+ elif reqId == self.reqIdCodes.TERMINATE:
+ self.terminate()
+
+ #! In / Out Methods (from GUI, to GUI)
+
+ def sendOutput(self,reqId, values): # values is a list
+ if reqId == self.reqIdCodes.GET_ERROR_MSG:
+ print(values["Title"],":", values["values"]["message"])
+ return
+ else:
+ # Labels
+ for label in myLabels:
+ if label["code"] == values[0]["code"]:
+ df = pd.Series(label).to_frame()
+ print (df)
+ # Content
+ df = pd.DataFrame(values)
+ df = df[list(values[0].keys())] # don't mess up with columns orders
+ if reqId == self.reqIdCodes.GET_ACCOUNTS:
+ pd.options.display.float_format = '{:,.0f}'.format
+ print (df)
+
+
+ #! IB Requests Methods (from GUI, to GUI) END
+
+ def getAccountsSummary(self,reqId,tag=AccountSummaryTags.AllTags):
+ # Subscribing to an account's information. Only one at a time!
+ # ! [reqaaccountupdates]
+ if reqId == self.reqIdCodes.GET_ACCOUNTS:
+ myAccountsValues.clear()
+ self.reqAccountSummary(reqId, "All", "NetLiquidation,FullInitMarginReq,FullMaintMarginReq")
+ # ! [reqaaccountupdates]
+
+ def myMessage(self, reqId:int, errorCode:int, errorString:str):
+ myMessage["Title"] = "Message"
+ myMessage["values"] = { "message":time.strftime("%H:%M:%S")+" "+str(reqId)+" "+str(errorCode)+": "+errorString}
+ self.sendOutput(self.reqIdCodes.GET_ERROR_MSG,myMessage)
+
+
+ # IB wrappers
+
+ @iswrapper
+ def nextValidId(self, orderId:int):
+ print("setting nextValidOrderId: ", orderId)
+ self.nextValidOrderId = orderId
+ self.myMessage(self.reqIdCodes.GET_ERROR_MSG,self.errorCodes.DEF_errorCodeConnected,"Connected")
+ # Start here
+ self.menu_overview(orderId)
+
+
+ def error(self, reqId:TickerId, errorCode:int, errorString:str):
+ if not any(s in errorString for s in ERR_MSG_NO_SHOW):
+ myMessage["Title"] = "Error"
+ myMessage["values"] = { "message":time.strftime("%H:%M:%S")+" "+str(reqId)+" "+str(errorCode)+": "+errorString}
+ self.sendOutput(self.reqIdCodes.GET_ERROR_MSG,myMessage)
+
+
+ @iswrapper
+ # ! [accountsummary]
+ def accountSummary(self, reqId: int, account: str, tag: str, value: str,
+ currency: str):
+ super().accountSummary(reqId, account, tag, value, currency)
+
+ portfolio_ref = self.myAccounts.getAccountRef(account)
+ if tag == "NetLiquidation":
+ percent = 100.0
+ else: percent = 0.0
+ myAccountsValues.append ({"code":1 , "portfolio_ref": portfolio_ref, "tag": tag, "value": float(value), "currency": currency, "percent": percent})
+
+ # ! [accountsummary]
+
+
+ @iswrapper
+ # ! [accountsummaryend]
+ def accountSummaryEnd(self, reqId: int):
+ global myAccountsValues
+ super().accountSummaryEnd(reqId)
+ # myAccountsValues["Last Update"] = time.strftime("%Y-%m-%d %H:%M:%S")
+ # calc percent and enumerate
+ myAccountsValues = sorted(myAccountsValues, key=lambda x: (x["portfolio_ref"], -x["percent"]))
+ i = 0
+ total = 0
+ for values in myAccountsValues:
+ if values["tag"] == 'NetLiquidation': total = values["value"]
+ else: myAccountsValues[i]["percent"] = myAccountsValues[i]["value"]/total*100
+ i += 1
+ code = 1
+ setLastUpdate(1)
+ self.sendOutput(reqId,myAccountsValues)
+ self.menu_overview(0) # endless loop
+ # ! [accountsummaryend]
+
+
+def main():
+
+ # start IB
+ ibApp = IBApp()
+ ibApp.connect("127.0.0.1", 7497, clientId=123)
+ ibApp.run()
+
+if __name__ == "__main__":
+ main()
diff --git a/python/gistfile1.py b/python/gistfile1.py
new file mode 100644
index 0000000..02a0cc8
--- /dev/null
+++ b/python/gistfile1.py
@@ -0,0 +1,43 @@
+"""
+ Tutorial / Python / CherryPy / REST API
+ https://cherrypy.readthedocs.org/en/3.3.0/tutorial/REST.html
+"""
+import cherrypy
+
+songs = {
+ '1': {
+ 'title': 'Lumberjack Song',
+ 'artist': 'Canadian Guard Choir'
+ },
+
+ '2': {
+ 'title': 'Always Look On the Bright Side of Life',
+ 'artist': 'Eric Idle'
+ },
+
+ '3': {
+ 'title': 'Spam Spam Spam',
+ 'artist': 'Monty Python'
+ }
+}
+
+
+class Songs(object):
+ exposed = True
+
+ def GET(self, song_id=None):
+ if song_id is None:
+ return('Here are all the songs we have: %s' % songs)
+ elif song_id in songs:
+ song = songs[song_id]
+ return('Song with the ID %s is called %s, and the artist is %s' %
+ (song_id, song['title'], song['artist']))
+ else:
+ return('No song with the ID %s :-(' % song_id)
+
+if __name__ == '__main__':
+ cherrypy.tree.mount(Songs(), '/api/songs', {
+ '/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}
+ })
+ cherrypy.engine.start()
+ cherrypy.engine.block()