def get(self): # token_data = jwt.decode(request.headers.get('AccessToken'), os.environ['SECRET_KEY']) users = list(app.db.users.find()) if users: resp = jsonResp_message(users, 200) else: resp = jsonResp_message("No users found", 200) return resp
def add(self): data = request.json expected_data = { "email": data["email"].lower(), "password": data["password"], } # Merge the posted data with the default user attributes self.defaults.update(expected_data) user = self.defaults # Encrypt the password user["password"] = pbkdf2_sha256.encrypt( user["password"], rounds=20000, salt_size=16 ) # Make sure there isn"t already a user with this email address existing_email = app.db.users.find_one({"email": user["email"]}) if existing_email: resp = jsonResp_message( { "message": "There's already an account with this email address", }, 200, ) else: insertion = app.db.users.insert(user) if insertion: # Log the user in (create and return tokens) access_token = encodeAccessToken(user["password"], user["email"]) refresh_token = encodeRefreshToken(user["password"], user["email"]) app.db.users.update_one( {"_id": user["_id"]}, {"$set": {"refresh_token": refresh_token}} ) resp = jsonResp_message( { "_id": user["_id"], "email": user["email"], "access_token": access_token, "refresh_token": refresh_token, }, 200, ) else: resp = jsonResp_message("User could not be added", 200) return resp
def get_open_orders(self): timestamp = int(round(tm.time() * 1000)) url = self.open_orders params = [("timestamp", timestamp), ("recvWindow", self.recvWindow)] headers = {"X-MBX-APIKEY": self.key} # Prepare request for signing r = requests.Request(url=url, params=params, headers=headers) prepped = r.prepare() query_string = urlparse(prepped.url).query total_params = query_string # Generate and append signature signature = hmac.new(self.secret.encode("utf-8"), total_params.encode("utf-8"), hashlib.sha256).hexdigest() params.append(("signature", signature)) res = requests.get(url=url, params=params, headers=headers) handle_error(res) data = res.json() if len(data) > 0: resp = jsonResp({ "message": "Open orders found!", "data": data }, 200) else: resp = jsonResp_message("No open orders found!", 200) return resp
def get_symbol_info(self): symbols = self._exchange_info()["symbols"] pair = request.view_args["pair"] symbol = next((s for s in symbols if s["symbol"] == pair), None) if symbol: return jsonResp({"data": symbol}, 200) else: return jsonResp_message("Pair not found", 200)
def block_response(self): """ Scanning already in progress """ global poll_percentage resp = jsonResp_message( f"Pearson correlation scanning is in progress {poll_percentage}%", 200) return resp
def close_deals(self): """ Close all deals - Deals should be stored as an array of orderIds - Delete (cancel) endpoint, with symbold and orderId """ deals = self.active_bot["deals"] for d in deals: if "deal_type" in d and ( d["status"] == "NEW" or d["status"] == "PARTIALLY_FILLED" ): order_id = d["order_id"] res = requests.delete( url=f'{self.bb_close_order_url}/{self.active_bot["pair"]}/{order_id}' ) if isinstance(handle_error(res), Response): return handle_error(res) # Sell everything pair = self.active_bot["pair"] base_asset = self.find_baseAsset(pair) balance = self.get_balances().json qty = round_numbers( float(next((s for s in symbols if s["symbol"] == symbol), None)["free"]), self.qty_precision, ) book_order = Book_Order(pair) price = float(book_order.matching_engine(True, qty)) if price: order = { "pair": pair, "qty": qty, "price": supress_notation(price, self.price_precision), } res = requests.post(url=self.bb_sell_order_url, json=order) else: order = { "pair": pair, "qty": qty, } res = requests.post(url=self.bb_sell_market_order_url, json=order) if isinstance(handle_error(res), Response): return handle_error(res) response = jsonResp_message("Deals closed successfully", 200) return response
def handle_error(req): try: req.raise_for_status() if isinstance(json.loads(req.content), dict): # Binance code errors if "code" in json.loads(req.content).keys(): response = req.json() if response["code"] == -2010: return jsonResp({ "message": "Not enough funds", "error": 1 }, 200) # Uknown orders ignored, they are used as a trial an error endpoint to close orders (close deals) if response["code"] == -2011: return return jsonResp_message(json.loads(req.content), 200) except requests.exceptions.HTTPError as err: if err: print(req.json()) return jsonResp_message(req.json(), 200) else: return err except requests.exceptions.Timeout: # Maybe set up for a retry, or continue in a retry loop return jsonResp_message("handle_error: Timeout", 408) except requests.exceptions.TooManyRedirects: # Tell the user their URL was bad and try a different one return jsonResp_message("handle_error: Too many Redirects", 429) except requests.exceptions.RequestException as e: # catastrophic error. bail. return jsonResp_message(f"Catastrophic error: {e}", 500)
def logout(self): try: tokenData = jwt.decode( request.headers.get("AccessToken"), os.environ["SECRET_KEY"] ) app.db.users.update( {"id": tokenData["userid"]}, {"$unset": {"access_token": ""}} ) # Note: At some point I need to implement Token Revoking/Blacklisting # General info here: https://flask-jwt-extended.readthedocs.io/en/latest/blacklist_and_token_revoking.html except: pass resp = jsonResp_message("User logged out", 200) return resp
def delete_all_orders(self): """ Delete All orders by symbol - Optimal for open orders table """ symbol = request.args["symbol"] timestamp = int(round(tm.time() * 1000)) url = self.open_orders # query params -> args # path params -> view_args symbol = request.args["symbol"] params = [ ("symbol", symbol), ("timestamp", timestamp), ("recvWindow", self.recvWindow), ] headers = {"X-MBX-APIKEY": self.key} # Prepare request for signing r = requests.Request(url=url, params=params, headers=headers) prepped = r.prepare() query_string = urlparse(prepped.url).query total_params = query_string # Generate and append signature signature = hmac.new(self.secret.encode("utf-8"), total_params.encode("utf-8"), hashlib.sha256).hexdigest() params.append(("signature", signature)) # Response after request res = requests.delete(url=url, params=params, headers=headers) handle_error(res) data = res.json() if len(data) > 0: resp = jsonResp({"message": "Orders deleted", "data": data}, 200) else: resp = jsonResp_message("No open orders found!", 200) return resp
def short_stop_limit_order(self): """ Part I of Short bot order: Stop loss (sell all) After safety orders are executed, if price keeps going down, execute Stop Loss order """ pair = self.active_bot["pair"] base_asset = self.find_baseAsset(pair) base_order_deal = next( ( bo_deal for bo_deal in self.active_bot["deals"] if bo_deal["deal_type"] == "base_order" ), None, ) price = float(base_order_deal["price"]) stop_loss = price * int(self.active_bot["stop_loss"]) / 100 stop_loss_price = price - stop_loss self.asset_qty = next( (b["free"] for b in self.get_balances().json if b["asset"] == base_asset), None, ) # Validations if price: if price <= float(self.MIN_PRICE): return jsonResp_message("[Short stop loss order] Price too low", 200) # Avoid common rate limits if float(self.asset_qty) <= float(self.MIN_QTY): return jsonResp_message("[Short stop loss order] Quantity too low", 200) if price * float(self.asset_qty) <= float(self.MIN_NOTIONAL): return jsonResp_message( "[Short stop loss order] Price x Quantity too low", 200 ) order = { "pair": pair, "qty": self.asset_qty, "price": supress_notation( stop_loss_price, self.price_precision ), # Theoretically stop_price, as we don't have book orders "stop_price": supress_notation(stop_loss_price, self.price_precision), } res = requests.post(url=self.bb_stop_sell_order_url, json=order) if isinstance(handle_error(res), Response): return handle_error(res) stop_limit_order = res.json() stop_limit_order = { "deal_type": "stop_limit", "order_id": stop_limit_order["orderId"], "strategy": "long", # change accordingly "pair": stop_limit_order["symbol"], "order_side": stop_limit_order["side"], "order_type": stop_limit_order["type"], "price": stop_limit_order["price"], "qty": stop_limit_order["origQty"], "fills": stop_limit_order["fills"], "time_in_force": stop_limit_order["timeInForce"], "status": stop_limit_order["status"], } self.active_bot["deals"].append(stop_limit_order) botId = app.db.bots.update_one( {"_id": self.active_bot["_id"]}, {"$push": {"deals": stop_limit_order}} ) if not botId: resp = jsonResp( { "message": "Failed to save short order stop_limit deal in the bot", "botId": str(self.active_bot["_id"]), }, 200, ) return resp
def response(self): """ Start scanning """ resp = jsonResp_message("Pearson correlation scanning started", 200) return resp
def update_take_profit(self, order_id): """ Update take profit after websocket order endpoint triggered - Close current opened take profit order - Create new take profit order - Update database by replacing old take profit deal with new take profit deal """ bot = self.active_bot for deal in bot["deals"]: if deal["order_id"] == order_id: so_deal_price = deal["price"] # Create new take profit order new_tp_price = float(so_deal_price) + ( float(so_deal_price) * float(bot["take_profit"]) / 100) asset = self.find_baseAsset(bot["pair"]) # First cancel old order to unlock balance close_order_params = { "symbol": bot["pair"], "orderId": order_id } cancel_response = requests.post(url=self.bb_close_order_url, params=close_order_params) if cancel_response.status_code != 200: print("Take profit order not found, no need to cancel") else: print("Old take profit order cancelled") qty = round_numbers(self.get_one_balance(asset), self.qty_precision) # Validations if new_tp_price: if new_tp_price <= float(self.MIN_PRICE): return jsonResp_message( "[Take profit order error] Price too low", 200) if qty <= float(self.MIN_QTY): return jsonResp_message( "[Take profit order error] Quantity too low", 200) if new_tp_price * qty <= float(self.MIN_NOTIONAL): return jsonResp_message( "[Take profit order error] Price x Quantity too low", 200) new_tp_order = { "pair": bot["pair"], "qty": qty, "price": supress_notation(new_tp_price, self.price_precision), } res = requests.post(url=self.bb_sell_order_url, json=new_tp_order) if isinstance(handle_error(res), Response): return handle_error(res) # New take profit order successfully created order = res.json() # Replace take_profit order take_profit_order = { "deal_type": "take_profit", "order_id": order["orderId"], "strategy": "long", # change accordingly "pair": order["symbol"], "order_side": order["side"], "order_type": order["type"], "price": order["price"], "qty": order["origQty"], "fills": order["fills"], "time_in_force": order["timeInForce"], "status": order["status"], } # Build new deals list new_deals = [] for d in bot["deals"]: if d["deal_type"] != "take_profit": new_deals.append(d) # Append now new take_profit deal new_deals.append(take_profit_order) self.active_bot["orders"] = new_deals botId = app.db.bots.update_one( {"_id": self.active_bot["_id"]}, {"$push": { "orders": take_profit_order }}, ) if not botId: print(f"Failed to update take_profit deal: {botId}") else: print( f"New take_profit deal successfully updated: {botId}") return