Enviar arquivos para "/"

main
lucas 2025-03-26 12:24:51 +00:00
parent e7c36f5018
commit 74e77f522d
5 changed files with 3621 additions and 0 deletions

3406
funding_cache.json Normal file

File diff suppressed because it is too large Load Diff

112
funding_rates.py Normal file
View File

@ -0,0 +1,112 @@
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")

13
logger.py Normal file
View File

@ -0,0 +1,13 @@
from datetime import datetime
def log_opportunity(opp: dict):
timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
message = (
f"[{timestamp}] {opp['symbol']} | BUY on {opp['buy_exchange']} @ {opp['buy_price']} "
f"-> SELL on {opp['sell_exchange']} @ {opp['sell_price']} | "
f"Net Spread: {opp['spread_percent']:.2f}% | Size: {opp['size']:.4f} "
f"| Funding BUY: {opp['buy_funding']:.5f}, SELL: {opp['sell_funding']:.5f}"
)
print(message)
#with open("opportunities.txt", "a") as f:
#f.write(message + "\n")

27
main.py Normal file
View File

@ -0,0 +1,27 @@
import asyncio
from exchanges.binance import BinancePerpStream
from exchanges.okx import OKXPerpStream
from exchanges.bybit import BybitPerpStream
from exchanges.hyperliquid import HyperliquidPerpStream
from logger import log_opportunity
from spread_calculator import check_arbitrage_opportunities
latest_ticks = {}
async def consumer(exchange, stream):
async for tick in stream():
latest_ticks[(tick['exchange'], tick['symbol'])] = tick
opportunities = check_arbitrage_opportunities(tick, latest_ticks)
for opp in opportunities:
log_opportunity(opp)
async def main():
tasks = [
consumer('binance', BinancePerpStream(["USDT"]).stream),
consumer('okx', OKXPerpStream(["USDT"]).stream),
consumer('bybit', BybitPerpStream(["USDT"]).stream)
]
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())

63
spread_calculator.py Normal file
View File

@ -0,0 +1,63 @@
import threading
import time
import json
from exchanges.binance import BinancePerpStream
from exchanges.okx import OKXPerpStream
from exchanges.bybit import BybitPerpStream
FEE = 0.0005 # taker fee
FUNDING_WEIGHT = 1 # pondera funding rate (ex: por 8h)
MIN_SPREAD = 0.4 # mínimo spread líquido para registrar
# Lista de exchanges que você quer considerar na arbitragem
ENABLED_EXCHANGES = {"bybit", "okx", "binance"}
# Carrega funding de arquivo externo salvo periodicamente
with open("funding_cache.json", "r") as f:
funding_cache = json.load(f)
def check_arbitrage_opportunities(new_tick, all_ticks):
opportunities = []
symbol = new_tick['symbol']
if new_tick['exchange'].lower() not in ENABLED_EXCHANGES:
return opportunities
for (ex_name, ex_symbol), tick in all_ticks.items():
if ex_symbol != symbol or ex_name == new_tick['exchange']:
continue
for buyer, seller in [(new_tick, tick), (tick, new_tick)]:
if buyer['exchange'].lower() not in ENABLED_EXCHANGES or seller['exchange'].lower() not in ENABLED_EXCHANGES:
continue
buy_price = buyer['ask'] * (1 + FEE)
sell_price = seller['bid'] * (1 - FEE)
if sell_price <= buy_price:
continue
spread = sell_price - buy_price
spread_percent = (spread / buy_price) * 100
size = min(buyer['ask_size'], seller['bid_size'])
buy_funding = funding_cache.get(symbol, {}).get(buyer['exchange'], 0.0) * 100
sell_funding = funding_cache.get(symbol, {}).get(seller['exchange'], 0.0) * 100
funding_carry = sell_funding - buy_funding
net_spread_percent = spread_percent + FUNDING_WEIGHT * funding_carry
if net_spread_percent > MIN_SPREAD:
opportunities.append({
'symbol': symbol,
'buy_exchange': buyer['exchange'],
'buy_price': buyer['ask'],
'sell_exchange': seller['exchange'],
'sell_price': seller['bid'],
'spread_percent': net_spread_percent,
'raw_spread_percent': spread_percent,
'buy_funding': buy_funding,
'sell_funding': sell_funding,
'size': size
})
return opportunities