Example #1
0
def trades(ctx, market, limit, start, stop):
    market = Market(market, bitshares_instance=ctx.bitshares)
    t = PrettyTable(["time", "quote", "base", "price"])
    t.align = 'r'
    for trade in market.trades(limit, start=start, stop=stop):
        t.add_row([
            str(trade["time"]),
            str(trade["quote"]),
            str(trade["base"]),
            "{:f} {}/{}".format(trade["price"],
                                trade["base"]["asset"]["symbol"],
                                trade["quote"]["asset"]["symbol"]),
        ])
    click.echo(str(t))
Example #2
0
def market_trade_history(market_pair: hug.types.text,
                         api_key: hug.types.text,
                         hug_timer=5):
    """Given a valid market_pair (e.g. USD:BTS) & a TX limit, output the market's trade history in JSON."""
    if (check_api_token(api_key) == True):  # Check the api key
        # API KEY VALID
        try:
            target_market = Market(market_pair)
        except:
            # Market is not valid
            return {
                'valid_market': False,
                'valid_key': True,
                'took': float(hug_timer)
            }

        temp_market_history = list(target_market.trades(limit=100))

        #print(temp_market_history)
        # (2017-12-24 15:37:21) 55.8699 USD 106.84792 BTS @ 1.912441583 BTS/USD
        market_history_json_list = []
        for market_trade in temp_market_history:
            str_market_trade = str(market_trade).split(
                " @ "
            )  # ["(2017-12-24 15:37:21) 55.8699 USD 106.84792 BTS", "1.912441583 BTS/USD"]
            trade_rate = str_market_trade[1]  # "1.912441583 BTS/USD"
            trade_time = (str_market_trade[0].split(") ")[0]).replace("(", "")
            trade_details = str_market_trade[0].split(") ")[1]
            split_trade = trade_details.split(" ")
            market_history_json_list.append({
                "datetime":
                trade_time.replace(" ", "T"),
                "bought":
                split_trade[0] + " " + split_trade[1],
                "sold":
                split_trade[2] + " " + split_trade[3],
                "rate ":
                trade_rate
            })

        return {
            'market_trade_history': market_history_json_list,
            'market': market_pair,
            'valid_market': True,
            'valid_key': True,
            'took': float(hug_timer)
        }
    else:
        # API KEY INVALID!
        return {'valid_key': False, 'took': float(hug_timer)}
Example #3
0
def trades(ctx, market, limit, start, stop):
    """ List trades in a market
    """
    market = Market(market, bitshares_instance=ctx.bitshares)
    t = [["time", "quote", "base", "price"]]
    for trade in market.trades(limit, start=start, stop=stop):
        t.append([
            str(trade["time"]),
            str(trade["quote"]),
            str(trade["base"]),
            "{:f} {}/{}".format(trade["price"],
                                trade["base"]["asset"]["symbol"],
                                trade["quote"]["asset"]["symbol"]),
        ])
    print_table(t)
Example #4
0
def trades_csv(ctx, base, asset, start, stop):
    if "T" in start:
        dt_start = formatTimeString(start)
    else:
        dt_start = formatDateString(start)
    if "T" in stop:
        dt_stop = formatTimeString(stop)
    else:
        dt_stop = formatDateString(stop)

    click.echo("%s-%s from %s to %s" % (base, asset, dt_start, dt_stop))
    pair = "%s-%s" % (base, asset)
    market = Market(pair, bitshares_instance=ctx.bitshares)
    trades = market.trades(start=dt_start, stop=dt_stop, limit=100)
    click.echo("%s,%s,%s,%s,%s" %
               ("Date", "Price", "Base_Amount", "Amount", "Pair"))
    for trade in trades:
        click.echo("%s,%s,%s,%s,%s/%s"  % \
            (trade["time"],
            trade["price"],trade["base"]["amount"], trade["quote"]["amount"],
            trade["base"]["symbol"], trade["quote"]["symbol"]))
Example #5
0
class MarketTab(QtGui.QWidget):
    def __init__(self, *args, **kwargs):
        self.iso = kwargs.pop("isolator", None)
        self.asset_a = kwargs.pop("asset_a", None)
        self.asset_b = kwargs.pop("asset_b", None)
        self._pairtag = self.asset_a["symbol"] + ":" + self.asset_b["symbol"]
        self.ping_callback = kwargs.pop("ping_callback", None)
        super(MarketTab, self).__init__(*args, **kwargs)
        self.ui = Ui_MarketTab()
        self.ui.setupUi(self)

        replaceAxis(self.ui.marketPlot, "bottom",
                    TimeAxisItem(orientation='bottom'))
        replaceAxis(
            self.ui.marketPlot, "left",
            CoinAxisItem(precision=self.asset_b["precision"],
                         orientation='left'))

        self.ui.marketPlot.setMenuEnabled(False)

        self.__vLine = pg.InfiniteLine(angle=90,
                                       movable=False,
                                       pen=PG_CROSSHAIR)
        self.__hLine = pg.InfiniteLine(angle=0,
                                       movable=False,
                                       pen=PG_CROSSHAIR)
        self.__textPrice = pg.TextItem('price')
        view = self.ui.marketPlot.getViewBox()
        view.addItem(self.__textPrice, ignoreBounds=True)
        view.addItem(self.__vLine, ignoreBounds=True)
        view.addItem(self.__hLine, ignoreBounds=True)
        view.setLimits(
            #			yMin = 0, # price never goes below zero
            xMin=datetime.datetime(2015, 1, 1).timestamp(),  # ~BTS started
            xMax=(datetime.datetime.now() +
                  datetime.timedelta(weeks=52)).timestamp()  # 1 year
        )

        self.start_time = None
        self.stop_time = None
        self.initfetch = 1
        self.ui.marketPlot.sigXRangeChanged.connect(self.updateRange)

        self.ui.marketPlot.scene().sigMouseMoved.connect(self.updateTooltip)
        #,
        #			axisItems={'bottom': TimeAxisItem(orientation='bottom')}

        self._index = 999

        self.ui.buyStack.setColumnCount(3)
        stretch_table(self.ui.buyStack, False, hidehoriz=True)
        self.ui.sellStack.setColumnCount(3)
        stretch_table(self.ui.sellStack, False, hidehoriz=True)

        self.subscribed = False
        self.updater = RemoteFetch(manager=self.iso.mainwin.Requests)

        self._frame_buy = {
            "group": self.ui.buyGroup,
            "mainLabel": self.ui.buyMainLabel,
            "mainAmt": self.ui.buyMainAmount,
            "altLabel": self.ui.buyAltLabel,
            "altAmt": self.ui.buyAltAmount,
            "price": self.ui.buyPrice,
            "account": self.ui.buyAccount,
            "expireEdit": self.ui.buyExpireEdit,
            "expireFOK": self.ui.buyFOK,
            "fee": self.ui.buyFeeAsset,
            "button": self.ui.buyButton,
            "_commit_inv": True,
            "table": self.ui.sellStack,
        }
        self._frame_sell = {
            "group": self.ui.sellGroup,
            "mainLabel": self.ui.sellMainLabel,
            "mainAmt": self.ui.sellMainAmount,
            "altLabel": self.ui.sellAltLabel,
            "altAmt": self.ui.sellAltAmount,
            "price": self.ui.sellPrice,
            "account": self.ui.sellAccount,
            "expireEdit": self.ui.sellExpireEdit,
            "expireFOK": self.ui.sellFOK,
            "fee": self.ui.sellFeeAsset,
            "button": self.ui.sellButton,
            "_commit_inv": False,
            "table": self.ui.buyStack,
        }
        self.setupFrame(self._frame_buy,
                        self.asset_a,
                        self.asset_b,
                        title="Buy ")
        self.setupFrame(self._frame_sell,
                        self.asset_a,
                        self.asset_b,
                        title="Sell ")

        #self.ui.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        #self.ui.table.customContextMenuRequested.connect(self.show_orders_submenu)

        self.ui.closeButton.clicked.connect(self.close_me)
        self.ui.swapButton.clicked.connect(self.swap_me)

    def close_me(self):
        app().mainwin.closeMarket(self._pairtag)

    def swap_me(self):
        try:
            app().mainwin.swapMarket(self._pairtag)
        except Exception as error:
            showexc(error)

    def close(self):
        # TODO: unsubscribe from market!!!
        pass

    def setupFrame(self, form, asset_a, asset_b, title="Trade "):
        form["group"].setTitle(title + asset_a["symbol"])
        form["mainLabel"].setText(asset_a["symbol"])
        form["altLabel"].setText(asset_b["symbol"])

        form["price"].valueChanged.connect(self.price_value_changed)
        form["price"].setSuffix(" " + asset_b["symbol"])
        form["price"].setDecimals(asset_b["precision"])
        form["price"].setMaximum(asset_b["options"]["max_supply"] *
                                 pow(10, asset_b["precision"]))
        form["mainAmt"].valueChanged.connect(self.main_amount_changed)
        form["mainAmt"].setDecimals(asset_a["precision"])
        form["mainAmt"].setMaximum(asset_a["options"]["max_supply"] *
                                   pow(10, asset_a["precision"]))
        form["altAmt"].valueChanged.connect(self.alt_amount_changed)
        form["altAmt"].setDecimals(asset_b["precision"])
        form["altAmt"].setMaximum(asset_b["options"]["max_supply"] *
                                  pow(10, asset_b["precision"]))

        form["table"].cellClicked.connect(self.cell_click)

        form["button"].clicked.connect(self.make_limit_order)

    def make_limit_order(self):
        form = self._frame_buy if self._frame_buy["button"] == self.sender(
        ) else self._frame_sell

        account_from = form["account"].currentText()
        sell_asset_name = self.asset_a["symbol"]
        sell_asset_amount = form["mainAmt"].value()
        buy_asset_name = self.asset_b["symbol"]
        buy_asset_amount = form["altAmt"].value()

        if form["_commit_inv"]:
            sell_asset_name = self.asset_b["symbol"]
            buy_asset_name = self.asset_a["symbol"]

        expire_seconds = deltasec(form["expireEdit"].text())
        expire_fok = form["expireFOK"].isChecked()

        fee_asset = anyvalvis(form["fee"], None)  #.currentText()
        buffer = app().mainwin.buffering()

        try:
            v = QTransactionBuilder.VSellAsset(account_from,
                                               sell_asset_name,
                                               sell_asset_amount,
                                               buy_asset_name,
                                               buy_asset_amount,
                                               expiration=expire_seconds,
                                               fill_or_kill=expire_fok,
                                               fee_asset=fee_asset,
                                               isolator=self.iso)
            if buffer:
                app().mainwin._txAppend(*v)
            else:
                QTransactionBuilder._QExec(self.iso, v)
        except Exception as error:
            showexc(error)
            return False
        return True

    def _set_spin_value(self, elem, value):
        elem.blockSignals(True)
        elem.setValue(value)
        elem.blockSignals(False)

    def price_value_changed(self):
        form = self._frame_buy if self._frame_buy["price"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if amt_a == 0 and price != 0:
            amt_a = amt_b / price
            self._set_spin_value(form["mainAmt"], amt_a)
            return

        amt_b = amt_a * price
        self._set_spin_value(form["altAmt"], amt_b)

    def main_amount_changed(self):
        form = self._frame_buy if self._frame_buy["mainAmt"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if price == 0:
            if amt_a == 0:
                return
            price = amt_b / amt_a
            self._set_spin_value(form["price"], price)
            return

        amt_b = amt_a * price
        self._set_spin_value(form["altAmt"], amt_b)

    def alt_amount_changed(self):
        form = self._frame_buy if self._frame_buy["altAmt"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if price == 0:
            if amt_a == 0:
                return
            price = amt_b / amt_a
            #form["price"].setValue(price)
            self._set_spin_value(form["price"], price)
            return

        amt_a = amt_b / price
        self._set_spin_value(form["mainAmt"], amt_a)

    def cell_click(self, row, column):
        form = self._frame_buy if self._frame_buy["table"] == self.sender(
        ) else self._frame_sell
        valstr = form["table"].item(row, column).text().replace(",", "")
        val = float(str.split(valstr, " ")[0])
        if column == 0:  # price
            form["price"].setValue(val)

        if column == 1:  # asset_a
            form["mainAmt"].setValue(val)

        if column == 2:  # asset_b
            form["altAmt"].setValue(val)

    def _refreshTitle(self):
        #self._title = _translate("MainWindow", "Market", None) + " " + self._pairtag
        self._title = self._pairtag
        return self._title

    def show_orders_submenu(self, position):
        menu = QtGui.QMenu()
        qaction(self, menu, "Cancel Order...", self.cancel_order)
        menu.exec_(self.ui.table.viewport().mapToGlobal(position))

    def cancel_order(self):
        table = self.ui.table
        indexes = table.selectionModel().selectedRows()
        if len(indexes) < 1:
            return
        index = indexes[0].row()

        order_id = table.item(index, 0).text()
        #b = table.item(index, 1).text()

        try:
            trx = QTransactionBuilder.QCancelOrder(
                self._account_name,
                order_id,
                #fee_asset=fee_asset,
                isolator=self.iso)
        except Exception as error:
            showexc(error)

    def desync(self):
        self.subscribed = False

    def resync(self):
        self.updater.fetch(self.mergeMarket_before,
                           self.iso, (self.asset_a, self.asset_b),
                           self._pairtag,
                           self.start_time,
                           self.stop_time,
                           ready_callback=self.mergeMarket_after,
                           ping_callback=self.ping_callback,
                           description="Refreshing market " + self._pairtag)

    def mergeMarket_before(self, iso, pair, tag, start, stop):

        asset_a = pair[0]
        asset_b = pair[1]
        rpc = iso.bts.rpc
        if not self.subscribed:
            s_id = rpc.get_subscription_id()
            subs = rpc.subscribe_to_market(s_id, asset_a["id"], asset_b["id"])
            self._s_id = s_id
            self._tags.append("!" + str(s_id))
            self.subscribed = True

        from bitshares.market import Market
        self.market = Market(base=asset_b,
                             quote=asset_a,
                             blockchain_instance=iso.bts)

        orders = self.market.orderbook()
        if self.initfetch:
            trades = self.market.trades(limit=100, start=start, stop=stop)
        else:
            trades = iso.getMarketBuckets(self.market["base"],
                                          self.market["quote"],
                                          start=start,
                                          stop=stop)

        return (
            orders,
            trades,
        )

    def mergeMarket_after(self, request_id, args):
        (
            orders,
            trades,
        ) = args

        self.fillTable(self.ui.buyStack, orders['bids'])
        self.fillTable(self.ui.sellStack, orders['asks'], True)
        self.plotChart(trades)

    def fillTable(self, table, orders, inv=False):
        color_a = COLOR_GREEN if inv else COLOR_RED
        color_b = COLOR_RED if inv else COLOR_GREEN
        table.setRowCount(0)

        j = -1
        for order in orders:
            j += 1

            table.insertRow(j)
            set_col(table, j, 0, price__repr(order, "base"))
            set_col(table,
                    j,
                    1,
                    str(order["quote"]),
                    color=color_a,
                    align="right")
            set_col(table,
                    j,
                    2,
                    str(order["base"]),
                    color=color_b,
                    align="right")

    def plotChart(self, trades):
        xs = []
        ys = []
        for trade in trades:
            dt = datetime.datetime.strptime(trade["date"], "%Y-%m-%dT%H:%M:%S")
            x = int(dt.timestamp())  #trade["sequence"]
            y = trade["price"]
            xs.append(x)
            ys.append(y)
        #print(xs, ys)
        #import numpy as np
        #x = np.random.normal(size=1000)
        #y = np.random.normal(size=1000)

        self.ui.marketPlot.blockSignals(True)
        self.ui.marketPlot.clear()
        self.ui.marketPlot.plot(xs, ys,
                                pen=PG_LINEPEN)  #, pen=None, symbol=None)
        obj = self.ui.marketPlot.getPlotItem()
        axis = obj.axes["bottom"]['item']
        axis._dayscale = False
        if len(xs) > 1:  # Hack: adjust X-axis display
            if (xs[-1] - xs[0]) >= 3600 * 24:  # >= 1 day
                axis._dayscale = True
        self.ui.marketPlot.blockSignals(False)

    def updateRange(self, view, rng):
        if self.initfetch > 0:
            self.initfetch -= 1
            return
        #price_low, price_high = rng[1] # (as integer)
        sec_start, sec_end = rng  #[0]
        self.start_time = datetime.datetime.fromtimestamp(sec_start)
        self.stop_time = datetime.datetime.fromtimestamp(sec_end)
        self.resync()

    def updateTooltip(self, pos):
        data = self.ui.marketPlot.getPlotItem().listDataItems()
        if len(data) < 1: return
        points = data[0].curve
        act_pos = points.mapFromScene(pos)
        xs, ys = points.getData()

        x, j = snapTo(act_pos.x(), xs)
        if j < 0:
            self.__vLine.hide()
            self.__hLine.hide()
            self.__textPrice.hide()
            return
        y = ys[j]

        #print("SNAP:", j, x, y)
        self.__vLine.setPos(x)
        self.__hLine.setPos(y)
        self.__textPrice.setPos(x, y)

        price = "{price:.{precision}f}".format(
            price=y, precision=self.asset_b["precision"])
        self.__textPrice.setText(price)

        self.__vLine.show()
        self.__hLine.show()
        self.__textPrice.show()
Example #6
0
class MarketTab(QtGui.QWidget):
    def __init__(self, *args, **kwargs):
        self.iso = kwargs.pop("isolator", None)
        self.asset_a = kwargs.pop("asset_a", None)
        self.asset_b = kwargs.pop("asset_b", None)
        self._pairtag = self.asset_a["symbol"] + ":" + self.asset_b["symbol"]
        self.ping_callback = kwargs.pop("ping_callback", None)
        super(MarketTab, self).__init__(*args, **kwargs)
        self.ui = Ui_MarketTab()
        self.ui.setupUi(self)

        replaceAxis(self.ui.marketPlot, "bottom",
                    TimeAxisItem(orientation='bottom'))
        #,
        #			axisItems={'bottom': TimeAxisItem(orientation='bottom')}

        self._index = 999

        self.ui.buyStack.setColumnCount(3)
        stretch_table(self.ui.buyStack, False, hidehoriz=True)
        self.ui.sellStack.setColumnCount(3)
        stretch_table(self.ui.sellStack, False, hidehoriz=True)

        self.subscribed = False
        self.updater = RemoteFetch()

        self._frame_buy = {
            "group": self.ui.buyGroup,
            "mainLabel": self.ui.buyMainLabel,
            "mainAmt": self.ui.buyMainAmount,
            "altLabel": self.ui.buyAltLabel,
            "altAmt": self.ui.buyAltAmount,
            "price": self.ui.buyPrice,
            "account": self.ui.buyAccount,
            "expireEdit": self.ui.buyExpireEdit,
            "expireFOK": self.ui.buyFOK,
            "fee": self.ui.buyFeeAsset,
            "button": self.ui.buyButton,
            "_commit_inv": True,
            "table": self.ui.sellStack,
        }
        self._frame_sell = {
            "group": self.ui.sellGroup,
            "mainLabel": self.ui.sellMainLabel,
            "mainAmt": self.ui.sellMainAmount,
            "altLabel": self.ui.sellAltLabel,
            "altAmt": self.ui.sellAltAmount,
            "price": self.ui.sellPrice,
            "account": self.ui.sellAccount,
            "expireEdit": self.ui.sellExpireEdit,
            "expireFOK": self.ui.sellFOK,
            "fee": self.ui.sellFeeAsset,
            "button": self.ui.sellButton,
            "_commit_inv": False,
            "table": self.ui.buyStack,
        }
        self.setupFrame(self._frame_buy,
                        self.asset_a,
                        self.asset_b,
                        title="Buy ")
        self.setupFrame(self._frame_sell,
                        self.asset_a,
                        self.asset_b,
                        title="Sell ")

        #self.ui.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        #self.ui.table.customContextMenuRequested.connect(self.show_orders_submenu)

        self.ui.closeButton.clicked.connect(self.close_me)
        self.ui.swapButton.clicked.connect(self.swap_me)

    def close_me(self):
        app().mainwin.closeMarket(self._pairtag)

    def swap_me(self):
        app().mainwin.swapMarket(self._pairtag)

    def close(self):
        # TODO: unsubscribe from market!!!
        pass

    def setupFrame(self, form, asset_a, asset_b, title="Trade "):
        form["group"].setTitle(title + asset_a["symbol"])
        form["mainLabel"].setText(asset_a["symbol"])
        form["altLabel"].setText(asset_b["symbol"])

        form["price"].valueChanged.connect(self.price_value_changed)
        form["price"].setDecimals(asset_a["precision"])
        form["price"].setMaximum(asset_a["options"]["max_supply"] *
                                 pow(10, asset_a["precision"]))
        form["mainAmt"].valueChanged.connect(self.main_amount_changed)
        form["mainAmt"].setDecimals(asset_a["precision"])
        form["mainAmt"].setMaximum(asset_a["options"]["max_supply"] *
                                   pow(10, asset_a["precision"]))
        form["altAmt"].valueChanged.connect(self.alt_amount_changed)
        form["altAmt"].setDecimals(asset_b["precision"])
        form["altAmt"].setMaximum(asset_b["options"]["max_supply"] *
                                  pow(10, asset_b["precision"]))

        form["table"].cellClicked.connect(self.cell_click)

        form["button"].clicked.connect(self.make_limit_order)

    def make_limit_order(self):
        form = self._frame_buy if self._frame_buy["button"] == self.sender(
        ) else self._frame_sell

        account_from = form["account"].currentText()
        sell_asset_name = self.asset_a["symbol"]
        sell_asset_amount = form["mainAmt"].value()
        buy_asset_name = self.asset_b["symbol"]
        buy_asset_amount = form["altAmt"].value()

        if form["_commit_inv"]:
            sell_asset_name = self.asset_b["symbol"]
            buy_asset_name = self.asset_a["symbol"]

        expire_seconds = deltasec(form["expireEdit"].text())
        expire_fok = form["expireFOK"].isChecked()

        fee_asset = anyvalvis(form["fee"], None)  #.currentText()

        try:
            trx = QTransactionBuilder.QSellAsset(account_from,
                                                 sell_asset_name,
                                                 sell_asset_amount,
                                                 buy_asset_name,
                                                 buy_asset_amount,
                                                 expiration=expire_seconds,
                                                 fill_or_kill=expire_fok,
                                                 fee_asset=fee_asset,
                                                 isolator=self.iso)
        except BaseException as error:
            showexc(error)

    def _set_spin_value(self, elem, value):
        elem.blockSignals(True)
        elem.setValue(value)
        elem.blockSignals(False)

    def price_value_changed(self):
        form = self._frame_buy if self._frame_buy["price"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if amt_a == 0 and price != 0:
            amt_a = amt_b / price
            self._set_spin_value(form["mainAmt"], amt_a)
            return

        amt_b = amt_a * price
        self._set_spin_value(form["altAmt"], amt_b)

    def main_amount_changed(self):
        form = self._frame_buy if self._frame_buy["mainAmt"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if price == 0 and amt_a != 0:
            price = amt_b / amt_a
            self._set_spin_value(form["price"], price)
            return

        amt_b = amt_a * price
        self._set_spin_value(form["altAmt"], amt_b)

    def alt_amount_changed(self):
        form = self._frame_buy if self._frame_buy["altAmt"] == self.sender(
        ) else self._frame_sell
        price = form["price"].value()
        amt_a = form["mainAmt"].value()
        amt_b = form["altAmt"].value()
        if price == 0 and amt_a != 0:
            price = amt_b / amt_a
            form["price"].setValue(price)
            self._set_spin_value(form["price"], price)
            return

        amt_a = amt_b / price
        self._set_spin_value(form["mainAmt"], amt_a)

    def cell_click(self, row, column):
        form = self._frame_buy if self._frame_buy["table"] == self.sender(
        ) else self._frame_sell
        valstr = form["table"].item(row, column).text().replace(",", "")
        val = float(str.split(valstr, " ")[0])
        if column == 0:  # price
            form["price"].setValue(val)

        if column == 1:  # asset_a
            form["mainAmt"].setValue(val)

        if column == 2:  # asset_b
            form["altAmt"].setValue(val)

    def _refreshTitle(self):
        #self._title = _translate("MainWindow", "Market", None) + " " + self._pairtag
        self._title = self._pairtag
        return self._title

    def show_orders_submenu(self, position):
        menu = QtGui.QMenu()
        qaction(self, menu, "Cancel Order...", self.cancel_order)
        menu.exec_(self.ui.table.viewport().mapToGlobal(position))

    def cancel_order(self):
        table = self.ui.table
        indexes = table.selectionModel().selectedRows()
        if len(indexes) < 1:
            return
        index = indexes[0].row()

        order_id = table.item(index, 0).text()
        #b = table.item(index, 1).text()

        try:
            trx = QTransactionBuilder.QCancelOrder(
                self._account_name,
                order_id,
                #fee_asset=fee_asset,
                isolator=self.iso)
        except BaseException as error:
            showexc(error)

    def desync(self):
        self.subscribed = False

    def resync(self):
        self.updater.fetch(self.mergeMarket_before,
                           self.iso, (self.asset_a, self.asset_b),
                           self._pairtag,
                           ready_callback=self.mergeMarket_after,
                           ping_callback=self.ping_callback,
                           description="Refreshing market")

    def mergeMarket_before(self, iso, pair, tag):

        #iso._wait_online(timeout=3) # will raise exception
        #if not(iso.is_connected()):
        #	raise Exception

        asset_a = pair[0]
        asset_b = pair[1]
        rpc = iso.bts.rpc
        if not self.subscribed:
            s_id = rpc.get_subscription_id()
            subs = rpc.subscribe_to_market(s_id, asset_a["id"], asset_b["id"])
            self._s_id = s_id
            self._tags.append("!" + str(s_id))
            self.subscribed = True

        from bitshares.market import Market
        self.market = Market(tag, bitshares_instance=iso.bts)

        orders = self.market.orderbook()
        trades = self.market.trades(limit=100, raw=True)

        return (
            orders,
            trades,
        )

    def mergeMarket_after(self, request_id, args):
        (
            orders,
            trades,
        ) = args

        self.fillTable(self.ui.buyStack, orders['bids'])
        self.fillTable(self.ui.sellStack, orders['asks'], True)
        self.plotChart(trades)

    def fillTable(self, table, orders, inv=False):
        color_a = COLOR_GREEN if inv else COLOR_RED
        color_b = COLOR_RED if inv else COLOR_GREEN
        table.setRowCount(0)

        j = -1
        for order in orders:
            j += 1

            table.insertRow(j)
            set_col(table, j, 0, str(order["price"]))
            set_col(table,
                    j,
                    1,
                    str(order["quote"]),
                    color=color_a,
                    align="right")
            set_col(table,
                    j,
                    2,
                    str(order["base"]),
                    color=color_b,
                    align="right")

    def plotChart(self, trades):
        xs = []
        ys = []
        import datetime
        for trade in trades:
            dt = datetime.datetime.strptime(trade["date"], "%Y-%m-%dT%H:%M:%S")
            x = int(dt.timestamp())  #trade["sequence"]
            y = float(trade["price"])
            xs.append(x)
            ys.append(y)
        #print(xs, ys)
        #import numpy as np
        #x = np.random.normal(size=1000)
        #y = np.random.normal(size=1000)

        self.ui.marketPlot.clear()
        self.ui.marketPlot.plot(xs, ys)  #, pen=None, symbol=None)
class Parser():
    def __init__(self, sett, **kwargs):
        """
        Инициализация класса, рынков и получение цены BTS

        :param set sett: настройки класса
            str quote - актив, стоимость которого считаем
            str base - актив, в котором считаем стоимость
            list additional_assets - дополнительные рынки для расчета, кроме BTS
            list usdt_assets - дополнительные рынки, стоимость которых равна 
                               стоимости актива base
            float base_depth - глубина расчета цены в стаканах в активе base
            float history_depth - глубина расчета цены совершенных сделок в 
                                  активе base
            int price_precision - количество знаков после запятой, для
                                  отображения цен
            int orderbook_limit - максимальное количество сделок из стакана
                                  получаемое от ноды
            int history_limit - максимальное количество сделок получаемое из
                                истории от ноды
            int history_period - максимальное количество дней, за которые
                                 берется история
            set viz_account - данные аккаунта для публикации в блокчейне VIZ
        """
        self.sett = sett
        self.quote_market = Market(self.sett['quote'] + ':BTS')
        self.base_market = Market('BTS:' + self.sett['base'])
        book = self.base_market.orderbook(limit=1)
        self.price = {}
        self.price['BTS'] = (book['asks'][0]['price'] + 
                             book['bids'][0]['price']) / 2
        self.bids = []
        self.asks = []

    def check_ask(self, base, core):
        """
        Функция для создания нового "виртуального" ордера в формате пары
        self.sett['quote'] / BTS
        Возвращает True в случае попадания этого ордера в выборку и False в
        противном случае

        :param bitshares.price.Order base: ордер на продажу из пары 
            self.sett['quote'] / BTS
        :param bitshares.price.Order core: ордер на продажу из пары 
            BTS / self.sett['base']

        Функция проверяет объемы в ордерах base и core и создает новый на основе
        меньшего из них
        """
        base_amount = base['base']['amount']
        core_amount = core['quote']['amount']
        if base_amount >= core_amount:
            new_base = base.copy()
            new_base['quote'] = Amount(
                core_amount / base['price'], 
                base['quote']['asset']
            )
            new_base['base'] = core['quote']
            order = Order(new_base['quote'], core['base'])
        else:
            new_core = core.copy()
            new_core['base'] = Amount(
                base_amount * core['price'], 
                core['base']['asset']
            )
            new_core['quote'] = base['base']
            order = Order(base['quote'], new_core['base'])
        return self.create_asks([order])

    def check_bid(self, base, core):
        """
        Функция для создания нового "виртуального" ордера в формате пары
        self.sett['quote'] / BTS
        Возвращает True в случае попадания этого ордера в выборку и False в 
        противном случае

        :param bitshares.price.Order base: ордер на покупку из пары 
            self.sett['quote'] / BTS
        :param bitshares.price.Order core: ордер на покупку из пары
            BTS / self.sett['base']

        Функция проверяет объемы в ордерах base и core и создает новый на основе
        меньшего из них
        """
        base_amount = base['base']['amount']
        core_amount = core['quote']['amount']
        if base_amount >= core_amount:
            new_base = base.copy()
            new_base['quote'] = Amount(
                core_amount / base['price'], 
                base['quote']['asset']
            )
            new_base['base'] = core['quote']
            order = Order(new_base['quote'], core['base'])
        else:
            new_core = core.copy()
            new_core['base'] = Amount(
                base_amount * core['price'], 
                core['base']['asset']
            )
            new_core['quote'] = base['base']
            order = Order(base['quote'], new_core['base'])
        return self.create_bids([order])

    def create_asks(self, asks):
        """
        Функция для формирования выборки ордеров на продажу.
        Возвращает True в случае попадания первого ордера в выборку и False в
        противном случае

        :param list(bitshares.price.Order) asks: список ордеров на продажу в
            формате пары self.sett['quote'] / BTS

        Функция сортирует ордера в порядке увеличения цены и отсекает все,
        которые не вписываются в параметр self.sett['base_depth']
        """
        tmp_asks = self.asks
        tmp_asks.extend(asks)
        tmp_asks.sort()
        self.asks = []
        asks_amount = 0
        result = False
        for ask in tmp_asks:
            self.asks.append(ask)
            if ask == asks[0]:
                result = True
            amount = ask['base']['amount'] * self.price['BTS']
            asks_amount += amount
            if asks_amount > self.sett['base_depth']:
                break
        return result

    def create_bids(self, bids):
        """
        Функция для формирования выборки ордеров на покупку.
        Возвращает True в случае попадания первого ордера в выборку и False в
        противном случае

        :param list(bitshares.price.Order) bids: список ордеров на покупку в
            формате пары self.sett['quote'] / BTS

        Функция сортирует ордера в порядке уменьшения цены и отсекает все,
        которые не вписываются в параметр self.sett['base_depth']
        """
        tmp_bids = self.bids
        tmp_bids.extend(bids)
        tmp_bids.sort(reverse=True)
        self.bids = []
        bids_amount = 0
        result = False
        for bid in tmp_bids:
            self.bids.append(bid)
            if bid == bids[0]:
                result = True
            amount = bid['base']['amount'] * self.price['BTS']
            bids_amount += amount
            if bids_amount > self.sett['base_depth']:
                break
        return result

    def get_additional_orderbook(self):
        """
        Функция для проверки ордеров в стаканах пар указанные в параметре
        self.sett['additional_assets']

        Функция получает из сети BitShares данные из всех стаканов указанных в
        параметре self.sett['additional_assets'] и формирует выборку на основе
        этих данных. Результаты ее работы сохраняются в переменных класса
        self.bids и self.asks
        """
        for asset in self.sett['additional_assets']:
            market_base = Market(self.sett['quote'] + ':' + asset)
            market_core = Market(asset + ':BTS')
            book_base = market_base.orderbook(
                limit=self.sett['orderbook_limit']
            )
            book_core = market_core.orderbook(
                limit=self.sett['orderbook_limit']
            )
            for bid_base in book_base['bids']:
                check = False
                base_amount = bid_base['base']['amount']
                bid_core = book_core['bids'][0]
                while base_amount > 0:
                    core_amount = bid_core['quote']['amount']
                    check = self.check_bid(bid_base, bid_core)
                    if base_amount >= core_amount:
                        bid_base['quote'] -= Amount(
                            core_amount / bid_base['price'], 
                            bid_base['quote']['asset']
                        )
                        bid_base['base'] -= bid_core['quote']
                        book_core['bids'].pop(0)
                        bid_core = book_core['bids'][0]
                    else:
                        bid_core['base'] -= Amount(
                            base_amount * bid_core['price'], 
                            bid_core['base']['asset']
                        )
                        bid_core['quote'] -= bid_base['base']
                        break
                    if not check:
                        base_amount = 0
                    else:
                        base_amount = bid_base['base']['amount']
                if not check:
                    break
            for ask_base in book_base['asks']:
                check = False
                base_amount = ask_base['base']['amount']
                ask_core = book_core['asks'][0]
                while base_amount > 0:
                    core_amount = ask_core['quote']['amount']
                    check = self.check_ask(ask_base, ask_core)
                    if base_amount >= core_amount:
                        ask_base['quote'] -= Amount(
                            core_amount / ask_base['price'], 
                            ask_base['quote']['asset']
                        )
                        ask_base['base'] -= ask_core['quote']
                        book_core['asks'].pop(0)
                        ask_core = book_core['asks'][0]
                    else:
                        ask_core['base'] -= Amount(
                            base_amount * ask_core['price'], 
                            ask_core['base']['asset']
                        )
                        ask_core['quote'] -= ask_base['base']
                        break
                    if not check:
                        base_amount = 0
                    else:
                        base_amount = ask_base['base']['amount']
                if not check:
                    break

    def get_history_depth(self):
        """
        Функция для рассчета средней цены совершенных сделок

        Функция собирает все данные из истории, сортирует их по дате, приводит
        цены к self.sett['base'] и отсекает все сделки выходящие за пределы
        self.sett['history_depth']. Результаты сохраняет в переменных класса
        self.history_amount и self.average_history_price
        """
        history = []
        sum = 0
        start_time = datetime.now() - timedelta(
            days=self.sett['history_period']
        )
        for trade in self.quote_market.trades(
            limit=self.sett['history_limit'], 
            start=start_time
        ):
            amount = trade['base']['amount'] * self.price['BTS']
            history.append(trade)
            sum += amount
            if sum > self.sett['history_depth']:
                break
        history.sort(key=self.sortByTime, reverse=True)
        assets = self.sett['additional_assets'].copy()
        assets.extend(self.sett['usdt_assets'])
        for asset in assets:
            market = Market(asset + ':BTS')
            book = market.orderbook(limit=1)
            self.price[asset] = (book['asks'][0]['price'] + 
                                 book['bids'][0]['price']) / 2*self.price['BTS']
            market = Market(self.sett['quote'] + ':' + asset)
            for trade in market.trades(
                limit=self.sett['history_limit'], 
                start=start_time
            ):
                history.append(trade)
                history.sort(key=self.sortByTime, reverse=True)
                amount = trade['base']['amount'] * self.price[asset]
                sum += amount
                break_bool = False
                while sum >= self.sett['history_depth']:
                    last = history.pop()
                    last_amount = (last['base']['amount'] * 
                                   self.price[last['base']['symbol']])
                    if last_amount > sum - self.sett['history_depth']:
                        history.append(last)
                        break
                    else:
                        sum -= last_amount
                    if (last['quote']['symbol'] == trade['quote']['symbol'] and
                        last['base']['symbol'] == trade['base']['symbol']):
                        if (collections.Counter(last) == 
                                collections.Counter(trade)):
                            break_bool = True
                            break
                if break_bool:
                    break
        sum = sum_quote = 0
        for h in history:
            sum += h['base']['amount'] * self.price[h['base']['symbol']]
            sum_quote += h['quote']['amount']
        if sum > self.sett['history_depth']:
            last = history.pop()
            ratio = sum / self.sett['history_depth']
            sum_quote = sum_quote / ratio
            sum = self.sett['history_depth']
        self.history_amount = sum_quote
        self.average_history_price = sum / sum_quote

    def get_market_depth(self):
        """
        Основная функция для расчета средней цены ордеров в стакане

        Функция поочередно вызывает дочерние функции self.get_orderbook(),
        self.get_additional_orderbook(), self.get_usdt_orderbook() для сбора
        ордеров из всех пар. А затем расчитывает среднюю цену и сохраняет данные
        в переменных класса self.average_bid_price и self.average_ask_price
        """
        self.get_orderbook()
        self.get_additional_orderbook()
        self.get_usdt_orderbook()
        quote = base = sum = 0
        for bid in self.bids:
            amount = bid['base']['amount'] * self.price['BTS']
            if sum + amount <= self.sett['base_depth']:
                quote += bid['quote']['amount']
                base += bid['base']['amount']
                sum += amount
            else:
                diff = self.sett['base_depth'] - sum
                ratio = diff / amount
                quote += bid['quote']['amount'] * ratio
                base += bid['base']['amount'] * ratio
                sum += diff
        self.average_bid_price = base / quote * self.price['BTS']
        quote = base = sum = 0
        for ask in self.asks:
            amount = ask['base']['amount'] * self.price['BTS']
            if sum + amount <= self.sett['base_depth']:
                quote += ask['quote']['amount']
                base += ask['base']['amount']
                sum += amount
            else:
                diff = self.sett['base_depth'] - sum
                ratio = diff / amount
                quote += ask['quote']['amount'] * ratio
                base += ask['base']['amount'] * ratio
                sum += diff
        self.average_ask_price = base / quote * self.price['BTS']

    def get_orderbook(self):
        """
        Функция получает ордера из пары self.sett['quote'] / BTS
        """
        orderbook = self.quote_market.orderbook(
            limit=self.sett['orderbook_limit']
        )
        self.create_bids(orderbook['bids'])
        self.create_asks(orderbook['asks'])

    def get_usdt_orderbook(self):
        """
        Функция для проверки ордеров в стаканах пар указанные в параметре
        self.sett['usdt_assets']

        Функция получает из сети BitShares данные из всех стаканов указанных в
        параметре self.sett['usdt_assets'] и формирует выборку на основе этих
        данных. Результаты ее работы сохраняются в переменных класса self.bids и
        self.asks
        """
        for asset in self.sett['usdt_assets']:
            market = Market(self.sett['quote'] + ':' + asset)
            book = market.orderbook(
                limit=self.sett['orderbook_limit']
            )
            for bid in book['bids']:
                amount = bid['base']['amount']
                bid = Order(
                    bid['quote'], 
                    Amount(amount / self.price['BTS'], 'BTS')
                )
                if not self.create_bids([bid]):
                    break
            for ask in book['asks']:
                amount = ask['base']['amount']
                ask = Order(
                    ask['quote'], 
                    Amount(amount / self.price['BTS'], 'BTS')
                )
                if not self.create_asks([ask]):
                    break

    def sortByTime(self, input):
        """
        Вспомогательная функция для сортировки
        """
        return input['time']
    def get_history_depth(self):
        """
        Функция для рассчета средней цены совершенных сделок

        Функция собирает все данные из истории, сортирует их по дате, приводит
        цены к self.sett['base'] и отсекает все сделки выходящие за пределы
        self.sett['history_depth']. Результаты сохраняет в переменных класса
        self.history_amount и self.average_history_price
        """
        history = []
        sum = 0
        start_time = datetime.now() - timedelta(
            days=self.sett['history_period']
        )
        for trade in self.quote_market.trades(
            limit=self.sett['history_limit'], 
            start=start_time
        ):
            amount = trade['base']['amount'] * self.price['BTS']
            history.append(trade)
            sum += amount
            if sum > self.sett['history_depth']:
                break
        history.sort(key=self.sortByTime, reverse=True)
        assets = self.sett['additional_assets'].copy()
        assets.extend(self.sett['usdt_assets'])
        for asset in assets:
            market = Market(asset + ':BTS')
            book = market.orderbook(limit=1)
            self.price[asset] = (book['asks'][0]['price'] + 
                                 book['bids'][0]['price']) / 2*self.price['BTS']
            market = Market(self.sett['quote'] + ':' + asset)
            for trade in market.trades(
                limit=self.sett['history_limit'], 
                start=start_time
            ):
                history.append(trade)
                history.sort(key=self.sortByTime, reverse=True)
                amount = trade['base']['amount'] * self.price[asset]
                sum += amount
                break_bool = False
                while sum >= self.sett['history_depth']:
                    last = history.pop()
                    last_amount = (last['base']['amount'] * 
                                   self.price[last['base']['symbol']])
                    if last_amount > sum - self.sett['history_depth']:
                        history.append(last)
                        break
                    else:
                        sum -= last_amount
                    if (last['quote']['symbol'] == trade['quote']['symbol'] and
                        last['base']['symbol'] == trade['base']['symbol']):
                        if (collections.Counter(last) == 
                                collections.Counter(trade)):
                            break_bool = True
                            break
                if break_bool:
                    break
        sum = sum_quote = 0
        for h in history:
            sum += h['base']['amount'] * self.price[h['base']['symbol']]
            sum_quote += h['quote']['amount']
        if sum > self.sett['history_depth']:
            last = history.pop()
            ratio = sum / self.sett['history_depth']
            sum_quote = sum_quote / ratio
            sum = self.sett['history_depth']
        self.history_amount = sum_quote
        self.average_history_price = sum / sum_quote
Example #9
0
def book(nodes, a=None, b=None):  #updates orderbook details

    # create fresh websocket connections for this child instance
    account = Account(USERNAME,
                      bitshares_instance=BitShares(nodes, num_retries=0))
    market = Market(BitPAIR,
                    bitshares_instance=BitShares(nodes, num_retries=0),
                    mode='head')
    node = nodes[0]
    begin = time.time()
    while time.time() < (begin + TIMEOUT):
        time.sleep(random())
        try:
            # add unix time to trades dictionary
            trades = market.trades(limit=100)
            for t in range(len(trades)):
                ts = time.strptime(str(trades[t]['time']), '%Y-%m-%d %H:%M:%S')
                trades[t]['unix'] = int(time.mktime(ts))
                fprice = '%.16f' % float(trades[t]['price'])
                trades[t]['fprice'] = fprice[:10] + ',' + fprice[10:]
            # last price
            # last = market.ticker()['latest']
            last = float(trades[0]['price'])
            slast = '%.16f' % last
            # complete account balances
            call = decimal(time.time())
            raw = list(account.balances)
            elapsed = float(decimal(time.time()) - call)
            if elapsed > 1:
                continue
            elapsed = '%.17f' % elapsed
            cbalances = {}
            for i in range(len(raw)):
                cbalances[raw[i]['symbol']] = float(raw[i]['amount'])
            # orderbook
            raw = market.orderbook(limit=20)
            bids = raw['bids']
            asks = raw['asks']
            sbidp = [('%.16f' % bids[i]['price']) for i in range(len(bids))]
            saskp = [('%.16f' % asks[i]['price']) for i in range(len(asks))]
            sbidv = [('%.2f' % float(bids[i]['quote'])).rjust(12, ' ')
                     for i in range(len(bids))]
            saskv = [('%.2f' % float(asks[i]['quote'])).rjust(12, ' ')
                     for i in range(len(asks))]
            bidv = [float(bids[i]['quote']) for i in range(len(bids))]
            askv = [float(asks[i]['quote']) for i in range(len(asks))]
            cbidv = list(np.cumsum(bidv))
            caskv = list(np.cumsum(askv))
            cbidv = [('%.2f' % i).rjust(12, ' ') for i in cbidv]
            caskv = [('%.2f' % i).rjust(12, ' ') for i in caskv]
            # dictionary of currency and assets in this market
            currency = float(account.balance(BitCURRENCY))
            assets = float(account.balance(BitASSET))
            balances = {BitCURRENCY: currency, BitASSET: assets}
            # dictionary of open orders in traditional format:
            # orderNumber, orderType, market, amount, price
            orders = []
            for order in market.accountopenorders():
                orderNumber = order['id']
                asset = order['base']['symbol']
                currency = order['quote']['symbol']
                amount = float(order['base'])
                price = float(order['price'])
                orderType = 'buy'
                if asset == BitASSET:
                    orderType = 'sell'
                    price = 1 / price
                else:
                    amount = amount / price
                orders.append({
                    'orderNumber': orderNumber,
                    'orderType': orderType,
                    'market': BitPAIR,
                    'amount': amount,
                    'price': ('%.16f' % price)
                })
            trades = trades[:10]
            stale = int(time.time() - float(trades[0]['unix']))
            # display orderbooks
            print("\033c")
            print(time.ctime(), '            ', int(time.time()), '   ', a, b)
            print('                        PING', (elapsed), '   ', node)
            print('')
            print('                        LAST',
                  slast[:10] + ',' + slast[10:], '   ', BitPAIR)
            print('')
            print('            ', sbidv[0], '  ',
                  (sbidp[0])[:10] + ',' + (sbidp[0])[10:], '   ',
                  (saskp[0])[:10] + ',' + (saskp[0])[10:], (saskv[0]))
            print('                                           ', 'BIDS', '   ',
                  'ASKS')
            for i in range(1, len(sbidp)):
                print(cbidv[i], sbidv[i], '  ',
                      (sbidp[i])[:10] + ',' + (sbidp[i])[10:], '   ',
                      (saskp[i])[:10] + ',' + (saskp[i])[10:], saskv[i],
                      caskv[i])
            print('')
            for o in orders:
                print(o)
            if len(orders) == 0:
                print('                                  NO OPEN ORDERS')
            print('')
            print('%s BALANCE:' % BitPAIR)
            print(balances)
            print('')
            print('MARKET HISTORY:', stale, 'since last trade')
            for t in trades:
                # print(t.items())
                print(t['unix'],
                      str(t['time'])[11:19], t['fprice'],
                      ('%.4f' % float(t['quote']['amount'])).rjust(12, ' '))
            print('')
            print('ctrl+shift+\ will EXIT to terminal')
            print('')
            print('COMPLETE HOLDINGS:')
            print(cbalances)
        except:
            pass