diff --git a/api/bots/bots.http b/api/bots/bots.http new file mode 100644 index 00000000..58606c04 --- /dev/null +++ b/api/bots/bots.http @@ -0,0 +1,4 @@ +### Test deactivation + +DELETE http://localhost:8008/bot/deactivate/66e78f0da419ebbf5c04683b HTTP/1.1 +content-Type: application/json diff --git a/api/bots/controllers.py b/api/bots/controllers.py index ba001ba7..ac231cd9 100644 --- a/api/bots/controllers.py +++ b/api/bots/controllers.py @@ -3,13 +3,10 @@ from datetime import datetime from bson.objectid import ObjectId from fastapi.exceptions import RequestValidationError - -from account.account import Account from base_producer import BaseProducer from tools.enum_definitions import BinbotEnums, Status from tools.exceptions import QuantityTooLow from tools.handle_error import ( - handle_binance_errors, json_response, json_response_message, json_response_error @@ -19,15 +16,34 @@ from fastapi import Query from bots.schemas import BotSchema, ErrorsRequestBody from deals.controllers import CreateDealController +from decimal import Decimal +from db import Database +from account.account import Account - -class Bot(Account): +class Bot(Database, Account): def __init__(self, collection_name="paper_trading"): super().__init__() - self.db_collection = self.db[collection_name] + self.db_collection = self._db[collection_name] self.base_producer = BaseProducer() self.producer = self.base_producer.start_producer() + @property + def price_precision(self): + self._price_precision = -1 * ( + Decimal(str(self.price_filter_by_symbol(self.symbol, "tickSize"))) + .as_tuple() + .exponent + ) + return self._price_precision + + @property + def qty_precision(self): + self._qty_precision = -1 * ( + Decimal(str(self.lot_size_by_symbol(self.symbol, "stepSize"))) + .as_tuple() + .exponent + ) + return self._qty_precision def get_active_pairs(self, symbol: str = None): """ @@ -125,12 +141,12 @@ def get_one(self, bot_id=None, symbol=None, status: Status=None): bot = self.db_collection.find_one(params) return bot - def create(self, data): + def create(self, data: BotSchema): """ Always creates new document """ try: - bot = data.dict() + bot = data.model_dump() bot["id"] = str(ObjectId()) self.db_collection.insert_one(bot) @@ -207,10 +223,7 @@ def deactivate(self, findId): 2. Sell Coins 3. Delete bot """ - bot = self.db_collection.find_one({"id": findId }) - resp = json_response_message( - "Not enough balance to close and sell. Please directly delete the bot." - ) + bot = self.db_collection.find_one({"id": findId, "status": Status.active}) if bot: orders = bot["orders"] @@ -221,30 +234,25 @@ def deactivate(self, findId): d["status"] == "NEW" or d["status"] == "PARTIALLY_FILLED" ): order_id = d["order_id"] - res = requests.delete( + requests.delete( url=f'{self.bb_close_order_url}/{bot["pair"]}/{order_id}' ) - error_msg = f"Failed to delete opened order {order_id}." - print(error_msg) - # Handle error and continue - handle_binance_errors(res) + self.update_deal_logs(f"Failed to delete opened order {order_id}.", self.active_bot) # Sell everything pair = bot["pair"] base_asset = self.find_baseAsset(pair) bot = BotSchema(**bot) - precision = self.price_precision - qty_precision = self.qty_precision balance = self.get_one_balance(base_asset) if balance: qty = float(balance) price = float(self.matching_engine(pair, False, qty)) - if price and float(supress_notation(qty, qty_precision)) < 1: + if price and float(supress_notation(qty, self.qty_precision)) < 1: order = { "pair": pair, - "qty": supress_notation(qty, qty_precision), - "price": supress_notation(price, precision), + "qty": supress_notation(qty, self.qty_precision), + "price": supress_notation(price, self.price_precision), } order_res = self.request( method="POST", url=self.bb_sell_order_url, json=order @@ -253,19 +261,11 @@ def deactivate(self, findId): else: order = { "pair": pair, - "qty": supress_notation(price, qty_precision), + "qty": supress_notation(price, self.qty_precision), } - try: - order_res = self.request( - method="POST", url=self.bb_sell_market_order_url, json=order - ) - except QuantityTooLow: - bot["status"] = "closed" - try: - self.bot_schema.update(bot) - except Exception as e: - resp = json_response_message(e) - return resp + order_res = self.request( + method="POST", url=self.bb_sell_market_order_url, json=order + ) # Enforce that deactivation occurs # If it doesn't, redo @@ -324,10 +324,13 @@ def deactivate(self, findId): "Active orders closed, sold base asset, deactivated" ) else: - self.db_collection.update_one( - {"id": findId}, {"$set": {"status": "error"}} - ) - return json_response_message("Not enough balance to close and sell") + msg = "Not enough balance to close and sell" + self.update_deal_logs(msg, self.active_bot) + return json_response_message(msg) + else: + return json_response_message( + "Active bot not found to deactivate." + ) def put_archive(self, botId): """ diff --git a/api/bots/routes.py b/api/bots/routes.py index b7bc7ad7..9c662464 100644 --- a/api/bots/routes.py +++ b/api/bots/routes.py @@ -98,8 +98,8 @@ def activate_by_id(id: str): @bot_blueprint.delete("/bot/deactivate/{id}", tags=["bots"]) def deactivate(id: str): """ - Deactivation means closing all deals and selling to GBP - Otherwise losses will be incurred + Deactivation means closing all deals and selling to + fiat. This is often used to prevent losses """ return Bot(collection_name="bots").deactivate(id) diff --git a/api/orders/controller.py b/api/orders/controller.py index 2023eca4..6b6cf3d3 100644 --- a/api/orders/controller.py +++ b/api/orders/controller.py @@ -2,7 +2,7 @@ from tools.exceptions import DeleteOrderError from db import Database from tools.enum_definitions import OrderType, TimeInForce, OrderSide -from tools.handle_error import json_response, json_response_error, json_response_message +from tools.handle_error import json_response, json_response_message from tools.round_numbers import supress_notation from decimal import Decimal diff --git a/binquant b/binquant index a29ff77e..b5ef18de 160000 --- a/binquant +++ b/binquant @@ -1 +1 @@ -Subproject commit a29ff77e5838dcc1406f94518bbe503647547c31 +Subproject commit b5ef18de317b3890c9b1a063f34db69c9772b719