예제 #1
0
 def init_bts_price(self):
     config_bts = self.config_bts
     self.client = BTS(config_bts["user"], config_bts["password"],
                       config_bts["host"], config_bts["port"])
     self.bts_market = BTSMarket(self.client)
     self.bts_price = BTSPriceAfterMatch(self.bts_market)
     # don't use cover order, because it's feed price related,
     # if there is a big order, feed price will down without stop
     self.bts_price.set_need_cover(False)
예제 #2
0
    def __init__(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        self.config_bts = json.load(fd_config)["client_default"]
        fd_config.close()

        config_bts = self.config_bts
        self.client = BTS(config_bts["user"], config_bts["password"],
                          config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.client)
        self.height = -1
예제 #3
0
    def init_market(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        config_bts = json.load(fd_config)[self.config["bts_client"]]
        fd_config.close()

        self.bts_client = BTS(config_bts["user"], config_bts["password"],
                              config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.bts_client)
        client_info = self.bts_client.get_info()
        self.height = int(client_info["blockchain_head_block_num"])
예제 #4
0
 def init_bts_price(self):
     config_bts = self.config_bts
     self.client = BTS(config_bts["user"], config_bts["password"],
                       config_bts["host"], config_bts["port"])
     self.bts_market = BTSMarket(self.client)
     self.bts_price = BTSPriceAfterMatch(self.bts_market)
     # don't use cover order, because it's feed price related,
     # if there is a big order, feed price will down without stop
     self.bts_price.set_need_cover(False)
예제 #5
0
class TestMain(object):
    logfile = open("/tmp/test-bts-api.log", 'a')
    config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
    fd_config = open(config_file)
    config_bts = json.load(fd_config)["client_default"]
    fd_config.close()
    client = BTS(config_bts["user"], config_bts["password"],
                 config_bts["host"], config_bts["port"])
    bts_market = BTSMarket(client)

    def test_float_precision(self):
        assert trim_float_precision(0.1323234234234234, 10000) == '0.1323'

    def test_fixed_point(self):
        assert to_fixed_point("hello 3.1415926E-3") == 'hello 0.0031415926'

    def test_get_median(self):
        assert get_median([1, 2, 3, 4]) == 2.5
        assert get_median([1, 2, 3]) == 2
        assert get_median([1]) == 1
        assert get_median([]) is None

    def test_info(self):
        result = self.client.get_info()
        pprint("======= test_info =========", self.logfile)
        pprint(result, self.logfile)
        assert result["blockchain_head_block_num"] > 1

    def test_asset_info(self):
        result = self.client.get_asset_info("BTS")
        assert result["symbol"] == "BTS"
        result = self.client.get_asset_info(0)
        assert result["id"] == 0
        pprint("======= test_asset_info =========", self.logfile)
        pprint(result, self.logfile)

    def test_is_asset_peg(self):
        assert self.client.is_peg_asset("CNY")
        assert not self.client.is_peg_asset("BTS")
        assert not self.client.is_peg_asset("BTSBOTS")

    def test_asset_precision(self):
        assert self.client.get_asset_precision("CNY") == 10**4
        assert self.client.get_asset_precision("BTS") == 10**5
        assert self.client.get_asset_precision("BTSBOTS") == 10**2

    def test_feed_price(self):
        feed_price_cny = self.client.get_feed_price("CNY")
        feed_price_usd = self.client.get_feed_price("USD")
        feed_price_usd_per_cny = self.client.get_feed_price("USD", base="CNY")
        assert feed_price_usd_per_cny == feed_price_usd / feed_price_cny
        assert not self.client.get_feed_price("BTSBOTS")

    def test_list_accounts(self):
        accounts = self.client._list_accounts()
        pprint("======= test__list_accounts =========", self.logfile)
        pprint(accounts, self.logfile)
        accounts = self.client.list_accounts()
        pprint("======= test_list_accounts =========", self.logfile)
        pprint(accounts, self.logfile)

    def test_get_balance(self):
        balance = self.client.get_balance()
        pprint("======= test_get_balance =========", self.logfile)
        pprint(balance, self.logfile)

    def test_transaction_history(self):
        trx_history = self.client.get_transaction_history(limit=-100)
        pprint("======= test_transaction_history =========", self.logfile)
        pprint(trx_history, self.logfile)

    def test_order_book(self):
        order_book = self.bts_market.get_order_book("CNY", "BTS")
        pprint("======= test_order_book =========", self.logfile)
        pprint(order_book, self.logfile)
        assert len(order_book) > 0

    #def test_publish_feeds(self):
    #def test_transfer(self):
    #def test_market_batch_update(self):

    def test_exchanges_yahoo(self):
        exchanges = Exchanges()
        rate_cny = exchanges.fetch_from_yahoo()
        pprint("======= test_exchanges_yahoo =========", self.logfile)
        pprint(rate_cny, self.logfile)
        assert rate_cny["USD"] > 0

    def test_exchanges_yunbi(self):
        exchanges = Exchanges()
        order_book = exchanges.fetch_from_yunbi()
        pprint("======= test_exchanges_yunbi =========", self.logfile)
        pprint(order_book, self.logfile)
        assert len(order_book["bids"]) > 0

    def test_exchanges_poloniex(self):
        exchanges = Exchanges()
        order_book = exchanges.fetch_from_poloniex()
        pprint("======= test_exchanges_poloniex =========", self.logfile)
        pprint(order_book, self.logfile)
        assert len(order_book["bids"]) > 0

    def test_exchanges_btc38(self):
        exchanges = Exchanges()
        order_book = exchanges.fetch_from_btc38()
        pprint("======= test_exchanges_btc38 =========", self.logfile)
        pprint(order_book, self.logfile)
        assert len(order_book["bids"]) > 0

    def test_exchanges_bter(self):
        exchanges = Exchanges()
        order_book = exchanges.fetch_from_bter()
        pprint("======= test_exchanges_bter =========", self.logfile)
        pprint(order_book, self.logfile)
        assert len(order_book["bids"]) > 0

    def test_bts_price_after_match(self):
        bts_price = BTSPriceAfterMatch(self.bts_market)
        bts_price.set_need_cover(False)
        bts_price.get_rate_from_yahoo()

        # get all order book
        bts_price.get_order_book_all()

        # can add weight here
        for order_type in bts_price.order_types:
            for _order in bts_price.order_book["wallet_usd"][order_type]:
                _order[1] *= 0.1

        # calculate real price
        volume, volume2, real_price = bts_price.get_real_price(spread=0.01)
        valid_depth = bts_price.get_valid_depth(price=real_price, spread=0.01)
        pprint("======= test_bts_price_after_match =========", self.logfile)
        pprint([volume, real_price, real_price / 1.01, real_price / 0.99],
               self.logfile)
        pprint(valid_depth, self.logfile)
        #pprint(bts_price.order_book, self.logfile)
        assert volume > 0

    def test_account_info(self):
        account_info = self.client.get_account_info("baozi")
        assert account_info is not None
        pprint("======= test_account_info =========", self.logfile)
        pprint(account_info, self.logfile)
        account_info = self.client.get_account_info("none.baozi")
        assert account_info is None

    def test_list_blocks(self):
        list_blocks = self.client.list_blocks(100000, 10)
        assert len(list_blocks) == 10
        pprint("======= test_list_blocks  =========", self.logfile)
        pprint(list_blocks, self.logfile)

    def test_list_active_delegates(self):
        active_delegates = self.client.list_active_delegates(0, 2)
        assert len(active_delegates) == 2
        pprint("======= test_list_active_delegates =========", self.logfile)
        pprint(active_delegates, self.logfile)

    def test_get_block_transactions(self):
        trxs = self.client.get_block_transactions(2549745)
        assert len(trxs) == 3
        pprint("======= test_get_block_transactions =========", self.logfile)
        pprint(trxs, self.logfile)
        trxs = self.client.get_block_transactions(2549737)
        assert len(trxs) == 0

    def test_my_order_book(self):
        order_book = self.bts_market.get_my_order_book("CNY", "BTS")
        assert order_book is not None
        pprint("======= test_my_order_book =========", self.logfile)
        pprint(order_book, self.logfile)

    def test_get_address_balances(self):
        balances = self.client.get_address_balances(
            "BTSJT2yrAEcPSYjX3yLmRgFaxA5zDsUHFBDe")
        pprint("======= test_address_bbalances =========", self.logfile)
        pprint(balances, self.logfile)
예제 #6
0
class GetMarketTrx(object):

    def __init__(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        self.config_bts = json.load(fd_config)["client_default"]
        fd_config.close()

        config_bts = self.config_bts
        self.client = BTS(config_bts["user"], config_bts["password"],
                          config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.client)
        self.height = -1

    def display_deal_trx(self, deal_trx):
        for trx in deal_trx:
            if trx["type"] == "bid":
                deal_type = "buy"
            else:
                deal_type = "sell"
            sys.stdout.write("\n%s %s %s %s %s at price %.4g %s/%s" % (
                trx["timestamp"], trx["block"], deal_type,
                trx["volume"], trx["base"], trx["price"],
                trx["quote"], trx["base"]))

    def display_place_trx(self, place_trx):
        trx_id = ""
        for trx in place_trx:
            if trx_id == trx["trx_id"]:
                sys.stdout.write(".")
                continue
            trx_id = trx["trx_id"]
            if trx["type"] == "cover":
                sys.stdout.write("\n%s %s %s %s %s %s" % (
                    trx["timestamp"], trx["block"], trx["trx_id"], trx["type"],
                    trx["amount"], trx["quote"]))
            elif trx["type"] == "add_collateral":
                sys.stdout.write("\n%s %s %s %s %s %s" % (
                    trx["timestamp"], trx["block"], trx["trx_id"], trx["type"],
                    trx["amount"], trx["base"]))
            else:
                if trx["type"] == "bid":
                    volume = trx["amount"] / trx["price"]
                else:
                    volume = trx["amount"]
                order_type = trx["type"]
                if trx["cancel"]:
                    order_type = "cancel " + trx["type"]
                if trx["price"]:
                    price_str = "%.4g" % trx["price"]
                else:
                    price_str = "unlimited"
                sys.stdout.write("\n%s %s %s %s %s %s at price %s %s/%s" % (
                    trx["timestamp"], trx["block"], trx["trx_id"], order_type,
                    volume, trx["base"], price_str,
                    trx["quote"], trx["base"]))

    def excute(self):
        client_info = self.client.get_info()
        height_now = int(client_info["blockchain_head_block_num"])
        if self.height == -1:
            self.height = height_now - 1000
        while self.height < height_now:
            self.height += 1
            trxs = self.client.get_block_transactions(self.height)
            recs = self.market.get_order_deal_rec(self.height)
            self.display_deal_trx(recs)
            recs = self.market.get_order_place_rec(trxs)
            self.display_place_trx(recs)
            self.market.update_order_owner(recs)
예제 #7
0
class DelegateTask(object):
    def __init__(self):
        self.load_config()
        self.init_bts_price()
        self.setup_log()
        self.init_task_feed_price()

    def load_config(self):
        config_file = os.getenv("HOME") + "/.python-bts/delegate_task.json"
        fd_config = open(config_file)
        config = json.load(fd_config)
        self.notify = config["notify"]
        self.config = config["delegate_task"]
        self.config_price_feed = self.config["price_feed"]
        self.config_withdraw_pay = self.config["withdraw_pay"]
        fd_config.close()
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        self.config_bts = json.load(fd_config)[self.config["bts_client"]]
        fd_config.close()

    def init_bts_price(self):
        config_bts = self.config_bts
        self.client = BTS(config_bts["user"], config_bts["password"],
                          config_bts["host"], config_bts["port"])
        self.bts_market = BTSMarket(self.client)
        self.bts_price = BTSPriceAfterMatch(self.bts_market)
        # don't use cover order, because it's feed price related,
        # if there is a big order, feed price will down without stop
        self.bts_price.set_need_cover(False)

    def setup_log(self):
        # Setting up Logger
        self.logger = logging.getLogger('bts')
        self.logger.setLevel(logging.INFO)
        formatter = logging.Formatter(
            '%(asctime)s[%(levelname)s]: %(message)s')
        fh = logging.handlers.RotatingFileHandler("/tmp/bts_delegate_task.log")
        fh.setFormatter(formatter)
        self.logger.addHandler(fh)

    def init_task_feed_price(self):
        peg_asset_list = [
            "KRW", "BTC", "SILVER", "GOLD", "TRY", "SGD", "HKD", "RUB", "SEK",
            "NZD", "CNY", "MXN", "CAD", "CHF", "AUD", "GBP", "JPY", "EUR",
            "USD", "SHENZHEN", "NASDAQC", "NIKKEI", "HANGSENG", "SHANGHAI"
        ]
        self.price_queue = {}
        for asset in peg_asset_list:
            self.price_queue[asset] = []
        self.time_publish_feed = 0
        self.last_publish_price = {}
        # Actually I think it may be beneficial to discount all feeds by 0.995
        # to give the market makers some breathing room and provide a buffer
        # against down trends.
        self.discount = 0.995

    def fetch_bts_price(self):
        # updat rate cny
        self.bts_price.get_rate_from_yahoo()

        # get all order book
        self.bts_price.get_order_book_all()

        # can add weight here
        for order_type in self.bts_price.order_types:
            for market in self.bts_price.order_book:
                if market not in self.config_price_feed["market_weight"]:
                    _weight = 0.0
                else:
                    _weight = self.config_price_feed["market_weight"][market]
                for _order in self.bts_price.order_book[market][order_type]:
                    _order[1] *= _weight

        # calculate real price
        volume, volume_sum, real_price = self.bts_price.get_real_price(
            spread=self.config_price_feed["price_limit"]["spread"])
        valid_depth = self.bts_price.get_valid_depth(
            price=real_price,
            spread=self.config_price_feed["price_limit"]["spread"])
        price_cny = real_price / self.bts_price.rate_btc["CNY"]
        self.logger.info("fetch price is %.5f CNY/BTS, volume is %.3f",
                         price_cny, volume)
        self.logger.info("efficent depth : %s" % valid_depth)
        return real_price

    # these MPA's precision is 100, it's too small,
    # have to change the price
    # but we should fixed these at BTS2.0
    def patch_nasdaqc(self, median_price):
        if "SHENZHEN" in median_price:
            median_price["SHENZHEN"] /= median_price["CNY"]
        if "SHANGHAI" in median_price:
            median_price["SHANGHAI"] /= median_price["CNY"]
        if "NASDAQC" in median_price:
            median_price["NASDAQC"] /= median_price["USD"]
        if "NIKKEI" in median_price:
            median_price["NIKKEI"] /= median_price["JPY"]
        if "HANGSENG" in median_price:
            median_price["HANGSENG"] /= median_price["HKD"]

    def get_median_price(self, bts_price_in_btc):
        median_price = {}
        for asset in self.price_queue:
            if asset not in self.bts_price.rate_btc or \
                    self.bts_price.rate_btc[asset] is None:
                continue
            self.price_queue[asset].append(bts_price_in_btc /
                                           self.bts_price.rate_btc[asset])
            if len(self.price_queue[asset]) > \
                    self.config_price_feed["price_limit"]["median_length"]:
                self.price_queue[asset].pop(0)
            median_price[asset] = sorted(self.price_queue[asset])[int(
                len(self.price_queue[asset]) / 2)]
        self.patch_nasdaqc(median_price)
        return median_price

    def get_feed_price(self):
        current_feed_price = {}
        for asset in self.price_queue:
            current_feed_price[asset] = self.client.get_feed_price(asset)
        return current_feed_price

    def publish_rule_check2(self, median_price):
        if time.time() - self.time_publish_feed > \
                self.config_price_feed["max_update_hours"] * 60 * 60:
            #self.logger.info("publish 1")
            return True
        change_min = self.config_price_feed["price_limit"]["change_min"]
        for asset in median_price:
            if asset not in self.last_publish_price:
                continue
            price_change = 100.0 * (
                median_price[asset] - self.last_publish_price[asset]) \
                / self.last_publish_price[asset]

            if fabs(price_change) > change_min:
                return True
        return False

    def publish_rule_check(self, median_price, current_feed_price):
        # When attempting to write a market maker the slow movement of the feed
        # can be difficult.
        # I would recommend the following:
        # if  REAL_PRICE < MEDIAN and YOUR_PRICE > MEDIAN publish price
        # if  you haven't published a price in the past 20 minutes
        #   if  REAL_PRICE > MEDIAN  and  YOUR_PRICE < MEDIAN and
        # abs( YOUR_PRICE - REAL_PRICE ) / REAL_PRICE  > 0.005 publish price
        # The goal is to force the price down rapidly and allow it to creep up
        # slowly.
        # By publishing prices more often it helps market makers maintain the
        # peg and minimizes opportunity for shorts to sell USD below the peg
        # that the market makers then have to absorb.
        # If we can get updates flowing smoothly then we can gradually reduce
        # the spread in the market maker bots.
        # note: all prices in USD per BTS
        if time.time() - self.time_publish_feed > \
                self.config_price_feed["max_update_hours"] * 60 * 60:
            return True
        for asset in median_price:
            price_change = 100.0 * (
                median_price[asset] - self.last_publish_price[asset]) \
                / self.last_publish_price[asset]

            # ignore if change less than 0.2%
            change_ignore = \
                self.config_price_feed["price_limit"]["change_ignore"]
            if fabs(price_change) < change_ignore:
                continue

            if current_feed_price[asset] and \
                    median_price[asset] < current_feed_price[asset] / \
                    self.discount and self.last_publish_price[asset] > \
                    current_feed_price[asset] / self.discount:
                # self.logger.info(
                #     "need update: %s %s %s" % (
                #         median_price[asset], self.last_publish_price[asset],
                #         current_feed_price[asset] / self.discount))
                return True
            # if  you haven't published a price in the past 20 minutes,
            # and the price change more than 0.5%
            change_min = self.config_price_feed["price_limit"]["change_min"]
            if fabs(price_change) > change_min and \
                    time.time() - self.time_publish_feed > 20 * 60:
                return True
        return False

    def check_median_price(self, median_price, current_feed_price):
        for asset in median_price.keys():
            if current_feed_price[asset] is None:
                continue
            price_change = 100.0 * (
                median_price[asset] - current_feed_price[asset]) \
                / current_feed_price[asset]
            # don't publish price which change more than "change_max"
            if fabs(price_change) > \
                    self.config_price_feed["price_limit"]["change_max"]:
                median_price.pop(asset)

    def publish_feed_price(self, median_price):
        self.time_publish_feed = time.time()
        self.last_publish_price = median_price
        publish_feeds = []
        for asset in median_price:
            publish_feeds.append([asset, median_price[asset] * self.discount])
        self.logger.info("publish price %s", publish_feeds)
        active_delegates = self.client.list_active_delegates()
        for delegate in self.config["delegate_list"]:
            if "allow_stand_by" in self.config["price_feed"] and \
                    self.config["price_feed"]["allow_stand_by"] == 1:
                self.client.publish_feeds(delegate, publish_feeds)
            elif any(d['name'] == delegate for d in active_delegates):
                self.client.publish_feeds(delegate, publish_feeds)

    def display_price(self, median_price, current_feed_price):
        os.system("clear")
        print("================ %s ===================" %
              time.strftime("%Y%m%dT%H%M%S", time.localtime(time.time())))
        print("   ASSET  RATE(/BTC)    CURRENT_FEED   LAST_PUBLISH")
        print("-----------------------------------------------------")
        for asset in sorted(median_price):
            if asset not in self.last_publish_price:
                continue
            _rate_btc = "%.3f" % 1 / self.bts_price.rate_btc[asset]
            if current_feed_price[asset] is None:
                _current_feed_price = None
            else:
                _current_feed_price = "%.4g" % current_feed_price[asset]
            _last_publish_price = "%.4g" % self.last_publish_price[asset]
            print('{: >8}'.format("%s" % asset),
                  '{: >10}'.format('%s' % _rate_btc),
                  '{: >15}'.format("%s" % _current_feed_price),
                  '{: >15}'.format('%s' % _last_publish_price))
        print("====================================================")

    def task_feed_price(self):
        bts_price_in_btc = self.fetch_bts_price()
        median_price = self.get_median_price(bts_price_in_btc)
        current_feed_price = self.get_feed_price()
        # if self.publish_rule_check(median_price, current_feed_price):
        if self.publish_rule_check2(median_price):
            self.check_median_price(median_price, current_feed_price)
            self.publish_feed_price(median_price)
        self.display_price(median_price, current_feed_price)

    def pay_notify(self, delegate_account, pay_account, pay_balance, percent):
        if self.notify["enable"] == 0:
            return
        mail_list = self.config_withdraw_pay["mail_list"]
        if pay_account not in mail_list:
            return
        sender = self.notify["sender"]
        msg_from = "From: %s <%s>\n" % (self.notify["name"], sender)
        msg_to = ""
        for receiver in mail_list[pay_account]:
            msg_to = msg_to + "To: <%s>\n" % receiver

        msg_subject = "Subject: pay day from %s\n" % delegate_account
        msg_content = "you have got payment %d*%.3f BTS\n" % (pay_balance,
                                                              percent)
        message = msg_from + msg_to + msg_subject + msg_content
        smtpObj = smtplib.SMTP(self.notify["smtp_server"])
        smtpObj.sendmail(sender, mail_list[pay_account], message)

    def withdraw_pay(self):
        for pay_list in self.config_withdraw_pay["pay_list"]:
            for delegate_account in pay_list["delegate_account"]:
                pay_balance = pay_list["pay_balance"]
                account_info = self.client.get_account_info(delegate_account)
                balance = float(
                    account_info['delegate_info']['pay_balance']) / (
                        self.client.get_asset_precision("BTS"))
                if balance > pay_balance + 10:
                    for pay_account, percent in pay_list["pay_account"]:
                        self.logger.info("withdraw pay %s %s %s" %
                                         (delegate_account, pay_account,
                                          pay_balance * percent))
                        self.client.delegate_withdraw_pay(
                            delegate_account, pay_account,
                            pay_balance * percent)
                        self.pay_notify(delegate_account, pay_account,
                                        pay_balance, percent)

    def task_withdraw_pay(self):
        self.withdraw_pay()

    def excute(self):
        run_timer = 0
        while True:
            try:
                if self.config_price_feed["run_timer"]and \
                        run_timer % self.config_price_feed["run_timer"] == 0:
                    self.task_feed_price()
                if self.config_withdraw_pay["run_timer"]and \
                        run_timer % self.config_withdraw_pay["run_timer"] == 0:
                    self.task_withdraw_pay()
            except Exception as e:
                self.logger.exception(e)
            run_timer += 1
            time.sleep(int(self.config["base_timer"]))
예제 #8
0
class PublishMarket(object):
    def __init__(self):
        self.pusher = None
        self.order_book = {}
        self.order_book_brief = {}
        self.load_config()
        self.init_market()

    def load_config(self):
        config_file = os.getenv("HOME")+"/.python-bts/publish_market.json"
        fd_config = open(config_file)
        self.config = json.load(fd_config)
        fd_config.close()

    def init_market(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        config_bts = json.load(fd_config)[self.config["bts_client"]]
        fd_config.close()

        self.bts_client = BTS(config_bts["user"], config_bts["password"],
                              config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.bts_client)
        client_info = self.bts_client.get_info()
        self.height = int(client_info["blockchain_head_block_num"])

    def myPublish(self, topic, event):
        if self.pusher:
            self.pusher.publish(topic, event, c=topic)

    def publish_deal_trx(self, deal_trx):
        for trx in deal_trx:
            if trx["type"] == "bid":
                deal_type = "buy"
            else:
                deal_type = "sell"
            prefix = get_prefix(trx["quote"], trx["base"])
            format_trx = [prefix, trx["block"], trx["timestamp"],
                          deal_type, trx["price"], trx["volume"]]
            self.myPublish(
                u'bts.orderbook.%s.trx' % (format_trx[0]), format_trx[1:])
            self.myPublish(u'bts.orderbook.trx', format_trx)
            print(format_trx)

    def publish_place_trx(self, place_trx):
        trx_id = ""
        for trx in place_trx:
            if trx_id == trx["trx_id"]:
                continue
            prefix = get_prefix(trx["quote"], trx["base"])
            trx_id = trx["trx_id"]
            if trx["cancel"]:
                trx["type"] = "cancel " + trx["type"]
            format_trx = [prefix, trx["block"], trx["timestamp"],
                          trx["type"], trx["price"], trx["amount"]]
            self.myPublish(
                u'bts.orderbook.%s.order' % (format_trx[0]), format_trx[1:])
            self.myPublish(u'bts.orderbook.order', format_trx)
            print(format_trx)

    def publish_order_book(self):
        market_list = self.config["market_list"]
        for quote, base in market_list:
            prefix = get_prefix(quote, base)
            order_book = self.market.get_order_book(
                quote, base, cover=True)
            order_book["bids"] = order_book["bids"][:10]
            order_book["asks"] = order_book["asks"][:10]
            if (prefix not in self.order_book or
               self.order_book[prefix] != order_book):
                self.order_book[prefix] = order_book
                self.myPublish(
                    u'bts.orderbook.%s' % prefix, order_book)
                self.publish_order_book_brief(quote, base, order_book)

    def publish_order_book_brief(self, quote, base, order_book):
        prefix = get_prefix(quote, base)
        order_book_brief = {"quote": quote, "base": base,
                            "ask1": None, "bid1": None}
        if order_book["bids"]:
            order_book_brief["bid1"] = order_book["bids"][0][0]
        if order_book["asks"]:
            order_book_brief["ask1"] = order_book["asks"][0][0]
        if (prefix not in self.order_book_brief or
           self.order_book_brief[prefix] != order_book_brief):
            self.order_book_brief[prefix] = order_book_brief
            self.myPublish(
                u'bts.orderbook.%s.brief' % prefix, order_book_brief)

    def execute(self):
        client_info = self.bts_client.get_info()
        height_now = int(client_info["blockchain_head_block_num"])
        if(self.height < height_now):
            time_stamp = client_info["blockchain_head_block_timestamp"]
            self.myPublish(u'bts.blockchain.info',
                           {"height": height_now, "time_stamp": time_stamp})
            self.publish_order_book()
        while self.height < height_now:
            self.height += 1
            trxs = self.bts_client.get_block_transactions(
                self.height)
            recs = self.market.get_order_deal_rec(self.height)
            self.publish_deal_trx(recs)
            recs = self.market.get_order_place_rec(trxs)
            self.publish_place_trx(recs)
            self.market.update_order_owner(recs)
예제 #9
0
class DelegateTask(object):

    def __init__(self):
        self.load_config()
        self.init_bts_price()
        self.setup_log()
        self.init_task_feed_price()

    def load_config(self):
        config_file = os.getenv("HOME")+"/.python-bts/delegate_task.json"
        fd_config = open(config_file)
        config = json.load(fd_config)
        self.notify = config["notify"]
        self.config = config["delegate_task"]
        self.config_price_feed = self.config["price_feed"]
        self.config_withdraw_pay = self.config["withdraw_pay"]
        fd_config.close()
        config_file = os.getenv("HOME")+"/.python-bts/bts_client.json"
        fd_config = open(config_file)
        self.config_bts = json.load(fd_config)[self.config["bts_client"]]
        fd_config.close()

    def init_bts_price(self):
        config_bts = self.config_bts
        self.client = BTS(config_bts["user"], config_bts["password"],
                          config_bts["host"], config_bts["port"])
        self.bts_market = BTSMarket(self.client)
        self.bts_price = BTSPriceAfterMatch(self.bts_market)
        # don't use cover order, because it's feed price related,
        # if there is a big order, feed price will down without stop
        self.bts_price.set_need_cover(False)

    def setup_log(self):
        # Setting up Logger
        self.logger = logging.getLogger('bts')
        self.logger.setLevel(logging.INFO)
        formatter = logging.Formatter(
            '%(asctime)s[%(levelname)s]: %(message)s')
        fh = logging.handlers.RotatingFileHandler("/tmp/bts_delegate_task.log")
        fh.setFormatter(formatter)
        self.logger.addHandler(fh)

    def init_task_feed_price(self):
        peg_asset_list = ["KRW", "BTC", "SILVER", "GOLD", "TRY",
                          "SGD", "HKD", "RUB", "SEK", "NZD", "CNY",
                          "MXN", "CAD", "CHF", "AUD", "GBP", "JPY",
                          "EUR", "USD", "SHENZHEN", "NASDAQC", "NIKKEI",
                          "HANGSENG", "SHANGHAI"]
        self.price_queue = {}
        for asset in peg_asset_list:
            self.price_queue[asset] = []
        self.time_publish_feed = 0
        self.last_publish_price = {}
        # Actually I think it may be beneficial to discount all feeds by 0.995
        # to give the market makers some breathing room and provide a buffer
        # against down trends.
        self.discount = 0.995

    def fetch_bts_price(self):
        # updat rate cny
        self.bts_price.get_rate_from_yahoo()

        # get all order book
        self.bts_price.get_order_book_all()

        # can add weight here
        for order_type in self.bts_price.order_types:
            for market in self.bts_price.order_book:
                if market not in self.config_price_feed["market_weight"]:
                    _weight = 0.0
                else:
                    _weight = self.config_price_feed["market_weight"][market]
                for _order in self.bts_price.order_book[market][order_type]:
                    _order[1] *= _weight

        # calculate real price
        volume, volume_sum, real_price = self.bts_price.get_real_price(
            spread=self.config_price_feed["price_limit"]["spread"])
        valid_depth = self.bts_price.get_valid_depth(
            price=real_price,
            spread=self.config_price_feed["price_limit"]["spread"])
        price_cny = real_price / self.bts_price.rate_btc["CNY"]
        self.logger.info("fetch price is %.5f CNY/BTS, volume is %.3f",
                         price_cny, volume)
        self.logger.info("efficent depth : %s" % valid_depth)
        return real_price

    # these MPA's precision is 100, it's too small,
    # have to change the price
    # but we should fixed these at BTS2.0
    def patch_nasdaqc(self, median_price):
        if "SHENZHEN" in median_price:
            median_price["SHENZHEN"] /= median_price["CNY"]
        if "SHANGHAI" in median_price:
            median_price["SHANGHAI"] /= median_price["CNY"]
        if "NASDAQC" in median_price:
            median_price["NASDAQC"] /= median_price["USD"]
        if "NIKKEI" in median_price:
            median_price["NIKKEI"] /= median_price["JPY"]
        if "HANGSENG" in median_price:
            median_price["HANGSENG"] /= median_price["HKD"]

    def get_median_price(self, bts_price_in_btc):
        median_price = {}
        for asset in self.price_queue:
            if asset not in self.bts_price.rate_btc or \
                    self.bts_price.rate_btc[asset] is None:
                continue
            self.price_queue[asset].append(bts_price_in_btc
                                           / self.bts_price.rate_btc[asset])
            if len(self.price_queue[asset]) > \
                    self.config_price_feed["price_limit"]["median_length"]:
                self.price_queue[asset].pop(0)
            median_price[asset] = sorted(
                self.price_queue[asset])[int(len(self.price_queue[asset]) / 2)]
        self.patch_nasdaqc(median_price)
        return median_price

    def get_feed_price(self):
        current_feed_price = {}
        for asset in self.price_queue:
            current_feed_price[asset] = self.client.get_feed_price(asset)
        return current_feed_price

    def publish_rule_check2(self, median_price):
        if time.time() - self.time_publish_feed > \
                self.config_price_feed["max_update_hours"] * 60 * 60:
            #self.logger.info("publish 1")
            return True
        change_min = self.config_price_feed["price_limit"]["change_min"]
        for asset in median_price:
            if asset not in self.last_publish_price:
                continue
            price_change = 100.0 * (
                median_price[asset] - self.last_publish_price[asset]) \
                / self.last_publish_price[asset]

            if fabs(price_change) > change_min:
                return True
        return False

    def publish_rule_check(self, median_price, current_feed_price):
        # When attempting to write a market maker the slow movement of the feed
        # can be difficult.
        # I would recommend the following:
        # if  REAL_PRICE < MEDIAN and YOUR_PRICE > MEDIAN publish price
        # if  you haven't published a price in the past 20 minutes
        #   if  REAL_PRICE > MEDIAN  and  YOUR_PRICE < MEDIAN and
        # abs( YOUR_PRICE - REAL_PRICE ) / REAL_PRICE  > 0.005 publish price
        # The goal is to force the price down rapidly and allow it to creep up
        # slowly.
        # By publishing prices more often it helps market makers maintain the
        # peg and minimizes opportunity for shorts to sell USD below the peg
        # that the market makers then have to absorb.
        # If we can get updates flowing smoothly then we can gradually reduce
        # the spread in the market maker bots.
        # note: all prices in USD per BTS
        if time.time() - self.time_publish_feed > \
                self.config_price_feed["max_update_hours"] * 60 * 60:
            return True
        for asset in median_price:
            price_change = 100.0 * (
                median_price[asset] - self.last_publish_price[asset]) \
                / self.last_publish_price[asset]

            # ignore if change less than 0.2%
            change_ignore = \
                self.config_price_feed["price_limit"]["change_ignore"]
            if fabs(price_change) < change_ignore:
                continue

            if current_feed_price[asset] and \
                    median_price[asset] < current_feed_price[asset] / \
                    self.discount and self.last_publish_price[asset] > \
                    current_feed_price[asset] / self.discount:
                # self.logger.info(
                #     "need update: %s %s %s" % (
                #         median_price[asset], self.last_publish_price[asset],
                #         current_feed_price[asset] / self.discount))
                return True
            # if  you haven't published a price in the past 20 minutes,
            # and the price change more than 0.5%
            change_min = self.config_price_feed["price_limit"]["change_min"]
            if fabs(price_change) > change_min and \
                    time.time() - self.time_publish_feed > 20 * 60:
                return True
        return False

    def check_median_price(self, median_price, current_feed_price):
        for asset in median_price.keys():
            if current_feed_price[asset] is None:
                continue
            price_change = 100.0 * (
                median_price[asset] - current_feed_price[asset]) \
                / current_feed_price[asset]
            # don't publish price which change more than "change_max"
            if fabs(price_change) > \
                    self.config_price_feed["price_limit"]["change_max"]:
                median_price.pop(asset)

    def publish_feed_price(self, median_price):
        self.time_publish_feed = time.time()
        self.last_publish_price = median_price
        publish_feeds = []
        for asset in median_price:
            publish_feeds.append(
                [asset, median_price[asset] * self.discount])
        self.logger.info("publish price %s", publish_feeds)
        active_delegates = self.client.list_active_delegates()
        for delegate in self.config["delegate_list"]:
            if "allow_stand_by" in self.config["price_feed"] and \
                    self.config["price_feed"]["allow_stand_by"] == 1:
                self.client.publish_feeds(delegate, publish_feeds)
            elif any(d['name'] == delegate for d in active_delegates):
                self.client.publish_feeds(delegate, publish_feeds)

    def display_price(self, median_price, current_feed_price):
        os.system("clear")
        print("================ %s ===================" %
              time.strftime("%Y%m%dT%H%M%S", time.localtime(time.time())))
        print("   ASSET  RATE(/BTC)    CURRENT_FEED   LAST_PUBLISH")
        print("-----------------------------------------------------")
        for asset in sorted(median_price):
            if asset not in self.last_publish_price:
                continue
            _rate_btc = "%.3f" % 1/self.bts_price.rate_btc[asset]
            if current_feed_price[asset] is None:
                _current_feed_price = None
            else:
                _current_feed_price = "%.4g" % current_feed_price[asset]
            _last_publish_price = "%.4g" % self.last_publish_price[asset]
            print(
                '{: >8}'.format("%s" % asset), '{: >10}'.
                format('%s' % _rate_btc), '{: >15}'.
                format("%s" % _current_feed_price), '{: >15}'.
                format('%s' % _last_publish_price))
        print("====================================================")

    def task_feed_price(self):
        bts_price_in_btc = self.fetch_bts_price()
        median_price = self.get_median_price(bts_price_in_btc)
        current_feed_price = self.get_feed_price()
        # if self.publish_rule_check(median_price, current_feed_price):
        if self.publish_rule_check2(median_price):
            self.check_median_price(median_price, current_feed_price)
            self.publish_feed_price(median_price)
        self.display_price(median_price, current_feed_price)

    def pay_notify(self, delegate_account, pay_account,
                   pay_balance, percent):
        if self.notify["enable"] == 0:
            return
        mail_list = self.config_withdraw_pay["mail_list"]
        if pay_account not in mail_list:
            return
        sender = self.notify["sender"]
        msg_from = "From: %s <%s>\n" % (self.notify["name"], sender)
        msg_to = ""
        for receiver in mail_list[pay_account]:
            msg_to = msg_to+"To: <%s>\n" % receiver

        msg_subject = "Subject: pay day from %s\n" % delegate_account
        msg_content = "you have got payment %d*%.3f BTS\n" % (
            pay_balance, percent)
        message = msg_from+msg_to+msg_subject+msg_content
        smtpObj = smtplib.SMTP(self.notify["smtp_server"])
        smtpObj.sendmail(sender, mail_list[pay_account], message)

    def withdraw_pay(self):
        for pay_list in self.config_withdraw_pay["pay_list"]:
            for delegate_account in pay_list["delegate_account"]:
                pay_balance = pay_list["pay_balance"]
                account_info = self.client.get_account_info(delegate_account)
                balance = float(account_info['delegate_info']['pay_balance'])/(
                    self.client.get_asset_precision("BTS"))
                if balance > pay_balance + 10:
                    for pay_account, percent in pay_list["pay_account"]:
                        self.logger.info("withdraw pay %s %s %s"
                                         % (delegate_account, pay_account,
                                            pay_balance*percent))
                        self.client.delegate_withdraw_pay(
                            delegate_account, pay_account, pay_balance*percent)
                        self.pay_notify(delegate_account, pay_account,
                                        pay_balance, percent)

    def task_withdraw_pay(self):
        self.withdraw_pay()

    def excute(self):
        run_timer = 0
        while True:
            try:
                if self.config_price_feed["run_timer"]and \
                        run_timer % self.config_price_feed["run_timer"] == 0:
                    self.task_feed_price()
                if self.config_withdraw_pay["run_timer"]and \
                        run_timer % self.config_withdraw_pay["run_timer"] == 0:
                    self.task_withdraw_pay()
            except Exception as e:
                self.logger.exception(e)
            run_timer += 1
            time.sleep(int(self.config["base_timer"]))
예제 #10
0
class BTSOrderBook(object):
    def __init__(self):
        self.market_list = [
            ["BOTSCNY", "BTS"], ["CNY", "BTS"], ["USD", "BTS"],
            ["GOLD", "BTS"], ["BTC", "BTS"], ["BDR.AAPL", "CNY"],
            ["NOTE", "BTS"]]
        self.init_done = False
        self.pusher = None
        self.order_book = {}
        self.deal_trx = {}
        self.place_trx = {}
        for quote, base in self.market_list:
            prefix = get_prefix(quote, base)
            self.order_book[prefix] = {}
            self.deal_trx[prefix] = []
            self.place_trx[prefix] = []
        self.init_market()
        self.execute()
        self.init_done = True

    def init_market(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        config_bts = json.load(fd_config)["client_default"]
        fd_config.close()

        self.bts_client = BTS(config_bts["user"], config_bts["password"],
                              config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.bts_client)
        client_info = self.bts_client.get_info()
        self.height = int(client_info["blockchain_head_block_num"]) - 180

    def myPublish(self, topic, event):
        if self.pusher and self.init_done:
            self.pusher.emit(topic, event, namespace="")

    def publish_deal_trx(self, deal_trx):
        for trx in deal_trx:
            if trx["type"] == "bid":
                deal_type = "buy"
            else:
                deal_type = "sell"
            prefix = get_prefix(trx["quote"], trx["base"])
            format_trx = [prefix, trx["block"], trx["timestamp"],
                          deal_type, trx["price"], trx["volume"]]
            self.myPublish(
                u'bts.orderbook.%s.trx' % (format_trx[0]), format_trx[1:])
            self.myPublish(u'bts.orderbook.trx', format_trx)
            market = format_trx[0]
            if market not in self.deal_trx:
                self.deal_trx[market] = []
            self.deal_trx[market].append(format_trx[1:])
            print(format_trx)

    def publish_place_trx(self, place_trx):
        trx_id = ""
        for trx in place_trx:
            if trx_id == trx["trx_id"]:
                continue
            prefix = get_prefix(trx["quote"], trx["base"])
            trx_id = trx["trx_id"]
            if trx["cancel"]:
                trx["type"] = "cancel " + trx["type"]
            format_trx = [prefix, trx["block"], trx["timestamp"],
                          trx["type"], trx["price"], trx["amount"]]
            self.myPublish(
                u'bts.orderbook.%s.order' % (format_trx[0]), format_trx[1:])
            self.myPublish(u'bts.orderbook.order', format_trx)
            market = format_trx[0]
            if market not in self.place_trx:
                self.place_trx[market] = []
            self.place_trx[market].append(format_trx[1:])
            print(format_trx)

    def publish_order_book(self):
        for quote, base in self.market_list:
            prefix = get_prefix(quote, base)
            order_book = self.market.get_order_book(
                quote, base)
            order_book["bids"] = order_book["bids"][:10]
            order_book["asks"] = order_book["asks"][:10]
            if (prefix not in self.order_book or
               self.order_book[prefix] != order_book):
                self.order_book[prefix] = order_book
                self.myPublish(
                    u'bts.orderbook.%s' % prefix, order_book)

    def execute(self):
        client_info = self.bts_client.get_info()
        height_now = int(client_info["blockchain_head_block_num"])
        if(self.height < height_now):
            time_stamp = client_info["blockchain_head_block_timestamp"]
            self.myPublish(u'bts.blockchain.info',
                           {"height": height_now, "time_stamp": time_stamp})
            self.publish_order_book()
        while self.height < height_now:
            self.height += 1
            trxs = self.bts_client.get_block_transactions(
                self.height)
            recs = self.market.get_order_deal_rec(self.height)
            self.publish_deal_trx(recs)
            recs = self.market.get_order_place_rec(trxs)
            self.publish_place_trx(recs)
            self.market.update_order_owner(recs)
예제 #11
0
 def init_bts(self):
     config_bts = self.config_bts
     self.bts_client = BTS(config_bts["user"], config_bts["password"],
                           config_bts["host"], config_bts["port"])
     self.delegate_num = int(self.bts_client.chain_info["delegate_num"])
     self.period = float(self.bts_client.chain_info["block_interval"])
예제 #12
0
class DelegateWatch(object):

    def __init__(self):
        self.confirm = 2
        self.load_config()
        self.init_bts()
        self.setup_log()
        self.init_watch()
        self.init_contact()

    def load_config(self):
        config_file = os.getenv("HOME")+"/.python-bts/delegate_watch.json"
        fd_config = open(config_file)
        self.config = json.load(fd_config)["delegate_watch"]
        fd_config.close()
        config_file = os.getenv("HOME")+"/.python-bts/bts_client.json"
        fd_config = open(config_file)
        self.config_bts = json.load(fd_config)[self.config["bts_client"]]
        fd_config.close()

    def init_bts(self):
        config_bts = self.config_bts
        self.bts_client = BTS(config_bts["user"], config_bts["password"],
                              config_bts["host"], config_bts["port"])
        self.delegate_num = int(self.bts_client.chain_info["delegate_num"])
        self.period = float(self.bts_client.chain_info["block_interval"])

    def setup_log(self):
        # Setting up Logger
        self.logger = logging.getLogger('bts')
        self.logger.setLevel(logging.INFO)
        formatter = logging.Formatter(
            '%(asctime)s[%(levelname)s]: %(message)s')
        fh = logging.handlers.RotatingFileHandler(
            "/tmp/bts_delegate_watch.log")
        fh.setFormatter(formatter)
        self.logger.addHandler(fh)

    def init_watch(self):
        client_info = self.bts_client.get_info()
        self.height = int(client_info["blockchain_head_block_num"])
        self.round_left = int(client_info["blockchain_blocks_left_in_round"])
        self.active_delegates = self.bts_client.list_active_delegates()
        self.active_offset = self.height
        for delegate in self.active_delegates:
            last_block_num = int(
                delegate["delegate_info"]["last_block_num_produced"])
            if last_block_num == self.height:
                break
            self.active_offset -= 1

    def init_contact(self):
        self.contact = {}
        mail_list = self.config["contact"]
        for mail in mail_list:
            if mail_list[mail]["enable"] == 0:
                continue
            for delegate in mail_list[mail]["delegates"]:
                if delegate not in self.contact:
                    self.contact[delegate] = [mail]
                else:
                    self.contact[delegate].append(mail)

    def process_missed_block(self, height):
        index = (height-self.active_offset) % self.delegate_num
        account = self.active_delegates[index]["name"]
        print("missed", height, account)
        self.logger.info("missed %s", account)
        self.active_offset -= 1
        self.notify(account, height)
        return account

    def get_block_delegate(self, height):
        index = (height-self.active_offset) % self.delegate_num
        account = self.active_delegates[index]["name"]
        print("......", height, account)
        self.logger.info("%d %s", height, account)
        return account

    def check_missed_block(self, height):
        limit = height - self.height + 1
        list_blocks = self.bts_client.list_blocks(height, limit)
        last_timestamp = -1
        for block in reversed(list_blocks):
            timestamp = int(block["timestamp"][-2:])
            block_num = int(block["block_num"])
            if last_timestamp != -1:
                period = (timestamp - last_timestamp + 60) % 60
                while period != self.period:
                    period -= self.period
                    self.process_missed_block(block_num)
                self.get_block_delegate(block_num)
            last_timestamp = timestamp

    def notify(self, account, height):
        if account not in self.contact:
            print("no contact")
            return
        print ("sent notify mail")
        sender = self.config["sender"]
        msg_from = "From: %s <%s>\n" % (self.config["name"], sender)
        msg_to = ""
        for receiver in self.contact[account]:
            msg_to = msg_to+"To: <%s>\n" % receiver

        msg_subject = "Subject: missed block attention for %s\n" % account
        msg_content = "you have missed block %d\n" % height
        message = msg_from+msg_to+msg_subject+msg_content
        print(message)
        smtpObj = smtplib.SMTP('localhost')
        smtpObj.sendmail(sender, self.contact[account], message)

    def execute(self):
        while True:
            try:
                client_info = self.bts_client.get_info()
                height = int(
                    client_info["blockchain_head_block_num"]) - self.confirm
                if height > self.height:
                    round_left = (int(
                        client_info["blockchain_blocks_left_in_round"]
                    ) + self.confirm - 1) % self.delegate_num + 1
                    if round_left > self.round_left:
                        if self.round_left != 1:
                            round_left = 1
                            height = self.height+self.round_left-1
                        else:
                            self.active_delegates = \
                                self.bts_client.list_active_delegates()
                    self.check_missed_block(height)
                    self.height = height
                    self.round_left = round_left
            except Exception as e:
                self.logger.exception(e)
            now = time.time()
            nexttime = int(now/self.period+1)*self.period - now
            time.sleep(nexttime+1)
예제 #13
0
class BTSOrderBook(object):
    def __init__(self):
        self.market_list = [["BOTSCNY", "BTS"], ["CNY", "BTS"], ["USD", "BTS"],
                            ["GOLD", "BTS"], ["BTC", "BTS"],
                            ["BDR.AAPL", "CNY"], ["NOTE", "BTS"]]
        self.init_done = False
        self.pusher = None
        self.order_book = {}
        self.deal_trx = {}
        self.place_trx = {}
        for quote, base in self.market_list:
            prefix = get_prefix(quote, base)
            self.order_book[prefix] = {}
            self.deal_trx[prefix] = []
            self.place_trx[prefix] = []
        self.init_market()
        self.execute()
        self.init_done = True

    def init_market(self):
        config_file = os.getenv("HOME") + "/.python-bts/bts_client.json"
        fd_config = open(config_file)
        config_bts = json.load(fd_config)["client_default"]
        fd_config.close()

        self.bts_client = BTS(config_bts["user"], config_bts["password"],
                              config_bts["host"], config_bts["port"])
        self.market = BTSMarket(self.bts_client)
        client_info = self.bts_client.get_info()
        self.height = int(client_info["blockchain_head_block_num"]) - 180

    def myPublish(self, topic, event):
        if self.pusher and self.init_done:
            self.pusher.emit(topic, event, namespace="")

    def publish_deal_trx(self, deal_trx):
        for trx in deal_trx:
            if trx["type"] == "bid":
                deal_type = "buy"
            else:
                deal_type = "sell"
            prefix = get_prefix(trx["quote"], trx["base"])
            format_trx = [
                prefix, trx["block"], trx["timestamp"], deal_type,
                trx["price"], trx["volume"]
            ]
            self.myPublish(u'bts.orderbook.%s.trx' % (format_trx[0]),
                           format_trx[1:])
            self.myPublish(u'bts.orderbook.trx', format_trx)
            market = format_trx[0]
            if market not in self.deal_trx:
                self.deal_trx[market] = []
            self.deal_trx[market].append(format_trx[1:])
            print(format_trx)

    def publish_place_trx(self, place_trx):
        trx_id = ""
        for trx in place_trx:
            if trx_id == trx["trx_id"]:
                continue
            prefix = get_prefix(trx["quote"], trx["base"])
            trx_id = trx["trx_id"]
            if trx["cancel"]:
                trx["type"] = "cancel " + trx["type"]
            format_trx = [
                prefix, trx["block"], trx["timestamp"], trx["type"],
                trx["price"], trx["amount"]
            ]
            self.myPublish(u'bts.orderbook.%s.order' % (format_trx[0]),
                           format_trx[1:])
            self.myPublish(u'bts.orderbook.order', format_trx)
            market = format_trx[0]
            if market not in self.place_trx:
                self.place_trx[market] = []
            self.place_trx[market].append(format_trx[1:])
            print(format_trx)

    def publish_order_book(self):
        for quote, base in self.market_list:
            prefix = get_prefix(quote, base)
            order_book = self.market.get_order_book(quote, base)
            order_book["bids"] = order_book["bids"][:10]
            order_book["asks"] = order_book["asks"][:10]
            if (prefix not in self.order_book
                    or self.order_book[prefix] != order_book):
                self.order_book[prefix] = order_book
                self.myPublish(u'bts.orderbook.%s' % prefix, order_book)

    def execute(self):
        client_info = self.bts_client.get_info()
        height_now = int(client_info["blockchain_head_block_num"])
        if (self.height < height_now):
            time_stamp = client_info["blockchain_head_block_timestamp"]
            self.myPublish(u'bts.blockchain.info', {
                "height": height_now,
                "time_stamp": time_stamp
            })
            self.publish_order_book()
        while self.height < height_now:
            self.height += 1
            trxs = self.bts_client.get_block_transactions(self.height)
            recs = self.market.get_order_deal_rec(self.height)
            self.publish_deal_trx(recs)
            recs = self.market.get_order_place_rec(trxs)
            self.publish_place_trx(recs)
            self.market.update_order_owner(recs)