def order(self, body): with BitFlyer.__urlopen("POST", "/v1/me/sendchildorder", data=body) as res: html = res.read().decode("utf-8") applog.info(html) child_order_acceptance_id = json.loads( html)["child_order_acceptance_id"] retry_count = 0 while True: if retry_count > 3: assert ("failed order") time.sleep(3) retry_count += 1 with BitFlyer.__urlopen("GET", "/v1/me/getchildorders", param={ "child_order_acceptance_id": child_order_acceptance_id }) as res: html = res.read().decode("utf-8") if len(json.loads(html)) > 0: applog.info(html) commission = json.loads(html)[0]["total_commission"] break return [child_order_acceptance_id, commission]
def __urlopen(method, path, *, param=None, data={}): paramtext = "" body = "" if method == "POST": body = json.dumps(data) else: if (param): paramtext = "?" + urllib.parse.urlencode(param) timestamp = str(time.time()) text = timestamp + method + path + paramtext + body applog.info(text) sign = hmac.new(bytes(bitflyer_api_secret.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest() headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36', 'ACCESS-KEY': bitflyer_api_key, 'ACCESS-TIMESTAMP': timestamp, 'ACCESS-SIGN': sign, 'Content-Type': 'application/json' } if method == "POST": req = urllib.request.Request(url=BitFlyer.__api_endpoint + path + paramtext, data=json.dumps(data).encode("utf-8"), headers=headers) else: req = urllib.request.Request(url=BitFlyer.__api_endpoint + path + paramtext, headers=headers) return fetch_url(req)
def health_check(self, dryrun): if dryrun: return True with BitFlyer.__urlopen_public("GET", "/v1/getboardstate") as res: html = res.read().decode("utf-8") j = json.loads(html) if j["state"] == "RUNNING" and j["health"] in [ "NORMAL", "BUSY", "VERY BUSY" ]: applog.info("Bitflyer API status:" + j["state"] + ", health:" + j["health"]) return True else: applog.warning("Bitflyer API has problem.") applog.warning("state:" + j["state"] + ", health:" + j["health"]) applog.warning( "watch:https://lightning.bitflyer.jp/docs?lang=ja&_ga=2.27791676.1496421283.1524886778-454668974.1522570784#%E6%9D%BF%E3%81%AE%E7%8A%B6%E6%85%8B" ) return False
def run(self, run_mode, is_binance, is_poloniex): applog.init(self.__prepare_dir(self.log_dir + "/app.log")) if run_mode == "RealTrade": dryrun = False else: dryrun = True mailer = Mailer() if mailer.is_use(): if not mailer.checkmailer(): applog.error("mailer not activation!") sys.exit() applog.info("========================================") applog.info("Start Triangular Arbitrage. RunMode = " + run_mode) applog.info("binance.comission_fee: %0.8f" % self.binance.comission_fee) applog.info("profit_lower_limit: %0.8f" % self.profit_lower_limit) applog.info("expected_final_profit_lower_btc_limit: %0.8f" % self.expected_final_profit_lower_btc_limit) applog.info("risk_hedge_of_target_currency_price: %d" % self.risk_hedge_of_target_currency_price) applog.info("========================================") self.binance.refresh_exchange_info() try: self.main_loop(dryrun, is_binance, is_poloniex) except Exception as e: applog.error(traceback.format_exc()) mailer.sendmail(traceback.format_exc(), "Assertion - Daryl Triangular")
def make_final_order(self, _orders, base_currency_name, route_type, risk_hedge): if self.__get_asset_lot(base_currency_name) == 0: print("You are no money. " + base_currency_name) return None via_symbol = _orders[1]["symbol"] via_currency_name = self.__get_basename_from_symbol(via_symbol) final_orders = [] for order in _orders: symbol = order["symbol"] depth = self.binance.depth(symbol) lot = 0.0 for ask in depth["asks" if order["side"] == "BUY" else "bids"]: price = float(ask[0]) lot = lot + float(ask[1]) if lot * price / risk_hedge > self.__get_lower_limit( self.__get_basename_from_symbol(symbol), True): break final_orders.append({ "symbol": order["symbol"], "side": order["side"], "price": price, "lot": lot, }) if route_type == "BUY_BUY_SELL": final_orders[0][ "base_lot"] = final_orders[0]["price"] * final_orders[0]["lot"] final_orders[1]["base_lot"] = final_orders[1][ "price"] * final_orders[1]["lot"] * final_orders[0]["price"] final_orders[2][ "base_lot"] = final_orders[2]["price"] * final_orders[2]["lot"] elif route_type == "BUY_SELL_SELL": final_orders[0][ "base_lot"] = final_orders[0]["price"] * final_orders[0]["lot"] final_orders[1]["base_lot"] = final_orders[1][ "price"] * final_orders[1]["lot"] * final_orders[2]["price"] final_orders[2][ "base_lot"] = final_orders[2]["price"] * final_orders[2]["lot"] elif route_type == "BUY_SELL_BUY": final_orders[0][ "base_lot"] = final_orders[0]["price"] * final_orders[0]["lot"] final_orders[1]["base_lot"] = final_orders[1][ "price"] * final_orders[1]["lot"] / final_orders[2]["price"] final_orders[2]["base_lot"] = final_orders[2]["lot"] elif route_type == "SELL_BUY_SELL": final_orders[0]["base_lot"] = final_orders[0]["lot"] final_orders[1]["base_lot"] = final_orders[1][ "price"] * final_orders[1]["lot"] / final_orders[0]["price"] final_orders[2][ "base_lot"] = final_orders[2]["price"] * final_orders[2]["lot"] raw_min_base_lot = min([ final_orders[0]["base_lot"], final_orders[1]["base_lot"], final_orders[2]["base_lot"] ]) min_base_lot = raw_min_base_lot / risk_hedge min_base_lot = min(min_base_lot, self.__get_asset_lot(base_currency_name)) if min_base_lot < self.__get_lower_limit(base_currency_name, True): applog.info( "Total must be at latest %f%s. (min_base_lot = %0.8f)" % (self.__get_lower_limit(base_currency_name, True), base_currency_name, min_base_lot)) return None if route_type == "BUY_BUY_SELL": final_orders[0]["final_lot"] = self.binance.lot_filter( final_orders[0]["lot"] * min_base_lot / final_orders[0]["base_lot"], final_orders[0]["symbol"]) # via lot final_orders[1]["final_lot"] = self.binance.lot_filter( final_orders[0]["final_lot"] / final_orders[1]["price"], final_orders[1]["symbol"], final_orders[2]["symbol"]) # target lot final_orders[2]["final_lot"] = final_orders[1][ "final_lot"] # target lot elif route_type == "BUY_SELL_SELL": final_orders[0]["final_lot"] = self.binance.lot_filter( final_orders[0]["lot"] * min_base_lot / final_orders[0]["base_lot"], final_orders[0]["symbol"], final_orders[1]["symbol"]) # target lot final_orders[1]["final_lot"] = final_orders[0][ "final_lot"] # target lot final_orders[2]["final_lot"] = self.binance.lot_filter( final_orders[1]["final_lot"] * final_orders[1]["price"], final_orders[2]["symbol"]) # via lot elif route_type == "BUY_SELL_BUY": final_orders[0]["final_lot"] = self.binance.lot_filter( final_orders[0]["lot"] * min_base_lot / final_orders[0]["base_lot"], final_orders[0]["symbol"], final_orders[1]["symbol"]) # target lot final_orders[1]["final_lot"] = final_orders[0][ "final_lot"] # target lot final_orders[2]["final_lot"] = self.binance.lot_filter( final_orders[1]["final_lot"] * final_orders[0]["price"], final_orders[0]["symbol"]) # base lot elif route_type == "SELL_BUY_SELL": final_orders[0]["final_lot"] = self.binance.lot_filter( final_orders[0]["lot"] * min_base_lot / final_orders[0]["base_lot"], final_orders[0]["symbol"]) # base lot final_orders[1]["final_lot"] = self.binance.lot_filter( final_orders[0]["final_lot"] / final_orders[2]["price"], final_orders[1]["symbol"], final_orders[2]["symbol"]) # target lot final_orders[2]["final_lot"] = final_orders[1][ "final_lot"] # target lot via_lot = final_orders[1]["final_lot"] * final_orders[1]["price"] if via_lot < self.__get_lower_limit(via_currency_name, False): applog.info("Total must be at latest %f%s. (via_lot = %0.8f)" % (self.__get_lower_limit(via_currency_name, False), via_currency_name, via_lot)) return None return final_orders
def trade_binance(self, triangle_order, dryrun): start_t = datetime.now() mailer = Mailer() route = triangle_order[2] base_currency_name = route[:3] viasymbole = triangle_order[8].split(":")[0] via_currency_name = viasymbole[len(viasymbole) - 3:] route_type = triangle_order[16] pre_orders = [ { "symbol": triangle_order[4].split(":")[0], "side": triangle_order[4].split(":")[1], "price": float(triangle_order[5]), "lot": float(triangle_order[6]), "base_lot": float(triangle_order[7]), }, { "symbol": triangle_order[8].split(":")[0], "side": triangle_order[8].split(":")[1], "price": float(triangle_order[9]), "lot": float(triangle_order[10]), "base_lot": float(triangle_order[11]), }, { "symbol": triangle_order[12].split(":")[0], "side": triangle_order[12].split(":")[1], "price": float(triangle_order[13]), "lot": float(triangle_order[14]), "base_lot": float(triangle_order[15]), }, ] orders = self.make_final_order(pre_orders, base_currency_name, route_type, 2) if orders is None: return False applog.info("") order_count = 0 for order in orders: pre_order = pre_orders[order_count] applog.info("%s(%s)" % (order["symbol"], order["side"])) applog.info( "price: %5.8f => %5.8f %s" % (pre_order["price"], order["price"], "" if pre_order["price"] == order["price"] else "(%+0.8f)" % (order["price"] - pre_order["price"]))) applog.info( "lot: %5.8f => %5.8f %s" % (pre_order["lot"], order["lot"], "" if pre_order["lot"] == order["lot"] else "(%+0.8f)" % (order["lot"] - pre_order["lot"]))) applog.info( "base_lot: %5.8f => %5.8f %s" % (pre_order["base_lot"], order["base_lot"], "" if pre_order["base_lot"] == order["base_lot"] else "(%+0.8f)" % (order["base_lot"] - pre_order["base_lot"]))) applog.info("final_lot: => %5.8f" % order["final_lot"]) order_count += 1 applog.info("") if route_type == "BUY_SELL_BUY": expected_profit = orders[2][ "final_lot"] - orders[0]["final_lot"] * orders[0]["price"] expected_fee = (lambda x: x - x * (1 - self.binance.comission_fee)**3)( orders[2]["final_lot"]) elif route_type == "SELL_BUY_SELL": expected_profit = orders[2]["final_lot"] * orders[2][ "price"] - orders[0]["final_lot"] expected_fee = (lambda x: x - x * (1 - self.binance.comission_fee)**3)( orders[2]["final_lot"] * orders[2]["price"]) else: expected_profit = orders[2]["final_lot"] * orders[2][ "price"] - orders[0]["final_lot"] * orders[0]["price"] expected_fee = (lambda x: x - x * (1 - self.binance.comission_fee)**3)( orders[2]["final_lot"] * orders[2]["price"]) expected_final_profit = expected_profit - expected_fee if expected_final_profit < self.expected_final_profit_lower_btc_limit: applog.info( "Situation has changed. expected_final_revenue = %0.8f < %0.8f" % (expected_final_profit, self.expected_final_profit_lower_btc_limit)) return False msgs = [""] msgs.append("[beta] %d JPY" % ((expected_final_profit) * 1000000)) msgs.append("Expected Final Profit:%0.8f%s" % (expected_final_profit, base_currency_name)) msgs.append("Expected fee:%0.8f%s" % (expected_fee, base_currency_name)) msgs.append( "Expected Profit:%0.8f%s 1st lot(%0.8f(%0.8f%s)) => 3rd lot(%0.8f(%0.8f%s))" % ( expected_profit, base_currency_name, orders[0]["final_lot"], orders[0]["final_lot"] * orders[0]["price"], base_currency_name, orders[2]["final_lot"], orders[2]["final_lot"] * orders[2]["price"], base_currency_name, )) msgs.append("") msgs.append( "1st order:%s(%s), price:%0.8f, lot:%0.8f, base_lot:%0.8f, final_lot:%0.8f" % (orders[0]["symbol"], orders[0]["side"], orders[0]["price"], orders[0]["lot"], orders[0]["base_lot"], orders[0]["final_lot"])) msgs.append( "2nd order:%s(%s), price:%0.8f, lot:%0.8f, base_lot:%0.8f, final_lot:%0.8f" % (orders[1]["symbol"], orders[1]["side"], orders[1]["price"], orders[1]["lot"], orders[1]["base_lot"], orders[1]["final_lot"])) msgs.append( "3rd order:%s(%s), price:%0.8f, lot:%0.8f, base_lot:%0.8f, final_lot:%0.8f" % (orders[2]["symbol"], orders[2]["side"], orders[2]["price"], orders[2]["lot"], orders[2]["base_lot"], orders[2]["final_lot"])) for msg in msgs: applog.info(msg) mailer.sendmail("\n".join(msgs), "%s - Create order - Daryl Triangular" % route) if not dryrun: order_count = 0 final_status = "" for order in orders: r = self.binance.order( order["symbol"], order["side"], binance.client.Client.ORDER_TYPE_LIMIT, binance.client.Client.TIME_IN_FORCE_GTC, order["final_lot"], "%0.8f" % order["price"], ) applog.info("binance.create_order" + str(r)) i = 0 while True: try: r = self.binance.get_order(r["symbol"], r["orderId"]) except BinanceAPIException as e: if e.status_code == -2013: # NO_SUCH_ORDER applog.info( "BinanceAPIException: Order does not exist. (%d)" % i) if i < 30: time.sleep(0.001) else: time.sleep(10) i += 1 continue if r["status"] == binance.client.Client.ORDER_STATUS_FILLED: applog.info("filled.") order_count += 1 break elif r["status"] in [ binance.client.Client.ORDER_STATUS_NEW, binance.client.Client. ORDER_STATUS_PARTIALLY_FILLED, ]: applog.info( "%s,%s,order_count:%d,price:%s,lot:%s,status:%s(%d)" % (r["symbol"], r["side"], order_count, r["price"], r["origQty"], r["status"], i)) if i < 30: time.sleep(0.001) elif i < 100: if order_count == 0 and r[ "status"] == binance.client.Client.ORDER_STATUS_NEW: applog.warning( "Skip triangular arbitrage. status=" + r["status"]) cancel_result = self.binance.cancel_order( r["symbol"], r["orderId"]) applog.info("Canceled. result=" + str(cancel_result)) mailer.sendmail( "%s,%s" % (r["symbol"], r["orderId"]), "Canceled - Daryl Triangular") final_status = "cancel" break time.sleep(0.01) else: time.sleep(10) i += 1 else: # binance.client.Client.ORDER_STATUS_CANCELED # binance.client.Client.ORDER_STATUS_EXPIRED # binance.client.Client.ORDER_STATUS_PENDING_CANCEL # binance.client.Client.ORDER_STATUS_REJECTED if order_count == 0: applog.warning( "Skip triangular arbitrage. status=" + r["status"]) cancel_result = self.binance.cancel_order( r["symbol"], r["orderId"]) applog.info("Canceled. result=" + str(cancel_result)) mailer.sendmail( "%s,%s" % (r["symbol"], r["orderId"]), "Canceled - Daryl Triangular") final_status = "cancel" break else: applog.error( "Failed triangular arbitrage. status=" + r["status"]) mailer.sendmail( "%s,%s" % (r["symbol"], r["orderId"]), "Failed - Daryl Triangular") final_status = "failed" break if final_status in ["cancel", "failed"]: break if order_count == 3: mailer.sendmail(route, "Successful - Daryl Triangular") final_status = "successful" self.trade_log(start_t, "Binance", route, expected_final_profit, final_status) return True