From 5e7ab5a09d184c91932e4636189d90bdb94766ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20S=C3=A1nchez?= Date: Mon, 6 Jan 2025 23:26:29 -0300 Subject: [PATCH] more work --- .gitignore | 1 + libraries/balance_accounts.py | 6 +- libraries/earner.py | 117 +++++++++++++++++++++++++++++ libraries/wrappers/earn_binance.py | 1 + main.py | 81 +++++++++++++++----- notes.txt | 67 ----------------- 6 files changed, 187 insertions(+), 86 deletions(-) create mode 100644 libraries/earner.py delete mode 100644 notes.txt diff --git a/.gitignore b/.gitignore index 6d4c9ca..f67a755 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ libraries/wrappers/__pycache__/* *.pyc *.pyo credentials.py +some_tests.py \ No newline at end of file diff --git a/libraries/balance_accounts.py b/libraries/balance_accounts.py index 92bf94e..8d69aa1 100644 --- a/libraries/balance_accounts.py +++ b/libraries/balance_accounts.py @@ -1,5 +1,9 @@ def balance_accounts(spot_balance, earn_balance, lower_limit, step_size, percentage): - target_spot_balance = (spot_balance + earn_balance) * percentage + spot_balance+=earn_balance + earn_balance=0 + + #target_spot_balance = (spot_balance + earn_balance) * percentage + target_spot_balance = spot_balance * percentage while abs(spot_balance - target_spot_balance) >= step_size: if spot_balance < target_spot_balance: diff --git a/libraries/earner.py b/libraries/earner.py new file mode 100644 index 0000000..f32fa57 --- /dev/null +++ b/libraries/earner.py @@ -0,0 +1,117 @@ +import time +from libraries.balance_accounts import balance_accounts + + +class earner: + def __init__(self, connector, config): + self.connector = connector + self.currency = config["currency"] + self.minimum_amount_in_trading_account = config["minimum_amount_in_trading_account"] + self.step_size = config["step_size"] + self.percentage = config["percentage"] + self.time_between_subscriptions = config["time_between_subscriptions"] + self.time_between_redemptions = config["time_between_redemptions"] + + self.last_subscription_time = 0 + self.last_redemption_time = 0 + self.start_time = time.time() + + self.trading_balance = 0 + self.earning_balance = 0 + + self.status_string = "" + + + def __str__(self): + return str(self.connector) + + + def get_last_subscription(self): + return {str(self.connector): self.last_subscription_time} + + + def get_last_redemption(self): + return {str(self.connector): self.last_redemption_time} + + + def get_status_string(self): + return self.status_string + + + def get_trading_balance(self): + return float(self.trading_balance) + + + def get_earning_balance(self): + return float(self.earning_balance) + + + def subscribe(self,amount): + print(f"{str(self.connector)} | Subscribing {amount} {self.currency}") + + if True: + self.last_subscription_time = time.time() + return 0 + return 1 + + + def redeem(self,amount): + print(f"{str(self.connector)} | Redeeming {amount} {self.currency}") + if True: + self.last_redemption_time = time.time() + return 0 + return 1 + + + def run(self): + ''' + 1. Get trading and earning balances + 2. Call balance_accounts + 3. If there are differences: + a. If subscribe side, subscribe if time_since_last_subscription>time_between_subscriptions + b. If redeem side, redeem if time_since_last_redeem>time_between_redemptions + 4. Output status to status_string + ''' + + #Get trading and earning balances + trading_balance_dict = self.connector.get_trading_balance(self.currency) + if "Error" not in trading_balance_dict: + self.trading_balance = trading_balance_dict[self.currency] + else: + self.trading_balance = None + + earning_balance_dict = self.connector.get_position(self.currency) + if earning_balance_dict=={}: + self.earning_balance = 0 + elif "Error" not in earning_balance_dict: + self.earning_balance = earning_balance_dict["amount"] + else: + self.earning_balance = None + + #Call balance_accounts + target_trading_amount, target_earning_amount = balance_accounts(float(self.trading_balance), + float(self.earning_balance), + self.minimum_amount_in_trading_account, + self.step_size, + self.percentage) + + #Check difference + earning_delta = 0 + if self.earning_balance is None: + print(f"{str(self.connector)} - There was an error fetching earning balance") + else: + if float(self.earning_balance)!=0: + earning_delta = target_earning_amount - float(self.earning_balance) + if earning_delta>self.step_size and time.time()-self.last_subscription_time>self.time_between_subscriptions: + self.subscribe(earning_delta) + if earning_delta<-self.step_size and time.time()-self.last_redemption_time>self.time_between_redemptions: + self.redeem(-earning_delta) + print(f"Difference: {earning_delta}") + + + #Output status to status_string + balances_string = f"Trading balance: {float(self.trading_balance):.2f} {self.currency} | Earning balance: {float(self.earning_balance):.2f} {self.currency}" + percentages_string = f"On earn: {float(self.earning_balance)/float(self.trading_balance):.2%}" + + self.status_string = f"{self} | {balances_string} | {percentages_string}" + \ No newline at end of file diff --git a/libraries/wrappers/earn_binance.py b/libraries/wrappers/earn_binance.py index 834fd25..1955256 100644 --- a/libraries/wrappers/earn_binance.py +++ b/libraries/wrappers/earn_binance.py @@ -27,6 +27,7 @@ class binance_earn: for item in account["balances"]: if item["asset"]==coin: return {coin: item["free"]} + return {"Error": "Coin not found"} def get_available_products(self, coin): diff --git a/main.py b/main.py index 72b7d03..f62c246 100644 --- a/main.py +++ b/main.py @@ -3,32 +3,39 @@ from libraries.wrappers import earn_binance from libraries.wrappers import earn_kucoin from libraries.wrappers import earn_okx from libraries.wrappers import earn_gateio +from libraries.earner import earner from threading import Thread import time +import datetime import json -class earner: - def __init__(self, connector, config): - self.connector = connector - self.currency = config["currency"] - self.minimum_amount_in_trading_account = config["minimum_amount_in_trading_account"] - self.step_size = config["step_size"] - self.percentage = config["percentage"] - self.time_between_subscriptions = config["time_between_subscriptions"] - self.time_between_redemptions = config["time_between_redemptions"] - - self.last_subscription = 0 - self.last_redemption = 0 - - def __str__(self): - return f"Earner for {self.connector} with {self.currency} and {self.minimum_amount_in_trading_account} minimum amount in trading account and {self.step_size} step size and {self.percentage} percentage" - def run(self): - print(self) +def seconds_to_time(total_seconds: float) -> str: + ''' + Takes an int or float as an input and it returns a D:HH:MM:SS formatted string. + + Parameters: + total_seconds (float): The number of seconds to convert + + Returns: + str: The formatted string + ''' + + time_delta = datetime.timedelta(seconds=total_seconds) + + hours = time_delta.seconds//3600 + remainder = time_delta.seconds%3600 + minutes = remainder//60 + seconds = remainder%60 + + return f"{time_delta.days}:{hours:02d}:{minutes:02d}:{seconds:02d}" if __name__=="__main__": + version = "2025.01.06" + start_time = time.time() + with open("config.json") as f: config = json.load(f) @@ -43,13 +50,51 @@ if __name__=="__main__": while True: threads = [] + + #Run earners for item in earners: threads.append(Thread(target=item.run)) for item in threads: item.start() for item in threads: item.join() - + + #Print status + print("="*80) + + subscriptions = [] + redemptions = [] + for item in earners: + print(item.get_status_string()) + subscriptions.append(item.get_last_subscription()) + redemptions.append(item.get_last_redemption()) + last_subscription = max(subscriptions, key=lambda d: next(iter(d.values()))) + last_subscription_key = next(iter(last_subscription.keys())) + last_subscription_value = next(iter(last_subscription.values())) + if last_subscription_value == 0: + last_subscription_key = "Never" + last_subscription_value = "" + else: + last_subscription_value = datetime.datetime.fromtimestamp(last_subscription_value).strftime('%Y-%m-%d %H:%M:%S') + last_redemption = max(redemptions, key=lambda d: next(iter(d.values()))) + last_redemption_key = next(iter(last_redemption.keys())) + last_redemption_value = next(iter(last_redemption.values())) + if last_redemption_value == 0: + last_redemption_key = "Never" + last_redemption_value = "" + else: + last_redemption_value = datetime.datetime.fromtimestamp(last_redemption_value).strftime('%Y-%m-%d %H:%M:%S') + + print("-"*80) + total_on_trading = sum([item.get_trading_balance() for item in earners]) + total_on_earning = sum([item.get_earning_balance() for item in earners]) + print(f"Version {version} | Total funds: {total_on_trading+total_on_earning:.2f} | Total on earn: {total_on_earning:.2f} ({total_on_earning/total_on_trading*100:.2f}%)") + print(f"Uptime: {seconds_to_time(time.time()-start_time)} | Last subscription: {last_subscription_key} - {last_subscription_value} | Last redemption: {last_redemption_key} - {last_redemption_value}") + + #Wait for next lap time.sleep(config["lap_time"]) + + + diff --git a/notes.txt b/notes.txt deleted file mode 100644 index 50d1752..0000000 --- a/notes.txt +++ /dev/null @@ -1,67 +0,0 @@ - ''' - Subscribe workflow in Binance: - 1. Get product id - 2. Subscribe - - Redeem workflow in Binance - 1. Redeem with the same product id - ''' - #print(binance.get_available_products("USDT")) - #print(binance.subscribe_product("USDT001", "10", auto_subscribe=False)) - #print(binance.get_position("USDT")) - #print(binance.redeem_product("USDT001", amount="10")) - - - - ''' - Subscribe workflow in Kucoin - 1. Get product id - 2. Subscribe - - Redeem workflow in Kucoin - 1. Get ORDER id (the order id of the position) - 2. Redeem - ''' - #print(kucoin.get_available_products("USDT")) - #print(kucoin.subscribe_product("2152", "10")) - #print(kucoin.get_position("USDT")) - #print(kucoin.redeem_product(order_id="2987632", amount="10")) - - - ''' - Subscribe workflow in OKX - 1. Transfer funds to funding account - 2. Confirm transfer via transfer_id - 3. Subscribe - - Redeem workflow in OKX - 1. Reddem product (with coin, no id is required) - 2. Transfer funds to trading account - 3. Confirm transfer via transfer_id - - ''' - #print(okx.get_available_products("USDT")) - #print(okx.transfer_to_funding("USDT","10")) - #print(okx.get_transfer_state("1064667293")) - #print(okx.subscribe_product("USDT", "10")) - #print(okx.get_position("USDT")) - #print(okx.redeem_product("USDT", "10")) - #print(okx.transfer_to_trading("USDT", "10")) - #print(okx.get_transfer_state("1064667720")) - - - ''' - Subscribe workflow in Gate.io - 1. Get minimum rate - 2. Subscribe product - 3. Use get_account to confirm (request returns error?) - - Redeem workflow in Gate.io - 1. Reddem product - 2. Use get_account to confirm (request returns error?) - ''' - - #print(gateio.get_min_rate("USDT")) - #print(gateio.subscribe_product("USDT", "10", "0.00000452")) - #print(gateio.get_position("USDT")) - #print(gateio.redeem_product("USDT", "10")) \ No newline at end of file