def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots self.mainWindow.pushButtonApply.released.connect(self.save_credentials) self.mainWindow.pushButtonGo.released.connect(self.execute_trade) self.mainWindow.tableAsk.clicked.connect(self.update_price_from_asks) self.mainWindow.tableBid.clicked.connect(self.update_price_from_bids) self.mainWindow.pushButtonCancel.released.connect(self.cancel_order) self.mainWindow.textBrowserStatus.anchorClicked.connect( self.order_selected) self.mainWindow.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.mainWindow.pushButtonSize.released.connect(self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect( self.update_price_best) self.mainWindow.pushButtonTotal.released.connect( self.recalculate_total) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.checkBoxLogTicker, 'tick'], [self.mainWindow.checkBoxLogTrade, 'TRADE'], [self.mainWindow.checkBoxLogDepth, 'depth'], ] # load credentials from configuration file self.load_credentials() self.show() self.raise_()
def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.slot_update_price_from_asks) self.ui.tableBid.clicked.connect(self.slot_update_price_from_bids) self.ui.pushButtonCancel.released.connect(self.cancel_order) self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_price_best) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, "tick"], [self.ui.checkBoxLogTrade, "TRADE"], [self.ui.checkBoxLogDepth, "depth"], ] # resets dynamic ui elements self.reset() # activate market self.market.start() # show main window self.adjustSize() self.show() self.raise_()
def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) self.market.signal_ticker.connect(self.update_titlebar) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.ui.tableBid.clicked.connect(self.update_edit_from_bid_book) self.ui.pushButtonCancel.released.connect(self.cancel_order) #enable clicking of OrderID links in the Trading textBrowser self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_edit_on_button) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, 'tick'], [self.ui.checkBoxLogTrade, 'trade'], [self.ui.checkBoxLogDepth, 'depth'], ] #connect the channel checkboxes to the handler that persists the #settings to the config file for x in self.logchannels: x[0].clicked.connect(self.set_ignorechan) #read the config file and re-apply previous settings self.get_ignorechan() # activate market self.market.start() # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) #User Orders TAB self.modelOwns = ModelOwns(self, self.market, preferences) self.ui.tableUserOrders.setModel(self.modelOwns) self.ui.tableUserOrders.resizeColumnsToContents() self.ui.tableUserOrders.clicked.connect(self.userorder_selected) #create the stop orders TAB. self.modelStops = ModelStops(self, self.market) self.ui.tableStopOrders.setModel(self.modelStops) self.ui.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.ui.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.ui.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.ui.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.ui.checkBoxActivateStopLossBot.clicked.connect( self.stopbot_act_deact) #for stuff in the Ticker TAB self.ui.pushButtonRefreshTicker.released.connect( self.refresh_and_display_ticker) self.ui.checkBoxAutoRefreshTicker.clicked.connect( self.autorefresh_ticker_selected) #populate the ticker tab fields. self.display_ticker() # show main window self.adjustSize() self.show() self.raise_()
class View(QMainWindow): ''' Represents the combined view / control. ''' def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) self.market.signal_ticker.connect(self.update_titlebar) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.ui.tableBid.clicked.connect(self.update_edit_from_bid_book) self.ui.pushButtonCancel.released.connect(self.cancel_order) #enable clicking of OrderID links in the Trading textBrowser self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_edit_on_button) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, 'tick'], [self.ui.checkBoxLogTrade, 'trade'], [self.ui.checkBoxLogDepth, 'depth'], ] #connect the channel checkboxes to the handler that persists the #settings to the config file for x in self.logchannels: x[0].clicked.connect(self.set_ignorechan) #read the config file and re-apply previous settings self.get_ignorechan() # activate market self.market.start() # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) #User Orders TAB self.modelOwns = ModelOwns(self, self.market, preferences) self.ui.tableUserOrders.setModel(self.modelOwns) self.ui.tableUserOrders.resizeColumnsToContents() self.ui.tableUserOrders.clicked.connect(self.userorder_selected) #create the stop orders TAB. self.modelStops = ModelStops(self, self.market) self.ui.tableStopOrders.setModel(self.modelStops) self.ui.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.ui.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.ui.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.ui.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.ui.checkBoxActivateStopLossBot.clicked.connect( self.stopbot_act_deact) #for stuff in the Ticker TAB self.ui.pushButtonRefreshTicker.released.connect( self.refresh_and_display_ticker) self.ui.checkBoxAutoRefreshTicker.clicked.connect( self.autorefresh_ticker_selected) #populate the ticker tab fields. self.display_ticker() # show main window self.adjustSize() self.show() self.raise_() def get_ignorechan(self): for x in self.logchannels: for y in self.preferences.ignore_channels: if x[1] == y: x[0].setChecked(False) def set_ignorechan(self): ignore_channels = "" for x in self.logchannels: if not x[0].isChecked(): ignore_channels += (x[1] + ' ') self.preferences.set("ignore_channels", ignore_channels) self.preferences.save() def adjust_for_mac(self): ''' Fixes some stuff that looks good on windows but bad on mac. ''' # the default fixed font is unreadable on mac, so replace it font = QtGui.QFont('Monaco', 11) changes = [ self.ui.tableAsk, self.ui.tableBid, self.ui.tableUserOrders, self.ui.tableStopOrders, self.ui.textBrowserLog, self.ui.textBrowserStatus, self.ui.lineEditOrder, self.ui.doubleSpinBoxBtc, self.ui.doubleSpinBoxPrice, self.ui.doubleSpinBoxTotal ] for x in changes: x.setFont(font) # the space between application title bar and # the ui elements is too small on mac margins = self.ui.widgetMain.layout().contentsMargins() margins.setTop(24) self.ui.widgetMain.layout().setContentsMargins(margins) def show_preferences(self): result = self.preferences.show() if result == True: self.status_message('Preferences changed, restarting market.') self.market.stop() self.preferences.apply() self.market.start() self.status_message('Market restarted successfully.') def display_ticker(self): if not self.market.ticker.ticker_fast.get("error"): self.ui.lineEdit1Buy.setText("$" + self.market.ticker.buy) self.ui.lineEdit2Sell.setText("$" + self.market.ticker.sell) self.ui.lineEdit3Last.setText("$" + self.market.ticker.last) else: self.ui.lineEdit1Buy.setText("Error") self.ui.lineEdit2Sell.setText("Error") self.ui.lineEdit3Last.setText("Error") if not self.market.ticker.ticker2.get("error"): self.ui.lineEdit4Volume.setText(self.market.ticker.volumestr) self.ui.lineEdit5High.setText("$" + self.market.ticker.high) self.ui.lineEdit6Low.setText("$" + self.market.ticker.low) self.ui.lineEdit7Avg.setText("$" + self.market.ticker.avg) self.ui.lineEdit8VWAP.setText("$" + self.market.ticker.vwap) else: self.ui.lineEdit4Volume.setText("Error") self.ui.lineEdit5High.setText("Error") self.ui.lineEdit6Low.setText("Error") self.ui.lineEdit7Avg.setText("Error") self.ui.lineEdit8VWAP.setText("Error") def refresh_and_display_ticker(self, dummy_1=None, dummy_2=None): self.market.ticker.refresh_both() self.display_ticker() def autorefresh_ticker_selected(self): if self.ui.checkBoxAutoRefreshTicker.isChecked(): interval = self.ui.spinBoxAutoRefreshTicker.value() self.market.ticker_refresh_timer = Timer(interval) self.market.ticker_refresh_timer.connect( self.refresh_and_display_ticker) else: if self.market.ticker_refresh_timer: self.market.ticker_refresh_timer.cancel() def add_stopOrder(self): size = float(self.ui.lineEdit1StopSize.text()) #read from input boxes price = float(self.ui.lineEdit2StopPrice.text()) self.ui.lineEdit1StopSize.setText('') #set input boxes to blank again self.ui.lineEdit2StopPrice.setText('') oid = len( self.market.gox.stopOrders ) + 1 #set OID number to a human number(OID# is actually just for us humans) self.market.gox.stopOrders.append([oid, size, price]) #add order to the list self.modelStops.changed() #trigger the changed function def stopOrder_selected(self, index): self.ui.lineEdit3StopID.setText( str(self.modelStops.get(index.row(), 0))) def remove_stopOrder(self): oid = self.ui.lineEdit3StopID.text() #read OID from the input box oid = int(oid) - 1 #change human OID to internal self.ui.lineEdit3StopID.setText('') #set input box to blank self.market.gox.stopOrders.remove( self.market.gox.stopOrders[oid]) #remove order from the list self.modelStops.changed() #trigger the changed function def stopbot_act_deact(self): if self.ui.checkBoxActivateStopLossBot.isChecked( ): #if the checkbox is active self.market.gox.stopbot_active = True #enable stop-loss bot else: self.market.gox.stopbot_active = False #or disable it. def update_titlebar(self, bid, ask): #change the title bar to match any updates from the ticker channel try: volstring = ", Vol: " + self.market.ticker.volumestr[:-4] + " BTC" #has some strange unicode char in it. except: volstring = "" newtitle = "MtGox Trading UI - Bid: {0}, Ask: {1}{2}".format( bid / 1E5, ask / 1E5, volstring) self.setWindowTitle( QtGui.QApplication.translate("ui", newtitle, None, QtGui.QApplication.UnicodeUTF8)) def get_selected_trade_type(self): if self.ui.radioButtonBuy.isChecked(): return 'BUY' else: return 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.ui.radioButtonBuy.toggle() else: self.ui.radioButtonSell.toggle() def slot_log(self, text): doOutput = False if self.ui.checkBoxLogSystem.isChecked(): doOutput = True for entry in self.logchannels: if entry[1] in text: doOutput = entry[0].isChecked() break if doOutput: logging.info(text) text = self.prepend_date(text) self.ui.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 logging.info(text) text = self.prepend_date(text) self.ui.textBrowserStatus.moveCursor(QTextCursor.End) self.ui.textBrowserStatus.append(text) def set_wallet_a(self, value): self.ui.pushButtonWalletA.setEnabled(value > 0) self.ui.pushButtonWalletA.setText( self.market.curr_base + ': ' + utilities.gox2str(value, self.market.curr_base)) def set_wallet_b(self, value): self.ui.pushButtonWalletB.setEnabled(value > 0) self.ui.pushButtonWalletB.setText( self.market.curr_quote + ': ' + utilities.gox2str(value, self.market.curr_quote, 5)) def get_trade_size(self): return self.ui.doubleSpinBoxBtc.value() def set_trade_size(self, value): value = utilities.gox2float(value, self.market.curr_base) self.ui.doubleSpinBoxBtc.setValue(value) def get_trade_price(self): return self.ui.doubleSpinBoxPrice.value() def set_trade_price(self, value): value = utilities.gox2float(value, self.market.curr_quote) self.ui.doubleSpinBoxPrice.setValue(value) def get_trade_total(self): return self.ui.doubleSpinBoxTotal.value() def set_trade_total(self, value): value = utilities.gox2float(value, self.market.curr_quote) self.ui.doubleSpinBoxTotal.setValue(value) def get_order_id(self): return str(self.ui.lineEditOrder.text()) def set_order_id(self, text): self.ui.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def display_wallet(self): self.set_wallet_a(self.market.get_balance(self.market.curr_base)) self.set_wallet_b(self.market.get_balance(self.market.curr_quote)) def set_trade_size_from_wallet(self): self.set_trade_size(self.market.get_balance(self.market.curr_base)) self.set_selected_trade_type('SELL') def set_trade_total_from_wallet(self): self.set_trade_total(self.market.get_balance(self.market.curr_quote)) self.set_selected_trade_type('BUY') def display_orderlag(self, ms, text): self.ui.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = price * size self.set_trade_total(utilities.float2gox(total, self.market.curr_quote)) trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message( 'Placing order: {0} {1} {2} at {3} {4} (total {5} {4})...'. format( # @IgnorePep8 trade_name, utilities.float2str(size, 8), self.market.curr_base, utilities.float2str(price, 5), self.market.curr_quote, utilities.float2str(total, 6))) priceGox = utilities.float2gox(price, self.market.curr_quote) sizeGox = utilities.float2gox(size, self.market.curr_base) if trade_type == 'BUY': self.market.buy(priceGox, sizeGox) else: self.market.sell(priceGox, sizeGox) def recalculate_size(self): #When the size button is clicked: price = self.get_trade_price() if price == 0: return total = self.get_trade_total() #divide Total by Price and fill the size edit box in. size = total / price self.set_trade_size(utilities.float2gox(size, self.market.curr_base)) def recalculate_total(self): #When the total button is clicked price = self.get_trade_price() size = self.get_trade_size() #Multiply Price by Size and fill the total edit box in total = price * size self.set_trade_total(utilities.float2gox(total, self.market.curr_quote)) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2str(size, self.market.curr_base) price = utilities.gox2str(price, self.market.curr_quote, 5) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message( "{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}" .format( # @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def userorder_selected(self, index): mapdict = {"ask": "SELL", "bid": "BUY"} self.set_selected_trade_type(mapdict[self.modelOwns.get_typ( index.row())]) self.set_trade_price(self.modelOwns.get_price(index.row())) self.set_trade_size(self.modelOwns.get_size(index.row())) self.set_order_id(self.modelOwns.get_oid(index.row())) def cancel_order(self): if self.ui.checkBoxCancelAll.isChecked(): self.market.cancel_by_type() self.ui.checkBoxCancelAll.setChecked(False) else: order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.market.cancel(order_id) def update_edit_from_ask_book(self, index): #when a order on the ask side is clicked #set the radio button to the opposite (buy) self.set_trade_price(self.modelAsk.get_price( index.row())) #set the price edit box self.set_trade_size(self.modelAsk.get_size( index.row())) #set the size edit box self.set_selected_trade_type('BUY') #set the BUY radiobutton def update_edit_from_bid_book(self, index): #when a order on the bids side is clicked #set the radio button to the opposite (sell) self.set_trade_price(self.modelBid.get_price( index.row())) #set the price edit box self.set_trade_size(self.modelBid.get_size( index.row())) #set the size edit box self.set_selected_trade_type('SELL') #set the SELL radiobutton def update_edit_on_button(self): #When Price button is clicked, fill in the edit boxes, trade_type = self.get_selected_trade_type() #depending on which radiobutton "Buy" or "Sell" is selected at the time, #get the OPPOSITE side's current best price and the corresponding size mapdict = {"SELL": self.modelBid, "BUY": self.modelAsk} self.set_trade_price(mapdict[trade_type].get_price(0)) self.set_trade_size(mapdict[trade_type].get_size(0)) #so you can fulfill that person's current best offer just by clicking Go. #(This functionality is something I want, and I can understand it being confusing having it on this button) #because the size button behaves normally the original way still. #similarly confusing having it map to the opposite side (but necessary) # def stop(self): self.market.stop()
def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots self.mainWindow.pushButtonApply.released.connect( self.save_credentials) self.mainWindow.pushButtonGo.released.connect( self.execute_trade) self.mainWindow.tableAsk.clicked.connect( self.update_price_from_asks) self.mainWindow.tableBid.clicked.connect( self.update_price_from_bids) self.mainWindow.pushButtonCancel.released.connect( self.cancel_order) self.mainWindow.textBrowserStatus.anchorClicked.connect( self.order_selected) self.mainWindow.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.mainWindow.pushButtonSize.released.connect( self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect( self.update_price_best) self.mainWindow.pushButtonTotal.released.connect( self.recalculate_total) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.checkBoxLogTicker, 'tick'], [self.mainWindow.checkBoxLogTrade, 'TRADE'], [self.mainWindow.checkBoxLogDepth, 'depth'], ] # load credentials from configuration file self.load_credentials() self.show() self.raise_()
class View(QMainWindow): ''' Represents the combined view / control. ''' # how the application-proposed bid will differ from the selected bid ADD_TO_BID = 1 # how the application-proposed ask will differ from the selected ask SUB_FROM_ASK = 1 PASSPHRASE = 'fffuuuuuuu' def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots self.mainWindow.pushButtonApply.released.connect( self.save_credentials) self.mainWindow.pushButtonGo.released.connect( self.execute_trade) self.mainWindow.tableAsk.clicked.connect( self.update_price_from_asks) self.mainWindow.tableBid.clicked.connect( self.update_price_from_bids) self.mainWindow.pushButtonCancel.released.connect( self.cancel_order) self.mainWindow.textBrowserStatus.anchorClicked.connect( self.order_selected) self.mainWindow.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.mainWindow.pushButtonSize.released.connect( self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect( self.update_price_best) self.mainWindow.pushButtonTotal.released.connect( self.recalculate_total) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.checkBoxLogTicker, 'tick'], [self.mainWindow.checkBoxLogTrade, 'TRADE'], [self.mainWindow.checkBoxLogDepth, 'depth'], ] # load credentials from configuration file self.load_credentials() self.show() self.raise_() def restart_gox(self): ''' Restarts gox by closing the connection (recommended by prof7bit) ''' if self.gox.client.socket: self.gox.client.socket.close() def get_selected_trade_type(self): if self.mainWindow.radioButtonBuy.isChecked(): return 'BUY' else: return 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.mainWindow.radioButtonBuy.toggle() else: self.mainWindow.radioButtonSell.toggle() def log(self, text): text = self.prepend_date(text) self.log_to_file(text) doOutput = False if self.mainWindow.checkBoxLogSystem.isChecked(): doOutput = True else: for entry in self.logchannels: if entry[0].isChecked() and entry[1] in text: doOutput = True if doOutput: self.mainWindow.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def log_to_file(self, text): if not self.logfile.closed: self.logfile.write('{}{}'.format(text, os.linesep)) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 self.mainWindow.textBrowserStatus.moveCursor(QTextCursor.End) text = self.prepend_date(text) self.mainWindow.textBrowserStatus.append(text) self.log_to_file(text) def set_wallet_btc(self, value): self.mainWindow.pushButtonWalletA.setEnabled(value > 0) self.mainWindow.pushButtonWalletA.setText( 'BTC: ' + utilities.internal2str(value)) def set_wallet_usd(self, value): self.mainWindow.pushButtonWalletB.setEnabled(value > 0) self.mainWindow.pushButtonWalletB.setText( 'USD: ' + utilities.internal2str(value, 5)) def get_trade_size(self): value = self.mainWindow.doubleSpinBoxBtc.value() return utilities.float2internal(value) def set_trade_size(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxBtc.setValue(value_float) def get_trade_price(self): value = self.mainWindow.doubleSpinBoxPrice.value() return utilities.float2internal(value) def set_trade_price(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxPrice.setValue(value_float) def get_trade_total(self): value = self.mainWindow.doubleSpinBoxTotal.value() return utilities.float2internal(value) def set_trade_total(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxTotal.setValue(value_float) def get_order_id(self): return str(self.mainWindow.lineEditOrder.text()) def set_order_id(self, text): self.mainWindow.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def save_credentials(self): ''' Tries to encrypt the credentials entered by the user and save them to the configuration file. Incomplete or inplausible credentials will not be saved. ''' key = str(format(self.mainWindow.lineEditKey.text())) secret = str(self.mainWindow.lineEditSecret.text()) if key == '': self.status_message("Credentials not saved (empty key).") return if secret == '': self.status_message("Credentials not saved (empty secret).") return try: utilities.assert_valid_key(key) except Exception: self.status_message("Credentials not saved (invalid key).") return try: secret = utilities.encrypt(secret, View.PASSPHRASE) except Exception: self.status_message("Credentials not saved (invalid secret).") return self.gox.config.set("gox", "secret_key", key) self.gox.config.set("gox", "secret_secret", secret) self.gox.config.save() self.status_message("Credentials saved.") self.load_credentials() self.restart_gox() def load_credentials(self): ''' Tries to load the credentials from the configuration file and display them to the user. If the credentials in the configuration file are invalid, they will not be loaded. ''' key = self.gox.config.get_string("gox", "secret_key") secret = self.gox.config.get_string("gox", "secret_secret") try: utilities.assert_valid_key(key) secret = utilities.decrypt(secret, View.PASSPHRASE) except Exception: key = '' secret = '' self.secret.key = key self.mainWindow.lineEditKey.setText(key) self.secret.secret = secret self.mainWindow.lineEditSecret.setText(secret) def display_wallet(self): self.set_wallet_usd( utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_wallet_btc( utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) def set_trade_size_from_wallet(self): self.set_trade_size( utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) self.set_selected_trade_type('SELL') def set_trade_total_from_wallet(self): self.set_trade_total( utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_selected_trade_type('BUY') def display_orderlag(self, ms, text): self.mainWindow.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = self.get_trade_total() trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message('Placing order: {0} {1} BTC at {2} USD (total {3} USD)...'.format(# @IgnorePep8 trade_name, utilities.internal2str(size), utilities.internal2str(price, 5), utilities.internal2str(total, 5))) sizeGox = utilities.internal2gox(size, 'BTC') priceGox = utilities.internal2gox(price, 'USD') if trade_type == 'BUY': self.gox.buy(priceGox, sizeGox) else: self.gox.sell(priceGox, sizeGox) def recalculate_size(self): price = self.get_trade_price() if price == 0: return total = self.get_trade_total() size = utilities.divide_internal(total, price) self.set_trade_size(size) def recalculate_total(self): price = self.get_trade_price() size = self.get_trade_size() total = utilities.multiply_internal(price, size) self.set_trade_total(total) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2internal(size, 'BTC') price = utilities.gox2internal(price, 'USD') size = utilities.internal2str(size) price = utilities.internal2str(price) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message("{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}".format(# @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def update_price_from_asks(self, index): self.set_trade_price(self.modelAsk.get_price(index.row()) - self.SUB_FROM_ASK) def update_price_from_bids(self, index): self.set_trade_price(self.modelBid.get_price(index.row()) + self.ADD_TO_BID) def cancel_order(self): order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.gox.cancel(order_id) def update_price_best(self): trade_type = self.get_selected_trade_type() if trade_type == 'BUY': price = self.modelBid.get_price(0) price += self.ADD_TO_BID self.set_trade_price(price) elif trade_type == 'SELL': price = self.modelAsk.get_price(0) price -= self.SUB_FROM_ASK self.set_trade_price(price)
def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) self.market.signal_ticker.connect(self.update_titlebar) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.ui.tableBid.clicked.connect(self.update_edit_from_bid_book) self.ui.pushButtonCancel.released.connect(self.cancel_order) #enable clicking of OrderID links in the Trading textBrowser self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_edit_on_button) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, 'tick'], [self.ui.checkBoxLogTrade, 'trade'], [self.ui.checkBoxLogDepth, 'depth'], ] #connect the channel checkboxes to the handler that persists the #settings to the config file for x in self.logchannels: x[0].clicked.connect(self.set_ignorechan) #read the config file and re-apply previous settings self.get_ignorechan() # activate market self.market.start() # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) #User Orders TAB self.modelOwns = ModelOwns(self, self.market, preferences) self.ui.tableUserOrders.setModel(self.modelOwns) self.ui.tableUserOrders.resizeColumnsToContents() self.ui.tableUserOrders.clicked.connect(self.userorder_selected) #create the stop orders TAB. self.modelStops = ModelStops(self, self.market) self.ui.tableStopOrders.setModel(self.modelStops) self.ui.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.ui.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.ui.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.ui.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.ui.checkBoxActivateStopLossBot.clicked.connect(self.stopbot_act_deact) #for stuff in the Ticker TAB self.ui.pushButtonRefreshTicker.released.connect(self.refresh_and_display_ticker) self.ui.checkBoxAutoRefreshTicker.clicked.connect(self.autorefresh_ticker_selected) #populate the ticker tab fields. self.display_ticker() # show main window self.adjustSize() self.show() self.raise_()
class View(QMainWindow): ''' Represents the combined view / control. ''' def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) self.market.signal_ticker.connect(self.update_titlebar) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.ui.tableBid.clicked.connect(self.update_edit_from_bid_book) self.ui.pushButtonCancel.released.connect(self.cancel_order) #enable clicking of OrderID links in the Trading textBrowser self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_edit_on_button) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, 'tick'], [self.ui.checkBoxLogTrade, 'trade'], [self.ui.checkBoxLogDepth, 'depth'], ] #connect the channel checkboxes to the handler that persists the #settings to the config file for x in self.logchannels: x[0].clicked.connect(self.set_ignorechan) #read the config file and re-apply previous settings self.get_ignorechan() # activate market self.market.start() # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) #User Orders TAB self.modelOwns = ModelOwns(self, self.market, preferences) self.ui.tableUserOrders.setModel(self.modelOwns) self.ui.tableUserOrders.resizeColumnsToContents() self.ui.tableUserOrders.clicked.connect(self.userorder_selected) #create the stop orders TAB. self.modelStops = ModelStops(self, self.market) self.ui.tableStopOrders.setModel(self.modelStops) self.ui.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.ui.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.ui.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.ui.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.ui.checkBoxActivateStopLossBot.clicked.connect(self.stopbot_act_deact) #for stuff in the Ticker TAB self.ui.pushButtonRefreshTicker.released.connect(self.refresh_and_display_ticker) self.ui.checkBoxAutoRefreshTicker.clicked.connect(self.autorefresh_ticker_selected) #populate the ticker tab fields. self.display_ticker() # show main window self.adjustSize() self.show() self.raise_() def get_ignorechan(self): for x in self.logchannels: for y in self.preferences.ignore_channels: if x[1] == y: x[0].setChecked(False) def set_ignorechan(self): ignore_channels = "" for x in self.logchannels: if not x[0].isChecked(): ignore_channels += (x[1] + ' ') self.preferences.set("ignore_channels",ignore_channels) self.preferences.save() def adjust_for_mac(self): ''' Fixes some stuff that looks good on windows but bad on mac. ''' # the default fixed font is unreadable on mac, so replace it font = QtGui.QFont('Monaco', 11) changes = [ self.ui.tableAsk, self.ui.tableBid, self.ui.tableUserOrders, self.ui.tableStopOrders, self.ui.textBrowserLog, self.ui.textBrowserStatus, self.ui.lineEditOrder, self.ui.doubleSpinBoxBtc, self.ui.doubleSpinBoxPrice, self.ui.doubleSpinBoxTotal ] for x in changes: x.setFont(font) # the space between application title bar and # the ui elements is too small on mac margins = self.ui.widgetMain.layout().contentsMargins() margins.setTop(24) self.ui.widgetMain.layout().setContentsMargins(margins) def show_preferences(self): result = self.preferences.show() if result == True: self.status_message('Preferences changed, restarting market.') self.market.stop() self.preferences.apply() self.market.start() self.status_message('Market restarted successfully.') def display_ticker(self): if not self.market.ticker.ticker_fast.get("error"): self.ui.lineEdit1Buy.setText("$" + self.market.ticker.buy) self.ui.lineEdit2Sell.setText("$" + self.market.ticker.sell) self.ui.lineEdit3Last.setText("$" + self.market.ticker.last) else: self.ui.lineEdit1Buy.setText("Error") self.ui.lineEdit2Sell.setText("Error") self.ui.lineEdit3Last.setText("Error") if not self.market.ticker.ticker2.get("error"): self.ui.lineEdit4Volume.setText(self.market.ticker.volumestr) self.ui.lineEdit5High.setText("$" + self.market.ticker.high) self.ui.lineEdit6Low.setText("$" + self.market.ticker.low) self.ui.lineEdit7Avg.setText("$" + self.market.ticker.avg) self.ui.lineEdit8VWAP.setText("$" + self.market.ticker.vwap) else: self.ui.lineEdit4Volume.setText("Error") self.ui.lineEdit5High.setText("Error") self.ui.lineEdit6Low.setText("Error") self.ui.lineEdit7Avg.setText("Error") self.ui.lineEdit8VWAP.setText("Error") def refresh_and_display_ticker(self,dummy_1=None,dummy_2=None): self.market.ticker.refresh_both() self.display_ticker() def autorefresh_ticker_selected(self): if self.ui.checkBoxAutoRefreshTicker.isChecked(): interval = self.ui.spinBoxAutoRefreshTicker.value() self.market.ticker_refresh_timer = Timer(interval) self.market.ticker_refresh_timer.connect(self.refresh_and_display_ticker) else: if self.market.ticker_refresh_timer: self.market.ticker_refresh_timer.cancel() def add_stopOrder(self): size = float(self.ui.lineEdit1StopSize.text()) #read from input boxes price = float(self.ui.lineEdit2StopPrice.text()) self.ui.lineEdit1StopSize.setText('') #set input boxes to blank again self.ui.lineEdit2StopPrice.setText('') oid = len(self.market.gox.stopOrders)+1 #set OID number to a human number(OID# is actually just for us humans) self.market.gox.stopOrders.append([oid,size,price]) #add order to the list self.modelStops.changed() #trigger the changed function def stopOrder_selected(self, index): self.ui.lineEdit3StopID.setText(str(self.modelStops.get(index.row(),0))) def remove_stopOrder(self): oid = self.ui.lineEdit3StopID.text() #read OID from the input box oid = int(oid)-1 #change human OID to internal self.ui.lineEdit3StopID.setText('') #set input box to blank self.market.gox.stopOrders.remove(self.market.gox.stopOrders[oid]) #remove order from the list self.modelStops.changed() #trigger the changed function def stopbot_act_deact(self): if self.ui.checkBoxActivateStopLossBot.isChecked(): #if the checkbox is active self.market.gox.stopbot_active = True #enable stop-loss bot else: self.market.gox.stopbot_active = False #or disable it. def update_titlebar(self,bid,ask): #change the title bar to match any updates from the ticker channel try: volstring = ", Vol: " + self.market.ticker.volumestr[:-4] + " BTC" #has some strange unicode char in it. except: volstring = "" newtitle = "MtGox Trading UI - Bid: {0}, Ask: {1}{2}".format(bid/1E5,ask/1E5,volstring) self.setWindowTitle(QtGui.QApplication.translate("ui", newtitle, None, QtGui.QApplication.UnicodeUTF8)) def get_selected_trade_type(self): if self.ui.radioButtonBuy.isChecked(): return 'BUY' else: return 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.ui.radioButtonBuy.toggle() else: self.ui.radioButtonSell.toggle() def slot_log(self, text): doOutput = False if self.ui.checkBoxLogSystem.isChecked(): doOutput = True for entry in self.logchannels: if entry[1] in text: doOutput = entry[0].isChecked() break if doOutput: logging.info(text) text = self.prepend_date(text) self.ui.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 logging.info(text) text = self.prepend_date(text) self.ui.textBrowserStatus.moveCursor(QTextCursor.End) self.ui.textBrowserStatus.append(text) def set_wallet_a(self, value): self.ui.pushButtonWalletA.setEnabled(value > 0) self.ui.pushButtonWalletA.setText( self.market.curr_base + ': ' + utilities.gox2str(value, self.market.curr_base)) def set_wallet_b(self, value): self.ui.pushButtonWalletB.setEnabled(value > 0) self.ui.pushButtonWalletB.setText( self.market.curr_quote + ': ' + utilities.gox2str(value, self.market.curr_quote, 5)) def get_trade_size(self): return self.ui.doubleSpinBoxBtc.value() def set_trade_size(self, value): value = utilities.gox2float(value,self.market.curr_base) self.ui.doubleSpinBoxBtc.setValue(value) def get_trade_price(self): return self.ui.doubleSpinBoxPrice.value() def set_trade_price(self, value): value = utilities.gox2float(value,self.market.curr_quote) self.ui.doubleSpinBoxPrice.setValue(value) def get_trade_total(self): return self.ui.doubleSpinBoxTotal.value() def set_trade_total(self, value): value = utilities.gox2float(value,self.market.curr_quote) self.ui.doubleSpinBoxTotal.setValue(value) def get_order_id(self): return str(self.ui.lineEditOrder.text()) def set_order_id(self, text): self.ui.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def display_wallet(self): self.set_wallet_a(self.market.get_balance(self.market.curr_base)) self.set_wallet_b(self.market.get_balance(self.market.curr_quote)) def set_trade_size_from_wallet(self): self.set_trade_size(self.market.get_balance(self.market.curr_base)) self.set_selected_trade_type('SELL') def set_trade_total_from_wallet(self): self.set_trade_total(self.market.get_balance(self.market.curr_quote)) self.set_selected_trade_type('BUY') def display_orderlag(self, ms, text): self.ui.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = price * size self.set_trade_total(utilities.float2gox(total,self.market.curr_quote)) trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message('Placing order: {0} {1} {2} at {3} {4} (total {5} {4})...'.format(# @IgnorePep8 trade_name, utilities.float2str(size, 8), self.market.curr_base, utilities.float2str(price, 5), self.market.curr_quote, utilities.float2str(total, 6))) priceGox = utilities.float2gox(price, self.market.curr_quote) sizeGox = utilities.float2gox(size, self.market.curr_base) if trade_type == 'BUY': self.market.buy(priceGox, sizeGox) else: self.market.sell(priceGox, sizeGox) def recalculate_size(self): #When the size button is clicked: price = self.get_trade_price() if price == 0: return total = self.get_trade_total() #divide Total by Price and fill the size edit box in. size = total / price self.set_trade_size(utilities.float2gox(size,self.market.curr_base)) def recalculate_total(self): #When the total button is clicked price = self.get_trade_price() size = self.get_trade_size() #Multiply Price by Size and fill the total edit box in total = price * size self.set_trade_total(utilities.float2gox(total,self.market.curr_quote)) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2str(size, self.market.curr_base) price = utilities.gox2str(price, self.market.curr_quote,5) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message("{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}".format(# @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def userorder_selected(self, index): mapdict = {"ask":"SELL","bid":"BUY"} self.set_selected_trade_type(mapdict[self.modelOwns.get_typ(index.row())]) self.set_trade_price(self.modelOwns.get_price(index.row())) self.set_trade_size(self.modelOwns.get_size(index.row())) self.set_order_id(self.modelOwns.get_oid(index.row())) def cancel_order(self): if self.ui.checkBoxCancelAll.isChecked(): self.market.cancel_by_type() self.ui.checkBoxCancelAll.setChecked(False) else: order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.market.cancel(order_id) def update_edit_from_ask_book(self, index): #when a order on the ask side is clicked #set the radio button to the opposite (buy) self.set_trade_price(self.modelAsk.get_price(index.row())) #set the price edit box self.set_trade_size(self.modelAsk.get_size(index.row())) #set the size edit box self.set_selected_trade_type('BUY') #set the BUY radiobutton def update_edit_from_bid_book(self, index): #when a order on the bids side is clicked #set the radio button to the opposite (sell) self.set_trade_price(self.modelBid.get_price(index.row())) #set the price edit box self.set_trade_size(self.modelBid.get_size(index.row())) #set the size edit box self.set_selected_trade_type('SELL') #set the SELL radiobutton def update_edit_on_button(self): #When Price button is clicked, fill in the edit boxes, trade_type = self.get_selected_trade_type() #depending on which radiobutton "Buy" or "Sell" is selected at the time, #get the OPPOSITE side's current best price and the corresponding size mapdict = {"SELL":self.modelBid,"BUY":self.modelAsk} self.set_trade_price(mapdict[trade_type].get_price(0)) self.set_trade_size(mapdict[trade_type].get_size(0)) #so you can fulfill that person's current best offer just by clicking Go. #(This functionality is something I want, and I can understand it being confusing having it on this button) #because the size button behaves normally the original way still. #similarly confusing having it map to the opposite side (but necessary) # def stop(self): self.market.stop()
class View(QMainWindow): """ Represents the combined view / control. """ # how the application-proposed bid # will differ from the selected bid (in pips) ADD_TO_BID_PIPS = 1 # how the application-proposed ask # will differ from the selected ask (in pips) SUB_FROM_ASK_PIPS = 1 def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.slot_update_price_from_asks) self.ui.tableBid.clicked.connect(self.slot_update_price_from_bids) self.ui.pushButtonCancel.released.connect(self.cancel_order) self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_price_best) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market, preferences) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market, preferences) self.ui.tableBid.setModel(self.modelBid) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, "tick"], [self.ui.checkBoxLogTrade, "TRADE"], [self.ui.checkBoxLogDepth, "depth"], ] # resets dynamic ui elements self.reset() # activate market self.market.start() # show main window self.adjustSize() self.show() self.raise_() def get_base_currency(self): return self.preferences.get_currency(Preferences.CURRENCY_INDEX_BASE) def get_quote_currency(self): return self.preferences.get_currency(Preferences.CURRENCY_INDEX_QUOTE) def reset(self): # initialize wallet values self.set_wallet_a(None) self.set_wallet_b(None) # adjust decimal values to current currencies self.adjust_decimals() def adjust_decimals(self): currencyQuote = self.get_quote_currency() currencyBase = self.get_base_currency() self.ui.doubleSpinBoxSize.setDecimals(currencyBase.decimals) self.ui.doubleSpinBoxPrice.setDecimals(currencyQuote.decimals) self.ui.doubleSpinBoxTotal.setDecimals(currencyQuote.decimals) def adjust_for_mac(self): """ Fixes some stuff that looks good on windows but bad on mac. """ # the default fixed font is unreadable on mac, so replace it font = QtGui.QFont("Monaco", 11) self.ui.tableAsk.setFont(font) self.ui.tableBid.setFont(font) self.ui.textBrowserLog.setFont(font) self.ui.textBrowserStatus.setFont(font) self.ui.lineEditOrder.setFont(font) self.ui.doubleSpinBoxSize.setFont(font) self.ui.doubleSpinBoxPrice.setFont(font) self.ui.doubleSpinBoxTotal.setFont(font) # the space between application title bar and # the ui elements is too small on mac margins = self.ui.widgetMain.layout().contentsMargins() margins.setTop(24) self.ui.widgetMain.layout().setContentsMargins(margins) def show_preferences(self): result = self.preferences.show() if result == True: self.status_message("Preferences changed, restarting market.") self.market.stop() self.preferences.apply() self.reset() self.market.start() self.status_message("Market restarted successfully.") def get_selected_trade_type(self): if self.ui.radioButtonBuy.isChecked(): return "BUY" else: return "SELL" def set_selected_trade_type(self, trade_type): if trade_type == "BUY": self.ui.radioButtonBuy.toggle() else: self.ui.radioButtonSell.toggle() def slot_log(self, text): logging.info(text) text = self.prepend_date(text) doOutput = False if self.ui.checkBoxLogSystem.isChecked(): doOutput = True for entry in self.logchannels: if entry[1] in text: doOutput = entry[0].isChecked() if doOutput: self.ui.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return "{}.{:0>3} {}".format(time.strftime("%X"), millis, text) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 logging.info(text) text = self.prepend_date(text) self.ui.textBrowserStatus.moveCursor(QTextCursor.End) self.ui.textBrowserStatus.append(text) def set_wallet_a(self, value): if value == None: self.ui.pushButtonWalletA.setEnabled(False) self.ui.pushButtonWalletA.setText("n/a") return self.ui.pushButtonWalletA.setEnabled(True) self.ui.pushButtonWalletA.setText(money.to_long_string(value, self.get_base_currency())) def set_wallet_b(self, value): if value == None: self.ui.pushButtonWalletB.setEnabled(False) self.ui.pushButtonWalletB.setText("n/a") return self.ui.pushButtonWalletB.setEnabled(True) self.ui.pushButtonWalletB.setText(money.to_long_string(value, self.get_quote_currency())) def get_trade_size(self): return money.to_money(self.ui.doubleSpinBoxSize.value()) def set_trade_size(self, value): self.ui.doubleSpinBoxSize.setValue(money.to_float(value)) def get_trade_price(self): return money.to_money(self.ui.doubleSpinBoxPrice.value()) def set_trade_price(self, value): self.ui.doubleSpinBoxPrice.setValue(money.to_float(value)) def get_trade_total(self): return money.to_money(self.ui.doubleSpinBoxTotal.value()) def set_trade_total(self, value): self.ui.doubleSpinBoxTotal.setValue(money.to_float(value)) def get_order_id(self): return str(self.ui.lineEditOrder.text()) def set_order_id(self, text): self.ui.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def display_wallet(self): self.set_wallet_a(self.market.get_balance(Preferences.CURRENCY_INDEX_BASE)) self.set_wallet_b(self.market.get_balance(Preferences.CURRENCY_INDEX_QUOTE)) def set_trade_size_from_wallet(self): self.set_trade_size(self.market.get_balance(Preferences.CURRENCY_INDEX_BASE)) self.set_selected_trade_type("SELL") def set_trade_total_from_wallet(self): self.set_trade_total(self.market.get_balance(Preferences.CURRENCY_INDEX_QUOTE)) self.set_selected_trade_type("BUY") def display_orderlag(self, ms, text): self.ui.labelOrderlag.setText("Trading Lag: " + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = money.multiply(price, size) trade_name = "BID" if trade_type == "BUY" else "ASK" self.status_message( "Placing order: {0} {1} at {2} (total {3})...".format( # @IgnorePep8 trade_name, money.to_long_string(size, self.get_base_currency()), money.to_long_string(price, self.get_quote_currency()), money.to_long_string(total, self.get_quote_currency()), ) ) if trade_type == "BUY": self.market.buy(price, size) else: self.market.sell(price, size) def recalculate_size(self): price = self.get_trade_price() if price == 0: return total = self.get_trade_total() size = money.divide(total, price) self.set_trade_size(size) def recalculate_total(self): price = self.get_trade_price() size = self.get_trade_size() total = money.multiply(price, size) self.set_trade_total(total) def display_userorder(self, price, size, order_type, oid, status): if order_type == "": self.status_message('Order <a href="{0}">{0}</a> {1}.'.format(oid, status)) if status == "removed" and self.get_order_id() == oid: self.set_order_id("") else: self.status_message( '{0} size: {1}, price: {2}, oid: <a href="{3}">{3}</a> - {4}'.format( # @IgnorePep8 str.upper(str(order_type)), money.to_long_string(size, self.get_base_currency()), money.to_long_string(price, self.get_quote_currency()), oid, status, ) ) if status == "post-pending": self.set_order_id(oid) def slot_update_price_from_asks(self, index): self.update_price_from_asks(index.row()) def update_price_from_asks(self, row): value = self.modelAsk.get_price(row) pip = money.pip(self.get_quote_currency()) * View.SUB_FROM_ASK_PIPS self.set_trade_price(value - pip) def slot_update_price_from_bids(self, index): self.update_price_from_bids(index.row()) def update_price_from_bids(self, row): value = self.modelBid.get_price(row) pip = money.pip(self.get_quote_currency()) * View.SUB_FROM_ASK_PIPS self.set_trade_price(value + pip) def cancel_order(self): order_id = self.get_order_id() self.status_message('Cancelling order <a href="{0}">{0}</a>...'.format(order_id)) self.market.cancel(order_id) def update_price_best(self): trade_type = self.get_selected_trade_type() if trade_type == "BUY": self.update_price_from_bids(0) elif trade_type == "SELL": self.update_price_from_asks(0) def stop(self): self.market.stop()
def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret #lazy users can create a section in the goxtool ini file such as : #[goxgui] #password = XXXXXXXX #and it will decrypt the saved credentials also in the ini file with this password try: self.passphrase = self.gox.config.get("goxgui", "password") except: self.passphrase = "" if self.passphrase: self.load_credentials(passphrase=self.passphrase) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.tickerCheckBox, 'tick'], [self.mainWindow.tradesCheckBox, 'trade'], [self.mainWindow.depthCheckBox, 'depth'], ] # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) self.adaptor.signal_ticker.connect(self.update_titlebar) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots #Account Balance TAB self.mainWindow.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) #Auth TAB self.mainWindow.pushButtonApply.released.connect(self.save_credentials) #Password TAB self.mainWindow.passwordButton.released.connect(self.load_credentials) #OrderBook TAB self.mainWindow.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.mainWindow.tableBid.clicked.connect(self.update_edit_from_bid_book) #User Orders TAB self.modelOwns = ModelOwns(self.gox) self.mainWindow.tableUserOrders.setModel(self.modelOwns) self.mainWindow.tableUserOrders.resizeColumnsToContents() self.mainWindow.tableUserOrders.clicked.connect(self.userorder_selected) #Trading Box self.mainWindow.pushButtonGo.released.connect(self.execute_trade) self.mainWindow.pushButtonCancel.released.connect(self.cancel_order) self.mainWindow.pushButtonSize.released.connect(self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect(self.update_edit_on_button) self.mainWindow.pushButtonTotal.released.connect(self.recalculate_total) #enable clicking of OrderID links in the Trading textBrowser self.mainWindow.textBrowserStatus.anchorClicked.connect(self.order_selected) #reset the mtgox socketIO socket when button is pushed. self.mainWindow.pushbuttonResetSocket.released.connect(self.restart_gox) #create the stop orders TAB. self.modelStops = ModelStops(self.gox) self.mainWindow.tableStopOrders.setModel(self.modelStops) self.mainWindow.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.mainWindow.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.mainWindow.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.mainWindow.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.mainWindow.checkBoxActivateStopLossBot.clicked.connect(self.stopbot_act_deact) #for stuff in the Ticker TAB self.mainWindow.pushButtonRefreshTicker.released.connect(self.refresh_and_display_ticker) self.mainWindow.checkBoxAutoRefreshTicker.clicked.connect(self.autorefresh_ticker_selected) self.show() self.raise_() self.initialize_ticker() self.refresh_and_display_ticker()
class View(QMainWindow): ''' Represents the combined view / control. ''' def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret #lazy users can create a section in the goxtool ini file such as : #[goxgui] #password = XXXXXXXX #and it will decrypt the saved credentials also in the ini file with this password try: self.passphrase = self.gox.config.get("goxgui", "password") except: self.passphrase = "" if self.passphrase: self.load_credentials(passphrase=self.passphrase) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.tickerCheckBox, 'tick'], [self.mainWindow.tradesCheckBox, 'trade'], [self.mainWindow.depthCheckBox, 'depth'], ] # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) self.adaptor.signal_ticker.connect(self.update_titlebar) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots #Account Balance TAB self.mainWindow.pushButtonWalletA.released.connect(self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect(self.set_trade_total_from_wallet) #Auth TAB self.mainWindow.pushButtonApply.released.connect(self.save_credentials) #Password TAB self.mainWindow.passwordButton.released.connect(self.load_credentials) #OrderBook TAB self.mainWindow.tableAsk.clicked.connect(self.update_edit_from_ask_book) self.mainWindow.tableBid.clicked.connect(self.update_edit_from_bid_book) #User Orders TAB self.modelOwns = ModelOwns(self.gox) self.mainWindow.tableUserOrders.setModel(self.modelOwns) self.mainWindow.tableUserOrders.resizeColumnsToContents() self.mainWindow.tableUserOrders.clicked.connect(self.userorder_selected) #Trading Box self.mainWindow.pushButtonGo.released.connect(self.execute_trade) self.mainWindow.pushButtonCancel.released.connect(self.cancel_order) self.mainWindow.pushButtonSize.released.connect(self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect(self.update_edit_on_button) self.mainWindow.pushButtonTotal.released.connect(self.recalculate_total) #enable clicking of OrderID links in the Trading textBrowser self.mainWindow.textBrowserStatus.anchorClicked.connect(self.order_selected) #reset the mtgox socketIO socket when button is pushed. self.mainWindow.pushbuttonResetSocket.released.connect(self.restart_gox) #create the stop orders TAB. self.modelStops = ModelStops(self.gox) self.mainWindow.tableStopOrders.setModel(self.modelStops) self.mainWindow.tableStopOrders.resizeColumnsToContents() #add stop orders into the stop database self.mainWindow.pushButton1StopAdd.released.connect(self.add_stopOrder) #on click, put Stop Order ID into the cancel button box. self.mainWindow.tableStopOrders.clicked.connect(self.stopOrder_selected) #remove a stop order self.mainWindow.pushButtonStopRemove.released.connect(self.remove_stopOrder) #activate the stop loss bot with the checkbox. self.mainWindow.checkBoxActivateStopLossBot.clicked.connect(self.stopbot_act_deact) #for stuff in the Ticker TAB self.mainWindow.pushButtonRefreshTicker.released.connect(self.refresh_and_display_ticker) self.mainWindow.checkBoxAutoRefreshTicker.clicked.connect(self.autorefresh_ticker_selected) self.show() self.raise_() self.initialize_ticker() self.refresh_and_display_ticker() def initialize_ticker(self): use_ssl = self.gox.config.get_bool("gox", "use_ssl") proto = {True: "https", False: "http"}[use_ssl] currency = self.gox.currency class Ticker(object): def __init__(self): self.buy = None self.sell = None self.last = None self.volume = None self.high = None self.low = None self.avg = None self.vwap = None self.refresh_both() def refresh_both(self): self.refresh_ticker2() self.refresh_tickerfast() def refresh_tickerfast(self): ticker_fast = http_request(proto + "://" + HTTP_HOST + "/api/2/BTC" + currency + "/money/ticker_fast") self.ticker_fast = json.loads(ticker_fast)["data"] self.create_fast(self.ticker_fast) def refresh_ticker2(self): ticker2 = http_request(proto + "://" + HTTP_HOST + "/api/2/BTC" + currency + "/money/ticker") self.ticker2 = json.loads(ticker2)["data"] self.create_ticker2(self.ticker2) def create_fast(self,ticker_fast): self.buy = ticker_fast["buy"]["value"] self.sell = ticker_fast["sell"]["value"] self.last = ticker_fast["last"]["value"] def create_ticker2(self,ticker2): self.buy = ticker2["buy"]["value"] self.sell = ticker2["sell"]["value"] self.last = ticker2["last"]["value"] self.volume = ticker2["vol"]["value"] self.volumestr = ticker2["vol"]["display"] self.high = ticker2["high"]["value"] self.low = ticker2["low"]["value"] self.avg = ticker2["avg"]["value"] self.vwap = ticker2["vwap"]["value"] self.ticker = Ticker() def display_ticker(self): if not self.ticker.ticker_fast.get("error"): self.mainWindow.lineEdit1Buy.setText("$" + self.ticker.buy) self.mainWindow.lineEdit2Sell.setText("$" + self.ticker.sell) self.mainWindow.lineEdit3Last.setText("$" + self.ticker.last) else: self.mainWindow.lineEdit1Buy.setText("Error") self.mainWindow.lineEdit2Sell.setText("Error") self.mainWindow.lineEdit3Last.setText("Error") if not self.ticker.ticker2.get("error"): self.mainWindow.lineEdit4Volume.setText(self.ticker.volumestr) self.mainWindow.lineEdit5High.setText("$" + self.ticker.high) self.mainWindow.lineEdit6Low.setText("$" + self.ticker.low) self.mainWindow.lineEdit7Avg.setText("$" + self.ticker.avg) self.mainWindow.lineEdit8VWAP.setText("$" + self.ticker.vwap) else: self.mainWindow.lineEdit4Volume.setText("Error") self.mainWindow.lineEdit5High.setText("Error") self.mainWindow.lineEdit6Low.setText("Error") self.mainWindow.lineEdit7Avg.setText("Error") self.mainWindow.lineEdit8VWAP.setText("Error") def refresh_and_display_ticker(self,dummy_1=None,dummy_2=None): self.ticker.refresh_both() self.display_ticker() def autorefresh_ticker_selected(self): if self.mainWindow.checkBoxAutoRefreshTicker.isChecked(): interval = self.mainWindow.spinBoxAutoRefreshTicker.value() self.ticker_refresh_timer = Timer(interval) self.ticker_refresh_timer.connect(self.refresh_and_display_ticker) else: if self.ticker_refresh_timer: self.ticker_refresh_timer.cancel() def add_stopOrder(self): size = float(self.mainWindow.lineEdit1StopSize.text()) #read from input boxes price = float(self.mainWindow.lineEdit2StopPrice.text()) self.mainWindow.lineEdit1StopSize.setText('') #set input boxes to blank again self.mainWindow.lineEdit2StopPrice.setText('') oid = len(self.gox.stopOrders)+1 #set OID number to a human number(OID# is actually just for us humans) self.gox.stopOrders.append([oid,size,price]) #add order to the list self.modelStops.changed() #trigger the changed function def stopOrder_selected(self, index): self.mainWindow.lineEdit3StopID.setText(str(self.modelStops.get(index.row(),0))) def remove_stopOrder(self): oid = self.mainWindow.lineEdit3StopID.text() #read OID from the input box oid = int(oid)-1 #change human OID to internal self.mainWindow.lineEdit3StopID.setText('') #set input box to blank self.gox.stopOrders.remove(self.gox.stopOrders[oid]) #remove order from the list self.modelStops.changed() #trigger the changed function def stopbot_act_deact(self): if self.mainWindow.checkBoxActivateStopLossBot.isChecked(): #if the checkbox is active self.gox.stopbot_active = True #enable stop-loss bot else: self.gox.stopbot_active = False #or disable it. def update_titlebar(self,bid,ask): #change the title bar to match any updates from the ticker channel try: volstring = ", Vol: " + self.ticker.volumestr[:-4] + " BTC" #has some strange unicode char in it. except: volstring = "" newtitle = "MtGox Trading UI - Bid: {0}, Ask: {1}{2}".format(bid/1E5,ask/1E5,volstring) self.setWindowTitle(QApplication.translate("MainWindow", newtitle, None, QApplication.UnicodeUTF8)) def restart_gox(self): self.gox.client.debug("Restarting MtGox SocketIO Client") self.gox.client.socket.close() self.gox.client.connected = False def get_selected_trade_type(self): return 'BUY' if self.mainWindow.radioButtonBuy.isChecked() else 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.mainWindow.radioButtonBuy.toggle() else: self.mainWindow.radioButtonSell.toggle() def log(self, text): text = self.prepend_date(text) self.log_to_file(text) doOutput = False for entry in self.logchannels: if not entry[0].isChecked(): #if the checkbox is unticked: if entry[1] in text: #and the message matches whichever checkbox return #then exit without printing anything #two loops are necessary, otherwise system being unchecked #will mute the 3 other checkboxes's respective messages #if we got this far, the message doesn't match any unticked boxes. if self.mainWindow.systemCheckBox.isChecked(): doOutput = True else: #if the system checkbox is NOT checked, do not print anything UNLESS: for entry in self.logchannels: #the message is one of the 3 "channels" in self.logchannels if entry[1] in text: doOutput = True if doOutput: #actually print it out,unless no boxes are ticked. self.mainWindow.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def log_to_file(self, text): if not self.logfile.closed: self.logfile.write('{}{}'.format(text, os.linesep)) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 self.mainWindow.textBrowserStatus.moveCursor(QTextCursor.End) text = self.prepend_date(text) self.mainWindow.textBrowserStatus.append(text) self.log_to_file(text) def set_wallet_btc(self, value): self.mainWindow.pushButtonWalletA.setEnabled(value > 0) self.mainWindow.pushButtonWalletA.setText( 'BTC: ' + utilities.internal2str(value)) def set_wallet_usd(self, value): self.mainWindow.pushButtonWalletB.setEnabled(value > 0) self.mainWindow.pushButtonWalletB.setText( 'USD: ' + utilities.internal2str(value, 5)) def get_trade_size(self): value = self.mainWindow.doubleSpinBoxBtc.value() return utilities.float2internal(value) def set_trade_size(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxBtc.setValue(value_float) def get_trade_price(self): value = self.mainWindow.doubleSpinBoxPrice.value() return utilities.float2internal(value) def set_trade_price(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxPrice.setValue(value_float) def get_trade_total(self): value = self.mainWindow.doubleSpinBoxTotal.value() return utilities.float2internal(value) def set_trade_total(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxTotal.setValue(value_float) def get_order_id(self): return str(self.mainWindow.lineEditOrder.text()) def set_order_id(self, text): self.mainWindow.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def userorder_selected(self, index): mapdict = {"ask":"SELL","bid":"BUY"} self.set_selected_trade_type(mapdict[self.modelOwns.get_typ(index.row())]) self.set_trade_price(self.modelOwns.get_price(index.row())) self.set_trade_size(self.modelOwns.get_size(index.row())) self.set_order_id(self.modelOwns.get_oid(index.row())) def save_credentials(self): ''' Tries to encrypt the credentials entered by the user and save them to the configuration file. Incomplete or inplausible credentials will not be saved. ''' def error_message(reason): #refactored to be a little cleaner phrase = 'Credentials not saved ' self.status_message(phrase + reason) return 0 key = str(format(self.mainWindow.lineEditKey.text())) secret = str(self.mainWindow.lineEditSecret.text()) if key == '': return error_message("(empty key).") if secret == '': return error_message("(empty secret).") #get the passphrase from Password Tab self.passphrase = str(self.mainWindow.passwordLineEdit.text()) #if the user never filled in the password box, cause an error, and #switch the current tab to the password tab for them to fill it in. if self.passphrase == '': self.mainWindow.tabWidget_1.setCurrentIndex(2) return error_message("(invalid password).") try: utilities.assert_valid_key(key) except Exception: return error_message("(invalid key).") try: secret = utilities.encrypt(secret, self.passphrase) except Exception: return error_message("(invalid secret).") self.gox.config.set("gox", "secret_key", key) self.gox.config.set("gox", "secret_secret", secret) self.gox.config.save() #if everything's OK, trigger a reload of credentials(below) self.load_credentials() def load_credentials(self,passphrase=''): ''' Tries to load the credentials from the configuration file and display them to the user. If the credentials in the configuration file are invalid, they will not be loaded. ''' savedPassword = True #a default condition is needed. key = self.gox.config.get_string("gox", "secret_key") secret = self.gox.config.get_string("gox", "secret_secret") if not passphrase: #if password is blank (default NOT stored) savedPassword = False #then change the default condition to False #and grab password from the password tab password box. self.passphrase = str(self.mainWindow.passwordLineEdit.text()) try: utilities.assert_valid_key(key) secret = utilities.decrypt(secret, self.passphrase) except Exception: key = '' secret = '' self.secret.key = key self.secret.secret = secret if not key == '' and not secret == '': #if everything is OK, set the placeholder text to show credentials were loaded OK self.mainWindow.lineEditKey.setPlaceholderText('Loaded Key From File') self.mainWindow.lineEditSecret.setPlaceholderText('Decrypted Secret Using Password') #and switch current tab back to the main Account Balance Tab self.mainWindow.tabWidget_1.setCurrentIndex(0) if not savedPassword: #check for default password. if not, restart the socket. self.status_message("Credentials changed. Restarting MtGox Client") self.restart_gox() #restart the gox socket. else: self.status_message("Key and Secret are blank. Enter them and click Apply.") self.mainWindow.tabWidget_1.setCurrentIndex(1) def display_wallet(self): self.set_wallet_usd(utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_wallet_btc(utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) #when the account balance buttons are clicked #set the size edit box to match def set_trade_size_from_wallet(self): self.set_trade_size(utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) self.set_selected_trade_type('SELL') #and check the sell radiobutton def set_trade_total_from_wallet(self): self.set_trade_total(utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_selected_trade_type('BUY') #and check the buy radiobutton def display_orderlag(self, ms, text): self.mainWindow.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = utilities.internal2str(self.get_trade_size()) price = utilities.internal2str(self.get_trade_price(), 5) total = utilities.internal2str(self.get_trade_total(), 5) trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message('Placing order: {0} {1} BTC at $ {2} USD (total $ {3} USD)...'.format(# @IgnorePep8 trade_name,size,price,total)) sizeGox = int(D(size)*B) priceGox = int(D(price)*U) mapdict = {"BUY":self.gox.buy,"SELL":self.gox.sell} mapdict[trade_type](priceGox, sizeGox) def recalculate_size(self): #When the size button is clicked: price = self.get_trade_price() if price == 0: return total = self.get_trade_total() #divide Total by Price and fill the size edit box in. size = utilities.divide_internal(total, price) self.set_trade_size(size) def recalculate_total(self): #When the total button is clicked price = self.get_trade_price() size = self.get_trade_size() #Multiply Price by Size and fill the total edit box in total = utilities.multiply_internal(price, size) self.set_trade_total(total) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2internal(size, 'BTC') price = utilities.gox2internal(price, 'USD') size = utilities.internal2str(size) price = utilities.internal2str(price,5) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message("{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}".format(# @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def cancel_order(self): order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.gox.cancel(order_id) def update_edit_from_ask_book(self, index): #when a order on the ask side is clicked #set the radio button to the opposite (buy) self.set_trade_price(self.modelAsk.get_price(index.row())) #set the price edit box self.set_trade_size(self.modelAsk.get_size(index.row())) #set the size edit box self.set_selected_trade_type('BUY') #set the BUY radiobutton def update_edit_from_bid_book(self, index): #when a order on the bids side is clicked #set the radio button to the opposite (sell) self.set_trade_price(self.modelBid.get_price(index.row())) #set the price edit box self.set_trade_size(self.modelBid.get_size(index.row())) #set the size edit box self.set_selected_trade_type('SELL') #set the SELL radiobutton def update_edit_on_button(self): #When Price button is clicked, fill in the edit boxes, trade_type = self.get_selected_trade_type() #depending on which radiobutton "Buy" or "Sell" is selected at the time, #get the OPPOSITE side's current best price and the corresponding size mapdict = {"SELL":self.modelBid,"BUY":self.modelAsk} self.set_trade_price(mapdict[trade_type].get_price(0)) self.set_trade_size(mapdict[trade_type].get_size(0)) #so you can fulfill that person's current best offer just by clicking Go. #(This functionality is something I want, and I can understand it being confusing having it on this button) #because the size button behaves normally the original way still. #similarly confusing having it map to the opposite side (but necessary) #
class View(QMainWindow): ''' Represents the combined view / control. ''' # how the application-proposed bid will differ from the selected bid ADD_TO_BID = 1000 # how the application-proposed ask will differ from the selected ask SUB_FROM_ASK = 1000 def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) # connect ui signals to our logic self.ui.pushButtonGo.released.connect( self.execute_trade) self.ui.tableAsk.clicked.connect( self.update_price_from_asks) self.ui.tableBid.clicked.connect( self.update_price_from_bids) self.ui.pushButtonCancel.released.connect( self.cancel_order) self.ui.textBrowserStatus.anchorClicked.connect( self.order_selected) self.ui.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.ui.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.ui.pushButtonSize.released.connect( self.recalculate_size) self.ui.pushButtonPrice.released.connect( self.update_price_best) self.ui.pushButtonTotal.released.connect( self.recalculate_total) self.ui.actionPreferences_2.triggered.connect( self.show_preferences) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self, self.market) self.ui.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self, self.market) self.ui.tableBid.setModel(self.modelBid) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, 'tick'], [self.ui.checkBoxLogTrade, 'TRADE'], [self.ui.checkBoxLogDepth, 'depth'], ] # activate market self.market.start() # show main window self.adjustSize() self.show() self.raise_() def adjust_for_mac(self): ''' Fixes some stuff that looks good on windows but bad on mac. ''' # the default fixed font is unreadable on mac, so replace it font = QtGui.QFont('Monaco', 11) self.ui.tableAsk.setFont(font) self.ui.tableBid.setFont(font) self.ui.textBrowserLog.setFont(font) self.ui.textBrowserStatus.setFont(font) self.ui.lineEditOrder.setFont(font) self.ui.doubleSpinBoxBtc.setFont(font) self.ui.doubleSpinBoxPrice.setFont(font) self.ui.doubleSpinBoxTotal.setFont(font) # the space between application title bar and # the ui elements is too small on mac margins = self.ui.widgetMain.layout().contentsMargins() margins.setTop(24) self.ui.widgetMain.layout().setContentsMargins(margins) def show_preferences(self): result = self.preferences.show() if result == True: self.status_message('Preferences changed, restarting market.') self.market.stop() self.preferences.apply() self.market.start() self.status_message('Market restarted successfully.') def get_selected_trade_type(self): if self.ui.radioButtonBuy.isChecked(): return 'BUY' else: return 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.ui.radioButtonBuy.toggle() else: self.ui.radioButtonSell.toggle() def slot_log(self, text): logging.info(text) text = self.prepend_date(text) doOutput = False if self.ui.checkBoxLogSystem.isChecked(): doOutput = True for entry in self.logchannels: if entry[1] in text: doOutput = entry[0].isChecked() if doOutput: self.ui.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 logging.info(text) text = self.prepend_date(text) self.ui.textBrowserStatus.moveCursor(QTextCursor.End) self.ui.textBrowserStatus.append(text) def set_wallet_btc(self, value): self.ui.pushButtonWalletA.setEnabled(value > 0) self.ui.pushButtonWalletA.setText( 'BTC: ' + utilities.internal2str(value)) def set_wallet_usd(self, value): self.ui.pushButtonWalletB.setEnabled(value > 0) self.ui.pushButtonWalletB.setText( 'USD: ' + utilities.internal2str(value, 5)) def get_trade_size(self): value = self.ui.doubleSpinBoxBtc.value() return utilities.float2internal(value) def set_trade_size(self, value): value_float = utilities.internal2float(value) self.ui.doubleSpinBoxBtc.setValue(value_float) def get_trade_price(self): value = self.ui.doubleSpinBoxPrice.value() return utilities.float2internal(value) def set_trade_price(self, value): value_float = utilities.internal2float(value) self.ui.doubleSpinBoxPrice.setValue(value_float) def get_trade_total(self): value = self.ui.doubleSpinBoxTotal.value() return utilities.float2internal(value) def set_trade_total(self, value): value_float = utilities.internal2float(value) self.ui.doubleSpinBoxTotal.setValue(value_float) def get_order_id(self): return str(self.ui.lineEditOrder.text()) def set_order_id(self, text): self.ui.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def display_wallet(self): self.set_wallet_usd( utilities.gox2internal(self.market.get_balance('USD'), 'USD')) self.set_wallet_btc( utilities.gox2internal(self.market.get_balance('BTC'), 'BTC')) def set_trade_size_from_wallet(self): self.set_trade_size( utilities.gox2internal(self.market.get_balance('BTC'), 'BTC')) self.set_selected_trade_type('SELL') def set_trade_total_from_wallet(self): self.set_trade_total( utilities.gox2internal(self.market.get_balance('USD'), 'USD')) self.set_selected_trade_type('BUY') def display_orderlag(self, ms, text): self.ui.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = self.get_trade_total() trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message('Placing order: {0} {1} BTC at {2} USD (total {3} USD)...'.format(# @IgnorePep8 trade_name, utilities.internal2str(size), utilities.internal2str(price, 5), utilities.internal2str(total, 5))) if trade_type == 'BUY': self.market.buy(price, size) else: self.market.sell(price, size) def recalculate_size(self): price = self.get_trade_price() if price == 0: return total = self.get_trade_total() size = utilities.divide_internal(total, price) self.set_trade_size(size) def recalculate_total(self): price = self.get_trade_price() size = self.get_trade_size() total = utilities.multiply_internal(price, size) self.set_trade_total(total) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2internal(size, 'BTC') price = utilities.gox2internal(price, 'USD') size = utilities.internal2str(size) price = utilities.internal2str(price) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message("{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}".format(# @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def update_price_from_asks(self, index): self.set_trade_price(self.modelAsk.get_price(index.row()) - self.SUB_FROM_ASK) def update_price_from_bids(self, index): self.set_trade_price(self.modelBid.get_price(index.row()) + self.ADD_TO_BID) def cancel_order(self): order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.market.cancel(order_id) def update_price_best(self): trade_type = self.get_selected_trade_type() if trade_type == 'BUY': price = self.modelBid.get_price(0) price += self.ADD_TO_BID self.set_trade_price(price) elif trade_type == 'SELL': price = self.modelAsk.get_price(0) price -= self.SUB_FROM_ASK self.set_trade_price(price) def stop(self): self.market.stop()
class View(QMainWindow): ''' Represents the combined view / control. ''' # how the application-proposed bid will differ from the selected bid ADD_TO_BID = 1 # how the application-proposed ask will differ from the selected ask SUB_FROM_ASK = 1 PASSPHRASE = 'fffuuuuuuu' def __init__(self, gox, secret, logfile): self.logfile = logfile QMainWindow.__init__(self) # setup UI self.mainWindow = Ui_MainWindow() self.mainWindow.setupUi(self) # setup gox objects self.gox = gox self.secret = secret # connect to gox signals self.adaptor = Adaptor(self.gox) self.adaptor.signal_log.connect(self.log) self.adaptor.signal_wallet.connect(self.display_wallet) self.adaptor.signal_orderlag.connect(self.display_orderlag) self.adaptor.signal_userorder.connect(self.display_userorder) # initialize and connect bid / ask table models self.modelAsk = ModelAsk(self.gox) self.mainWindow.tableAsk.setModel(self.modelAsk) self.modelBid = ModelBid(self.gox) self.mainWindow.tableBid.setModel(self.modelBid) # connect signals from UI Qt components to our own slots self.mainWindow.pushButtonApply.released.connect(self.save_credentials) self.mainWindow.pushButtonGo.released.connect(self.execute_trade) self.mainWindow.tableAsk.clicked.connect(self.update_price_from_asks) self.mainWindow.tableBid.clicked.connect(self.update_price_from_bids) self.mainWindow.pushButtonCancel.released.connect(self.cancel_order) self.mainWindow.textBrowserStatus.anchorClicked.connect( self.order_selected) self.mainWindow.pushButtonWalletA.released.connect( self.set_trade_size_from_wallet) self.mainWindow.pushButtonWalletB.released.connect( self.set_trade_total_from_wallet) self.mainWindow.pushButtonSize.released.connect(self.recalculate_size) self.mainWindow.pushButtonPrice.released.connect( self.update_price_best) self.mainWindow.pushButtonTotal.released.connect( self.recalculate_total) # associate log channels with their check boxes self.logchannels = [ [self.mainWindow.checkBoxLogTicker, 'tick'], [self.mainWindow.checkBoxLogTrade, 'TRADE'], [self.mainWindow.checkBoxLogDepth, 'depth'], ] # load credentials from configuration file self.load_credentials() self.show() self.raise_() def restart_gox(self): ''' Restarts gox by closing the connection (recommended by prof7bit) ''' if self.gox.client.socket: self.gox.client.socket.close() def get_selected_trade_type(self): if self.mainWindow.radioButtonBuy.isChecked(): return 'BUY' else: return 'SELL' def set_selected_trade_type(self, trade_type): if trade_type == 'BUY': self.mainWindow.radioButtonBuy.toggle() else: self.mainWindow.radioButtonSell.toggle() def log(self, text): text = self.prepend_date(text) self.log_to_file(text) doOutput = False if self.mainWindow.checkBoxLogSystem.isChecked(): doOutput = True else: for entry in self.logchannels: if entry[0].isChecked() and entry[1] in text: doOutput = True if doOutput: self.mainWindow.textBrowserLog.append(text) def prepend_date(self, text): millis = int(round(time.time() * 1000)) % 1000 return '{}.{:0>3} {}'.format(time.strftime('%X'), millis, text) def log_to_file(self, text): if not self.logfile.closed: self.logfile.write('{}{}'.format(text, os.linesep)) def status_message(self, text): # call move cursor before append to work around link clicking bug # see: https://bugreports.qt-project.org/browse/QTBUG-539 self.mainWindow.textBrowserStatus.moveCursor(QTextCursor.End) text = self.prepend_date(text) self.mainWindow.textBrowserStatus.append(text) self.log_to_file(text) def set_wallet_btc(self, value): self.mainWindow.pushButtonWalletA.setEnabled(value > 0) self.mainWindow.pushButtonWalletA.setText( 'BTC: ' + utilities.internal2str(value)) def set_wallet_usd(self, value): self.mainWindow.pushButtonWalletB.setEnabled(value > 0) self.mainWindow.pushButtonWalletB.setText( 'USD: ' + utilities.internal2str(value, 5)) def get_trade_size(self): value = self.mainWindow.doubleSpinBoxBtc.value() return utilities.float2internal(value) def set_trade_size(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxBtc.setValue(value_float) def get_trade_price(self): value = self.mainWindow.doubleSpinBoxPrice.value() return utilities.float2internal(value) def set_trade_price(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxPrice.setValue(value_float) def get_trade_total(self): value = self.mainWindow.doubleSpinBoxTotal.value() return utilities.float2internal(value) def set_trade_total(self, value): value_float = utilities.internal2float(value) self.mainWindow.doubleSpinBoxTotal.setValue(value_float) def get_order_id(self): return str(self.mainWindow.lineEditOrder.text()) def set_order_id(self, text): self.mainWindow.lineEditOrder.setText(text) def order_selected(self, url): self.set_order_id(str(url.toString())) def save_credentials(self): ''' Tries to encrypt the credentials entered by the user and save them to the configuration file. Incomplete or inplausible credentials will not be saved. ''' key = str(format(self.mainWindow.lineEditKey.text())) secret = str(self.mainWindow.lineEditSecret.text()) if key == '': self.status_message("Credentials not saved (empty key).") return if secret == '': self.status_message("Credentials not saved (empty secret).") return try: utilities.assert_valid_key(key) except Exception: self.status_message("Credentials not saved (invalid key).") return try: secret = utilities.encrypt(secret, View.PASSPHRASE) except Exception: self.status_message("Credentials not saved (invalid secret).") return self.gox.config.set("gox", "secret_key", key) self.gox.config.set("gox", "secret_secret", secret) self.gox.config.save() self.status_message("Credentials saved.") self.load_credentials() self.restart_gox() def load_credentials(self): ''' Tries to load the credentials from the configuration file and display them to the user. If the credentials in the configuration file are invalid, they will not be loaded. ''' key = self.gox.config.get_string("gox", "secret_key") secret = self.gox.config.get_string("gox", "secret_secret") try: utilities.assert_valid_key(key) secret = utilities.decrypt(secret, View.PASSPHRASE) except Exception: key = '' secret = '' self.secret.key = key self.mainWindow.lineEditKey.setText(key) self.secret.secret = secret self.mainWindow.lineEditSecret.setText(secret) def display_wallet(self): self.set_wallet_usd( utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_wallet_btc( utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) def set_trade_size_from_wallet(self): self.set_trade_size( utilities.gox2internal(self.gox.wallet['BTC'], 'BTC')) self.set_selected_trade_type('SELL') def set_trade_total_from_wallet(self): self.set_trade_total( utilities.gox2internal(self.gox.wallet['USD'], 'USD')) self.set_selected_trade_type('BUY') def display_orderlag(self, ms, text): self.mainWindow.labelOrderlag.setText('Trading Lag: ' + text) def execute_trade(self): trade_type = self.get_selected_trade_type() size = self.get_trade_size() price = self.get_trade_price() total = self.get_trade_total() trade_name = 'BID' if trade_type == 'BUY' else 'ASK' self.status_message( 'Placing order: {0} {1} BTC at {2} USD (total {3} USD)...'. format( # @IgnorePep8 trade_name, utilities.internal2str(size), utilities.internal2str(price, 5), utilities.internal2str(total, 5))) sizeGox = utilities.internal2gox(size, 'BTC') priceGox = utilities.internal2gox(price, 'USD') if trade_type == 'BUY': self.gox.buy(priceGox, sizeGox) else: self.gox.sell(priceGox, sizeGox) def recalculate_size(self): price = self.get_trade_price() if price == 0: return total = self.get_trade_total() size = utilities.divide_internal(total, price) self.set_trade_size(size) def recalculate_total(self): price = self.get_trade_price() size = self.get_trade_size() total = utilities.multiply_internal(price, size) self.set_trade_total(total) def display_userorder(self, price, size, order_type, oid, status): size = utilities.gox2internal(size, 'BTC') price = utilities.gox2internal(price, 'USD') size = utilities.internal2str(size) price = utilities.internal2str(price) if order_type == '': self.status_message("Order <a href=\"{0}\">{0}</a> {1}.".format( oid, status)) if status == 'removed' and self.get_order_id() == oid: self.set_order_id('') else: self.status_message( "{0} size: {1}, price: {2}, oid: <a href=\"{3}\">{3}</a> - {4}" .format( # @IgnorePep8 str.upper(str(order_type)), size, price, oid, status)) if status == 'post-pending': self.set_order_id(oid) def update_price_from_asks(self, index): self.set_trade_price( self.modelAsk.get_price(index.row()) - self.SUB_FROM_ASK) def update_price_from_bids(self, index): self.set_trade_price( self.modelBid.get_price(index.row()) + self.ADD_TO_BID) def cancel_order(self): order_id = self.get_order_id() self.status_message( "Cancelling order <a href=\"{0}\">{0}</a>...".format(order_id)) self.gox.cancel(order_id) def update_price_best(self): trade_type = self.get_selected_trade_type() if trade_type == 'BUY': price = self.modelBid.get_price(0) price += self.ADD_TO_BID self.set_trade_price(price) elif trade_type == 'SELL': price = self.modelAsk.get_price(0) price -= self.SUB_FROM_ASK self.set_trade_price(price)
def __init__(self, preferences, market): QMainWindow.__init__(self) self.preferences = preferences self.market = market # set up main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # improve ui on mac if utilities.platform_is_mac(): self.adjust_for_mac() # connect market signals to our logic self.market.signal_log.connect(self.slot_log) self.market.signal_wallet.connect(self.display_wallet) self.market.signal_orderlag.connect(self.display_orderlag) self.market.signal_userorder.connect(self.display_userorder) self.market.signal_ticker.connect(self.update_ticker) # connect ui signals to our logic self.ui.pushButtonGo.released.connect(self.execute_trade) self.ui.tableAsk.clicked.connect(self.slot_update_price_from_asks) self.ui.tableBid.clicked.connect(self.slot_update_price_from_bids) self.ui.pushButtonCancel.released.connect(self.cancel_order) self.ui.textBrowserStatus.anchorClicked.connect(self.order_selected) self.ui.pushButtonSize.released.connect(self.recalculate_size) self.ui.pushButtonPrice.released.connect(self.update_price_best) self.ui.pushButtonTotal.released.connect(self.recalculate_total) self.ui.actionPreferences_2.triggered.connect(self.show_preferences) # associate log channels with their check boxes self.logchannels = [ [self.ui.checkBoxLogTicker, "tick"], [self.ui.checkBoxLogTrade, "TRADE"], [self.ui.checkBoxLogDepth, "depth"], ] # set correct resizing for the bid and ask tables self.ui.tableAsk.horizontalHeader().setResizeMode(QHeaderView.Stretch) self.ui.tableBid.horizontalHeader().setResizeMode(QHeaderView.Stretch) # set up info table self.info = Info(self, self.preferences, self.ui.tableInfo.clicked) self.ui.tableInfo.setModel(self.info) self.ui.tableInfo.horizontalHeader().setResizeMode(QHeaderView.Stretch) # connect to signals from info table self.info.signal_base_balance_clicked.connect(self.set_trade_size_from_wallet) self.info.signal_quote_balance_clicked.connect(self.set_trade_total_from_wallet) # initializes dynamic ui elements self.init() # activate market self.market.start() # show main window self.adjustSize() self.show() self.raise_()