merge: chore/cleanup-remove-bloat-and-secrets into main
This commit is contained in:
commit
bde2f3a97d
1173 changed files with 437609 additions and 0 deletions
0
app/analyzer/__init__.py
Normal file
0
app/analyzer/__init__.py
Normal file
65
app/analyzer/multi_chain.py
Normal file
65
app/analyzer/multi_chain.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
"""
|
||||
Multi-chain portfolio scanner.
|
||||
Scans the same wallet address across all supported chains.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from app.adapters.binance_web3 import CHAIN_IDS, CHAIN_NAMES, get_wallet_holdings
|
||||
from app.analyzer.portfolio import build_portfolio
|
||||
|
||||
SCAN_DELAY = 0.3 # seconds between chain requests
|
||||
|
||||
|
||||
def scan_all_chains(address: str) -> dict:
|
||||
"""
|
||||
Scan a wallet across all supported chains.
|
||||
|
||||
Returns:
|
||||
{
|
||||
"chains": {
|
||||
"BSC": {"total_value": float, "token_count": int, "change_24h_pct": float},
|
||||
...
|
||||
},
|
||||
"grand_total": float,
|
||||
"grand_change_24h_usd": float,
|
||||
"grand_change_24h_pct": float,
|
||||
"errors": [...],
|
||||
}
|
||||
"""
|
||||
result = {
|
||||
"chains": {},
|
||||
"grand_total": 0.0,
|
||||
"grand_change_24h_usd": 0.0,
|
||||
"grand_change_24h_pct": 0.0,
|
||||
"errors": [],
|
||||
}
|
||||
|
||||
grand_yesterday = 0.0
|
||||
|
||||
for chain_key, chain_id in CHAIN_IDS.items():
|
||||
try:
|
||||
holdings = get_wallet_holdings(address, chain_id)
|
||||
portfolio = build_portfolio(holdings)
|
||||
|
||||
if portfolio["total_value"] > 0:
|
||||
chain_name = CHAIN_NAMES.get(chain_id, chain_key.upper())
|
||||
result["chains"][chain_name] = {
|
||||
"total_value": portfolio["total_value"],
|
||||
"token_count": portfolio["token_count"],
|
||||
"change_24h_usd": portfolio["change_24h_usd"],
|
||||
"change_24h_pct": portfolio["change_24h_pct"],
|
||||
}
|
||||
result["grand_total"] += portfolio["total_value"]
|
||||
result["grand_change_24h_usd"] += portfolio["change_24h_usd"]
|
||||
grand_yesterday += portfolio["total_value"] - portfolio["change_24h_usd"]
|
||||
|
||||
time.sleep(SCAN_DELAY)
|
||||
|
||||
except Exception as e:
|
||||
result["errors"].append(f"{chain_key.upper()}: {e}")
|
||||
|
||||
if grand_yesterday > 0:
|
||||
result["grand_change_24h_pct"] = (result["grand_change_24h_usd"] / grand_yesterday) * 100
|
||||
|
||||
return result
|
||||
47
app/analyzer/pnl_calculator.py
Normal file
47
app/analyzer/pnl_calculator.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""
|
||||
Token-level PnL calculator.
|
||||
Calculates profit/loss when user provides their average buy price.
|
||||
"""
|
||||
|
||||
|
||||
def calculate_token_pnl(token: dict, avg_cost: float) -> dict:
|
||||
"""
|
||||
Calculate PnL for a specific token given user's average buy price.
|
||||
|
||||
Args:
|
||||
token: Enriched token dict from build_portfolio()
|
||||
avg_cost: User's average buy price in USD
|
||||
|
||||
Returns:
|
||||
{
|
||||
"symbol": str,
|
||||
"qty": float,
|
||||
"avg_cost": float,
|
||||
"current_price": float,
|
||||
"cost_basis": float,
|
||||
"current_value": float,
|
||||
"pnl_usd": float,
|
||||
"pnl_pct": float,
|
||||
"is_profit": bool,
|
||||
}
|
||||
"""
|
||||
qty = token.get("qty", 0)
|
||||
current_price = token.get("price", 0)
|
||||
|
||||
cost_basis = avg_cost * qty
|
||||
current_value = current_price * qty
|
||||
pnl_usd = current_value - cost_basis
|
||||
pnl_pct = (pnl_usd / cost_basis * 100) if cost_basis > 0 else 0.0
|
||||
|
||||
return {
|
||||
"symbol": token.get("symbol", "?"),
|
||||
"name": token.get("name", "?"),
|
||||
"qty": qty,
|
||||
"avg_cost": avg_cost,
|
||||
"current_price": current_price,
|
||||
"cost_basis": cost_basis,
|
||||
"current_value": current_value,
|
||||
"pnl_usd": pnl_usd,
|
||||
"pnl_pct": pnl_pct,
|
||||
"is_profit": pnl_usd >= 0,
|
||||
}
|
||||
65
app/analyzer/portfolio.py
Normal file
65
app/analyzer/portfolio.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
"""
|
||||
Portfolio aggregator: calculates total value and 24h change from holdings.
|
||||
"""
|
||||
|
||||
|
||||
def build_portfolio(holdings: list) -> dict:
|
||||
"""
|
||||
Aggregate token holdings into a portfolio summary.
|
||||
|
||||
Args:
|
||||
holdings: Raw list from get_wallet_holdings()
|
||||
|
||||
Returns:
|
||||
{
|
||||
"tokens": [...enriched tokens with usd_value, qty],
|
||||
"total_value": float,
|
||||
"change_24h_usd": float,
|
||||
"change_24h_pct": float,
|
||||
"token_count": int,
|
||||
}
|
||||
"""
|
||||
tokens = []
|
||||
total_value = 0.0
|
||||
total_value_yesterday = 0.0
|
||||
|
||||
for item in holdings:
|
||||
price = float(item.get("price") or 0)
|
||||
qty_raw = item.get("remainQty") or "0"
|
||||
qty = float(qty_raw) if qty_raw else 0.0
|
||||
change_24h = float(item.get("percentChange24h") or 0)
|
||||
|
||||
usd_value = price * qty
|
||||
if usd_value < 0.01:
|
||||
continue # skip dust
|
||||
|
||||
usd_value_yesterday = usd_value / (1 + change_24h / 100) if change_24h != -100 else usd_value
|
||||
|
||||
total_value += usd_value
|
||||
total_value_yesterday += usd_value_yesterday
|
||||
|
||||
tokens.append(
|
||||
{
|
||||
"symbol": item.get("symbol", "?"),
|
||||
"name": item.get("name", "?"),
|
||||
"contractAddress": item.get("contractAddress", ""),
|
||||
"qty": qty,
|
||||
"price": price,
|
||||
"usd_value": usd_value,
|
||||
"change_24h_pct": change_24h,
|
||||
"risk_level": item.get("riskLevel", "UNKNOWN"),
|
||||
}
|
||||
)
|
||||
|
||||
tokens.sort(key=lambda t: t["usd_value"], reverse=True)
|
||||
|
||||
change_24h_usd = total_value - total_value_yesterday
|
||||
change_24h_pct = (change_24h_usd / total_value_yesterday * 100) if total_value_yesterday > 0 else 0.0
|
||||
|
||||
return {
|
||||
"tokens": tokens,
|
||||
"total_value": total_value,
|
||||
"change_24h_usd": change_24h_usd,
|
||||
"change_24h_pct": change_24h_pct,
|
||||
"token_count": len(tokens),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue