From 24bb025f86eb131139270a0604fd56c64310838e Mon Sep 17 00:00:00 2001 From: Lee Garam Date: Sat, 8 Jun 2024 22:25:49 +0900 Subject: [PATCH] Improved User Leaderboard Ranking + bugfix The changes optimize fetching of current cryptocurrency prices by preloading all necessary values from Redis in a single operation. Additionally, the refactored code ensures accurate mapping of user balances to their corresponding users by using a dictionary, and streamlines the calculation of total asset values by filtering out non-asset columns only once, leading to more efficient and accurate ranking of users based on their total asset value. (There were some bugs calculating the estimated asset values incorrectly) --- tasks/ranking_user_leaderboard.py | 48 ++++++++++++------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/tasks/ranking_user_leaderboard.py b/tasks/ranking_user_leaderboard.py index dadf096..bff9394 100644 --- a/tasks/ranking_user_leaderboard.py +++ b/tasks/ranking_user_leaderboard.py @@ -12,9 +12,8 @@ import json from sqlalchemy.orm import Session from rich.console import Console -from typing import List +from typing import List, Dict -# Note that these packages are in the parent directory import sys sys.path.append("..") from database_session import get_sqlite3_db, get_redis_db @@ -29,39 +28,30 @@ def rank_user_leaderboard(update_interval_in_seconds: int) -> None: sqlite3_db: Session = next(get_sqlite3_db()) redis_db = get_redis_db() - # Get the user's balances from the SQLite3 database + # Get the users and their balances from the SQLite3 database users: List[User] = sqlite3_db.query(User).all() - user_balances: List[Balance] = sqlite3_db.query(Balance).all() + user_balances: Dict[int, Balance] = {balance.user_id: balance for balance in sqlite3_db.query(Balance).all()} - user_total_assets = {user.id: user_balance.KRW for user, user_balance in zip(users, user_balances)} + user_total_assets = {} - # Calculate the user's total asset value by considering the current market price(Redis) - # (user's total asset value) = (user's KRW balance) + (user's BTC balance) * (current BTC price) + ... (for all currencies) - for user in users: - for user_balance_data_row in user_balances: - # Get all available cryptocurrency types - availble_crypto_asset_type: List[str] = user_balance_data_row.__table__.columns.keys() - availble_crypto_asset_type.remove("id") - availble_crypto_asset_type.remove("user_id") - availble_crypto_asset_type.remove("created_at") - availble_crypto_asset_type.remove("KRW") - for column in user_balance_data_row.__table__.columns.keys(): - if column.endswith("_average_unit_price"): - availble_crypto_asset_type.remove(column) + # Fetch all available cryptocurrency types and current prices + all_crypto_assets = [col for col in Balance.__table__.columns.keys() if col not in {"id", "user_id", "created_at", "KRW"} and not col.endswith("_average_unit_price")] + crypto_prices = {crypto: json.loads(redis_db.get(f"KRW-{crypto}"))["trade_price"] for crypto in all_crypto_assets} - # Calculate the user's total asset value - for crypto_asset_type in availble_crypto_asset_type: - current_crypto_price = redis_db.get(f"KRW-{crypto_asset_type}") - current_crypto_price = json.loads(current_crypto_price)["trade_price"] - user_total_assets[user.id] += user_balance_data_row.__getattribute__(crypto_asset_type) * current_crypto_price + # Calculate the user's total asset value + for user in users: + balance = user_balances.get(user.id) + if balance: + total_asset_value = balance.KRW # Start with KRW balance + for crypto in all_crypto_assets: + crypto_balance = getattr(balance, crypto, 0) + current_price = crypto_prices.get(crypto, 0) + total_asset_value += crypto_balance * current_price - # Now we have the user's total asset value in the user_total_assets dictionary - # i.e. {1: 1000000, 2: 2000000, ...} + user_total_assets[user.id] = total_asset_value - # Associate the user's name with the sorted_user_total_assets's key(user ID) - user_total_assets_with_name = {} - for user in users: - user_total_assets_with_name[user.username] = user_total_assets[user.id] + # Associate the user's name with their total asset value + user_total_assets_with_name = {user.username: user_total_assets[user.id] for user in users if user.id in user_total_assets} # Sort the user's total asset value in descending order sorted_user_total_assets = dict(sorted(user_total_assets_with_name.items(), key=lambda item: item[1], reverse=True))