diff --git a/changelog.txt b/changelog.txt index 72b42cd..6f6821d 100755 --- a/changelog.txt +++ b/changelog.txt @@ -3,7 +3,9 @@ . Minor fixes to string handling . Fixed misplaced lock . Fixed config dict mutation - +. Decorator for api endpoints error handling +. Input validation in api parameters +. credentials.py import fallback 2026.06.03: . Fixed tp mode 2 non-functional diff --git a/exchange_wrapper.py b/exchange_wrapper.py index 88ad781..cb3802a 100755 --- a/exchange_wrapper.py +++ b/exchange_wrapper.py @@ -1,6 +1,5 @@ import collections import time -import credentials import sqlite3 from contextlib import contextmanager from requests import get as requests_get @@ -8,6 +7,11 @@ from json import load, dumps from copy import deepcopy from urllib.parse import quote +try: + import credentials +except ModuleNotFoundError: + print("ERROR: credentials.py not found. Copy credentials.py.example to credentials.py and fill in your API keys") + raise class Broker: def __init__(self,exchange,broker_config,config_filename): diff --git a/main.py b/main.py index d979698..0e8a524 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from sys import argv, stdout from os import _exit as os_exit from json import load from datetime import date +from functools import wraps from threading import Thread, Lock from waitress import serve from concurrent.futures import ThreadPoolExecutor, as_completed @@ -494,9 +495,21 @@ def display_splashscreen(): ######### API ########### ######################### +def api_error_handler(f): + @wraps(f) + def wrapper(*args,**kwargs): + try: + return f(*args,**kwargs) + except Exception as e: + broker.logger.log_this(f"API error in {f.__name__}: {e}", 1) + return jsonify({"Error": "Halp"}) + return wrapper + + base_api = Flask(__name__) @base_api.route("/global_status", methods=['GET']) +@api_error_handler def return_global_status(): ''' GET request @@ -511,6 +524,7 @@ def return_global_status(): @base_api.route("/paused_traders", methods=['GET']) +@api_error_handler def return_paused_status(): ''' GET request @@ -524,6 +538,7 @@ def return_paused_status(): @base_api.route("/worker_status", methods=['GET']) +@api_error_handler def return_worker_status(): ''' GET request @@ -535,16 +550,13 @@ def return_worker_status(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - base = request.args.get("base") - quote = request.args.get("quote") - return unwrapped_return_worker_status(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + base = request.args.get("base") + quote = request.args.get("quote") + return unwrapped_return_worker_status(base,quote) @base_api.route("/view_old_long", methods=["GET"]) +@api_error_handler def return_old_long(): ''' GET request @@ -557,17 +569,14 @@ def return_old_long(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - base = request.args.get("base") - quote = request.args.get("quote") - from_file = request.args.get("from_file") - return unwrapped_view_old_long(base,quote,from_file) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + base = request.args.get("base") + quote = request.args.get("quote") + from_file = request.args.get("from_file") + return unwrapped_view_old_long(base,quote,from_file) @base_api.route("/switch_to_long_price", methods=["GET"]) +@api_error_handler def return_switch_price(): ''' GET request @@ -579,17 +588,13 @@ def return_switch_price(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - base = request.args.get("base") - quote = request.args.get("quote") - return unwrapped_switch_to_long_price(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) - + base = request.args.get("base") + quote = request.args.get("quote") + return unwrapped_switch_to_long_price(base,quote) @base_api.route("/base_add_so_calculation", methods=["GET"]) +@api_error_handler def return_base_add_so_calculation(): ''' GET request @@ -601,16 +606,13 @@ def return_base_add_so_calculation(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - base = request.args.get("base") - quote = request.args.get("quote") - return unwrapped_base_add_so_calculation(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + base = request.args.get("base") + quote = request.args.get("quote") + return unwrapped_base_add_so_calculation(base,quote) @base_api.route("/get_all_worker_status", methods=['GET']) +@api_error_handler def return_all_worker_status(): ''' GET request @@ -625,6 +627,7 @@ def return_all_worker_status(): @base_api.route("/add_pair", methods=['POST']) +@api_error_handler def add_pair(): ''' POST request @@ -636,19 +639,16 @@ def add_pair(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_add_pair(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_add_pair(base,quote) @base_api.route("/remove_pair", methods=['POST']) +@api_error_handler def remove_pair(): ''' POST request @@ -660,19 +660,16 @@ def remove_pair(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_remove_pair(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_remove_pair(base,quote) @base_api.route("/restart_pair", methods=['POST']) +@api_error_handler def restart_pair(): ''' POST request @@ -684,19 +681,16 @@ def restart_pair(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_restart_pair(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_restart_pair(base,quote) @base_api.route("/import_pair", methods=['POST']) +@api_error_handler def import_pair(): ''' POST request @@ -710,19 +704,16 @@ def import_pair(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_import_pair(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_import_pair(base,quote) @base_api.route("/switch_to_long", methods=['POST']) +@api_error_handler def switch_to_long(): ''' POST request @@ -735,20 +726,17 @@ def switch_to_long(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - calculate_profits = data["calculate_profits"] - return unwrapped_switch_to_long(base,quote,calculate_profits) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + calculate_profits = data["calculate_profits"] + return unwrapped_switch_to_long(base,quote,calculate_profits) @base_api.route("/switch_to_short", methods=['POST']) +@api_error_handler def switch_to_short(): ''' POST request @@ -760,19 +748,16 @@ def switch_to_short(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_switch_to_short(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_switch_to_short(base,quote) @base_api.route("/load_old_long", methods=['POST']) +@api_error_handler def load_old_long(): ''' POST request @@ -784,19 +769,16 @@ def load_old_long(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_load_old_long(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_load_old_long(base,quote) @base_api.route("/add_so", methods=['POST']) +@api_error_handler def add_so(): ''' POST request @@ -809,20 +791,17 @@ def add_so(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_add_safety_orders(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_add_safety_orders(base,quote,amount) @base_api.route("/mod_tp_level", methods=['POST']) +@api_error_handler def mod_tp_level(): ''' POST request @@ -835,20 +814,17 @@ def mod_tp_level(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_mod_tp_level(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_mod_tp_level(base,quote,amount) @base_api.route("/mod_order_size", methods=['POST']) +@api_error_handler def mod_order_size(): ''' POST request @@ -861,20 +837,17 @@ def mod_order_size(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_mod_order_size(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_mod_order_size(base,quote,amount) @base_api.route("/mod_concurrent_safety_orders", methods=['POST']) +@api_error_handler def mod_concurrent_safety_orders(): ''' POST request @@ -887,20 +860,17 @@ def mod_concurrent_safety_orders(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_mod_concurrent_safety_orders(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_mod_concurrent_safety_orders(base,quote,amount) @base_api.route("/mod_boosted_concurrent_safety_orders", methods=['POST']) +@api_error_handler def mod_boosted_concurrent_safety_orders(): ''' POST request @@ -913,20 +883,17 @@ def mod_boosted_concurrent_safety_orders(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_mod_boosted_concurrent_safety_orders(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_mod_boosted_concurrent_safety_orders(base,quote,amount) @base_api.route("/mod_default_order_size", methods=['POST']) +@api_error_handler def mod_default_order_size(): ''' POST request @@ -937,18 +904,15 @@ def mod_default_order_size(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - amount = data["amount"] - return unwrapped_mod_default_order_size(amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + amount = data["amount"] + return unwrapped_mod_default_order_size(amount) @base_api.route("/mod_global_tp_level", methods=['POST']) +@api_error_handler def mod_global_tp_level(): ''' POST request @@ -959,18 +923,15 @@ def mod_global_tp_level(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - amount = data["amount"] - return unwrapped_mod_global_tp_level(amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + amount = data["amount"] + return unwrapped_mod_global_tp_level(amount) @base_api.route("/last_call", methods=['POST']) +@api_error_handler def last_call(): ''' POST request @@ -982,19 +943,16 @@ def last_call(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_last_call(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_last_call(base,quote) @base_api.route("/deferred_last_call", methods=['POST']) +@api_error_handler def deferred_last_call(): ''' POST request @@ -1007,20 +965,17 @@ def deferred_last_call(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - yyyymmdd = data["yyyymmdd"] - return unwrapped_deferred_last_call(base,quote,yyyymmdd) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + yyyymmdd = data["yyyymmdd"] + return unwrapped_deferred_last_call(base,quote,yyyymmdd) @base_api.route("/toggle_pause", methods=['POST']) +@api_error_handler def toggle_pause(): ''' POST request @@ -1032,19 +987,16 @@ def toggle_pause(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_toggle_pause(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_toggle_pause(base,quote) @base_api.route("/global_last_call", methods=['POST']) +@api_error_handler def global_last_call(): ''' POST request @@ -1059,6 +1011,7 @@ def global_last_call(): @base_api.route("/cancel_global_last_call", methods=['POST']) +@api_error_handler def cancel_global_last_call(): ''' POST request @@ -1073,6 +1026,7 @@ def cancel_global_last_call(): @base_api.route("/add_quote", methods=['POST']) +@api_error_handler def add_quote(): ''' POST request @@ -1085,20 +1039,17 @@ def add_quote(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - amount = data["amount"] - return unwrapped_add_quote(base,quote,amount) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + amount = data["amount"] + return unwrapped_add_quote(base,quote,amount) @base_api.route("/missing_pairs", methods=['GET']) +@api_error_handler def missing_pairs(): ''' GET request @@ -1113,6 +1064,7 @@ def missing_pairs(): @base_api.route("/toggle_cleanup", methods=['POST']) +@api_error_handler def toggle_cleanup(): ''' POST request @@ -1124,19 +1076,16 @@ def toggle_cleanup(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_toggle_cleanup(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_toggle_cleanup(base,quote) @base_api.route("/toggle_autoswitch", methods=['POST']) +@api_error_handler def toggle_autoswitch(): ''' POST request @@ -1148,19 +1097,16 @@ def toggle_autoswitch(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_toggle_autoswitch(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_toggle_autoswitch(base,quote) @base_api.route("/force_trader_close", methods=['POST']) +@api_error_handler def force_trader_close(): ''' POST request @@ -1172,19 +1118,16 @@ def force_trader_close(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_force_trader_close(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_force_trader_close(base,quote) @base_api.route("/toggle_liquidate_after_switch", methods=['POST']) +@api_error_handler def toggle_liquidate_after_switch(): ''' POST request @@ -1196,19 +1139,16 @@ def toggle_liquidate_after_switch(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_toggle_liquidate_after_switch(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_toggle_liquidate_after_switch(base,quote) @base_api.route("/toggle_check_old_long_price", methods=['POST']) +@api_error_handler def toggle_check_old_long_price(): ''' POST request @@ -1220,19 +1160,16 @@ def toggle_check_old_long_price(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_toggle_check_old_long_price(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_toggle_check_old_long_price(base,quote) @base_api.route("/switch_quote_currency", methods=['POST']) +@api_error_handler def switch_quote_currency(): ''' POST request @@ -1245,20 +1182,17 @@ def switch_quote_currency(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - new_quote = data["new_quote"] - return unwrapped_switch_quote_currency(base,quote,new_quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + new_quote = data["new_quote"] + return unwrapped_switch_quote_currency(base,quote,new_quote) @base_api.route("/toggle_log_orders", methods=['POST']) +@api_error_handler def toggle_log_orders(): ''' POST request @@ -1273,6 +1207,7 @@ def toggle_log_orders(): @base_api.route("/toggle_restart", methods=['POST']) +@api_error_handler def toggle_restart(): ''' POST request @@ -1287,6 +1222,7 @@ def toggle_restart(): @base_api.route("/toggle_telegram", methods=['POST']) +@api_error_handler def toggle_telegram(): ''' POST request @@ -1301,6 +1237,7 @@ def toggle_telegram(): @base_api.route("/server_time", methods=['GET']) +@api_error_handler def server_time(): ''' GET request @@ -1329,6 +1266,7 @@ def get_log_list(): @base_api.route("/refresh_log_cache", methods=['POST']) +@api_error_handler def refresh_log_cache(): ''' POST request @@ -1336,14 +1274,11 @@ def refresh_log_cache(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - return unwrapped_refresh_log_cache() - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + return unwrapped_refresh_log_cache() @base_api.route("/get_balance", methods=['GET']) +@api_error_handler def get_balance(): ''' GET request @@ -1359,6 +1294,7 @@ def get_balance(): @base_api.route("/get_deals_cache", methods=['GET']) +@api_error_handler def get_deals_cache(): ''' GET request @@ -1373,6 +1309,7 @@ def get_deals_cache(): @base_api.route("/trader_time", methods=['GET']) +@api_error_handler def trader_time(): ''' GET request @@ -1387,6 +1324,7 @@ def trader_time(): @base_api.route("/edit_loop_wait_time", methods=['POST']) +@api_error_handler def loop_wait_time(): ''' POST request @@ -1397,18 +1335,15 @@ def loop_wait_time(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - wait_time = data["wait_time"] - return unwrapped_loop_wait_time(wait_time) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + wait_time = data["wait_time"] + return unwrapped_loop_wait_time(wait_time) @base_api.route("/edit_call_wait_time", methods=['POST']) +@api_error_handler def call_wait_time(): ''' POST request @@ -1419,18 +1354,15 @@ def call_wait_time(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - wait_time = data["wait_time"] - return unwrapped_call_wait_time(wait_time) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + wait_time = data["wait_time"] + return unwrapped_call_wait_time(wait_time) @base_api.route("/edit_cooldown_multiplier", methods=['POST']) +@api_error_handler def edit_cooldown_multiplier(): ''' POST request @@ -1441,18 +1373,15 @@ def edit_cooldown_multiplier(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - multiplier = data["cooldown_multiplier"] - return unwrapped_edit_cooldown_multiplier(multiplier) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + multiplier = data["cooldown_multiplier"] + return unwrapped_edit_cooldown_multiplier(multiplier) @base_api.route("/reload_markets", methods=['POST']) +@api_error_handler def reload_markets(): ''' POST request @@ -1468,6 +1397,7 @@ def reload_markets(): @base_api.route("/reload_trader_config", methods=['POST']) +@api_error_handler def reload_trader_config(): ''' POST request @@ -1479,16 +1409,12 @@ def reload_trader_config(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys: return jsonify({'Error': 'API key invalid'}), 401 - try: - if request.json is None: - return jsonify({'Error': 'request.json is None'}) - data = request.json - base = data["base"] - quote = data["quote"] - return unwrapped_reload_trader_config(base,quote) - except Exception as e: - print(e) - return jsonify({'Error': 'Halp'}) + if request.json is None: + return jsonify({'Error': 'request.json is None'}) + data = request.json + base = data["base"] + quote = data["quote"] + return unwrapped_reload_trader_config(base,quote) def run_API(): @@ -1500,6 +1426,11 @@ def run_API(): # Unwrapped API functions # ########################### +def validate_pair_param(base, quote): + if not base or not quote or not base.isalpha() or not quote.isalpha(): + raise ValueError(f"Invalid pair: {base}/{quote}") + return base.upper(), quote.upper() + def unwrapped_global_status(): ''' Returns the global status dictionary of the instance. @@ -1524,6 +1455,7 @@ def unwrapped_return_worker_status(base,quote): Returns: dict: The status dictionary of the trader. ''' + base, quote = validate_pair_param(base,quote) symbol = f"{base}/{quote}" with traders_lock: for instance in running_traders: @@ -1555,6 +1487,7 @@ def unwrapped_add_pair(base,quote): jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" @@ -1591,7 +1524,8 @@ def unwrapped_remove_pair(base,quote): Returns: jsonified dictionary detailing the outcome of the operation. ''' - + + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1616,6 +1550,7 @@ def unwrapped_restart_pair(base,quote): jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) if restart_pair_no_json(base,quote)==0: return jsonify({"Success": "Pair restarted"}) return jsonify({"Error": "Halp"}) @@ -1633,6 +1568,7 @@ def unwrapped_import_pair(base,quote): jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" import_instance(base,quote) @@ -1658,6 +1594,7 @@ def unwrapped_switch_to_long(base,quote,calculate_profits): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) ignore_old_long = int(calculate_profits)!=1 #Close trader and orders and pull info our of the orders if f"{base}{quote}" not in broker.get_pairs(): @@ -1687,6 +1624,7 @@ def unwrapped_switch_to_short(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) #Close trader and orders and pull info our of the orders symbol = f"{base}/{quote}" if f"{base}{quote}" not in broker.get_pairs(): @@ -1732,6 +1670,7 @@ def unwrapped_load_old_long(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) #Load the file try: symbol = f"{base}/{quote}" @@ -1771,6 +1710,7 @@ def unwrapped_view_old_long(base,quote,from_file): jsonify: A jsonified dictionary containing the old_long info. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" if int(from_file)==1: @@ -1802,6 +1742,7 @@ def unwrapped_switch_to_long_price(base,quote): jsonify: A jsonified dictionary containing the old_long info. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1834,6 +1775,7 @@ def unwrapped_add_safety_orders(base,quote,amount): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1865,6 +1807,7 @@ def unwrapped_base_add_so_calculation(base,quote): jsonify: A jsonified dictionary with the amount of orders that can be added. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1894,6 +1837,7 @@ def unwrapped_mod_tp_level(base,quote,amount): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1920,6 +1864,7 @@ def unwrapped_mod_order_size(base,quote,amount): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1947,6 +1892,7 @@ def unwrapped_mod_concurrent_safety_orders(base,quote,amount): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -1974,6 +1920,7 @@ def unwrapped_mod_boosted_concurrent_safety_orders(base,quote,amount): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2040,6 +1987,7 @@ def unwrapped_last_call(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2070,6 +2018,7 @@ def unwrapped_deferred_last_call(base,quote,yyyymmdd): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: year = yyyymmdd[:4] month = yyyymmdd[4:6] @@ -2102,6 +2051,7 @@ def unwrapped_toggle_pause(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" toggle_pauses.append(symbol) @@ -2166,6 +2116,8 @@ def unwrapped_add_quote(base,quote,amount): Returns: json: A jsonified dictionary detailing the outcome of the operation. ''' + + base, quote = validate_pair_param(base,quote) with traders_lock: for instance in running_traders: if f"{base}/{quote}"==instance.status.get_pair(): @@ -2228,6 +2180,7 @@ def unwrapped_missing_pairs(): Returns: jsonify: A jsonify object with a list of the missing pairs ''' + try: missing_pairs = broker.get_pairs() for trader in running_traders: @@ -2251,6 +2204,7 @@ def unwrapped_toggle_cleanup(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2278,6 +2232,7 @@ def unwrapped_force_trader_close(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2305,6 +2260,7 @@ def unwrapped_toggle_autoswitch(base,quote): A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2336,6 +2292,7 @@ def unwrapped_toggle_liquidate_after_switch(base,quote): A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2367,6 +2324,7 @@ def unwrapped_toggle_check_old_long_price(base,quote): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" with traders_lock: @@ -2399,6 +2357,7 @@ def unwrapped_switch_quote_currency(base,quote,new_quote): jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + base, quote = validate_pair_param(base,quote) try: symbol = f"{base}/{quote}" for trader in running_traders: @@ -2631,6 +2590,8 @@ def unwrapped_reload_trader_config(base,quote): Returns: jsonify: A jsonified dictionary detailing the outcome of the operation. ''' + + base, quote = validate_pair_param(base,quote) symbol = f"{base}/{quote}" for trader in running_traders: if trader.status.get_pair() == symbol: