perp-arbitrage/funding_rates.py

113 lines
4.0 KiB
Python

import requests
import json
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed
# === GET SYMBOLS FROM EXCHANGES ===
def get_binance_symbols():
url = "https://fapi.binance.com/fapi/v1/exchangeInfo"
data = requests.get(url).json()
return [s["symbol"].replace("USDT", "/USDT") for s in data["symbols"] if s["quoteAsset"] == "USDT"]
def get_bybit_symbols():
url = "https://api.bybit.com/v5/market/instruments-info"
params = {"category": "linear"}
r = requests.get(url, params=params)
data = r.json()
return [
s["symbol"].replace("USDT", "/USDT")
for s in data.get("result", {}).get("list", [])
if "USDT" in s["symbol"]
]
def get_okx_symbols():
url = "https://www.okx.com/api/v5/public/instruments"
params = {"instType": "SWAP"}
data = requests.get(url, params=params).json()
return [s["instId"].replace("-USDT-SWAP", "/USDT") for s in data.get("data", []) if s["instId"].endswith("USDT-SWAP")]
# === LOAD SYMBOLS ===
bybit_symbols = get_bybit_symbols()
binance_symbols = get_binance_symbols()
okx_symbols = get_okx_symbols()
symbols = sorted(list(set(bybit_symbols + binance_symbols + okx_symbols)))
# === FUNDING FUNCTIONS ===
def normalize_symbol(symbol):
return symbol.replace("/", "")
def get_binance_funding(symbol):
try:
url = "https://fapi.binance.com/fapi/v1/premiumIndex"
params = {"symbol": normalize_symbol(symbol)}
data = requests.get(url, params=params, timeout=3).json()
return "binance", symbol, float(data.get("lastFundingRate", 0))
except Exception as e:
print(f"[Binance] {symbol} funding error: {e}")
return "binance", symbol, 0.0
def get_bybit_funding(symbol):
try:
url = "https://api.bybit.com/v5/market/tickers"
params = {"category": "linear"}
data = requests.get(url, params=params, timeout=3).json()
for item in data.get("result", {}).get("list", []):
if item.get("symbol") == normalize_symbol(symbol):
return "bybit", symbol, float(item.get("fundingRate", 0))
except Exception as e:
print(f"[Bybit] {symbol} funding error: {e}")
return "bybit", symbol, 0.0
def get_okx_funding(symbol):
try:
inst_id = symbol.replace("/", "-") + "-SWAP"
url = "https://www.okx.com/api/v5/public/funding-rate"
params = {"instId": inst_id}
data = requests.get(url, params=params, timeout=3).json()
return "okx", symbol, float(data.get("data", [{}])[0].get("fundingRate", 0))
except Exception as e:
print(f"[OKX] {symbol} funding error: {e}")
return "okx", symbol, 0.0
def get_hyperliquid_funding(symbol):
try:
symbol_usd = symbol.replace("USDT", "USD").replace("/", "")
url = "https://api.hyperliquid.xyz/info"
headers = {"Content-Type": "application/json"}
payload = {"type": "metaAndAssetCtxs"}
response = requests.post(url, headers=headers, json=payload)
data = response.json()
assets = data[0]["universe"]
asset_data = data[1]
for i in range(len(assets)):
name = assets[i].get("name", "").upper()
if name == symbol_usd.replace("USD", "").upper():
return "hyperliquid", symbol, float(asset_data[i].get("funding", 0))
except Exception as e:
print(f"[Hyperliquid Funding Error] {symbol}: {e}")
return "hyperliquid", symbol, 0.0
funding_funcs = [
get_binance_funding,
get_bybit_funding,
get_okx_funding,
get_hyperliquid_funding # ✅ incluir aqui
]
funding_data = defaultdict(dict)
with ThreadPoolExecutor(max_workers=20) as executor:
tasks = [executor.submit(func, symbol) for symbol in symbols for func in funding_funcs]
for future in as_completed(tasks):
exchange, symbol, rate = future.result()
funding_data[symbol][exchange] = rate
# Save to file
with open("funding_cache.json", "w") as f:
json.dump(funding_data, f, indent=2)
print("[✓] funding_cache.json atualizado com sucesso")