File size: 4,792 Bytes
acbb457
e3d6d2c
22b5453
d02d32c
 
 
22b5453
acbb457
22b5453
e3d6d2c
 
 
 
 
acbb457
d02d32c
e3d6d2c
22b5453
e3d6d2c
 
 
 
 
 
4151818
e3d6d2c
22b5453
e3d6d2c
d02d32c
 
 
942233d
 
d02d32c
942233d
 
d02d32c
22b5453
d02d32c
e3d6d2c
 
37551cd
22b5453
e3d6d2c
 
 
acbb457
22b5453
d02d32c
 
 
 
 
 
 
 
 
 
 
 
e3d6d2c
 
 
 
d02d32c
226f4cb
22b5453
e3d6d2c
d02d32c
22b5453
d02d32c
22b5453
4151818
e3d6d2c
 
d02d32c
22b5453
e3d6d2c
4151818
22b5453
d02d32c
e3d6d2c
93e57cf
d02d32c
 
 
 
 
 
 
 
e3d6d2c
 
 
d02d32c
e3d6d2c
 
 
 
 
 
 
 
 
22b5453
d02d32c
e3d6d2c
 
 
 
 
 
d02d32c
 
e3d6d2c
d02d32c
 
 
 
 
 
e3d6d2c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import requests
import time
from decimal import Decimal, ROUND_HALF_UP

# --- Global Cache --- #
CACHE = {}
CACHE_DURATION = 120  # TTL set to 2 minutes

# Headers to mimic browser behavior and avoid basic scraping blocks
STEALTH_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
    "Connection": "keep-alive",
}

# --- Helper --- #
def format_compact(num):
    """Converts large numbers into human-readable strings (e.g., 1.5M, 2B)."""
    if num is None or num == 0: return "0"
    for unit in ['', 'K', 'M', 'B', 'T']:
        if abs(num) < 1000.0:
            return f"{num:,.2f}{unit}".replace(".00", "")
        num /= 1000.0
    return f"{num:,.2f}P"

def calculate_deep_dive(coin_id: str, user_keys: dict):
    """Fetches market data from CoinGecko, calculates ratios, and returns a structured payload."""
    coin_id = coin_id.strip().lower()
    
    # --- Cache Check --- #
    now = time.time()
    global CACHE
    CACHE = {k: v for k, v in CACHE.items() if now < v['expires']}
    if coin_id in CACHE:
       print(f"    ⚡ Serving {coin_id} from cache")
       return CACHE[coin_id]['data']

    # --- API Request Setup --- #
    cg_key = str(user_keys.get("COINGECKO_API_KEY", "")).strip()
    base_url = "https://api.coingecko.com/api/v3"
    headers = STEALTH_HEADERS.copy()
    
    # Handle both Demo and Pro API key formats
    if cg_key and cg_key != "CONFIG_REQUIRED_CG":
        headers["x-cg-demo-api-key" if cg_key.startswith("CG-") else "x-cg-pro-api-key"] = cg_key

    try:
        # Request only necessary fields to reduce payload size and latency
        url = (f"{base_url}/coins/{coin_id}?"
               "localization=false&"
               "tickers=false&"  
               "market_data=true&"
               "community_data=false&"
               "developer_data=false&"
               "sparkline=false&"
               "price_change_percentage=1h") 

        r = requests.get(url, headers=headers, timeout=15)
        
        if r.status_code == 429: return {"status": "error", "message": "Rate Limit Hit. Please wait."}
        if r.status_code != 200: return {"status": "error", "message": f"API {r.status_code}"}
            
        res = r.json()
        mkt = res.get('market_data', {})
        symbol = res.get('symbol', '').upper()
        
        # --- Logic Extraction ---
        mcap = mkt.get('market_cap', {}).get('usd', 0) or 0
        vol = mkt.get('total_volume', {}).get('usd', 0) or 0 
        
        p_ch_24h = mkt.get('price_change_percentage_24h', 0) or 0
        p_ch_1h = mkt.get('price_change_percentage_1h_in_currency', {}).get('usd', 0) or 0 
        
        d_vol = Decimal(str(vol))
        d_mcap = Decimal(str(mcap))
        
        # Volume-to-Market-Cap Ratio (VTMR)
        vtmr_val = (d_vol / d_mcap).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) if d_mcap > 0 else 0
        
        # Volume-to-Price-Change (VTPC) - Proxy for liquidity absorption
        vtpc_val = vol / abs(p_ch_24h) if p_ch_24h != 0 else 0
        current_price = mkt.get('current_price', {}).get('usd', 0)

        # --- Link Construction --- #
        links = {
            "cg": f"https://www.coingecko.com/en/coins/{coin_id}",
            "tv": f"https://www.tradingview.com/chart/?symbol={symbol}USDT"
        }

        # --- Payload Construction --- #
        data_payload = {
            "status": "success",
            "vitals": {
                "name": res.get('name', 'Unknown'),
                "symbol": symbol,
                "price": f"${current_price:,.8f}" if current_price < 1 else f"${current_price:,.2f}",
                "mcap": f"${format_compact(mcap)}",
                "vol24h": f"${format_compact(vol)}"
            },
            "ratios": {
                "vtmr": f"{vtmr_val}x",
                "vtpc": f"${format_compact(vtpc_val)}"
            },
            "velocity": {
                "h1": f"{p_ch_1h:+.2f}%",
                "h24": f"{p_ch_24h:+.2f}%",
                "d7": f"{(mkt.get('price_change_percentage_7d') or 0):+.2f}%",
                "m1": f"{(mkt.get('price_change_percentage_30d') or 0):+.2f}%",
                "y1": f"{(mkt.get('price_change_percentage_1y') or 0):+.2f}%"
            },
            "supply": {
                "total": format_compact(mkt.get('total_supply', 0))
            },
            "links": links 
        }

        # --- Save to Cache --- #
        CACHE[coin_id] = {'data': data_payload, 'expires': now + CACHE_DURATION}
        
        return data_payload

    except Exception as e:
        return {"status": "error", "message": str(e)}