Sample codes and details.

Sample Code:

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()

Math formula

Output