UNPKG

futu-sdk

Version:

futu-api 的高性能精简易用版;基于*.proto静态编译,提供高性能的多层封装但层层开放的易用接口,获取最原始最完整的量化数据;相较于Python, nodejs更接近Web, 从而轻而易举搭建一个可视化交易站点,这是进行可控量化交易的不二选择。

91 lines (81 loc) 3.11 kB
import crypto from 'crypto'; import type { WebSocket as NodeSocket } from 'ws'; import { encodeBuffer, decodeBuffer } from './utils'; import { WebRequest, ReqFn, SubFn } from './fetch'; export interface IWebRequest extends Omit<WebRequest, 'InitWebSocket'> {} /** * getFutuApi 内部实例化 WebSocket 连接,并基于 WebSocket 封装了的查询对象 WebRequest, 你可以使用 `webRequest` 查询各种接口, * 也可以使用 `webSocket` 来关闭连接(`webSocket.close()`)和实现事件监听(如 onclose/onopen/onmessage等) * * Example: * * ```ts * import { getFutuApi } from 'futu-sdk'; * // import { Trd_Common } from 'futu-proto'; * * const { webRequest, webSocket } = getFutuApi('ws://127.0.0.1:33333', '9d261112869397f0'); * try { * const { accList } = (await webRequest.GetAccList({ userID: 0, needGeneralSecAccount: true })) || {}; * console.log({ accList }); * } finally { * webSocket.close(); * } * ``` * @param wsUrl 连接本地 WebSocket 的地址,如 `ws://127.0.0.1:33333` * @param key WebSocket 密钥,每次启动 OpenD_GUI 不设置都会随机生成,CLI 暂不清楚如何获取这个密钥 * @returns `{ webRequest: WebRequest, webSocket: WebSocket }` */ export const getFutuApi = (wsUrl: string, key: string) => { const reqCallback = new Map<number, (result: any) => void>(); const subCallback = {} as Record<number, Array<(result: any) => void>>; const webSocket = typeof window === 'undefined' ? (new (require('ws'))(wsUrl) as NodeSocket) : new WebSocket(wsUrl); webSocket.binaryType = 'arraybuffer'; const reqFn: ReqFn = (cmd, protobuf, callback) => { const { session, message } = encodeBuffer(cmd, protobuf); if (webSocket.readyState !== WebSocket.OPEN) { throw new Error('WebSocket closed'); } webSocket.send(message); reqCallback.set(session, callback); return () => { reqCallback.delete(session); }; }; const subFn: SubFn = (cmd, callback) => { if (!subCallback[cmd]) { subCallback[cmd] = []; } subCallback[cmd].push(callback); return () => { subCallback[cmd] = subCallback[cmd].filter(cb => cb !== callback); }; }; const reqFnPromise = new Promise<ReqFn>((resolve, reject) => { webSocket.addEventListener('open', () => { new WebRequest(Promise.resolve(reqFn), subFn) .InitWebSocket({ websocketKey: key ? crypto.createHash('md5').update(key).digest('hex') : '', programmingLanguage: 'JavaScript', }) .then(() => resolve(reqFn)) .catch(reject); }); webSocket.addEventListener('message', event => { const { session, cmd, message } = decodeBuffer(event.data as ArrayBuffer); if (subCallback[cmd]) { for (const callback of subCallback[cmd]) { callback(message); } } else { reqCallback.get(session)?.(message); } }); webSocket.addEventListener('close', event => { reject(event); }); }); return { webSocket, webRequest: new WebRequest(reqFnPromise, subFn) as IWebRequest, }; };