I'm trying to get data in the order of bybit -> cloudflare worker -> google scripts. I got the value from bybit to the worker via API, but when I get the value from the worker via google scripts, I always get only the initial value. There is a phenomenon where I can't get the new data from bybit.
I'll share the workers' code and the google script code.
// Workers code
export default {
async fetch(request) {
const API_KEY = "";
const API_SECRET = "";
const endpoint = "https://api.bybit.com/v5/position/closed-pnl";
const category = "linear"; // USDT 선물
const symbol = "BTCUSDT"; // 원하는 종목
const recvWindow = "5000";
const now = Date.now();
// 📌 임시로 특정 날짜 지정 (예: 2024년 2월 1일)
const startTime = 1740441600000; // ✅ 필요 시 변경 가능 (추후 삭제 예정)
const endTime = 1740787200000;
const timestamp = now.toString();
// 📌 Bybit 규칙: 파라미터를 알파벳 순으로 정렬해야 함!
const params = [
`api_key=${API_KEY}`,
`category=${category}`,
`endTime=${endTime}`,
`recv_window=${recvWindow}`,
`startTime=${startTime}`,
`symbol=${symbol}`,
`timestamp=${timestamp}`
].sort().join("&");
const signature = await generateSignature(API_SECRET, params);
try {
const response = await fetch(`${endpoint}?${params}&sign=${signature}`, {
method: "GET",
headers: { "Content-Type": "application/json" }
});
const result = await response.json();
console.log(`📌 최신 데이터 요청 결과:`, JSON.stringify(result, null, 2));
if (result.retCode === 0) {
const processedData = (result.result.list || []).map(item => ({
symbol: item.symbol,
leverage: item.leverage,
updatedTime: convertToKST(item.updatedTime), // ✅ 한국 시간 변환 적용
side: item.side === "Buy" ? "Long" : "Short", // ✅ Buy → Long, Sell → Short 변환
closedPnl: item.closedPnl,
qty: item.qty
}));
return new Response(JSON.stringify(processedData), { headers: { "Content-Type": "application/json" } });
} else {
console.warn(`🚨 요청 실패: ${result.retMsg}`);
}
} catch (error) {
console.error(`🚨 요청 중 에러 발생: ${error.message}`);
}
return new Response(JSON.stringify([]), { headers: { "Content-Type": "application/json" } });
}
};
// 📌 HMAC SHA-256 서명 생성
async function generateSignature(secret, payload) {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
return [...new Uint8Array(signature)].map(b => b.toString(16).padStart(2, "0")).join("");
}
// 📌 한국 시간(KST) 변환 함수
function convertToKST(timestamp) {
const date = new Date(Number(timestamp)); // ✅ 숫자로 변환 후 Date 객체 생성
if (isNaN(date.getTime())) return "Invalid Date"; // 🚨 변환 실패 시 Invalid Date 반환
return date.toLocaleString("ko-KR", { timeZone: "Asia/Seoul" }); // ✅ 한국 시간 변환
}
//google scripts code
function updateBybitData() {
const CLOUDFLARE_URL = "https://frosty-waterfall-ca94.gizldaos100.workers.dev/"; // Cloudflare Worker URL
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("BybitPnL");
if (!sheet) {
Logger.log("🚨 오류: 'BybitPnL' 시트를 찾을 수 없습니다!");
return;
}
const options = {
muteHttpExceptions: true,
headers: {
"User-Agent": "Mozilla/5.0 (compatible; GoogleAppsScript)",
"Cache-Control": "no-cache" // 🚀 캐시 무시하고 최신 데이터 요청
}
};
// ✅ API 호출
const response = UrlFetchApp.fetch(CLOUDFLARE_URL, options);
Utilities.sleep(2000); // 2초 기다리기
const statusCode = response.getResponseCode();
Logger.log(`📌 HTTP 응답 상태 코드: ${statusCode}`);
if (statusCode !== 200) {
Logger.log(`🚨 오류: API 응답이 200이 아님! (${statusCode})`);
return;
}
const contentText = response.getContentText();
Logger.log(`📌 Cloudflare 응답 데이터: ${contentText}`);
let jsonData;
try {
jsonData = JSON.parse(contentText);
} catch (error) {
Logger.log(`🚨 JSON 파싱 오류: ${error.message}`);
return;
}
if (!Array.isArray(jsonData) || jsonData.length === 0) {
Logger.log("⚠️ 가져온 데이터가 없거나 응답 형식이 올바르지 않습니다.");
return;
}
Logger.log(`✅ 가져온 데이터 개수: ${jsonData.length}`);
const lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1, jsonData.length, 6).setValues(
jsonData.map(entry => [
entry.symbol,
entry.leverage,
entry.updatedTime,
entry.side,
entry.closedPnl,
entry.qty
])
);
Logger.log("✅ Google Sheets 업데이트 완료!");
}
// Workers log
📌 최신 데이터 요청 결과: {
"retCode": 0,
"retMsg": "OK",
"result": {
"nextPageCursor": "efbb3171-75c1-4b57-b53c-bcfe972fd619%3A1740442479423%2Cefbb3171-75c1-4b57-b53c-bcfe972fd619%3A1740442479423",
"category": "linear",
"list": [
{
"symbol": "BTCUSDT",
"orderType": "Market",
"leverage": "7",
"updatedTime": "1740442479423",
"side": "Buy",
"orderId": "efbb3171-75c1-4b57-b53c-bcfe972fd619",
"closedPnl": "47.92136761",
"avgEntryPrice": "92327.7",
"qty": "0.034",
"cumEntryValue": "3139.1418",
"createdTime": "1740442479419",
"orderPrice": "91841.1",
"closedSize": "0.034",
"avgExitPrice": "90846.8",
"execType": "Trade",
"fillCount": "2",
"cumExitValue": "3088.7912"
}
]
},
"retExtInfo": {},
"time": 1741581760788
}
// google appscripts log
오후 1:43:31 알림 실행이 시작됨
오후 1:43:33 정보 📌 HTTP 응답 상태 코드: 200
오후 1:43:33 정보 📌 Cloudflare 응답 데이터: []
오후 1:43:33 정보 ⚠️ 가져온 데이터가 없거나 응답 형식이 올바르지 않습니다.
오후 1:43:34 알림 실행이 완료됨
Added LOG record. This action was performed manually and I am not good at coding and all the codes were done using gpt so I do not know the details well. Sorry. If you ask me to solve the problem, I will share the details as much as possible.
UrlFetchApp.fetch(CLOUDFLARE_URL+`?r=${Math.random()}`, options);
console.log(jsonData)
and timestamps. See debugging on ericlippert.com/2014/03/05/how-to-debug-small-programscurl
or postman on frosty-waterfall-ca94.gizldaos100.workers.dev Currently, I only get empty array.