169 lines
4.3 KiB
JavaScript
169 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* TQQQ Finnhub 24/7 모니터링 시스템
|
|
*
|
|
* Finnhub WebSocket으로 실시간 모니터링
|
|
* Stop-Loss: $47.00 (환경변수로 변경 가능)
|
|
*/
|
|
|
|
const WebSocket = require('ws');
|
|
const { exec } = require('child_process');
|
|
|
|
// 환경변수
|
|
const FINNHUB_API_KEY = process.env.FINNHUB_API_KEY || '';
|
|
const STOP_LOSS_PRICE = parseFloat(process.env.TQQQ_STOP_LOSS || '47.00');
|
|
const TICKER = 'TQQQ';
|
|
const DISCORD_CHANNEL = '1469190686145384513'; // #jarvis-market
|
|
|
|
// 상태
|
|
let lastPrice = null;
|
|
let alertSent = false;
|
|
let consecutiveBreaches = 0;
|
|
const BREACH_THRESHOLD = 3;
|
|
let lastAlertTime = 0;
|
|
const ALERT_COOLDOWN = 300000; // 5분
|
|
|
|
/**
|
|
* Stop-Loss 체크
|
|
*/
|
|
function checkStopLoss(price) {
|
|
const now = Date.now();
|
|
lastPrice = price;
|
|
|
|
if (price < STOP_LOSS_PRICE) {
|
|
consecutiveBreaches++;
|
|
console.log(`⚠️ Price: $${price.toFixed(2)} | Stop-Loss: $${STOP_LOSS_PRICE} | Breaches: ${consecutiveBreaches}/${BREACH_THRESHOLD}`);
|
|
|
|
if (consecutiveBreaches >= BREACH_THRESHOLD && !alertSent && (now - lastAlertTime) > ALERT_COOLDOWN) {
|
|
sendDiscordAlert(price);
|
|
alertSent = true;
|
|
lastAlertTime = now;
|
|
}
|
|
} else {
|
|
if (consecutiveBreaches > 0) {
|
|
console.log(`✅ Price recovered: $${price.toFixed(2)}`);
|
|
consecutiveBreaches = 0;
|
|
alertSent = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Discord 알림
|
|
*/
|
|
function sendDiscordAlert(price) {
|
|
const kstTime = new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' });
|
|
const krwPrice = Math.round(price * 1465.09); // USD to KRW
|
|
const message = `🚨 **TQQQ Stop-Loss 트리거**
|
|
|
|
**현재가:** $${price.toFixed(2)} (₩${krwPrice.toLocaleString('ko-KR')})
|
|
**손절선:** $${STOP_LOSS_PRICE.toFixed(2)}
|
|
**시각:** ${kstTime}
|
|
|
|
⚠️ 즉시 확인 필요!`;
|
|
|
|
// Use message tool via Node.js (direct API call)
|
|
const https = require('https');
|
|
const payload = JSON.stringify({
|
|
action: 'send',
|
|
channel: 'discord',
|
|
target: `channel:${DISCORD_CHANNEL}`,
|
|
message: message
|
|
});
|
|
|
|
const options = {
|
|
hostname: 'localhost',
|
|
port: 18789,
|
|
path: '/rpc',
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Content-Length': Buffer.byteLength(payload)
|
|
}
|
|
};
|
|
|
|
const req = https.request(options, (res) => {
|
|
let data = '';
|
|
res.on('data', (chunk) => { data += chunk; });
|
|
res.on('end', () => {
|
|
if (res.statusCode === 200) {
|
|
console.log('✅ Discord 알림 전송 완료');
|
|
} else {
|
|
console.error(`❌ Discord 알림 실패: ${res.statusCode} ${data}`);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', (error) => {
|
|
console.error(`❌ Discord 알림 실패: ${error.message}`);
|
|
});
|
|
|
|
req.write(JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
id: Date.now(),
|
|
method: 'tools/call',
|
|
params: {
|
|
name: 'message',
|
|
arguments: {
|
|
action: 'send',
|
|
channel: 'discord',
|
|
target: `channel:${DISCORD_CHANNEL}`,
|
|
message: message
|
|
}
|
|
}
|
|
}));
|
|
req.end();
|
|
}
|
|
|
|
/**
|
|
* Finnhub WebSocket
|
|
*/
|
|
function startWebSocket() {
|
|
console.log('🔌 Finnhub WebSocket 연결 시작...');
|
|
|
|
const ws = new WebSocket(`wss://ws.finnhub.io?token=${FINNHUB_API_KEY}`);
|
|
|
|
ws.on('open', () => {
|
|
console.log('✅ Finnhub WebSocket 연결됨');
|
|
ws.send(JSON.stringify({ type: 'subscribe', symbol: TICKER }));
|
|
console.log(`📡 ${TICKER} 구독 시작`);
|
|
console.log(`🛑 Stop-Loss: $${STOP_LOSS_PRICE.toFixed(2)}`);
|
|
});
|
|
|
|
ws.on('message', (data) => {
|
|
const message = JSON.parse(data);
|
|
|
|
if (message.type === 'trade' && message.data && message.data.length > 0) {
|
|
message.data.forEach(trade => {
|
|
checkStopLoss(trade.p);
|
|
});
|
|
}
|
|
});
|
|
|
|
ws.on('error', (error) => {
|
|
console.error(`❌ WebSocket 에러: ${error.message}`);
|
|
});
|
|
|
|
ws.on('close', () => {
|
|
console.log('🔌 WebSocket 연결 종료');
|
|
console.log('🔄 5초 후 재연결...');
|
|
setTimeout(startWebSocket, 5000);
|
|
});
|
|
|
|
return ws;
|
|
}
|
|
|
|
// 시작
|
|
console.log('🚀 TQQQ Finnhub 24/7 모니터링');
|
|
console.log(`📊 Ticker: ${TICKER}`);
|
|
console.log(`🛑 Stop-Loss: $${STOP_LOSS_PRICE.toFixed(2)}`);
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
|
|
if (!FINNHUB_API_KEY) {
|
|
console.error('❌ FINNHUB_API_KEY 환경변수 필요');
|
|
process.exit(1);
|
|
}
|
|
|
|
startWebSocket();
|