feat(kpi): add kpi_tracker module for performance logging
This commit is contained in:
133
tools/kpi_tracker.py
Normal file
133
tools/kpi_tracker.py
Normal file
@ -0,0 +1,133 @@
|
||||
import os
|
||||
import csv
|
||||
import time
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
from typing import Dict, Optional
|
||||
|
||||
# Setup Logger
|
||||
logger = logging.getLogger("KPI_TRACKER")
|
||||
logger.setLevel(logging.INFO)
|
||||
# Basic handler if not already handled by parent
|
||||
if not logger.handlers:
|
||||
ch = logging.StreamHandler()
|
||||
formatter = logging.Formatter('%(asctime)s - KPI - %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
||||
KPI_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs', 'kpi_history.csv')
|
||||
|
||||
def initialize_kpi_csv():
|
||||
"""Creates the CSV with headers if it doesn't exist."""
|
||||
if not os.path.exists(os.path.dirname(KPI_FILE)):
|
||||
os.makedirs(os.path.dirname(KPI_FILE))
|
||||
|
||||
if not os.path.exists(KPI_FILE):
|
||||
with open(KPI_FILE, 'w', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow([
|
||||
"Timestamp",
|
||||
"Date",
|
||||
"NAV_Total_USD",
|
||||
"Benchmark_HODL_USD",
|
||||
"Alpha_USD",
|
||||
"Uniswap_Val_USD",
|
||||
"Uniswap_Fees_Claimed_USD",
|
||||
"Uniswap_Fees_Unclaimed_USD",
|
||||
"Hedge_Equity_USD",
|
||||
"Hedge_PnL_Realized_USD",
|
||||
"Hedge_Fees_Paid_USD",
|
||||
"ETH_Price",
|
||||
"Fee_Coverage_Ratio"
|
||||
])
|
||||
|
||||
def calculate_hodl_benchmark(initial_eth: Decimal, initial_usdc: Decimal, current_eth_price: Decimal) -> Decimal:
|
||||
"""Calculates value if assets were just held."""
|
||||
return (initial_eth * current_eth_price) + initial_usdc
|
||||
|
||||
def log_kpi_snapshot(
|
||||
snapshot_data: Dict[str, float]
|
||||
):
|
||||
"""
|
||||
Logs a KPI snapshot to CSV.
|
||||
Expected keys in snapshot_data:
|
||||
- initial_eth, initial_usdc
|
||||
- current_eth_price
|
||||
- uniswap_pos_value_usd
|
||||
- uniswap_fees_claimed_usd
|
||||
- uniswap_fees_unclaimed_usd
|
||||
- hedge_equity_usd
|
||||
- hedge_pnl_realized_usd
|
||||
- hedge_fees_paid_usd
|
||||
- wallet_eth_bal, wallet_usdc_bal (Optional, for full NAV)
|
||||
"""
|
||||
try:
|
||||
initialize_kpi_csv()
|
||||
|
||||
# Convert all inputs to Decimal for precision
|
||||
price = Decimal(str(snapshot_data.get('current_eth_price', 0)))
|
||||
|
||||
# 1. Benchmark (HODL)
|
||||
init_eth = Decimal(str(snapshot_data.get('initial_eth', 0)))
|
||||
init_usdc = Decimal(str(snapshot_data.get('initial_usdc', 0)))
|
||||
benchmark_val = calculate_hodl_benchmark(init_eth, init_usdc, price)
|
||||
|
||||
# 2. Strategy NAV (Net Asset Value)
|
||||
# NAV = Uni Pos + Uni Fees (Claimed+Unclaimed) + Hedge Equity + (Wallet Surplus - Initial Wallet Surplus?)
|
||||
# For simplicity, we focus on the Strategy PnL components:
|
||||
# Strategy Val = (Current Uni Pos) + (Claimed Fees) + (Unclaimed Fees) + (Hedge PnL Realized) + (Hedge Unrealized?)
|
||||
# Note: Hedge Equity usually includes margin. We strictly want "Value Generated".
|
||||
|
||||
uni_val = Decimal(str(snapshot_data.get('uniswap_pos_value_usd', 0)))
|
||||
uni_fees_claimed = Decimal(str(snapshot_data.get('uniswap_fees_claimed_usd', 0)))
|
||||
uni_fees_unclaimed = Decimal(str(snapshot_data.get('uniswap_fees_unclaimed_usd', 0)))
|
||||
|
||||
# Hedge PnL (Realized + Unrealized) is better than Equity for PnL tracking,
|
||||
# but Equity represents actual redeemable cash. Let's use Equity if provided, or PnL components.
|
||||
hedge_equity = Decimal(str(snapshot_data.get('hedge_equity_usd', 0)))
|
||||
hedge_fees = Decimal(str(snapshot_data.get('hedge_fees_paid_usd', 0)))
|
||||
|
||||
# Simplified NAV for Strategy Comparison:
|
||||
# We assume 'hedge_equity' is the Liquidation Value of the hedge account.
|
||||
# But if we want strictly "Strategy Performance", we usually do:
|
||||
# Current Value = Uni_Val + Unclaimed + Hedge_Equity
|
||||
# (Assuming Hedge_Equity started at 0 or we track delta? No, usually Hedge Account has deposit).
|
||||
|
||||
# Let's define NAV as Total Current Liquidation Value of Strategy Components
|
||||
current_nav = uni_val + uni_fees_unclaimed + uni_fees_claimed + hedge_equity
|
||||
|
||||
# Alpha
|
||||
alpha = current_nav - benchmark_val
|
||||
|
||||
# Coverage Ratio
|
||||
total_hedge_cost = abs(hedge_fees) # + funding if available
|
||||
total_uni_earnings = uni_fees_claimed + uni_fees_unclaimed
|
||||
|
||||
if total_hedge_cost > 0:
|
||||
coverage_ratio = total_uni_earnings / total_hedge_cost
|
||||
else:
|
||||
coverage_ratio = Decimal("999.0") # Infinite/Good
|
||||
|
||||
# Write
|
||||
with open(KPI_FILE, 'a', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow([
|
||||
int(time.time()),
|
||||
time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
f"{current_nav:.2f}",
|
||||
f"{benchmark_val:.2f}",
|
||||
f"{alpha:.2f}",
|
||||
f"{uni_val:.2f}",
|
||||
f"{uni_fees_claimed:.2f}",
|
||||
f"{uni_fees_unclaimed:.2f}",
|
||||
f"{hedge_equity:.2f}",
|
||||
f"{snapshot_data.get('hedge_pnl_realized_usd', 0):.2f}",
|
||||
f"{hedge_fees:.2f}",
|
||||
f"{price:.2f}",
|
||||
f"{coverage_ratio:.2f}"
|
||||
])
|
||||
|
||||
logger.info(f"📊 KPI Logged | NAV: ${current_nav:.2f} | Benchmark: ${benchmark_val:.2f} | Alpha: ${alpha:.2f}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to log KPI: {e}")
|
||||
Reference in New Issue
Block a user