diff --git a/auto_profit_transfer.py b/auto_profit_transfer.py index abd3d2658..8d08ace1d 100644 --- a/auto_profit_transfer.py +++ b/auto_profit_transfer.py @@ -7,7 +7,12 @@ import json import argparse import asyncio -from procedures import create_binance_bot, make_get_filepath +from procedures import ( + create_binance_bot, + create_bybit_bot, + make_get_filepath, + load_exchange_key_secret, +) from pure_funcs import get_template_live_config, flatten from njit_funcs import round_dynamic from time import sleep @@ -49,9 +54,15 @@ async def main(): config["user"] = args.user config["symbol"] = "BTCUSDT" # dummy symbol config["market_type"] = "futures" - bot = await create_binance_bot(config) + exchange, _, _ = load_exchange_key_secret(args.user) + if exchange == "binance": + bot = await create_binance_bot(config) + elif exchange == "bybit": + bot = await create_bybit_bot(config) + else: + raise Exception(f"unknown exchange {exchange}") transfer_log_fpath = make_get_filepath( - os.path.join("logs", f"automatic_profit_transfer_log_{config['user']}.json") + os.path.join("logs", f"automatic_profit_transfer_log_{exchange}_{config['user']}.json") ) try: already_transferred_ids = set(json.load(open(transfer_log_fpath))) @@ -60,7 +71,7 @@ async def main(): already_transferred_ids = set() logging.info(f"no previous transfers to load") while True: - now = (await bot.public_get(bot.endpoints["time"]))["serverTime"] + now = await bot.get_server_time() try: income = await bot.get_all_income(start_time=now - 1000 * 60 * 60 * 24) except Exception as e: @@ -71,14 +82,17 @@ async def main(): income = [e for e in income if e["token"] == args.quote] profit = sum([e["income"] for e in income]) to_transfer = round_dynamic(profit * args.percentage, 4) + if args.quote in ["USDT", "BUSD", "USDC"]: + to_transfer = round(to_transfer, 4) if to_transfer > 0: try: - transferred = await bot.private_post( - bot.endpoints["futures_transfer"], - {"asset": args.quote, "amount": to_transfer, "type": 2}, - base_endpoint=bot.spot_base_endpoint, - ) + transferred = await bot.transfer_from_derivatives_to_spot(args.quote, to_transfer) logging.info(f"income: {profit} transferred {to_transfer} {args.quote}") + if exchange == "bybit": + if "ret_msg" not in transferred or transferred["ret_msg"] != "OK": + print(f"error with transfer {transferred}") + continue + logging.info(f"{transferred}") already_transferred_ids.update([e["transaction_id"] for e in income]) json.dump(list(already_transferred_ids), open(transfer_log_fpath, "w")) except Exception as e: diff --git a/binance.py b/binance.py index 664dde4c2..2641f204c 100644 --- a/binance.py +++ b/binance.py @@ -163,6 +163,7 @@ async def init_market_type(self): self.endpoints["transfer"] = "/sapi/v1/asset/transfer" self.endpoints["futures_transfer"] = "/sapi/v1/futures/transfer" self.endpoints["account"] = "/api/v3/account" + self.endpoints["server_time"] = "/api/v3/time" async def _init(self): await self.init_market_type() @@ -194,6 +195,19 @@ async def _init(self): await self.init_order_book() await self.update_position() + async def get_server_time(self): + now = await self.public_get( + self.endpoints["server_time"], base_endpoint=self.spot_base_endpoint + ) + return now["serverTime"] + + async def transfer_from_derivatives_to_spot(self, coin: str, amount: float): + return await self.private_post( + self.endpoints["futures_transfer"], + {"asset": coin, "amount": amount, "type": 2}, + base_endpoint=self.spot_base_endpoint, + ) + async def execute_leverage_change(self): lev = 7 # arbitrary return await self.private_post( diff --git a/bybit.py b/bybit.py index 115d0a3bc..cdc870e8b 100644 --- a/bybit.py +++ b/bybit.py @@ -6,6 +6,7 @@ from time import time from typing import Union, List, Dict from urllib.parse import urlencode +from uuid import uuid4 import aiohttp import numpy as np @@ -47,6 +48,7 @@ def __init__(self, config: dict): "balance": "/v2/private/wallet/balance", "exchange_info": "/v2/public/symbols", "ticker": "/v2/public/tickers", + "funds_transfer": "/asset/v1/private/transfer", } self.session = aiohttp.ClientSession(headers={"referer": "passivbotbybit"}) @@ -111,6 +113,7 @@ def init_market_type(self): self.endpoints["balance"] = "/v2/private/wallet/balance" self.endpoints["exchange_info"] = "/v2/public/symbols" self.endpoints["ticker"] = "/v2/public/tickers" + self.endpoints["funds_transfer"] = "/asset/v1/private/transfer" async def _init(self): info = await self.public_get(self.endpoints["exchange_info"]) @@ -161,7 +164,9 @@ async def public_get(self, url: str, params: dict = {}) -> dict: result = await response.text() return json.loads(result) - async def private_(self, type_: str, base_endpoint: str, url: str, params: dict = {}) -> dict: + async def private_( + self, type_: str, base_endpoint: str, url: str, params: dict = {}, json_: bool = False + ) -> dict: timestamp = int(time() * 1000) params.update({"api_key": self.key, "timestamp": timestamp}) for k in params: @@ -174,8 +179,12 @@ async def private_(self, type_: str, base_endpoint: str, url: str, params: dict urlencode(sort_dict_keys(params)).encode("utf-8"), hashlib.sha256, ).hexdigest() - async with getattr(self.session, type_)(base_endpoint + url, params=params) as response: - result = await response.text() + if json_: + async with getattr(self.session, type_)(base_endpoint + url, json=params) as response: + result = await response.text() + else: + async with getattr(self.session, type_)(base_endpoint + url, params=params) as response: + result = await response.text() return json.loads(result) async def private_get(self, url: str, params: dict = {}, base_endpoint: str = None) -> dict: @@ -194,6 +203,22 @@ async def private_post(self, url: str, params: dict = {}, base_endpoint: str = N params, ) + async def transfer_from_derivatives_to_spot(self, coin: str, amount: float): + params = { + "coin": coin, + "amount": str(amount), + "from_account_type": "CONTRACT", + "to_account_type": "SPOT", + "transfer_id": str(uuid4()), + } + return await self.private_( + "post", self.base_endpoint, self.endpoints["funds_transfer"], params=params, json_=True + ) + + async def get_server_time(self): + now = await self.public_get("/v2/public/time") + return float(now["time_now"]) * 1000 + async def fetch_position(self) -> dict: position = {} if "linear_perpetual" in self.market_type: @@ -400,6 +425,23 @@ async def get_all_income( income_type: str = "Trade", end_time: int = None, ): + if symbol is None: + all_income = [] + all_positions = await self.private_get(self.endpoints["position"], params={"symbol": ""}) + symbols = sorted( + set( + [ + x["data"]["symbol"] + for x in all_positions["result"] + if float(x["data"]["size"]) > 0 + ] + ) + ) + for symbol in symbols: + all_income += await self.get_all_income( + symbol=symbol, start_time=start_time, income_type=income_type, end_time=end_time + ) + return sorted(all_income, key=lambda x: x["timestamp"]) limit = 50 income = [] page = 1 @@ -413,7 +455,7 @@ async def get_all_income( ) if len(fetched) == 0: break - print_(["fetched income", ts_to_date(fetched[0]["timestamp"])]) + print_(["fetched income", symbol, ts_to_date(fetched[0]["timestamp"])]) if fetched == income[-len(fetched) :]: break income += fetched