487 lines
20 KiB
Python
487 lines
20 KiB
Python
import libraries.balance_accounts as balance_accounts
|
|
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 libraries.colors import colors
|
|
from threading import Thread
|
|
from flask import Flask, jsonify, request
|
|
from waitress import serve
|
|
import time
|
|
import datetime
|
|
import json
|
|
import sqlite3
|
|
import socket
|
|
|
|
|
|
def load_keys_from_db(file_name: str) -> list:
|
|
'''
|
|
Load valid API keys
|
|
|
|
Parameters
|
|
----------
|
|
file_name : str
|
|
Name of the database file
|
|
|
|
Returns
|
|
-------
|
|
valid_keys : list
|
|
List of valid API keys
|
|
'''
|
|
|
|
database_connection = sqlite3.connect(file_name)
|
|
database_cursor = database_connection.cursor()
|
|
database_cursor.execute("SELECT * FROM credentials_table")
|
|
data = database_cursor.fetchall()
|
|
database_connection.close()
|
|
|
|
valid_keys = [line[1] for line in data]
|
|
|
|
return valid_keys
|
|
|
|
|
|
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}"
|
|
|
|
|
|
def 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
|
|
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])
|
|
total_funds = total_on_earning+total_on_trading
|
|
time_of_day = datetime.datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')
|
|
print(f"Last subscription: {last_subscription_key}{last_subscription_value} | Last redemption: {last_redemption_key}{last_redemption_value}")
|
|
print(f"Version {version} | Total funds: {total_funds:.2f} USDT | On earn: {total_on_earning:.2f} USDT ({total_on_earning/total_funds*100:.2f}%)")
|
|
print(f"{time_of_day} | Uptime: {seconds_to_time(time.time()-start_time)}")
|
|
print(colors.blue+"="*80+colors.white)
|
|
#Wait for next lap
|
|
time.sleep(config["lap_time"])
|
|
|
|
|
|
|
|
#########################
|
|
######### API ###########
|
|
#########################
|
|
|
|
earn_api = Flask(__name__)
|
|
|
|
@earn_api.route("/toggle_pause", methods=['POST'])
|
|
def toggle_pause():
|
|
'''
|
|
GET request
|
|
|
|
Parameters:
|
|
broker: str
|
|
'''
|
|
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.toggle_pause()
|
|
return jsonify({'Status': item.get_is_paused()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_step_size", methods=["GET"])
|
|
def get_step_size():
|
|
'''
|
|
GET request
|
|
|
|
Parameters:
|
|
broker: str
|
|
'''
|
|
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'step_size': item.get_step_size()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/set_step_size", methods=["POST"])
|
|
def set_step_size():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
new_step_size = request.json["new_step_size"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if new_step_size is None:
|
|
return jsonify({'Error': 'new_step_size is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.set_step_size(new_step_size)
|
|
return jsonify({'step_size': new_step_size})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_percentage", methods=["GET"])
|
|
def get_percentage():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'percentage': item.get_percentage()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/set_percentage", methods=["POST"])
|
|
def set_percentage():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
new_percentage = request.json["new_percentage"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if new_percentage is None:
|
|
return jsonify({'Error': 'new_step_size is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.set_percentage(new_percentage)
|
|
return jsonify({'percentage': new_percentage})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_time_between_subscriptions", methods=["GET"])
|
|
def get_time_between_subscriptions():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'time_between_subscriptions': item.get_time_between_subscriptions()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/set_time_between_subscriptions", methods=["POST"])
|
|
def set_time_between_subscriptions():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
new_time_between_subscriptions = request.json["new_time_between_subscriptions"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if new_time_between_subscriptions is None:
|
|
return jsonify({'Error': 'new_step_size is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.set_time_between_subscriptions(new_time_between_subscriptions)
|
|
return jsonify({'percentage': new_time_between_subscriptions})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_time_between_redemptions", methods=["GET"])
|
|
def get_time_between_redemptions():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'time_between_redemptions': item.get_time_between_redemptions()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/set_time_between_redemptions", methods=["POST"])
|
|
def set_time_between_redemptions():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
new_time_between_redemptions = request.json["new_time_between_redemptions"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if new_time_between_redemptions is None:
|
|
return jsonify({'Error': 'new_step_size is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.set_time_between_redemptions(new_time_between_redemptions)
|
|
return jsonify({'percentage': new_time_between_redemptions})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_minimum_amount_in_trading_account", methods=["GET"])
|
|
def get_minimum_amount_in_trading_account():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'minimum_amount_in_trading_account': item.get_minimum_amount_in_trading_account()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/set_minimum_amount_in_trading_account", methods=["POST"])
|
|
def set_minimum_amount_in_trading_account():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.json["broker"]
|
|
new_minimum_amount_in_trading_account = request.json["new_minimum_amount_in_trading_account"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if new_minimum_amount_in_trading_account is None:
|
|
return jsonify({'Error': 'new_step_size is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
item.set_minimum_amount_in_trading_account(new_minimum_amount_in_trading_account)
|
|
return jsonify({'percentage': new_minimum_amount_in_trading_account})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_last_subscription", methods=["GET"])
|
|
def get_last_subscription():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'last_subscription': item.get_last_subscription()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_last_redemption", methods=["GET"])
|
|
def get_last_redemption():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'last_redemption': item.get_last_redemption()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/get_total_balance", methods=["GET"])
|
|
def get_total_balance():
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
broker = request.args.get("broker")
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
return jsonify({'trading_balance': item.get_trading_balance(), 'earning_balance': item.get_earning_balance()})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/subscribe", methods=["POST"])
|
|
def subscribe():
|
|
'''
|
|
args:
|
|
broker: broker name
|
|
force_pause: True or False
|
|
'''
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
amount = request.json["amount"]
|
|
force_pause = request.json["force_pause"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if amount is None:
|
|
return jsonify({'Error': 'amount is None'})
|
|
if force_pause is None:
|
|
return jsonify({'Error': 'force_pause is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
subscription = item.subscribe(amount, force_pause=force_pause)
|
|
return jsonify({'Success': str(subscription)})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
@earn_api.route("/redeem", methods=["POST"])
|
|
def redeem():
|
|
'''
|
|
args:
|
|
broker: broker name
|
|
force_pause: True or False
|
|
'''
|
|
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
|
|
valid_brokers = [str(item.connector) for item in earners]
|
|
if request.json is None:
|
|
return jsonify({'Error': 'request.json is None'})
|
|
broker = request.json["broker"]
|
|
amount = request.json["amount"]
|
|
force_pause = request.json["force_pause"]
|
|
if broker is None:
|
|
return jsonify({'Error': 'broker is None'})
|
|
if broker not in valid_brokers:
|
|
return jsonify({'Error': 'broker not valid'})
|
|
if amount is None:
|
|
return jsonify({'Error': 'amount is None'})
|
|
if force_pause is None:
|
|
return jsonify({'Error': 'force_pause is None'})
|
|
for item in earners:
|
|
if str(item.connector)==broker:
|
|
subscription = item.redeem(amount, force_pause=force_pause)
|
|
return jsonify({'Success': str(subscription)})
|
|
return jsonify({'Error': 'broker not found'})
|
|
return jsonify({'Error': 'API key not valid'}), 401
|
|
|
|
|
|
def run_API(port):
|
|
serve(earn_api, host="0.0.0.0", port=port)
|
|
#earn_api.run(host="0.0.0.0", port=port)
|
|
|
|
|
|
if __name__=="__main__":
|
|
|
|
version = "2025.01.09"
|
|
start_time = time.time()
|
|
|
|
with open("config.json") as f:
|
|
config = json.load(f)
|
|
|
|
connectors = {"binance": earn_binance.binance_earn(),
|
|
"gateio": earn_gateio.gateio_earn(),
|
|
"kucoin": earn_kucoin.kucoin_earn(),
|
|
"okx": earn_okx.okx_earn()}
|
|
earners = []
|
|
|
|
for item in config["exchanges"]:
|
|
earners.append(earner(connectors[item], config["exchanges"][item]))
|
|
|
|
#Load valid API keys
|
|
valid_keys = load_keys_from_db("keys/api_credentials.db")
|
|
|
|
#Threads to run: main loop and flask api
|
|
main_threads = [Thread(target=main),Thread(target=run_API, args=(config["api_port"],))]
|
|
|
|
#Iterate indefinitely:
|
|
for m in main_threads:
|
|
m.start()
|
|
|
|
|
|
|
|
|
|
|
|
|