Copy import requests
import pandas as pd
import os
import websocket
import json
import ssl
import threading
import datetime
# Set Binance API Key as an environment variable
api_key = os . getenv ( 'BINANCE_API_KEY' )
latest_funding_rates = {}
def on_message ( ws , message ):
global latest_funding_rates
data = json . loads (message)
if 'stream' in data :
symbol = data [ 'stream' ]. split ( '@' ) [ 0 ] . upper () # Extract symbol from stream name in uppercase
latest_funding_rates [ symbol ] = float (data[ 'data' ][ 'r' ]) # Store the rate
def on_error ( ws , error ):
print ( "Error:" , error)
def on_close ( ws , code , reason ):
print ( f "WebSocket closed with code { code } , reason { reason } " )
def on_open ( ws ):
global streams
symbols = get_futures_symbols ()
streams = [ f " { symbol . lower () } @markPrice" for symbol in symbols]
subscribe_message = json . dumps ({
"method" : "SUBSCRIBE" ,
"params" : streams,
"id" : 1
})
ws . send (subscribe_message)
def connect_websocket ():
websocket . enableTrace ( False )
ws = websocket . WebSocketApp ( "wss://dstream.binance.com/stream" ,
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close)
def run_ws ():
ws . run_forever (sslopt = { "cert_reqs" : ssl.CERT_NONE})
thread = threading . Thread (target = run_ws)
thread . start ()
return ws , thread
def get_futures_symbols ():
url = 'https://dapi.binance.com/dapi/v1/exchangeInfo'
headers = { 'X-MBX-APIKEY' : api_key }
response = requests . get (url, headers = headers)
if response . status_code == 200 :
data = response . json ()
symbols = [item [ 'symbol' ] for item in data [ 'symbols' ] if item [ 'contractType' ] == 'PERPETUAL' ]
return symbols
return []
# Global weights for APR calculation
WEIGHTS = [W3 , W7 , W30 , W_prev , W_next]
def calculate_apr_score ( group , symbol ):
symbol = symbol . upper () # Ensure the symbol is in uppercase
aprs = [
group . head ( 9 ) [ 'fundingRate' ] . mean () * 3 * 360 * 100 ,
group . head ( 21 ) [ 'fundingRate' ] . mean () * 3 * 360 * 100 ,
group . head ( 90 ) [ 'fundingRate' ] . mean () * 3 * 360 * 100 ,
group . iloc [ 1 ] [ 'fundingRate' ] * 3 * 360 * 100 ,
latest_funding_rates . get (symbol, group.iloc[ 0 ][ 'fundingRate' ]) * 3 * 360 * 100 # Use real-time rate if available
]
apr_score = sum (apr * weight for apr, weight in zip (aprs, WEIGHTS))
return apr_score
def get_funding_rate_history ( symbol , display_days = 30 ):
url = 'https://dapi.binance.com/dapi/v1/fundingRate'
params = { 'symbol' : symbol , 'limit' : display_days * 3 }
headers = { 'X-MBX-APIKEY' : api_key }
response = requests . get (url, params = params, headers = headers)
if response . status_code == 200 :
data = response . json ()
df = pd . DataFrame (data)
df [ 'fundingRate' ] = pd . to_numeric (df[ 'fundingRate' ], errors = 'coerce' )
df [ 'Time' ] = pd . to_datetime (df[ 'fundingTime' ], unit = 'ms' , utc = True ). dt . tz_convert ( None )
return df . sort_values ( 'Time' , ascending = False ). head (display_days)
return pd . DataFrame ()
def process_data ():
ws , thread = connect_websocket ()
thread . join (timeout = 30 ) # Wait for the WebSocket to collect data
symbols = get_futures_symbols ()
all_data_frames = []
for symbol in symbols :
df = get_funding_rate_history (symbol, 60 )
if not df . empty :
df [ 'symbol' ] = symbol
all_data_frames . append (df)
final_data_list = []
for data in all_data_frames :
symbol = data [ 'symbol' ]. iloc [ 0 ]
apr_score = calculate_apr_score (data, symbol)
latest_data = data . sort_values ( 'Time' , ascending = False ). iloc [ 0 ]. to_dict ()
latest_data [ 'Previous Funding Rate' ] = data . iloc [ 1 ] [ 'fundingRate' ] * 360 * 3 * 100
latest_data [ 'Next Funding Rate' ] = latest_funding_rates . get (symbol, data.iloc[ 0 ][ 'fundingRate' ]) * 360 * 3 * 100
latest_data [ '3 Day Cum Funding APR' ] = data . head ( 9 ) [ 'fundingRate' ] . mean () * 360 * 3 * 100
latest_data [ '7 Day Cum Funding APR' ] = data . head ( 21 ) [ 'fundingRate' ] . mean () * 360 * 3 * 100
latest_data [ '30 Day Cum Funding APR' ] = data . head ( 90 ) [ 'fundingRate' ] . mean () * 360 * 3 * 100
latest_data [ 'APR Score' ] = f " { apr_score :.3f } %"
# Select only the desired columns
final_data_list . append ({
'symbol' : latest_data[ 'symbol' ],
'Time' : latest_data[ 'Time' ],
'Previous Funding Rate' : f " { latest_data[ 'Previous Funding Rate' ] :.3f } %" ,
'Next Funding Rate' : f " { latest_data[ 'Next Funding Rate' ] :.3f } %" ,
'3 Day Cum Funding APR' : f " { latest_data[ '3 Day Cum Funding APR' ] :.3f } %" ,
'7 Day Cum Funding APR' : f " { latest_data[ '7 Day Cum Funding APR' ] :.3f } %" ,
'30 Day Cum Funding APR' : f " { latest_data[ '30 Day Cum Funding APR' ] :.3f } %" ,
'APR Score' : latest_data[ 'APR Score' ]
})
final_data = pd . DataFrame (final_data_list)
final_data . sort_values ( 'APR Score' , ascending = False , inplace = True )
# Add timestamp to file name
timestamp = datetime . datetime . utcnow (). strftime ( '%Y-%m- %d _%H-%M-%S_UTC' )
file_name = f 'APR_results_ { timestamp } .csv'
final_data . to_csv (file_name, index = False )
print ( f "Data saved to ' { file_name } '" )
if ws . sock and ws . sock . connected :
ws . close ()
if __name__ == "__main__" :
process_data ()