AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
120
skills/openclaw-self-healing/scripts/realtime-tqqq-monitor.js
Normal file
120
skills/openclaw-self-healing/scripts/realtime-tqqq-monitor.js
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Finnhub 실시간 TQQQ/QQQ 모니터
|
||||
* Usage: FINNHUB_API_KEY=your_key node realtime-tqqq-monitor.js
|
||||
*/
|
||||
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const FINNHUB_API_KEY = process.env.FINNHUB_API_KEY || 'd62ho41r01qlugeq3ge0d62ho41r01qlugeq3geg';
|
||||
|
||||
if (!FINNHUB_API_KEY) {
|
||||
console.error('❌ Error: FINNHUB_API_KEY가 설정되지 않았습니다.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const socket = new WebSocket(`wss://ws.finnhub.io?token=${FINNHUB_API_KEY}`);
|
||||
|
||||
const prices = {
|
||||
TQQQ: null,
|
||||
QQQ: null,
|
||||
};
|
||||
|
||||
const STOP_LOSS_PERCENT = -10; // -10% 손절선
|
||||
let initialPortfolio = 9000; // $9,000
|
||||
|
||||
socket.on('open', () => {
|
||||
console.log('✅ Finnhub WebSocket 연결 성공!');
|
||||
console.log('📊 실시간 모니터링 시작...\n');
|
||||
|
||||
// TQQQ, QQQ 구독
|
||||
socket.send(JSON.stringify({ type: 'subscribe', symbol: 'TQQQ' }));
|
||||
socket.send(JSON.stringify({ type: 'subscribe', symbol: 'QQQ' }));
|
||||
});
|
||||
|
||||
socket.on('message', (data) => {
|
||||
try {
|
||||
const message = JSON.parse(data);
|
||||
|
||||
if (message.type === 'trade' && message.data) {
|
||||
message.data.forEach(trade => {
|
||||
const symbol = trade.s;
|
||||
const price = trade.p;
|
||||
const volume = trade.v;
|
||||
const timestamp = new Date(trade.t).toLocaleTimeString('ko-KR');
|
||||
|
||||
// 가격 업데이트
|
||||
if (symbol === 'TQQQ' || symbol === 'QQQ') {
|
||||
const prevPrice = prices[symbol];
|
||||
prices[symbol] = price;
|
||||
|
||||
const change = prevPrice ? ((price - prevPrice) / prevPrice * 100).toFixed(2) : '0.00';
|
||||
const changeIcon = parseFloat(change) >= 0 ? '📈' : '📉';
|
||||
|
||||
console.log(`${changeIcon} ${symbol}: $${price.toFixed(2)} | 변동: ${change}% | 거래량: ${volume.toLocaleString()} | ${timestamp}`);
|
||||
|
||||
// 손절선 체크 (TQQQ만)
|
||||
if (symbol === 'TQQQ') {
|
||||
checkStopLoss(price);
|
||||
checkTargets(price);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error parsing message:', error.message);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
console.error('❌ WebSocket Error:', error.message);
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
console.log('🔌 WebSocket 연결 종료');
|
||||
});
|
||||
|
||||
// 손절선 체크
|
||||
function checkStopLoss(currentPrice) {
|
||||
// 예시: $46 평단가 기준
|
||||
const avgPrice = 46;
|
||||
const stopLossPrice = avgPrice * (1 + STOP_LOSS_PERCENT / 100);
|
||||
const currentLoss = ((currentPrice - avgPrice) / avgPrice * 100).toFixed(2);
|
||||
|
||||
if (currentPrice <= stopLossPrice) {
|
||||
console.log('\n🚨🚨🚨 손절선 터짐! 🚨🚨🚨');
|
||||
console.log(`현재가: $${currentPrice.toFixed(2)}`);
|
||||
console.log(`손절선: $${stopLossPrice.toFixed(2)}`);
|
||||
console.log(`손실: ${currentLoss}%`);
|
||||
console.log('⚠️ 즉시 전량 매도 필요!\n');
|
||||
} else if (currentPrice <= stopLossPrice * 1.02) {
|
||||
console.log(`\n⚠️ 경고: 손절선 2% 근접 ($${currentPrice.toFixed(2)} vs $${stopLossPrice.toFixed(2)})\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// 목표가 체크
|
||||
function checkTargets(currentPrice) {
|
||||
const avgPrice = 46;
|
||||
const targets = [
|
||||
{ price: 50, name: '1차 목표', action: '30% 매도' },
|
||||
{ price: 54, name: '2차 목표 (복구)', action: '50% 매도' },
|
||||
{ price: 58, name: '3차 목표', action: '전량 매도' },
|
||||
];
|
||||
|
||||
targets.forEach(target => {
|
||||
if (currentPrice >= target.price && currentPrice <= target.price * 1.01) {
|
||||
const profit = ((currentPrice - avgPrice) / avgPrice * 100).toFixed(2);
|
||||
console.log(`\n🎯 ${target.name} 도달!`);
|
||||
console.log(`현재가: $${currentPrice.toFixed(2)} | 수익: +${profit}%`);
|
||||
console.log(`액션: ${target.action}\n`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ctrl+C로 종료 시
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n👋 모니터링 종료...');
|
||||
socket.close();
|
||||
process.exit(0);
|
||||
});
|
||||
Reference in New Issue
Block a user