Enviar arquivos para "/"
parent
e7c36f5018
commit
74e77f522d
File diff suppressed because it is too large
Load Diff
|
@ -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")
|
|
@ -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")
|
|
@ -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())
|
|
@ -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
|
Loading…
Reference in New Issue