Files
PolyHermes/scripts/ws_binance_btc_usdc_klines.js
WrBug b50e43c239 feat(crypto-tail): 策略最小价差(无/固定/自动) + 前端默认与文案
- 后端: 最小价差 DB/Entity/DTO、Binance K线 REST+WS、自动价差 IQR 预计算与执行时校验
- 前端: 最小价差(自动-固定-无),默认自动,label 旁 info 说明,选择自动不展示建议约
- i18n: minSpreadModeTip 说明不写死标的
- 文档: crypto-tail-strategy-min-spread-flow.md
- scripts: Binance K线拉取与 WS 示例

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:16:44 +08:00

93 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 通过币安 WebSocket 订阅 BTC/USDC 15 分钟 K 线推送。
*
* 文档: https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams
* - 现货 K 线流: wss://stream.binance.com:9443/ws/btcusdc@kline_15m
* - 服务端约每 20 秒发 pingws 库会自动回 pong
* - K 线推送频率: 15m 约每 2 秒更新一次;x=true 表示该根 K 线已收盘
*
* 依赖: npm install ws(在 scripts 目录下执行)
*
* 使用方法:
* node scripts/ws_binance_btc_usdc_klines.js
* node scripts/ws_binance_btc_usdc_klines.js --interval 1m
* node scripts/ws_binance_btc_usdc_klines.js --url "wss://stream.binance.com:9443/ws/btcusdc@kline_15m"
* Ctrl+C 退出
*/
import WebSocket from 'ws';
const BINANCE_WS_BASE = 'wss://stream.binance.com:9443';
function parseArgs() {
const args = process.argv.slice(2);
const out = { symbol: 'btcusdc', interval: '15m', url: null };
for (let i = 0; i < args.length; i++) {
if (args[i] === '--symbol' && args[i + 1]) {
out.symbol = String(args[i + 1]).toLowerCase();
i++;
} else if (args[i] === '--interval' && args[i + 1]) {
out.interval = args[i + 1];
i++;
} else if (args[i] === '--url' && args[i + 1]) {
out.url = args[i + 1];
i++;
}
}
return out;
}
function formatKline(msg) {
if (msg.e !== 'kline') {
return JSON.stringify(msg);
}
const k = msg.k || {};
const tMs = Number(k.t) || 0;
const tsStr = new Date(tMs).toISOString().replace('T', ' ').slice(0, 19);
const closed = k.x ? ' [CLOSED]' : '';
return ` ${tsStr} O:${k.o} H:${k.h} L:${k.l} C:${k.c} V:${k.v}${closed}`;
}
function run(wsUrl) {
console.log(`Connecting: ${wsUrl}`);
console.log('(Ctrl+C to exit)\n');
const ws = new WebSocket(wsUrl);
ws.on('open', () => {
// ws 库收到 ping 会自动回 pong,无需手动处理
});
ws.on('message', (data) => {
try {
const msg = JSON.parse(data.toString());
if (msg.result !== undefined && msg.id !== undefined) return;
if (msg.code !== undefined && msg.code !== 0) {
console.error('Error:', msg);
return;
}
console.log(formatKline(msg));
} catch {
console.log(data.toString());
}
});
ws.on('error', (err) => {
console.error('WebSocket error:', err.message);
process.exit(1);
});
ws.on('close', (code, reason) => {
if (code !== 1000) {
console.error(`Connection closed: ${code} ${reason?.toString() || ''}`);
process.exit(1);
}
});
}
const args = parseArgs();
const wsUrl = args.url || `${BINANCE_WS_BASE}/ws/${args.symbol}@kline_${args.interval}`;
run(wsUrl);