Skip to content

Commit

Permalink
Fix precision due to previous refactoring to Account class (#608)
Browse files Browse the repository at this point in the history
* Fix precision due to previous refactoring to Account class

* Panic sell fixes

* Fix bot logs

* Panic sell/close property fixes

* Consistent link redirects

* Fix missing inherited precisions
  • Loading branch information
carkod authored Sep 28, 2024
1 parent d9c32b6 commit 1a79e80
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 197 deletions.
10 changes: 4 additions & 6 deletions api/account/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
class Account(BinbotApi):
def __init__(self):
self.db = setup_db()
self._price_precision: int = 0
self._qty_precision: int = 0
pass

def setup_mongocache(self):
Expand All @@ -38,17 +36,17 @@ def calculate_price_precision(self, symbol) -> int:
.as_tuple()
.exponent
)
self._price_precision = int(precision)
return self._price_precision
price_precision = int(precision)
return price_precision

def calculate_qty_precision(self, symbol) -> int:
precision = -1 * (
Decimal(str(self.lot_size_by_symbol(symbol, "stepSize")))
.as_tuple()
.exponent
)
self._qty_precision = int(precision)
return self._qty_precision
qty_precision = int(precision)
return qty_precision

def _exchange_info(self, symbol=None):
"""
Expand Down
168 changes: 132 additions & 36 deletions api/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,31 @@
from tools.exceptions import IsolateBalanceError
from py3cw.request import Py3CW


class BinanceApi:
"""
Binance API URLs
https://binance.github.io/binance-api-swagger/
"""

api_servers = ["https://api.binance.com", "https://api3.binance.com", "https://api-gcp.binance.com"]
api_servers = [
"https://api.binance.com",
"https://api1.binance.com",
"https://api3.binance.com",
"https://api-gcp.binance.com",
]
market_api_servers = ["https://data-api.binance.vision", "https://api3.binance.com"]
BASE = api_servers[randrange(2) - 1]
MARKET_DATA_BASE = market_api_servers[randrange(3) - 1]
WAPI = f"{BASE}/api/v3/depth"
WS_BASE = "wss://stream.binance.com:9443/stream?streams="

recvWindow = 9000
secret = os.getenv("BINANCE_SECRET")
key = os.getenv("BINANCE_KEY")
secret: str = os.getenv("BINANCE_SECRET", "abc")
key: str = os.getenv("BINANCE_KEY", "abc")
server_time_url = f"{MARKET_DATA_BASE}/api/v3/time"
account_url = f"{BASE}/api/v3/account"
# Binance always returning forbidden for other APIs
account_url = f"{api_servers[1]}/api/v3/account"
exchangeinfo_url = f"{MARKET_DATA_BASE}/api/v3/exchangeInfo"
ticker_price_url = f"{MARKET_DATA_BASE}/api/v3/ticker/price"
ticker24_url = f"{MARKET_DATA_BASE}/api/v3/ticker/24hr"
Expand All @@ -41,7 +48,7 @@ class BinanceApi:
wallet_balance_url = f"{BASE}/sapi/v1/asset/wallet/balance"

# order, user data, only works with api.binance host
user_data_stream = f"https://api.binance.com/api/v3/userDataStream"
user_data_stream = "https://api.binance.com/api/v3/userDataStream"

withdraw_url = f"{BASE}/wapi/v3/withdraw.html"
withdraw_history_url = f"{BASE}/wapi/v3/withdrawHistory.html"
Expand All @@ -61,7 +68,9 @@ class BinanceApi:
margin_order = f"{BASE}/sapi/v1/margin/order"
max_borrow_url = f"{BASE}/sapi/v1/margin/maxBorrowable"

def request(self, url, method="GET", session: Session=None, payload={}, **kwargs):
def request(
self, url, method="GET", session: Session = None, payload: dict = {}, **kwargs
):
"""
Standard request
- No signed
Expand All @@ -78,9 +87,13 @@ def get_server_time(self):
data = self.request(url=self.server_time_url)
return data["serverTime"]

def signed_request(self, url, method="GET", payload={}):
def signed_request(self, url, method="GET", payload: dict = {}):
"""
USER_DATA, TRADE signed requests
Arguments are all the same as requests
except payload, which is centrally formatted
here to become a JSON
"""
session = Session()
query_string = urlencode(payload, True)
Expand Down Expand Up @@ -117,26 +130,27 @@ def get_listen_key(self):
"""
No security endpoints
"""

def ticker_24(self, type: str = "FULL", symbol: str | None = None):
"""
Weight 40 without symbol
https://github.com/carkod/binbot/issues/438
Using cache
"""
params = {
"type": type
}
params = {"type": type}
if symbol:
params["symbol"] = symbol

# mongo_cache = self.setup_mongocache()
# expire_after = 15m because candlesticks are 15m
# session = CachedSession('ticker_24_cache', backend=mongo_cache, expire_after=15)
data = self.request(url=self.ticker24_url, params=params)
return data

def get_raw_klines(self, symbol, interval, limit=500, start_time=None, end_time=None):
def get_raw_klines(
self, symbol, interval, limit=500, start_time=None, end_time=None
):
"""
Get raw klines
"""
Expand All @@ -156,11 +170,13 @@ def get_raw_klines(self, symbol, interval, limit=500, start_time=None, end_time=
"""
USER_DATA endpoints
"""

def get_account_balance(self):
"""
Get account balance
"""
data = self.signed_request(self.account_url)
payload = {"omitZeroBalances": "true"}
data = self.signed_request(self.account_url, payload=payload)
return data

def get_wallet_balance(self):
Expand All @@ -175,52 +191,111 @@ def get_wallet_balance(self):
return data

def cancel_margin_order(self, symbol, order_id):
return self.signed_request(self.margin_order, method="DELETE", payload={"symbol": symbol, "orderId": order_id})
return self.signed_request(
self.margin_order,
method="DELETE",
payload={"symbol": symbol, "orderId": order_id},
)

def enable_isolated_margin_account(self, symbol):
return self.signed_request(self.isolated_account_url, method="POST", payload={"symbol": symbol})

return self.signed_request(
self.isolated_account_url, method="POST", payload={"symbol": symbol}
)

def disable_isolated_margin_account(self, symbol):
"""
Very high weight, use as little as possible
There is a cronjob that disables all margin isolated accounts everyday
check market_updates
"""
return self.signed_request(self.isolated_account_url, method="DELETE", payload={"symbol": symbol})
return self.signed_request(
self.isolated_account_url, method="DELETE", payload={"symbol": symbol}
)

def get_isolated_account(self, symbol):
"""
https://developers.binance.com/docs/margin_trading/account/Query-Isolated-Margin-Account-Info
Request weight: 10(IP)
"""
return self.signed_request(self.isolated_account_url, payload={"symbol": symbol})
return self.signed_request(
self.isolated_account_url, payload={"symbol": symbol}
)

def transfer_isolated_margin_to_spot(self, asset, symbol, amount):
return self.signed_request(self.margin_isolated_transfer_url, method="POST", payload={"transFrom": "ISOLATED_MARGIN", "transTo": "SPOT", "asset": asset, "symbol": symbol, "amount": amount})
return self.signed_request(
self.margin_isolated_transfer_url,
method="POST",
payload={
"transFrom": "ISOLATED_MARGIN",
"transTo": "SPOT",
"asset": asset,
"symbol": symbol,
"amount": amount,
},
)

def transfer_spot_to_isolated_margin(self, asset: str, symbol: str, amount: str):
return self.signed_request(self.margin_isolated_transfer_url, method="POST", payload={"transFrom": "SPOT", "transTo": "ISOLATED_MARGIN", "asset": asset, "symbol": symbol, "amount": amount})
return self.signed_request(
self.margin_isolated_transfer_url,
method="POST",
payload={
"transFrom": "SPOT",
"transTo": "ISOLATED_MARGIN",
"asset": asset,
"symbol": symbol,
"amount": amount,
},
)

def create_margin_loan(self, asset, symbol, amount, isIsolated=True):
if not isIsolated:
isIsolated = "FALSE"
else:
isIsolated = "TRUE"

return self.signed_request(self.loan_record_url, method="POST", payload={"asset": asset, "symbol": symbol, "amount": amount, "isIsolated": isIsolated})
return self.signed_request(
self.loan_record_url,
method="POST",
payload={
"asset": asset,
"symbol": symbol,
"amount": amount,
"isIsolated": isIsolated,
},
)

def get_max_borrow(self, asset, isolated_symbol:str | None = None):
return self.signed_request(self.max_borrow_url, payload={"asset": asset, "isolatedSymbol": isolated_symbol })
def get_max_borrow(self, asset, isolated_symbol: str | None = None):
return self.signed_request(
self.max_borrow_url,
payload={"asset": asset, "isolatedSymbol": isolated_symbol},
)

def get_margin_loan_details(self, asset: str, isolatedSymbol: str):
return self.signed_request(self.loan_record_url, payload={"asset": asset, "isolatedSymbol": isolatedSymbol})
return self.signed_request(
self.loan_record_url,
payload={"asset": asset, "isolatedSymbol": isolatedSymbol},
)

def get_margin_repay_details(self, asset: str, isolatedSymbol: str):
return self.signed_request(self.margin_repay_url, payload={"asset": asset, "isolatedSymbol": isolatedSymbol})
return self.signed_request(
self.margin_repay_url,
payload={"asset": asset, "isolatedSymbol": isolatedSymbol},
)

def repay_margin_loan(self, asset: str, symbol: str, amount: float, isIsolated: str):
return self.signed_request(self.margin_repay_url, method="POST", payload={"asset": asset, "symbol": symbol, "amount": amount, "isIsolated": isIsolated})
def repay_margin_loan(
self, asset: str, symbol: str, amount: float, isIsolated: str
):
return self.signed_request(
self.margin_repay_url,
method="POST",
payload={
"asset": asset,
"symbol": symbol,
"amount": amount,
"isIsolated": isIsolated,
},
)

def get_isolated_balance(self, symbol=None) -> List:
"""
Expand All @@ -235,7 +310,9 @@ def get_isolated_balance(self, symbol=None) -> List:
info = self.signed_request(url=self.isolated_account_url, payload=payload)
assets = info["assets"]
if len(assets) == 0:
raise IsolateBalanceError("Hit symbol 24hr restriction or not available (requires transfer in)")
raise IsolateBalanceError(
"Hit symbol 24hr restriction or not available (requires transfer in)"
)
return assets

def get_isolated_balance_total(self):
Expand All @@ -246,17 +323,21 @@ def get_isolated_balance_total(self):
because this is the one that supports the most assets
"""
info = self.signed_request(url=self.isolated_account_url, payload={})
assets = info['totalNetAssetOfBtc']
assets = info["totalNetAssetOfBtc"]
if len(assets) == 0:
raise IsolateBalanceError("Hit symbol 24hr restriction or not available (requires transfer in)")
raise IsolateBalanceError(
"Hit symbol 24hr restriction or not available (requires transfer in)"
)
return assets

def transfer_dust(self, assets: List[str]):
"""
Transform small balances to BNB
"""
list_assets = ",".join(assets)
response = self.signed_request(url=self.dust_transfer_url, method="POST", payload={"asset": list_assets})
response = self.signed_request(
url=self.dust_transfer_url, method="POST", payload={"asset": list_assets}
)
return response

def query_open_orders(self, symbol):
Expand All @@ -269,7 +350,7 @@ def query_open_orders(self, symbol):
open_orders = self.signed_request(self.open_orders, payload={"symbol": symbol})
return open_orders

def get_all_orders(self, symbol, order_id : int=None, start_time=None):
def get_all_orders(self, symbol, order_id: int = None, start_time=None):
"""
Get all orders given symbol and order_id
Expand All @@ -285,14 +366,29 @@ def get_all_orders(self, symbol, order_id : int=None, start_time=None):
At least one of order_id or (start_time and end_time) must be sent
"""
if order_id > 0:
return self.signed_request(self.all_orders_url, payload={"symbol": symbol, "orderId": order_id})

return self.signed_request(
self.all_orders_url, payload={"symbol": symbol, "orderId": order_id}
)

elif start_time:
return self.signed_request(self.all_orders_url, payload={"symbol": symbol, "startTime": start_time})
return self.signed_request(
self.all_orders_url, payload={"symbol": symbol, "startTime": start_time}
)

else:
raise ValueError("At least one of order_id or (start_time and end_time) must be sent")
raise ValueError(
"At least one of order_id or (start_time and end_time) must be sent"
)

def delete_opened_order(self, symbol, order_id):
"""
Cancel single order
"""
return self.signed_request(
self.order_url,
method="DELETE",
payload={"symbol": symbol, "orderId": order_id},
)


class BinbotApi(BinanceApi):
Expand Down
Loading

0 comments on commit 1a79e80

Please sign in to comment.