hyper_dashboard / app.py
Trireme's picture
Upload folder using huggingface_hub
e5a819a verified
Raw
History Blame Contribute Delete
7.51 kB
# -*- coding: utf-8 -*-
import asyncio
from pymongo import MongoClient
import aiohttp
import pandas as pd
import requests
import json
import urllib.parse
import gradio as gr
import datetime
import os
import subprocess, json
def get_lp_positions(node_path="meteora/index.js"):
"""
Runs the Node.js script, parses the output, and returns a combined DataFrame
with tokenX and tokenY positions.
"""
try:
# Run Node.js script
result = subprocess.run(
["node", node_path],
capture_output=True,
text=True
)
# Optional: print warnings
if result.stderr.strip():
print("Node STDERR:", result.stderr)
# Handle Node.js errors
if result.returncode != 0:
print("Node.js exited with an error")
return pd.DataFrame() # return empty DataFrame on failure
# Parse JSON output
positions = json.loads(result.stdout) if result.stdout.strip() else []
if not positions:
return pd.DataFrame()
# Normalize JSON to DataFrame
lp_df_real = pd.json_normalize(positions)
lp_df_real.drop(columns=['positionAccountAddress'], inplace=True, errors='ignore')
# Split tokenY
y_df_real = lp_df_real[['tokenYAddress','tokenYAmount']].copy()
lp_df_real.rename(columns={'tokenXAddress':'symbol','tokenXAmount':'balance'}, inplace=True)
y_df_real.rename(columns={'tokenYAddress':'symbol','tokenYAmount':'balance'}, inplace=True)
# Combine tokenX and tokenY DataFrames
final_lp_df = pd.concat([lp_df_real, y_df_real], ignore_index=True).dropna(axis=1)
return final_lp_df
except Exception as e:
print(f"Error in get_lp_positions: {e}")
return pd.DataFrame() # fallback empty DataFrame
wallets = [
"41KL7hbeA2dDuX6gCUFhVaozACnx2hLvGBuY4tgrpJ31",
"5Xihd2FdAXZnER7HWjT8GiZm14hyPiUpGfqLLL86yaa9",
"fYYLvdb82NkUQ6EgT7U6N52A2cN6V1dBKEJuvDwwjKj"
]
def parse_solscan_results(results):
if isinstance(results, dict):
results = [results]
records = []
for entry in results:
data = entry.get("data", {})
if not data or not data.get("success"):
continue
payload = data.get("data", {})
# Native SOL
native = payload.get("native_balance", {})
if native:
records.append({
"symbol": native.get("token_symbol", "SOL"),
"balance": native.get("balance", 0),
"value": native.get("value", 0)
})
# Tokens
for token in payload.get("tokens", []):
records.append({
"symbol": token.get("token_symbol"),
"balance": token.get("balance", 0),
"value": token.get("value", 0)
})
df = pd.DataFrame(records, columns=["address","symbol", "balance", "value"])
if not df.empty:
df = df.groupby(["symbol"], as_index=False).agg({
"balance": "sum",
"value": "sum"
})
return df
def parse_solscan_results_addresses(results):
if isinstance(results, dict):
results = [results]
records = []
for entry in results:
address = entry.get("address")
data = entry.get("data", {})
if not data or not data.get("success"):
continue
payload = data.get("data", {})
# Native SOL
native = payload.get("native_balance", {})
if native:
records.append({
"address": address,
"symbol": native.get("token_symbol", "SOL"),
"balance": native.get("balance", 0),
"value": native.get("value", 0)
})
# Tokens
for token in payload.get("tokens", []):
records.append({
"address": address,
"symbol": token.get("token_symbol"),
"balance": token.get("balance", 0),
"value": token.get("value", 0)
})
# Make sure DataFrame has the expected columns even if empty
df = pd.DataFrame(records, columns=["address", "symbol", "balance", "value"])
# Only group if DataFrame is not empty
if not df.empty:
df = df.groupby(["address", "symbol"], as_index=False).agg({
"balance": "sum",
"value": "sum"
})
return df
async def get_sol_balances(sol_addresses):
SOLSCAN_KEY = os.getenv('SOLSCAN_KEY')
headers = {
"token": SOLSCAN_KEY
}
async def fetch_wallet(session, wallet_address):
url = f"https://pro-api.solscan.io/v2.0/account/portfolio?address={wallet_address}&exclude_low_score_tokens=false"
async with session.get(url, headers=headers) as response:
if response.status != 200:
return {"address": wallet_address, "error": f"HTTP {response.status}"}
try:
data = await response.json()
except Exception as e:
return {"address": wallet_address, "error": str(e)}
res = {"address": wallet_address, "data": data}
return parse_solscan_results_addresses([res])
async with aiohttp.ClientSession() as session:
tasks = [fetch_wallet(session, addr) for addr in sol_addresses]
results = await asyncio.gather(*tasks)
final_df = pd.concat(results, ignore_index=True)
final_df = final_df.groupby(["address","symbol"], as_index=False).agg({
"balance": "sum",
"value": "sum"
})
final_df = final_df[final_df['symbol'].isin(['SOL', 'WSOL','HYPER'])]
return final_df.groupby('symbol').agg({"balance":"sum","value":"sum"}).reset_index()
async def get_all_dfs():
starting_df = pd.DataFrame([{"symbol":"HYPER","balance":5000000,"value":68500},{"symbol":"SOL","balance":240,"value":42960}])
final_df = await get_sol_balances(wallets)
hyper_price = float(final_df.loc[final_df['symbol']=='HYPER','value'] / final_df.loc[final_df['symbol']=='HYPER','balance'])
sol_price = float(final_df.loc[final_df['symbol']=='SOL','value'] / final_df.loc[final_df['symbol']=='SOL','balance'])
symbolMap = {'So11111111111111111111111111111111111111112':'SOL','Aq8Gocyvyyi8xk5EYxd6viUfVmVvs9T9R6mZFzZFpump':'HYPER'}
priceMap = {'SOL':sol_price,'HYPER':hyper_price}
lp_df0 = get_lp_positions()
lp_df0['symbol'] = lp_df0['symbol'].map(symbolMap)
lp_df0['value'] = lp_df0['balance'] * lp_df0['symbol'].map(priceMap)
lp_df0 = lp_df0.groupby('symbol').agg({'balance':'sum','value':'sum'}).reset_index()
#lp_df = pd.DataFrame([{"symbol":"HYPER","balance":2_800_000,"value":2_800_000*hyper_price}])
total_balances = pd.concat([final_df,lp_df0]).groupby('symbol').agg({"balance":"sum","value":"sum"}).reset_index()
pnl = total_balances['value'].sum() - starting_df['value'].sum()
pnl = f"{pnl:,.2f}"
return starting_df,lp_df0,final_df,total_balances,pnl
with gr.Blocks() as demo:
gr.Markdown("## HYPER Balances ")
starting = gr.DataFrame(label= "Starting Balances ")
lp_real = gr.DataFrame(label="LP Position Balances")
final = gr.DataFrame(label = "Wallet Balances")
total_balances = gr.DataFrame(label="Total")
pnl = gr.Textbox(label="PnL ($) (AUM)")
demo.load(
fn= get_all_dfs,
inputs = [],
outputs=[starting,lp_real,final,total_balances,pnl]
)
demo.launch(debug=True, share=True)