import requestsimport pandas as pdimport osimport websocketimport jsonimport sslimport threadingimport datetime# Set Binance API Key as an environment variableapi_key = os.getenv('BINANCE_API_KEY')latest_funding_rates ={}defon_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 ratedefon_error(ws,error):print("Error:", error)defon_close(ws,code,reason):print(f"WebSocket closed with code {code}, reason {reason}")defon_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)defconnect_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)defrun_ws(): ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) thread = threading.Thread(target=run_ws) thread.start()return ws, threaddefget_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 symbolsreturn []# Global weights for APR calculationWEIGHTS = [W3, W7, W30, W_prev, W_next]defcalculate_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 inzip(aprs, WEIGHTS))return apr_scoredefget_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()defprocess_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)ifnot 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()
Math formula
1. Average Funding Rate Calculation:
Average Funding Rate_n = (Sum of Funding Rates over the last n periods) / n
Here, n represents the number of periods, such as 3, 7, or 30 days.
2. Annualization of the Funding Rate:
Annualized Funding APR_n = Average Funding Rate_n x 360 x 3 x 100
This converts the average funding rate for n days into an annual percentage rate by multiplying by the number of days in a year (365) and then converting it to a percentage by multiplying by 100.
3. APR Score Calculation with Weights:
APR Score = (Annualized Funding APR_3 x W_3) + (Annualized Funding APR_7 x W_7) + (Annualized Funding APR_30 x W_30) + (Annualized Funding APR_next x W_next) + (Annualized Funding APR_prev x W_prev)
The APR Score is calculated by taking the weighted average of several Annualized Funding APRs for different periods. This is done by multiplying each Annualized Funding APR by its respective weight and then summing up these products.