Spaces:
Running
Running
Create deep_diver_engine.py
Browse files
src/services/deep_diver_engine.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import time
|
| 3 |
+
|
| 4 |
+
# Reusing the exact Stealth Headers from spot_engine.py
|
| 5 |
+
STEALTH_HEADERS = {
|
| 6 |
+
"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",
|
| 7 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
| 8 |
+
"Accept-Language": "en-US,en;q=0.9",
|
| 9 |
+
"Accept-Encoding": "gzip, deflate, br",
|
| 10 |
+
"Connection": "keep-alive",
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
def calculate_deep_dive(ticker: str, user_keys: dict):
|
| 14 |
+
"""
|
| 15 |
+
Quantitative Engine for Single-Ticker Analysis.
|
| 16 |
+
Fetches real-time depth and volume metrics from CoinGecko.
|
| 17 |
+
"""
|
| 18 |
+
cg_key = user_keys.get("COINGECKO_API_KEY", "")
|
| 19 |
+
|
| 20 |
+
# Matching the API key logic from spot_engine.py
|
| 21 |
+
is_pro = "pro-api" in cg_key or (cg_key and len(cg_key) > 25)
|
| 22 |
+
base_url = "https://pro-api.coingecko.com/api/v3" if is_pro else "https://api.coingecko.com/api/v3"
|
| 23 |
+
|
| 24 |
+
headers = STEALTH_HEADERS.copy()
|
| 25 |
+
if cg_key:
|
| 26 |
+
# Use demo key header if not Pro
|
| 27 |
+
key_header = "x-cg-pro-api-key" if is_pro else "x-cg-demo-api-key"
|
| 28 |
+
headers[key_header] = cg_key
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
# 1. Resolve Ticker to CoinGecko ID
|
| 32 |
+
search_url = f"{base_url}/search?query={ticker}"
|
| 33 |
+
search_res = requests.get(search_url, headers=headers, timeout=10).json()
|
| 34 |
+
|
| 35 |
+
if not search_res.get('coins'):
|
| 36 |
+
return {"status": "error", "message": f"Ticker '{ticker}' not found."}
|
| 37 |
+
|
| 38 |
+
coin_id = search_res['coins'][0]['id']
|
| 39 |
+
|
| 40 |
+
# 2. Fetch Deep Market Data with Depth
|
| 41 |
+
# We explicitly request 'tickers' to get the +/- 2% depth data
|
| 42 |
+
market_url = f"{base_url}/coins/{coin_id}?localization=false&tickers=true&market_data=true"
|
| 43 |
+
res = requests.get(market_url, headers=headers, timeout=15).json()
|
| 44 |
+
|
| 45 |
+
mkt = res.get('market_data', {})
|
| 46 |
+
tickers_list = res.get('tickers', [])
|
| 47 |
+
|
| 48 |
+
# --- QUANTITATIVE MATH ---
|
| 49 |
+
|
| 50 |
+
# VTPC: Volatility to Price Change (Abs % Vol Change / Abs % Price Change)
|
| 51 |
+
vol_change_pct = abs(mkt.get('total_volume_change_24h_percentage', 0))
|
| 52 |
+
price_change_pct = abs(mkt.get('price_change_percentage_24h', 0))
|
| 53 |
+
vtpc = round(vol_change_pct / price_change_pct, 2) if price_change_pct != 0 else 0
|
| 54 |
+
|
| 55 |
+
# VTAPC: Volume to Absorbed Price Change
|
| 56 |
+
# (Total Volume / Price Change %) = USD required for 1% move
|
| 57 |
+
total_vol = mkt.get('total_volume', {}).get('usd', 0)
|
| 58 |
+
vtapc = round(total_vol / price_change_pct, 0) if price_change_pct != 0 else 0
|
| 59 |
+
|
| 60 |
+
# VTMR: Volume to Market Cap Ratio
|
| 61 |
+
mcap = mkt.get('market_cap', {}).get('usd', 0)
|
| 62 |
+
vtmr = round(total_vol / mcap, 4) if mcap != 0 else 0
|
| 63 |
+
|
| 64 |
+
# --- REAL LIQUIDITY CALCULATION (LTMR) ---
|
| 65 |
+
# Summing the 2% depth (cost to move) across top 5 tickers
|
| 66 |
+
total_depth_2pct = 0
|
| 67 |
+
for t in tickers_list[:10]: # Look at top 10 liquid pairs
|
| 68 |
+
up = t.get('cost_to_move_up_usd', 0)
|
| 69 |
+
down = t.get('cost_to_move_down_usd', 0)
|
| 70 |
+
total_depth_2pct += (up + down)
|
| 71 |
+
|
| 72 |
+
# LTMR: Liquidity to Market Cap Ratio
|
| 73 |
+
ltmr = round(total_depth_2pct / mcap, 6) if mcap != 0 else 0
|
| 74 |
+
|
| 75 |
+
return {
|
| 76 |
+
"status": "success",
|
| 77 |
+
"ticker": ticker.upper(),
|
| 78 |
+
"vitals": {
|
| 79 |
+
"price": f"${mkt.get('current_price', {}).get('usd', 0):,.2f}",
|
| 80 |
+
"mcap": f"${mcap:,.0f}",
|
| 81 |
+
"vol24h": f"${total_vol:,.0f}"
|
| 82 |
+
},
|
| 83 |
+
"ratios": {
|
| 84 |
+
"vtpc": f"{vtpc}x",
|
| 85 |
+
"vtmr": vtmr,
|
| 86 |
+
"vtapc": f"${vtapc:,.0f}",
|
| 87 |
+
"ltmr": f"{ltmr:.5f}"
|
| 88 |
+
},
|
| 89 |
+
"velocity": {
|
| 90 |
+
"h24": f"{mkt.get('price_change_percentage_24h', 0):+.2f}%",
|
| 91 |
+
"w1": f"{mkt.get('price_change_percentage_7d', 0):+.2f}%",
|
| 92 |
+
"m1": f"{mkt.get('price_change_percentage_30d', 0):+.2f}%"
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
except Exception as e:
|
| 97 |
+
return {"status": "error", "message": f"Engine Error: {str(e)}"}
|