Mercurial > lbo > hg > stex
changeset 17:77bb87bc9e7f
Client: Add spin boxes and fix layout bug
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 01 Mar 2019 21:56:07 +0100 |
parents | 227434cd0f01 |
children | 219117c06ab9 |
files | client/client.py |
diffstat | 1 files changed, 56 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/client/client.py Fri Mar 01 17:58:07 2019 +0100 +++ b/client/client.py Fri Mar 01 21:56:07 2019 +0100 @@ -129,6 +129,7 @@ stock = {} priceUpdated = core.pyqtSignal(str) + depotChanged = core.pyqtSignal() def add_stock(self, stocksym, stock): if stocksym not in self.stock: @@ -142,9 +143,10 @@ if price == 0: price = 1 if price * num > self.cash: - return False + num = int(self.cash / price) self.cash -= price * num stock.change_hold(num) + self.depotChanged.emit() return True def sell(self, stocksym, num): @@ -153,9 +155,10 @@ stock = self.stock[stocksym] price = stock.current_price if num > stock.current_num: - return False + num = stock.current_num self.cash += price * num stock.change_hold(-num) + self.depotChanged.emit() return True def update(self, message): @@ -165,6 +168,7 @@ if sym in self.stock: self.stock[sym].update(upd) self.priceUpdated.emit(sym) + self.depotChanged.emit() def total_value(self): value = 0 @@ -172,25 +176,22 @@ value += stock.current_num * stock.current_price return value - def serialize(self): + def to_dict(self): s = {'cash': self.cash, 'stock': {}, '_stockdepot': True} for sym, stock in self.stock.items(): stock_sum = {'num': stock.current_num} s['stock'][sym] = stock_sum - return json.dumps(s) + return s class DepotStock: """DepotStock is a position of stock in a single company in the depot. It manages its own price and volume as well as some statistics.""" sym = '' - mydepot = None current_price = -1 total_buy_price = 0 current_num = 0 - MAXHIST = 500 - price_history = [] def __init__(self, sym): self.sym = sym @@ -200,20 +201,17 @@ upd is a stock update message. """ - if self.current_price >= 0: - self.price_history.append(self.current_price) - if len(self.price_history) > self.MAXHIST: - self.price_history = self.price_history[25:] self.current_price = upd['price'] if upd['split']: self.current_num = self.current_num * 2 def change_hold(self, diff): - self.current_num += diff + assert -diff <= self.current_num if diff > 0: self.total_buy_price += diff * self.current_price if diff < 0: self.total_buy_price += diff * self.avg_buy_price() + self.current_num += diff def avg_buy_price(self): return self.total_buy_price / (self.current_num or 1) @@ -238,7 +236,7 @@ def __init__(self, sym, dim): super().__init__() - super().setMinimumSize(300, 200) + super().setMinimumSize(300, 300) self.sym = sym self.series = chart.QLineSeries(self) self.avg_buy_series = chart.QLineSeries(self) @@ -257,27 +255,29 @@ # update_stock sets a new stock price. def update_stock(self, value): """Update data series used for plotting graphs.""" - mn, mx = 1e9, -1e9 - for v in self.series.pointsVector(): - if v.y() < mn: - mn = v.y() - if v.y() > mx: - mx = v.y() + if value is not None: + mn, mx = 1e9, -1e9 + for v in self.series.pointsVector(): + if v.y() < mn: + mn = v.y() + if v.y() > mx: + mx = v.y() - previous, nxt = (self.current - 1) % self.MAX_LEN, (self.current + 1) % self.MAX_LEN - self.series.replace(self.current, self.current, value) + previous, nxt = (self.current - 1) % self.MAX_LEN, (self.current + 1) % self.MAX_LEN + self.series.replace(self.current, self.current, value) - self.upd_series.clear() - self.upd_series.append(self.current, 0) - self.upd_series.append(self.current, max(mx, self.avg_buy_price)) + self.upd_series.clear() + self.upd_series.append(self.current, 0) + self.upd_series.append(self.current, max(mx, self.avg_buy_price)) + + self.current += 1 + if self.current >= self.MAX_LEN: + self.current = 0 self.avg_buy_series.clear() self.avg_buy_series.append(0, self.avg_buy_price) self.avg_buy_series.append(self.MAX_LEN - 1, self.avg_buy_price) - self.current += 1 - if self.current >= self.MAX_LEN: - self.current = 0 self.plot() def plot(self): @@ -314,6 +314,15 @@ buy.clicked.connect(self.on_buy) sell = wid.QPushButton(' SELL ') sell.clicked.connect(self.on_sell) + quantity = wid.QSpinBox() + quantity.setMinimumSize(30, 30) + quantity.setMinimum(1) + quantity.setMaximum(1e5) + quantity.setAccelerated(True) + quantity.setSuffix(' pc') + quantity.setValue(10) + self.quantity_spinner = quantity + self.current_state = wid.QLineEdit() self.current_state.setReadOnly(True) self.current_state.setAlignment(core.Qt.AlignCenter) @@ -321,18 +330,21 @@ hbox = wid.QHBoxLayout() hbox.addWidget(buy) hbox.addWidget(sell) + hbox.addWidget(self.quantity_spinner) hbox.addWidget(self.current_state) return hbox def on_buy(self): - if not self.depot.buy(self.sym, 1): + if not self.depot.buy(self.sym, self.quantity_spinner.value()): print("Warning: couldn't buy {}".format(self.depotstock.sym)) self.update_values() + self.graph.update_stock(None) def on_sell(self): - if not self.depot.sell(self.sym, 1): + if not self.depot.sell(self.sym, self.quantity_spinner.value()): print("Warning: couldn't sell {}".format(self.depotstock.sym)) self.update_values() + self.graph.update_stock(None) # Triggered by the depot when there is new data for any stock (so we filter if the update is for us) @core.pyqtSlot(str) @@ -340,8 +352,8 @@ if sym != self.sym: return val = self.depotstock.current_price / 100 + self.update_values() self.graph.update_stock(val) - self.update_values() def update_values(self): val = self.depotstock.current_price / 100 @@ -357,7 +369,7 @@ def __init__(self, depot): super().__init__() self.depot = depot - self.depot.priceUpdated.connect(self.on_depot_update) + self.depot.depotChanged.connect(self.on_depot_update) self.hbox = wid.QHBoxLayout(self) self.depot_value_widget = wid.QLineEdit() @@ -366,10 +378,10 @@ self.hbox.addWidget(wid.QLabel('Current Depot Value: ')) self.hbox.addWidget(self.depot_value_widget) - self.on_depot_update('') + self.on_depot_update() - @core.pyqtSlot(str) - def on_depot_update(self, sym_): + @core.pyqtSlot() + def on_depot_update(self): stock = self.depot.total_value() / 100 cash = self.depot.cash / 100 self.depot_value_widget.setText('{:.2f} ø = {:.2f} ø (Cash) + {:.2f} ø (Stock)'.format(stock + cash, cash, stock)) @@ -408,6 +420,7 @@ except Exception as e: return + class CallbackSocket(core.QObject): """CallbackSocket sends messages to the stex server and receives responses.""" creds = None @@ -437,8 +450,9 @@ self.try_send({'_stocklogin': True}) def send_depot(self, depot): - summary = depot.serialize() - self.try_send(summary) + summary = depot.to_dict() + if not self.try_send(summary): + self.queue.pop(0) def wrap(self, msg): return json.dumps({ @@ -457,10 +471,12 @@ try: self.socket.send_string(msg) self.waiting = True + return True except Exception as e: print ('DEBUG: Send failed on REQ socket: ', e) self.queue.append(msg) assert len(self.queue) < 5 + return False @core.pyqtSlot(int) def on_reply(self, _sock): @@ -518,7 +534,7 @@ self.mainvbox.addWidget(self.depot_widget) self.mainvbox.addWidget(self.waiting) self.show() - + self.sock = ClientSocket(self.zctx, self.creds) self.sock.on_new_message.connect(self.on_new_data) self.callback_sock = CallbackSocket(self.zctx, self.creds) @@ -528,6 +544,7 @@ @core.pyqtSlot(dict) def on_new_data(self, stockdata): + """React to new stock data from the server.""" self.waiting.hide() self.depot.update(stockdata) for sym, upd in sorted(stockdata.items()): @@ -535,7 +552,9 @@ depotstock = DepotStock(sym) sg = StockGraph(sym, None) sw = StockWidget(sg, self.depot, depotstock) + sw.setParent(self) self.stock_widgets[sym] = sw + sw.show() self.add_stock_widget(sw) self.depot.add_stock(sym, depotstock) self.depot.priceUpdated.connect(sw.update)