From ad759fe60a54eb91d41b53ec8d0a7945a803245e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20S=C3=A1nchez?= Date: Sat, 30 Nov 2024 20:06:59 -0300 Subject: [PATCH] backtests --- utils/commander.py | 24 ++++++++- utils/statistics_server_v3.py | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/utils/commander.py b/utils/commander.py index 4e46960..684abb9 100644 --- a/utils/commander.py +++ b/utils/commander.py @@ -43,6 +43,7 @@ TRADERS 65) toggle_pause 66) toggle_cleanup 67) toggle_autoswitch 68) toggle_check_old_long_price 69) switch_quote_currency 70) reload_safety_order 71) view_old_long 72) switch_price +73) backtests 98) Change broker 99) Exit ''' @@ -614,4 +615,25 @@ if __name__=="__main__": base,quote = trading_pair.split("/") url = f"{base_url}{port}/switch_to_long_price?base={base}"e={quote}" print(json.loads(requests.get(url,headers=headers).content)) - input("Press ENTER to continue ") \ No newline at end of file + input("Press ENTER to continue ") + + elif command==73: + print("Returns backtests of the pairs available in an exchange") + broker = input("Exchange? (binance, gateio, kucoin or okx): ") + amount = input("Amount of days to consider? ") + max_rank = input("Maximum CoinMarketCap rank? ") + if not validate_int(amount): + print("The amount of days specified is invalid") + break + if not validate_int(max_rank): + print("The max_rank specified is invalid") + break + if input("Proceed? (Y/n) ") in ["Y","y",""]: + url = f"{base_url}/statistics_server/fetch_backtests?exchange_name={broker}&days={amount}&max_rank={max_rank}" + result = json.loads(requests.get(url,headers=headers).content) + #for item in result: + # print(item, round(result[item],2)) + sorted_result = {key: value for key, value in sorted(result.items(),key=lambda item: item[1])} + for item in sorted_result: + print(item, sorted_result[item]) + input("Press ENTER to continue ") \ No newline at end of file diff --git a/utils/statistics_server_v3.py b/utils/statistics_server_v3.py index 3aa2ee2..0eb055e 100644 --- a/utils/statistics_server_v3.py +++ b/utils/statistics_server_v3.py @@ -2,6 +2,9 @@ import sqlite3 import sys import datetime import time +import ccxt +import credentials +import requests from flask import Flask, jsonify, request @@ -32,6 +35,12 @@ hashes_db = {"fetch_last_n_deals":0, "total_profit_by_pair":0} +def get_market_caps(limit): + api_key = credentials.get_credentials("CMC")["key"] + url = f"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY={api_key}&convert=USD&limit={limit}" + return requests.get(url).json()["data"] + + def load_keys_from_db(file_name): #valid_keys = [] @@ -219,8 +228,97 @@ def last_n_lines(file_name,width,amount=4,full_log=False): return result[:amount],len(file_contents) +def return_parkinson_backtests(broker, days, max_rank): + ''' + Returns a dictionary containing backtests with the format {coin: value} + ''' + if broker not in ["binance", "gateio", "kucoin", "okx"]: + return {} + + evaluation_dictionary = {} + start_of_day = int(time.mktime(datetime.datetime.now().date().timetuple())) + since = int(start_of_day - 60*60*24*days) + + # Getting the data from the database + print("Querying database...") + conn = sqlite3.connect(f"data/{broker}.db") + cursor = conn.cursor() + cursor.execute('SELECT * FROM volatilities_table WHERE timestamp > ?', (since,)) + rows = cursor.fetchall() + conn.close() + + # Parse the data + print("Parsing the data...") + for row in rows: + if row[0] not in evaluation_dictionary: + evaluation_dictionary[row[0]] = [row[2]] + else: + evaluation_dictionary[row[0]].append(row[2]) + + #Calculate weighted averages + print("Calculating weighted averages") + weighted_averages = {} + for key in evaluation_dictionary: + multiplier = len(evaluation_dictionary[key]) + total = 0 + for value in evaluation_dictionary[key][::-1]: + total+=value*multiplier/len(evaluation_dictionary[key]) + multiplier-=1 + weighted_averages[key] = total/len(evaluation_dictionary[key]) + + #Filter by rank + print("Filtering results by CMC rank") + coins_accepted = [] + market_caps = get_market_caps(max_rank) + for result in market_caps: + coins_accepted.append(result["symbol"]) + + for coin in weighted_averages.copy(): + if coin.split("/")[0] not in coins_accepted: + del(weighted_averages[coin]) + + + #Checking open markets + print("Filtering results by market state") + exchange_class = getattr(ccxt, broker) + broker = exchange_class({ + "apiKey": "", + "secret": "", + "timeout": 30000, + "enableRateLimit": True, + 'options': { + 'newOrderRespType': 'FULL'} + }) + + markets = broker.load_markets() + for key in weighted_averages.copy(): + if key not in markets or not markets[key]["active"]: + del(weighted_averages[key]) + + return weighted_averages + + stats_api = Flask(__name__) +@stats_api.route("/fetch_backtests") +def fetch_backtests(): + ''' + GET request + Parameters: 'exchange_name" -> string + 'days' -> int + 'max_rank' -> int + ''' + if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys: + try: + broker = request.args.get("exchange_name") + days = int(request.args.get("days")) # type: ignore + max_rank = int(request.args.get("max_rank")) # type: ignore + return return_parkinson_backtests(broker,days,max_rank) + except Exception as e: + print(e) + return jsonify({"HORROR": f"{e}"}) + return jsonify({'Error': 'API key invalid'}), 401 + @stats_api.route("/clear_caches") def clear_hashes():