Complete Funding Rate Arb: real API data for live + paper

New module: strategies/funding_arb.py
  - get_funding_rates(): fetches predicted funding from Hyperliquid
    Uses metaAndAssetCtxs (primary) + predictedFundings (fallback)
  - funding_arb_signal(): generates entry/exit signals
    Entry: |annual_rate| > threshold (3% testnet, 5% mainnet)
    Exit:  rate drops below 2% or flips sign
  - 30s cache to avoid rate-limiting

Live node:
  - Replaced proxy-based funding (20-period return) with real API
  - Calls get_funding_rates(use_testnet=True) every compute_signals()
  - Lowered threshold to 3% APR for testnet (lower liquidity)

Paper trader:
  - Replaced manual funding calc with unified funding_arb_signal()
  - Proper entry/exit logic with position tracking
  - 5% APR threshold for mainnet data

Current rates: BTC +0.87% APR, ETH -0.82% APR
(Arb fires when rates exceed threshold during volatility)
This commit is contained in:
ramseshk
2026-08-05 07:09:29 +00:00
parent 84efb4014a
commit 70d43fefe0
3 changed files with 191 additions and 29 deletions
+31 -18
View File
@@ -236,27 +236,40 @@ def compute_signals():
elif up <= 3:
STRATEGIES["Iceberg Detection"]["signals"].append({"time":time.time(),"signal":"SELL","strength":1-up/10})
# Funding Arb — use actual mainnet funding rate
if funding_rates and isinstance(funding_rates[-1], dict):
btc_fr = funding_rates[-1].get("BTC", 0)
# Annualized: funding every 8h → 3× daily → 1095× yearly
annual_fr = abs(btc_fr) * 365 * 3 if btc_fr else 0
# Log funding rate periodically
import random as _random_fr
if _random_fr.random() < 0.02:
# Funding Rate Arb — unified module with real API data
try:
from strategies.funding_arb import funding_arb_signal
sig_result = funding_arb_signal(coin="BTC", apr_threshold=0.05, apr_exit=0.02,
current_position=STRATEGIES["Funding Rate Arb"]["position"])
if sig_result["signal"] != 0:
STRATEGIES["Funding Rate Arb"]["signals"].append({
"time": time.time(),
"signal": "SELL" if sig_result["signal"] < 0 else "BUY",
"strength": min(1.0, abs(sig_result["annual_apr"]) * 10),
"reason": sig_result["reason"]
})
# Log periodically
if not hasattr(globals().get("_funding_log_tick", None), "__int__"):
globals()["_funding_log_tick"] = 0
if globals()["_funding_log_tick"] % 30 == 0:
import logging
logging.getLogger("ftdt-paper").info(
"{} Funding rate: {:.6f}% 8h | {:.2f}% APR | signal={}".format(
"[Fund]", btc_fr*100, annual_fr*100,
"SELL" if btc_fr > 0 else "BUY" if btc_fr < 0 else "NONE"
f"[Fund] APR={sig_result['annual_apr']*100:.2f}% | "
f"8h={sig_result['rate_8h']*100:.6f}% | "
f"signal={sig_result['signal']}"
)
globals()["_funding_log_tick"] = globals().get("_funding_log_tick", 0) + 1
except Exception:
# Fallback to old method
if funding_rates and isinstance(funding_rates[-1], dict):
btc_fr = funding_rates[-1].get("BTC", 0)
annual_fr = abs(btc_fr) * 365 * 3 if btc_fr else 0
if annual_fr > 0.05:
STRATEGIES["Funding Rate Arb"]["signals"].append(
{"time":time.time(),"signal":"SELL" if btc_fr > 0 else "BUY",
"strength": min(0.6, annual_fr * 50),
"reason": "funding_{:.1f}pct_apr".format(annual_fr*100)}
)
)
if annual_fr > 0.05: # >5% APR (production threshold)
STRATEGIES["Funding Rate Arb"]["signals"].append(
{"time":time.time(),"signal":"SELL" if btc_fr > 0 else "BUY",
"strength": min(0.6, annual_fr * 50),
"reason": "funding_{:.1f}pct_apr".format(annual_fr*100)}
)
# Pairs: BTC/ETH ratio Z-score
if len(btc_prices) >= 20 and len(eth_prices) >= 20: