i’m code: import axios from ‘axios’;
import crypto from ‘crypto’;
import dotenv from ‘dotenv’;
dotenv.config();
// — CẤU HÌNH API BINANCE FUTURES (Ed25519 với Private Key PEM) —
const API_KEY = process.env.BINANCE_API_KEY;
const PRIVATE_KEY_PEM_RAW = process.env.BINANCE_PRIVATE_KEY_PEM;
// Đảm bảo Private Key PEM được định dạng đúng (thay \n thành \n)
const PRIVATE_KEY_PEM = PRIVATE_KEY_PEM_RAW ? PRIVATE_KEY_PEM_RAW.replace(/\n/g, ‘\n’).trim() : null;
const BASE_URL = ‘https://fapi.binance.com’;
const POSITION_ENDPOINT = ‘/fapi/v2/positionRisk’; // Endpoint để đọc vị thế
// Hàm lấy thời gian định dạng HH:mm:ss
function getFormattedTime() {
return new Date().toLocaleTimeString(‘en-US’, {
hour12: false,
hour: ‘2-digit’,
minute: ‘2-digit’,
second: ‘2-digit’
});
}
// Kiểm tra biến môi trường
if (!API_KEY || !PRIVATE_KEY_PEM) {
console.error([${getFormattedTime()}] ❌ Lỗi: Cần cấu hình đầy đủ BINANCE_API_KEY và BINANCE_PRIVATE_KEY_PEM trong .env
);
console.error(Gợi ý: BINANCE_PRIVATE_KEY_PEM nên bao gồm toàn bộ nội dung file PEM, ví dụ: "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----"
);
process.exit(1);
}
// Hàm ký payload bằng Ed25519 sử dụng Node.js ‘crypto’
function signWithNodeCrypto(method, path, params) {
const sortedParamKeys = Object.keys(params).sort();
const queryString = sortedParamKeys.map(key => ${key}=${encodeURIComponent(params[key])}
).join(‘&’);
const payload = ${params.timestamp}${method}${path}${queryString}
;
console.log([${getFormattedTime()}] [DEBUG] Payload để ký: "${payload}"
);
try {
const signer = crypto.createSign('Ed25519'); // Sử dụng Ed25519
signer.update(payload);
const signature = signer.sign(PRIVATE_KEY_PEM, 'hex'); // Ký bằng Private Key PEM
return signature;
} catch (error) {
console.error(`[${getFormattedTime()}] ❌ Lỗi khi ký với Node Crypto (PEM): ${error.message}`);
if (error.code === 'ERR_OSSL_EVP_INVALID_DIGEST') {
console.error(`Gợi ý: Lỗi 'Invalid digest' thường do Private Key PEM không đúng định dạng hoặc không phải Ed25519.`);
}
throw error;
}
}
async function main() {
console.log([${getFormattedTime()}] Đang kết nối Binance Futures và đọc vị thế (Ed25519 với Private Key PEM)...
);
console.log([${getFormattedTime()}] [DEBUG] API Key đã tải: ${API_KEY.slice(0, 8)}...
);
try {
const method = 'GET';
const path = POSITION_ENDPOINT; // /fapi/v2/positionRisk
const timestamp = Date.now();
const recvWindow = 5000;
const positionParams = {
timestamp: timestamp.toString(),
recvWindow: recvWindow.toString()
};
// Ký payload
const signature = signWithNodeCrypto(method, path, positionParams);
// Tạo query string và URL cuối cùng
const positionQueryString = Object.keys(positionParams)
.sort()
.map(key => `${key}=${encodeURIComponent(positionParams[key])}`)
.join('&');
const finalUrl = `${BASE_URL}${POSITION_ENDPOINT}?${positionQueryString}`;
// Cấu hình headers
const headers = {
'X-BINANCE-APIKEY': API_KEY,
'X-BINANCE-TIMESTAMP': timestamp,
'X-BINANCE-SIGNATURE': signature,
};
console.log(`[${getFormattedTime()}] [DEBUG] Gọi URL (GET vị thế): ${finalUrl}`);
console.log(`[${getFormattedTime()}] [DEBUG] Headers: ${JSON.stringify(headers, null, 2)}`);
console.log(`[${getFormattedTime()}] [DEBUG] X-BINANCE-SIGNATURE đã gửi: ${signature}`);
// Gửi yêu cầu GET
const response = await axios.get(finalUrl, { headers });
const positions = response.data;
let hasOpenPosition = false;
if (positions && positions.length > 0) {
console.log(`[${getFormattedTime()}] ✅ Các vị thế đang mở:`);
for (const position of positions) {
const positionAmt = parseFloat(position.positionAmt);
if (positionAmt !== 0) {
hasOpenPosition = true;
const side = positionAmt > 0 ? 'LONG' : 'SHORT';
console.log(
` - Symbol: ${position.symbol || 'N/A'}, ` +
`Side: ${side}, ` +
`Amount: ${positionAmt}, ` +
`Entry Price: ${position.entryPrice || 'N/A'}, ` +
`Unrealized PNL: ${position.unrealizedProfit || 'N/A'}, ` +
`Leverage: ${position.leverage || 'N/A'}`
);
}
}
}
if (!hasOpenPosition) {
console.log(`[${getFormattedTime()}] ✅ Không có vị thế nào đang mở.`);
}
} catch (error) {
console.error(`[${getFormattedTime()}] ❌ Lỗi khi đọc vị thế: ${error.response?.data?.msg || error.message}`);
if (error.response?.data) {
console.error(`[${getFormattedTime()}] Chi tiết lỗi từ Binance:`, error.response.data);
if (error.response.data.code === -2014) {
console.error(`[${getFormattedTime()}] Gợi ý: Lỗi -2014 'API-key format invalid'.`);
console.error(` - Đảm bảo BINANCE_API_KEY đúng và khớp với Public Key bạn đã đăng ký trên Binance.`);
console.error(` - Kiểm tra quyền API: 'Enable Reading' và 'Enable Futures'.`);
} else if (error.response.data.code === -1021) {
console.error(`[${getFormattedTime()}] Gợi ý: Lỗi -1021 'Timestamp lệch'. Đồng bộ thời gian máy hoặc tăng recvWindow.`);
} else if (error.response.data.code === -1022) {
console.error(`[${getFormattedTime()}] Gợi ý: Lỗi -1022 'Signature for this request is not valid'.`);
console.error(` - Rất có thể Private Key PEM bạn đang dùng không khớp với Public Key trên Binance.`);
console.error(` - Đảm bảo nội dung 'BINANCE_PRIVATE_KEY_PEM' trong .env là toàn bộ nội dung file PEM chuẩn.`);
}
}
}
}
main().catch(error => console.error([${getFormattedTime()}] ❌ Lỗi không xác định: ${error.message}
));
Error signing:
[13:52:18] Đang kết nối Binance Futures và đọc vị thế (Ed25519 với Private Key PEM)…
[13:52:18] [DEBUG] API Key đã tải: IJnYz3D1…
[13:52:18] [DEBUG] Payload để ký: “1748501538210GET/fapi/v2/positionRiskrecvWindow=5000×tamp=1748501538210”
[13:52:18] Lỗi khi ký với Node Crypto (PEM): Invalid digest
[13:52:18] Lỗi khi đọc vị thế: Invalid digest
how to fix this error??