这是一个专门为FMZ平台开发的OKX私有频道WebSocket连接测试脚本,使用经过验证的exchange.HMAC函数实现安全认证,帮助开发者快速验证私有频道连接和数据推送功能。
exchange.HMAC函数// ⚠️ 重要:请替换为您的实际API凭证
var accessKey = "your_api_key_here"; // 🔑 您的OKX API Key
var secretKey = "your_secret_key_here"; // 🔑 您的OKX Secret Key
var passphrase = "your_passphrase_here"; // 🔑 您的OKX Passphrase
| 错误码 | 说明 | 解决方案 |
|---|---|---|
| 50111 | API Key无效 | 检查API Key是否完整正确 |
| 50113 | 签名验证失败 | 检查Secret Key和Passphrase |
| 50102 | 时间戳错误 | 确认系统时间准确(偏差<30秒) |
| 50103 | 请求过于频繁 | 降低请求频率,等待后重试 |
测试成功后,您可以: 1. 将认证逻辑集成到实盘策略中 2. 扩展订阅更多私有频道(订单、交易等) 3. 添加业务逻辑处理实时数据推送 4. 优化错误处理和重连机制
exchange.HMAC函数,兼容性最佳作者说明: 这个脚本是基于FMZ平台特性和OKX API官方文档开发的测试工具,经过实际验证可用。如果您在使用过程中遇到问题,欢迎交流讨论!
// ============================================================================
// FMZ可用HMAC私有频道WebSocket测试脚本
// 使用已验证可用的exchange.HMAC函数
// ============================================================================
function getLogin(pAccessKey, pSecretKey, pPassphrase) {
// 签名函数,用于登录 - 使用已验证可用的exchange.HMAC
var ts = (new Date().getTime() / 1000).toString();
var message = ts + "GET" + "/users/self/verify";
var signature = "";
try {
// 使用已验证可用的exchange.HMAC函数
signature = exchange.HMAC("sha256", "base64", message, pSecretKey);
Log("✅ 使用exchange.HMAC生成签名成功");
} catch (e) {
Log("❌ 签名生成失败:", e.toString());
return null;
}
var login = {
"op": "login",
"args":[{
"apiKey" : pAccessKey,
"passphrase" : pPassphrase,
"timestamp" : ts,
"sign" : signature
}]
};
return login;
}
var client_private = null;
function main() {
Log("🚀 开始FMZ可用HMAC私有频道测试");
SetErrorFilter("timeout");
var exchangeName = exchange.GetName();
var currency = exchange.GetCurrency();
Log("📊 交易所:", exchangeName);
Log("💰 交易对:", currency);
// 验证HMAC函数可用性
if (typeof exchange.HMAC === 'function') {
Log("✅ exchange.HMAC 函数可用");
// 测试签名生成
try {
var testSig = exchange.HMAC("sha256", "base64", "test_message", "test_secret");
Log("✅ HMAC签名测试成功:", testSig.substring(0, 16) + "...");
} catch (e) {
Log("❌ HMAC签名测试失败:", e.toString());
return;
}
} else {
Log("❌ exchange.HMAC 函数不可用,无法继续");
return;
}
// ⚠️ 重要:请替换为您的实际API凭证
var accessKey = "your_api_key_here"; // 🔑 替换为您的OKX API Key
var secretKey = "your_secret_key_here"; // 🔑 替换为您的OKX Secret Key
var passphrase = "your_passphrase_here"; // 🔑 替换为您的OKX Passphrase
// 检查API凭证是否已配置
if (accessKey === "your_api_key_here" || secretKey === "your_secret_key_here") {
Log("❌ 请先配置API凭证!");
Log("💡 请在代码中将以下变量替换为您的实际API凭证:");
Log(" 1. 登录OKX官网: https://www.okx.com/account/my-api");
Log(" 2. 创建或查看API Key");
Log(" 3. 复制以下信息到代码中:");
Log(" - API Key (accessKey)");
Log(" - Secret Key (secretKey)");
Log(" - Passphrase (passphrase)");
Log(" 4. 确保API权限包含'读取'权限");
Log(" 5. 如果设置了IP白名单,确保包含FMZ服务器IP");
return;
}
// 测试登录消息生成
var testLogin = getLogin(accessKey, secretKey, passphrase);
if (!testLogin) {
Log("❌ 登录消息生成失败,无法继续");
return;
}
Log("🔐 API凭证已配置,登录消息生成成功");
Log("📤 登录消息预览:", JSON.stringify({
"op": "login",
"args": [{
"apiKey": accessKey.substring(0, 8) + "...",
"passphrase": "***",
"timestamp": testLogin.args[0].timestamp,
"sign": testLogin.args[0].sign.substring(0, 16) + "..."
}]
}, null, 2));
// 订阅配置
var posSubscribe = {
"op": "subscribe",
"args": [{
"channel": "positions",
"instType": "ANY"
}]
};
var accountSubscribe = {
"op": "subscribe",
"args": [{
"channel": "account"
}]
};
// 连接私有频道
Log("⏳ 连接私有频道WebSocket...");
client_private = Dial("wss://ws.okx.com:8443/ws/v5/private");
if (client_private) {
Log("✅ 私有频道WebSocket连接成功!");
// 发送登录认证
Log("🔐 发送登录认证...");
client_private.write(JSON.stringify(testLogin));
// 等待服务器响应 - 官方要求3秒
Log("⏳ 等待服务器认证响应(3秒)...");
Sleep(3000);
// 订阅频道
Log("📡 订阅持仓频道...");
client_private.write(JSON.stringify(posSubscribe));
Log("📡 订阅账户频道...");
client_private.write(JSON.stringify(accountSubscribe));
// 开始监听
monitorMessages();
} else {
Log("❌ 私有频道WebSocket连接失败");
Log("💡 可能的原因:");
Log(" 1. 网络连接问题");
Log(" 2. OKX服务器维护");
Log(" 3. 防火墙阻止连接");
}
}
function monitorMessages() {
Log("📨 开始监听私有频道消息...");
if (client_private) {
var lastPingTS = new Date().getTime();
var messageCount = 0;
var authSuccess = false;
var subscribeSuccess = false;
var privateDataReceived = false;
var testStartTime = new Date().getTime();
var testDuration = 45000; // 测试45秒
Log("⏰ 测试时长:", testDuration / 1000, "秒");
while (new Date().getTime() - testStartTime < testDuration) {
try {
var buf = client_private.read(1000); // 1秒超时
if (buf) {
messageCount++;
Log("📨 消息 #" + messageCount + ":", buf);
// 分析消息
var analysis = analyzeMessage(buf);
if (analysis.isLogin && analysis.success) {
authSuccess = true;
Log("🎉 认证成功标记已设置");
}
if (analysis.isSubscribe && analysis.success) {
subscribeSuccess = true;
Log("✅ 订阅成功标记已设置");
}
if (analysis.isPrivateData) {
privateDataReceived = true;
Log("🔑 私有数据接收标记已设置");
}
}
// 发送心跳包
var nowPingTS = new Date().getTime();
if (nowPingTS - lastPingTS > 10 * 1000) {
client_private.write("ping");
lastPingTS = nowPingTS;
Log("💓 发送心跳包");
}
// 每10秒显示一次状态
var elapsed = new Date().getTime() - testStartTime;
if (elapsed % 10000 < 1000 && elapsed > 5000) {
Log("📊 中间状态 (" + Math.floor(elapsed/1000) + "s):",
"消息:" + messageCount,
"认证:" + (authSuccess ? "✅" : "❌"),
"订阅:" + (subscribeSuccess ? "✅" : "❌"),
"数据:" + (privateDataReceived ? "✅" : "❌"));
}
} catch (e) {
Log("⚠️ 消息处理异常:", e.toString());
}
}
// 最终测试结果
Log("=== 最终测试结果 ===");
Log("📊 测试时长:", testDuration / 1000, "秒");
Log("📨 收到消息数:", messageCount);
Log("🔐 认证状态:", authSuccess ? "✅ 成功" : "❌ 失败");
Log("📡 订阅状态:", subscribeSuccess ? "✅ 成功" : "❌ 失败");
Log("🔑 私有数据:", privateDataReceived ? "✅ 已接收" : "❌ 未接收");
// 结论和建议
if (authSuccess && subscribeSuccess) {
Log("🎉 私有频道WebSocket测试成功!");
Log("💡 您可以在实盘策略中使用此方式获取:");
Log(" - 实时账户余额变化");
Log(" - 实时持仓信息更新");
Log(" - 订单状态变化通知");
if (!privateDataReceived) {
Log("ℹ️ 未收到私有数据推送是正常的(没有余额/持仓变化时)");
}
} else if (authSuccess) {
Log("⚠️ 认证成功但订阅失败");
Log("💡 请检查订阅权限和频道配置");
} else if (messageCount > 0) {
Log("❌ 连接成功但认证失败");
Log("💡 请检查API凭证:");
Log(" 1. API Key是否正确");
Log(" 2. Secret Key是否正确");
Log(" 3. Passphrase是否正确");
Log(" 4. API权限是否包含读取权限");
Log(" 5. IP白名单设置是否正确");
} else {
Log("❌ 未收到任何消息");
Log("💡 可能是网络连接问题");
}
}
}
function analyzeMessage(message) {
var result = {
isLogin: false,
isSubscribe: false,
isPrivateData: false,
success: false
};
try {
var data = JSON.parse(message);
if (data.event === "login") {
result.isLogin = true;
if (data.code === "0") {
result.success = true;
Log("🎉 私有频道登录认证成功!");
} else {
Log("❌ 私有频道登录认证失败:", data.msg, "错误码:", data.code);
// 详细错误分析和解决建议
switch(data.code) {
case "50111":
Log("💡 API Key无效:");
Log(" - 检查API Key是否完整正确");
Log(" - 确认API Key未被删除或禁用");
break;
case "50113":
Log("💡 签名验证失败:");
Log(" - 检查Secret Key是否正确");
Log(" - 检查Passphrase是否正确");
Log(" - 确认系统时间准确");
break;
case "50102":
Log("💡 时间戳错误:");
Log(" - 检查系统时间是否准确");
Log(" - 时间偏差不能超过30秒");
break;
case "50103":
Log("💡 请求过于频繁:");
Log(" - 降低请求频率");
Log(" - 等待一段时间后重试");
break;
default:
Log("💡 其他错误 - 错误码:", data.code);
Log(" - 请查看OKX API文档获取详细说明");
}
}
} else if (data.event === "subscribe") {
result.isSubscribe = true;
if (data.code === "0") {
result.success = true;
Log("✅ 私有频道订阅成功:", data.arg.channel);
} else {
Log("❌ 私有频道订阅失败:", data.msg, "频道:", data.arg ? data.arg.channel : "未知");
}
} else if (data.arg && (data.arg.channel === "positions" || data.arg.channel === "account")) {
result.isPrivateData = true;
Log("🔑 收到私有数据推送:", data.arg.channel);
if (data.data && data.data.length > 0) {
Log("📊 数据详情 (" + data.arg.channel + "):",
JSON.stringify(data.data[0]).substring(0, 80) + "...");
} else {
Log("📊 数据为空(正常 - 表示没有" + data.arg.channel + "变化)");
}
} else if (data.event === "error") {
Log("❌ 服务器错误消息:", JSON.stringify(data));
} else {
Log("ℹ️ 其他消息类型:", data.event || "未知事件");
}
} catch (e) {
// 非JSON消息处理
if (message === "pong") {
Log("💓 收到心跳响应: pong");
} else if (message.length < 50) {
Log("⚠️ 短消息:", message);
} else {
Log("⚠️ 非JSON消息:", message.substring(0, 30) + "...");
}
}
return result;
}
function onexit() {
if (client_private) {
var ret = client_private.close();
Log("🔒 关闭私有频道连接!", ret);
}
Log("🔚 FMZ可用HMAC私有频道测试结束");
Log("💡 如果测试成功,您可以将此代码集成到实盘策略中");
}