Multi-platform futures funding rate acquisition and monitoring strategy
Description:
This strategy is used to obtain and monitor funding rates from multiple futures platforms such as OKCoin, Binance, Bitget, etc. It polls the perpetual contract markets of various exchanges through parallel threads, and obtains funding rate data, while using a delay mechanism to optimize the request frequency.
This article makes some changes to the strategy to support display and funding rate alarm push functions.
Open source address: https://www.fmz.com/strategy/470345
Function:
- Multi-platform support : Synchronize funding rates across multiple trading platforms and set different request delays for each platform.
- Specific symbol acquisition : Supports obtaining the funding rate of specific trading pairs (such as BTC/USDT, ETH/USDT).
- Optimize for different platforms : Distinguish between platforms that do not need to query each market one by one (such as Binance) and platforms that need to traverse all markets (such as OKCoin).
- Rate display : Displays the funding rates of multiple trading platforms. Because the collection intervals are different, they are uniformly adjusted to 24h rates for direct comparison.
- Rate warning push : A certain threshold can be set, and when the 24h equivalent rate exceeds the customization, it will be pushed to the FMZ mobile APP.
Directions:
You can adjust the platform list, symbol list and polling interval as needed to meet your specific trading needs.
Strategy code
The script is divided into several main parts:
- startFundingWorker : Starts a separate thread for each exchange to monitor the funding rate to avoid throttling caused by a single thread requesting too much data.
- getFundings : Reads funding rate data for a specified exchange from storage.
- UpdateStatus : Processes and updates the funding rate table for all exchanges, displays the aggregated data as a table, and logs the symbols with high fees.
- main : Starts the main program, starts the monitoring thread and regularly updates the aggregated funding rate status.
// Start the funding rate monitoring thread and create a separate thread for each exchange's funding rate datafunctionstartFundingWorker() { exchanges.forEach((_, pos) => { __Thread(function (pos) { let e = exchanges[pos] let eName = e.GetName() // Set request delays for different exchanges to prevent frequent requests from causing throttlinglet delaySettings = { 'Futures_OKCoin': 20, 'Futures_Binance': 500, 'Futures_MEXC': 100, } // Need to traverse the list of exchange names for all markets, these exchanges do not support getting all trading pairs at oncelet needInterate = ['Futures_OKCoin', 'Futures_Bitget','Futures_OKX', 'Futures_KuCoin', 'Futures_MEXC'] // Set delay based on exchange namelet delay = function () { let n = delaySettings[eName] if (n) { Sleep(n) } } // Set the update interval to update every two minuteslet epoch = 60000 * 2; let ts = 0; let fundings = {} // Infinite loop, get funding rate at fixed intervalswhile (true) { let now = newDate().getTime() if (now - ts < epoch) { // If the update cycle is not reached, pause for 1 second and then continue checkingSleep(1000) continue } let markets = e.GetMarkets() if (!markets) { // If market information cannot be obtained, try again after a delaySleep(1000) continue } // If the exchange is in the list that needs to be traversed, request the funding rate for each marketif (needInterate.includes(eName)) { for (let symbol in markets) { if (symbol.includes('.swap') && symbol.includes('_USDT')) { let ret = e.GetFundings(symbol) if (ret) { for (let r of ret) { fundings[r.Symbol] = r } } delay(); } } } else { // For exchanges not in the traversal list, only request the funding rate of USDT.swaplet ret = e.GetFundings('USDT.swap') if (ret) { for (let r of ret) { fundings[r.Symbol] = r } } } // Update data timestamp ts = now // Stores the exchange's funding rate data__threadSetData(0, eName+"_funding", fundings) } }, pos) }) } // Get the funding rate data of the specified exchangefunctiongetFundings(eName) { let efundings = __threadGetData(0, eName+"_funding") if (!efundings) { returnnull } return efundings } // Update the funding rate table and display it in the logfunctionUpdateStatus(){ let table = { type: 'table', title: 'Funding Rate%', cols: ['index', 'symbol'], // Initialization column, containing symbolrows: [] }; let fundingRates = {}; exchanges.forEach((e) => { let eName = e.GetName(); if (fundings[eName]) { for (let symbol in fundings[eName]) { // Parse short symbol names and remove unnecessary prefixeslet short_symbol = symbol.split('_')[0].replace(/^(100|1000|10000|100000|1000000|10000000)|^(100|1000|10000|100000|1000000|10000000)$/g, ''); let rate = fundings[eName][symbol].Rate; let day = 24 / (fundings[eName][symbol].Interval / 3600000) // Initialize symbol data structureif (!fundingRates[short_symbol]) { fundingRates[short_symbol] = { total: 0, count: 0, day_rate: {}, next_time: {}, last_time:0}; } // Record and push rates that exceed the thresholdif (Math.abs(rate) > 0.01 && Date.now() - fundingRates[short_symbol].last_time > 30*60*1000) { Log(e.GetName(), symbol, rate, '@') fundingRates[short_symbol].last_time = Date.now() } fundingRates[short_symbol].total += rate; fundingRates[short_symbol].count++; fundingRates[short_symbol].day_rate[eName] = _N(rate * day , 6); // Record rates fundingRates[short_symbol].next_time[eName] = _N((fundings[eName][symbol].Time - Date.now()) / 3600000 , 1) + 'h' } } }); // Added rate columns and next update time columns for each exchangefor (let e of exchanges) { table.cols.push(e.GetName()+' Rate'); table.cols.push('Next Time'); } table.cols.push('Average Rate'); // Add an average rate columnlet i = 0; // Iterate over each symbol and fill in the datafor (let symbol in fundingRates) { let data = fundingRates[symbol]; if (data.count == 1) { continue// Symbols containing only a single data point are ignored } let averageRate = data.total / data.count; // Calculate average ratelet row = [i++, symbol]; for (let e of exchanges) { row.push(data.day_rate[e.GetName()] || null); // Filling the fees of various exchanges row.push(data.next_time[e.GetName()] || null); } row.push(_N(averageRate, 6)); // Filling average rate table.rows.push(row); } LogStatus('`' + JSON.stringify(table) + '`'); } // Main function, start funding rate monitoring and status updatevar fundings = {} functionmain() { startFundingWorker() // Start monitoring threads for each exchangewhile (true) { exchanges.forEach((e) => { let eName = e.GetName() let eFundings = getFundings(eName) fundings[eName] = eFundings }) Sleep(15000) // Update every 15 secondsUpdateStatus() } }