基于P站(Poloniex)的广度优先搜索算法来获得任意两种币的兑换价格
币圈的P站是Poloniex,前几年被孙宇晨收购了,它是一个交易所。我很久之前用过Poloniex,当时对其印象并不是很好。
不过,现在我对其好感增加,因为币安买下的coinmarketcap免费的接口就很多限制。
https://api.poloniex.com/markets/price
能返回所有交易配对,比如这样的:
[ { "symbol": "BTS_BTC", "price": "0.0000000186", "time": 1731852313035, "dailyChange": "-0.0462", "ts": 1731852313054 }, { "symbol": "DASH_BTC", "price": "0.000317", "time": 1731848096481, "dailyChange": "0.0063", "ts": 1731848096489 }, ... ... ]
这个JSON返回的结构是一个数组,每个元素是个结构体,也就是一个币价的具体配对信息,我们可以看成是一条边Edge两个顶点Vertice,这样就是一个图结构(带权图 Weighted Graph,权值就是兑换价格),虽然给的是单边,但其实是个双向的,比如USD_BTC得值可以反过来推得BTC到USD的价格。我们可以设计一个算法,从币价A到币价B,可以通过BFS广度优先搜索算法来获取价格。比如有配对A_B、B_C、C_D我们就可以获得A_D的值。
深度优先搜索算法DFS也可以,不过这个算法会返回找到的第一条路径,并不能保证是最短的,最短的确实是最准确的,因为链也长,转换精度就会下降。
当然,可能存在多条路径,最理想的状态是把这些路径都求出来,取个平均啥的,不过这样就得暴力搜索所有的路径了,算法时间复杂度就会比较高。
以下是BFS广度优先算法的代码,Javascript的,可以用于网页前端或者NodeJs后端,甚至是CloudFlare Serverless Worker或者是其它无服务框架:Azure Function、AWS Lambda等。
const fetch = require('node-fetch'); async function getTicker(a, b) { try { const response = await fetch('https://api.poloniex.com/markets/price'); const data = await response.json(); // 创建一个哈希表来存储代币对及其价格 const pairMap = new Map(); // 使用直接对及其反向对填充哈希表 for (const { symbol, price } of data) { const [base, quote] = symbol.split('_').map(token => token.toLowerCase()); if (!pairMap.has(base)) pairMap.set(base, new Map()); if (!pairMap.has(quote)) pairMap.set(quote, new Map()); pairMap.get(base).set(quote, parseFloat(price)); pairMap.get(quote).set(base, 1 / parseFloat(price)); // 添加反向边 } // 将 token 转换为小写 a = a.toLowerCase(); b = b.toLowerCase(); // BFS 查找从 a 到 b 的转换率 const queue = [[a, 1]]; // 从初始代币和兑换率 1 开始 const visited = new Set([a]); while (queue.length > 0) { const [currentToken, currentRate] = queue.shift(); if (currentToken === b) return currentRate; // Check connected tokens for (const [nextToken, rate] of (pairMap.get(currentToken) || new Map())) { if (!visited.has(nextToken)) { visited.add(nextToken); queue.push([nextToken, currentRate * rate]); } } } // 如果未找到路径,则返回 null return null; } catch (error) { console.error("获取或处理数据时出错:", error); return null; } } // Example usage: (async () => { const rate = await getTicker('btc', 'trx'); console.log('BTC 到 TRX 的兑换率:', rate); })();
代码的一些简单说明:
- API 数据提取:从 P站 API 提取数据并将响应解析为 JSON。
- 映射对:以每个代币作为键创建一个映射,其中值是它可以直接转换为的另一个代币映射,以及兑换率。
- 双向映射:通过反转反向转换的价格来存储直接对和反向对。
- 广度优先搜索:使用队列遍历从 a 到 b 的路径。对于每个代币,都会检查其邻居(可转换代币)。如果找到 b,该函数将返回累积率;如果没有,则继续,直到所有选项都用尽。
- 处理无路径:如果未找到转换路径,则函数返回 null。
如果有多组兑换,我们可以改成传入一个数组,这样就不用多次调用P站的API了。
const fetch = require('node-fetch'); async function getToken(pairs) { try { const response = await fetch('https://api.poloniex.com/markets/price'); const data = await response.json(); // 创建一个哈希表来存储代币对及其价格 const pairMap = new Map(); // 使用直接对及其反向对填充哈希表 for (const { symbol, price } of data) { const [base, quote] = symbol.split('_').map(token => token.toLowerCase()); if (!pairMap.has(base)) pairMap.set(base, new Map()); if (!pairMap.has(quote)) pairMap.set(quote, new Map()); pairMap.get(base).set(quote, parseFloat(price)); pairMap.get(quote).set(base, 1 / parseFloat(price)); // 添加一条反向边 } // 使用 BFS 查找单个对的转换率的辅助函数 const findConversionRate = (a, b) => { a = a.toLowerCase(); b = b.toLowerCase(); if (a === b) return 1; // 直接转换 const queue = [[a, 1]]; const visited = new Set([a]); while (queue.length > 0) { const [currentToken, currentRate] = queue.shift(); // 出队列 if (currentToken === b) return currentRate; for (const [nextToken, rate] of (pairMap.get(currentToken) || new Map())) { if (!visited.has(nextToken)) { visited.add(nextToken); queue.push([nextToken, currentRate * rate]); } } } return null; // 路径没找到 }; // 迭代列表并查找转换率 const results = pairs.map(([a, b]) => findConversionRate(a, b)); return results; } catch (error) { console.error("Error fetching or processing data:", error); return pairs.map(() => null); // 如果有错误,则返回每对的 null } } // Example usage: (async () => { const conversionRates = await getToken([['btc', 'trx'], ['usd', 'steem']]); console.log('兑换结果:', conversionRates); })();
简单的代码说明:
- 参数更新:getToken 现在接受成对的元组数组,其中每个元组代表一对 [a, b]。
- 辅助函数:findConversionRate 处理每对的转换,实现与以前相同的 BFS 方法。
- 映射每对:函数迭代数组里的每个配对币,应用 findConversionRate 计算转换率,并将结果存储在数组中。
- 错误处理:如果出现 API 或处理错误,则返回一个空值数组,与输入数组的长度匹配。
这个修改后的函数现在可以接受一个数组,并在一次Poloniex API调用中返回数组里每个配对的兑换率。
英文:Crypto Token Exchange Rate Computation Based on Breadth First Search Algorithm on Poloniex API
区块链技术
- 基于P站(Poloniex)的广度优先搜索算法来获得任意两种币的兑换价格
- 加密货币只是区块链的冰山一角
- 警惕区块链上的小额诈骗转帐
- 区块链: 硬分叉与软分叉
- 区块链中Layer 1和Layer 2协议的简介
- 孙老板/师卖在区块链上卖沙子 (营销/沙币/孙哥/ICO)
- 和媳妇聊聊 区块链 (Web3.0, 还有共识算法 PoW, PoS, DPoS)
- 我玩区块链虚拟货币的原则
- SteemIt 就可以把图片也存在区块链上了
- Steem转波场U的信任问题(Steem/SBD to USDT TRC-20)
相关文章:
- HPZ800服务器主板太老不支持超过2TB的大硬盘 我家里一直用的是HPZ800服务器, 很吵, 很老, 虽然这台服务器已经有十年之久(我在EBAY上买来用了五年多了), 但是即使放到今天, 这服务器速度依旧很快, 很稳定. 由于服务器用的是ECC较验内存, 所以基本上不重启关机. HPZ800主机有两个硬核CPU – 因特志强 X5650 – 每个CPU是12核....
- 给孩子零花钱培养孩子正确的金钱观价值观 两个娃已经不知不觉7岁8岁了. 媳妇和我商量一下决定给孩子每人每周5英镑的零花钱(Pocket Money). 这样他们慢慢的就有自己的小积蓄备将来不时之需: 比如朋友聚会生日啥的需要准备礼物. 同时, 我们决定不再给孩子买零食(薯片啥的). 孩子一天好几餐, 晚上睡觉前还得吃零食, 我们就多买了很多水果面包, 健康的食物多吃一些总不是啥坏事. 孩子可以用这些零钱买自己想要的东西, 我们也不再过问. 孩子有自己的决定权. 第一周的时候,...
- 测测你的幸运 – Linux Fortune-Teller LINUX 下有很好很好玩的命令,之前已经介绍过: figlet, rig, curl. 现在推荐另一个 命令 fortune 是用来随机显示一段(句)话的.fortune 在英文里就是幸运的意思. 这个命令可以不需要 参数 如果没有 可以通过 apt-get...
- 负电价活久见: 安装Octopus智能电表省电费甚至赚钱 前几周我的电气公司 Octopus 终于来装智能电表了(Smart Meter),虽然是免费安装的,但是排队排了有两三年了吧。因为之前一直写邮件催的时候就老是说 Not Ready。 收到邮件说可以安装智能电表我还是相当开心和期待的,因为已经听说这玩意好,但是还是得亲身体验一下。工程师来安装大概不到2小时,其中需要停电闸一会儿,重新接下线。装好后,给了个小册子,自动切换到了 Agile 的电价,也就是每半小时的电价都不一样,提前一天可以在手机App和网站上查得。 正好在原来的电价计费合同快要结束前2天换到了智能电表计价 Octopus Agile方式,但是系统还是扣了我75英镑 Exit Fee (提前合同结束得交违约费),不过我一个电话打过去,公司很爽快就给我退了。...
- ChatGPT-4 使用 Math Wolfram 插件解决数学脑筋急转弯问题 这篇文章, 我们看一个简单的数学问题(脑筋急转弯), 并用 Python 解决它. 我们看一下LLM(大型语言模型): ChatGPT3.5和ChatGPT4. 通过 ChatGPT-Plus 订阅(目前每月 20 美元 + VAT增值税), 我们可以启用...
- 微软面试题: 三角形的面积是多少? 据说是一个印度人杀入微软最后的面试, 面试官给了这么一道小学数学几何题: 这哥门也有疑问 可是最后还是坚持 答案 30 (底 X 高 / 2) 不存在 这是个陷井: 这个直角三角形是不存在的. 两个小直角三角形的勾股定理:...
- 给STEEM中文微信群加了个机器人 之前说到我的公众号 justyyuk 可以查询几种虚拟货币的实时价钱, 但是有点不方便, 因为很多朋友在群里聊天得切换到公众号, 这下好了, 今天往STEEM中文微信群(还有编程群)加了个机器人, 在聊天的时候想了解价钱就直接输入货币代号即可, 如: 既方便自己, 又能方便别人(省事, 价格信息会同时显示给其它成员). 注: 这机器人不是我做的, 只是我拉进来的,...
- Javascript 中 sleep 函数实现 Javascript 中并没有 built-in 的 sleep 函数支持, 在 async/await/Promise 的支持之前, 我们可以用 busy-waiting 的方式来模拟: 1 2 3...