Compare commits

..

13 Commits

4 changed files with 324 additions and 274 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ keys/api_credentials.db
*.pyc *.pyc
*.pyo *.pyo
credentials.py credentials.py
some_tests.py some_tests.py
tests.py

View File

@ -32,10 +32,13 @@ class earner:
def __str__(self): def __str__(self):
return str(self.connector) return str(self.connector)
def get_currency(self):
return self.currency
def write_to_log(self,message): def write_to_log(self,message):
with open("earn.log", "a") as f: with open("earn.log", "a") as f:
f.write(str(self.connector) + datetime.datetime.now().strftime(' [%Y/%m/%d %H:%M:%S] ') + message + "\n") f.write(datetime.datetime.now().strftime(f"[%Y/%m/%d %H:%M:%S] {str(self.connector).upper()} | {message}\n"))
def toggle_pause(self): def toggle_pause(self):
self.is_paused = not self.is_paused self.is_paused = not self.is_paused
@ -114,14 +117,21 @@ class earner:
return self.status_string return self.status_string
def get_trading_balance(self): def get_trading_balance(self):
return float(self.trading_balance) try:
return float(self.trading_balance)
except Exception as e:
self.write_to_log(f"{e}")
return 0.0
def get_earning_balance(self): def get_earning_balance(self):
return float(self.earning_balance) try:
return float(self.earning_balance)
except Exception as e:
self.write_to_log(f"{e}")
return 0.0
def subscribe(self,amount,force_pause=False): def subscribe(self,amount,force_pause=False):
print(f"{datetime.datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')} | {str(self.connector).upper()} | Subscribing {amount} {self.currency}") print(f"{datetime.datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')} | {str(self.connector).upper()} | {colors.green}Subscribing{colors.white} {amount} {self.currency}")
self.write_to_log(f"{colors.green}Subscribing{colors.white} {amount} {self.currency}") self.write_to_log(f"{colors.green}Subscribing{colors.white} {amount} {self.currency}")
if force_pause: if force_pause:
@ -157,7 +167,7 @@ class earner:
return 1 return 1
def redeem(self,amount,force_pause=False): def redeem(self,amount,force_pause=False):
print(f"{datetime.datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')} | {str(self.connector).upper()} | Redeeming {amount} {self.currency}") print(f"{datetime.datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')} | {str(self.connector).upper()} | {colors.red}Redeeming{colors.white} {amount} {self.currency}")
self.write_to_log(f"{colors.red}Redeeming{colors.white} {amount} {self.currency}") self.write_to_log(f"{colors.red}Redeeming{colors.white} {amount} {self.currency}")
if force_pause: if force_pause:
@ -237,11 +247,11 @@ class earner:
if self.earning_balance is None: if self.earning_balance is None:
print(f"{str(self.connector).upper()} - There was an error fetching earning balance") print(f"{str(self.connector).upper()} - There was an error fetching earning balance")
else: else:
if float(self.earning_balance)!=0: #if float(self.earning_balance)!=0:
earning_delta = target_earning_amount - Decimal(self.earning_balance) earning_delta = target_earning_amount - Decimal(self.earning_balance)
if earning_delta>Decimal(self.step_size) and time.time()-self.last_subscription_time>self.time_between_subscriptions: if earning_delta>Decimal(self.step_size) and time.time()-self.last_subscription_time>self.time_between_subscriptions:
self.subscribe(earning_delta) self.subscribe(earning_delta)
if earning_delta<-Decimal(self.step_size) and time.time()-self.last_redemption_time>self.time_between_redemptions: elif earning_delta<-Decimal(self.step_size) and time.time()-self.last_redemption_time>self.time_between_redemptions:
self.redeem(-earning_delta) self.redeem(-earning_delta)
#print(f"{str(self.connector)} - Difference: {earning_delta}") #print(f"{str(self.connector)} - Difference: {earning_delta}")
else: else:

View File

@ -177,7 +177,7 @@ class okx_earn:
"amount": response["data"][0]["amt"] "amount": response["data"][0]["amt"]
} }
else: else:
return {"Error": response} return {"Error": response, "Rate": str(rate)}
def redeem_product(self, coin, amount): def redeem_product(self, coin, amount):
@ -207,6 +207,8 @@ class okx_earn:
Returns the 24hs average lending rate Returns the 24hs average lending rate
''' '''
rate = self.earning_api.get_public_borrow_info(coin) rate = self.earning_api.get_public_borrow_info(coin)
if rate["data"][0]["avgRate"]=='0':
return str(rate["data"][0]["estRate"])
return str(rate["data"][0]["avgRate"]) return str(rate["data"][0]["avgRate"])

563
main.py
View File

@ -1,23 +1,25 @@
import libraries.balance_accounts as balance_accounts
from libraries.wrappers import earn_binance from libraries.wrappers import earn_binance
from libraries.wrappers import earn_kucoin from libraries.wrappers import earn_kucoin
from libraries.wrappers import earn_okx from libraries.wrappers import earn_okx
from libraries.wrappers import earn_gateio from libraries.wrappers import earn_gateio
from libraries.earner import earner from libraries.earner import earner
from libraries.colors import colors from libraries.colors import colors
from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Thread from threading import Thread
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
from waitress import serve from waitress import serve
from functools import wraps
import time import time
import datetime import datetime
import json import json
import sqlite3 import sqlite3
import socket import signal
import os
def load_keys_from_db(file_name: str) -> list: def load_keys_from_db(file_name: str) -> list:
''' '''
Load valid API keys Load valid API keys to a set
Parameters Parameters
---------- ----------
@ -36,7 +38,7 @@ def load_keys_from_db(file_name: str) -> list:
data = database_cursor.fetchall() data = database_cursor.fetchall()
database_connection.close() database_connection.close()
valid_keys = [line[1] for line in data] valid_keys = {line[1] for line in data}
return valid_keys return valid_keys
@ -63,16 +65,15 @@ def seconds_to_time(total_seconds: float) -> str:
def main(): def main():
executor = ThreadPoolExecutor(max_workers=len(earners))
while True: while True:
threads = []
#Run earners #Run earners
for item in earners: futures = [executor.submit(e.run) for e in earners]
threads.append(Thread(target=item.run)) for future in as_completed(futures):
for item in threads: try:
item.start() future.result()
for item in threads: except Exception as e:
item.join() print(f"Error in thread - {e}")
#Print status #Print status
subscriptions = [] subscriptions = []
@ -97,7 +98,6 @@ def main():
last_redemption_value = "" last_redemption_value = ""
else: else:
last_redemption_value = datetime.datetime.fromtimestamp(last_redemption_value).strftime('@%Y/%m/%d %H:%M:%S') last_redemption_value = datetime.datetime.fromtimestamp(last_redemption_value).strftime('@%Y/%m/%d %H:%M:%S')
print("-"*90) print("-"*90)
total_on_trading = sum([item.get_trading_balance() for item in earners]) 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_on_earning = sum([item.get_earning_balance() for item in earners])
@ -107,6 +107,7 @@ def main():
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"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(f"{time_of_day} | Uptime: {seconds_to_time(time.time()-start_time)}")
print(colors.blue+"="*90+colors.white) print(colors.blue+"="*90+colors.white)
#Wait for next lap #Wait for next lap
time.sleep(config["lap_time"]) time.sleep(config["lap_time"])
@ -118,7 +119,48 @@ def main():
earn_api = Flask(__name__) earn_api = Flask(__name__)
#Helper functions
def require_api_key(func):
'''
Validates API key
'''
@wraps(func)
def wrapper(*args, **kwargs):
key = request.headers.get("X-API-KEY")
if not key or key not in valid_keys:
return jsonify({'Error': 'API key not valid'}), 401
return func(*args, **kwargs)
return wrapper
@earn_api.route("/get_global_status", methods=['GET'])
@require_api_key
def get_global_status():
'''
GET request
Parameters:
None
'''
response = {}
for item in earners:
response[str(item.connector)] = {"currency": item.get_currency(),
"trading_balance": item.get_trading_balance(),
"earning_balance": item.get_earning_balance(),
"is_paused": item.get_is_paused(),
"step_size": item.get_step_size(),
"percentage": item.get_percentage(),
"minimum_amount_in_trading_account": item.get_minimum_amount_in_trading_account(),
"time_between_subscriptions": item.get_time_between_subscriptions(),
"time_between_redemptions": item.get_time_between_redemptions(),
"last_subscription": item.get_last_subscription(),
"last_redemption": item.get_last_redemption()}
response["uptime"] = time.time() - start_time
return jsonify(response)
@earn_api.route("/toggle_pause", methods=['POST']) @earn_api.route("/toggle_pause", methods=['POST'])
@require_api_key
def toggle_pause(): def toggle_pause():
''' '''
GET request GET request
@ -126,25 +168,23 @@ def toggle_pause():
Parameters: Parameters:
broker: str broker: str
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: if request.json is None:
valid_brokers = [str(item.connector) for item in earners] return jsonify({'Error': 'request.json is None'})
if request.json is None: broker = request.json["broker"]
return jsonify({'Error': 'request.json is None'}) if broker is None:
broker = request.json["broker"] return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: item.toggle_pause()
if str(item.connector)==broker: return jsonify({'Status': item.get_is_paused()})
item.toggle_pause() return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_step_size", methods=["GET"])
@require_api_key
def get_step_size(): def get_step_size():
''' '''
GET request GET request
@ -152,299 +192,275 @@ def get_step_size():
Parameters: Parameters:
broker: str broker: str
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'step_size': item.get_step_size()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/set_step_size", methods=["POST"])
def set_step_size(): @require_api_key
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: def set_step_size():
valid_brokers = [str(item.connector) for item in earners] if request.json is None:
if request.json is None: return jsonify({'Error': 'request.json is None'})
return jsonify({'Error': 'request.json is None'}) broker = request.json["broker"]
broker = request.json["broker"] new_step_size = request.json["new_step_size"]
new_step_size = request.json["new_step_size"] if broker is None:
if broker is None: return jsonify({'Error': 'broker is None'})
return jsonify({'Error': 'broker is None'}) if broker not in valid_broker_list:
if broker not in valid_brokers: return jsonify({'Error': 'broker not valid'})
return jsonify({'Error': 'broker not valid'}) if new_step_size is None:
if new_step_size is None: return jsonify({'Error': 'new_step_size is None'})
return jsonify({'Error': 'new_step_size is None'}) for item in earners:
for item in earners: if str(item.connector)==broker:
if str(item.connector)==broker: item.set_step_size(new_step_size)
item.set_step_size(new_step_size) return jsonify({'step_size': new_step_size})
return jsonify({'step_size': new_step_size}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_percentage", methods=["GET"]) @earn_api.route("/get_percentage", methods=["GET"])
@require_api_key
def get_percentage(): def get_percentage():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'percentage': item.get_percentage()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/set_percentage", methods=["POST"])
def set_percentage(): @require_api_key
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: def set_percentage():
valid_brokers = [str(item.connector) for item in earners] if request.json is None:
if request.json is None: return jsonify({'Error': 'request.json is None'})
return jsonify({'Error': 'request.json is None'}) broker = request.json["broker"]
broker = request.json["broker"] new_percentage = request.json["new_percentage"]
new_percentage = request.json["new_percentage"] if broker is None:
if broker is None: return jsonify({'Error': 'broker is None'})
return jsonify({'Error': 'broker is None'}) if broker not in valid_broker_list:
if broker not in valid_brokers: return jsonify({'Error': 'broker not valid'})
return jsonify({'Error': 'broker not valid'}) if new_percentage is None:
if new_percentage is None: return jsonify({'Error': 'new_step_size is None'})
return jsonify({'Error': 'new_step_size is None'}) for item in earners:
for item in earners: if str(item.connector)==broker:
if str(item.connector)==broker: item.set_percentage(new_percentage)
item.set_percentage(new_percentage) return jsonify({'percentage': new_percentage})
return jsonify({'percentage': new_percentage}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_time_between_subscriptions", methods=["GET"]) @earn_api.route("/get_time_between_subscriptions", methods=["GET"])
def get_time_between_subscriptions(): @require_api_key
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: def get_time_between_subscriptions():
valid_brokers = [str(item.connector) for item in earners] broker = request.args.get("broker")
broker = request.args.get("broker") if broker is None:
if broker is None: return jsonify({'Error': 'broker is None'})
return jsonify({'Error': 'broker is None'}) if broker not in valid_broker_list:
if broker not in valid_brokers: return jsonify({'Error': 'broker not valid'})
return jsonify({'Error': 'broker not valid'}) for item in earners:
for item in earners: if str(item.connector)==broker:
if str(item.connector)==broker: return jsonify({'time_between_subscriptions': item.get_time_between_subscriptions()})
return jsonify({'time_between_subscriptions': item.get_time_between_subscriptions()}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_time_between_subscriptions", methods=["POST"]) @earn_api.route("/set_time_between_subscriptions", methods=["POST"])
@require_api_key
def set_time_between_subscriptions(): def set_time_between_subscriptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: if request.json is None:
valid_brokers = [str(item.connector) for item in earners] return jsonify({'Error': 'request.json is None'})
if request.json is None: broker = request.json["broker"]
return jsonify({'Error': 'request.json is None'}) new_time_between_subscriptions = request.json["new_time_between_subscriptions"]
broker = request.json["broker"] if broker is None:
new_time_between_subscriptions = request.json["new_time_between_subscriptions"] return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: if new_time_between_subscriptions is None:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'new_step_size is None'})
if new_time_between_subscriptions is None: for item in earners:
return jsonify({'Error': 'new_step_size is None'}) if str(item.connector)==broker:
for item in earners: item.set_time_between_subscriptions(new_time_between_subscriptions)
if str(item.connector)==broker: return jsonify({'time_between_subscriptions': new_time_between_subscriptions})
item.set_time_between_subscriptions(new_time_between_subscriptions) return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_time_between_redemptions", methods=["GET"])
@require_api_key
def get_time_between_redemptions(): def get_time_between_redemptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'time_between_redemptions': item.get_time_between_redemptions()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/set_time_between_redemptions", methods=["POST"])
@require_api_key
def set_time_between_redemptions(): def set_time_between_redemptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: if request.json is None:
valid_brokers = [str(item.connector) for item in earners] return jsonify({'Error': 'request.json is None'})
if request.json is None: broker = request.json["broker"]
return jsonify({'Error': 'request.json is None'}) new_time_between_redemptions = request.json["new_time_between_redemptions"]
broker = request.json["broker"] if broker is None:
new_time_between_redemptions = request.json["new_time_between_redemptions"] return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: if new_time_between_redemptions is None:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'new_step_size is None'})
if new_time_between_redemptions is None: for item in earners:
return jsonify({'Error': 'new_step_size is None'}) if str(item.connector)==broker:
for item in earners: item.set_time_between_redemptions(new_time_between_redemptions)
if str(item.connector)==broker: return jsonify({'time_between_redemptions': new_time_between_redemptions})
item.set_time_between_redemptions(new_time_between_redemptions) return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_minimum_amount_in_trading_account", methods=["GET"])
@require_api_key
def get_minimum_amount_in_trading_account(): def get_minimum_amount_in_trading_account():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'minimum_amount_in_trading_account': item.get_minimum_amount_in_trading_account()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/set_minimum_amount_in_trading_account", methods=["POST"])
@require_api_key
def set_minimum_amount_in_trading_account(): def set_minimum_amount_in_trading_account():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.json["broker"]
valid_brokers = [str(item.connector) for item in earners] new_minimum_amount_in_trading_account = request.json["new_minimum_amount_in_trading_account"]
broker = request.json["broker"] if broker is None:
new_minimum_amount_in_trading_account = request.json["new_minimum_amount_in_trading_account"] return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: if new_minimum_amount_in_trading_account is None:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'new_step_size is None'})
if new_minimum_amount_in_trading_account is None: for item in earners:
return jsonify({'Error': 'new_step_size is None'}) if str(item.connector)==broker:
for item in earners: item.set_minimum_amount_in_trading_account(new_minimum_amount_in_trading_account)
if str(item.connector)==broker: return jsonify({'minimum_amount_in_trading_account': new_minimum_amount_in_trading_account})
item.set_minimum_amount_in_trading_account(new_minimum_amount_in_trading_account) return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_last_subscription", methods=["GET"])
@require_api_key
def get_last_subscription(): def get_last_subscription():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'last_subscription': item.get_last_subscription()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_last_redemption", methods=["GET"])
@require_api_key
def get_last_redemption(): def get_last_redemption():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'last_redemption': item.get_last_redemption()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/get_total_balance", methods=["GET"])
@require_api_key
def get_total_balance(): def get_total_balance():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: broker = request.args.get("broker")
valid_brokers = [str(item.connector) for item in earners] if broker is None:
broker = request.args.get("broker") return jsonify({'Error': 'broker is None'})
if broker is None: if broker not in valid_broker_list:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker not valid'})
if broker not in valid_brokers: for item in earners:
return jsonify({'Error': 'broker not valid'}) if str(item.connector)==broker:
for item in earners: return jsonify({'trading_balance': item.get_trading_balance(), 'earning_balance': item.get_earning_balance()})
if str(item.connector)==broker: return jsonify({'Error': 'broker not found'})
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"]) @earn_api.route("/subscribe", methods=["POST"])
@require_api_key
def subscribe(): def subscribe():
''' '''
args: args:
broker: broker name broker: broker name
amount: amount to subscribe
force_pause: True or False force_pause: True or False
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: if request.json is None:
valid_brokers = [str(item.connector) for item in earners] return jsonify({'Error': 'request.json is None'})
if request.json is None: broker = request.json["broker"]
return jsonify({'Error': 'request.json is None'}) amount = request.json["amount"]
broker = request.json["broker"] force_pause = False
amount = request.json["amount"] if "force_pause" in request.json:
force_pause = request.json["force_pause"] force_pause = request.json["force_pause"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if amount is None: if amount is None:
return jsonify({'Error': 'amount is None'}) return jsonify({'Error': 'amount is None'})
if force_pause is None: if force_pause is None:
return jsonify({'Error': 'force_pause is None'}) return jsonify({'Error': 'force_pause is None'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
subscription = item.subscribe(amount, force_pause=force_pause) subscription = item.subscribe(amount, force_pause=force_pause)
return jsonify({'Success': str(subscription)}) return jsonify({'Success': str(subscription)})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/redeem", methods=["POST"]) @earn_api.route("/redeem", methods=["POST"])
@require_api_key
def redeem(): def redeem():
''' '''
args: args:
broker: broker name broker: broker name
amount: amount to redeem
force_pause: True or False force_pause: True or False
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: if request.json is None:
valid_brokers = [str(item.connector) for item in earners] return jsonify({'Error': 'request.json is None'})
if request.json is None: broker = request.json["broker"]
return jsonify({'Error': 'request.json is None'}) amount = request.json["amount"]
broker = request.json["broker"] force_pause = False
amount = request.json["amount"] if "force_pause" in request.json:
force_pause = request.json["force_pause"] force_pause = request.json["force_pause"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if amount is None: if amount is None:
return jsonify({'Error': 'amount is None'}) return jsonify({'Error': 'amount is None'})
if force_pause is None: if force_pause is None:
return jsonify({'Error': 'force_pause is None'}) return jsonify({'Error': 'force_pause is None'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
subscription = item.redeem(amount, force_pause=force_pause) redemption = item.redeem(amount, force_pause=force_pause)
return jsonify({'Success': str(subscription)}) return jsonify({'Success': str(redemption)})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
def run_API(port): def run_API(port):
@ -452,9 +468,22 @@ def run_API(port):
#earn_api.run(host="0.0.0.0", port=port) #earn_api.run(host="0.0.0.0", port=port)
executor = None
#Shutdown handler
def shutdown_handler(signum, _):
print(f"Received signal {signum}, shutting down as gracefully as possible...")
if executor:
executor.shutdown(wait=True, timeout=5)
os._exit(0)
# Register signals for shutdown handler
signal.signal(signal.SIGINT, shutdown_handler)
signal.signal(signal.SIGTERM, shutdown_handler)
if __name__=="__main__": if __name__=="__main__":
version = "2025.01.11" version = "2025.11.20"
start_time = time.time() start_time = time.time()
with open("config.json") as f: with open("config.json") as f:
@ -469,15 +498,23 @@ if __name__=="__main__":
for item in config["exchanges"]: for item in config["exchanges"]:
earners.append(earner(connectors[item], config["exchanges"][item])) earners.append(earner(connectors[item], config["exchanges"][item]))
#Valid broker list
valid_broker_list = [str(item.connector) for item in earners]
#Load valid API keys #Load valid API keys
valid_keys = load_keys_from_db("keys/api_credentials.db") valid_keys = load_keys_from_db("keys/api_credentials.db")
#Threads to run: main loop and flask api #Threads to run: main loop and flask api
main_threads = [Thread(target=main),Thread(target=run_API, args=(config["api_port"],))] api_thread = Thread(target=run_API, args=(config["api_port"],), daemon=True)
#Iterate indefinitely: #Iterate indefinitely:
for m in main_threads: api_thread.start()
m.start()
try:
main()
except KeyboardInterrupt:
api_thread.join()
shutdown_handler(signal.SIGINT, None)