AI에게 좋은 질문을 하려면, 먼저 "뭘 물어볼지" 알아야 합니다. 핵심 지표 6개만 기억하세요.
| 지표 | 뜻 (한 줄) | 기준선 |
|---|---|---|
| PER | 주가 ÷ 주당순이익 "몇 년 벌면 본전인가?" |
업종 평균보다 낮으면 → 저평가 |
| PBR | 주가 ÷ 주당순자산 "회사 문 닫으면 남는 돈은?" |
1.0 미만 → 자산 대비 싼 편 |
| ROE | 순이익 ÷ 자기자본 "내 돈으로 얼마나 잘 버나?" |
15%+ → 우량 기업 |
| 배당수익률 | 배당금 ÷ 주가 "들고 있으면 매년 얼마?" |
3%+ → 고배당 |
| 매출성장률 | 전년 대비 매출 증가율 "회사가 얼마나 빨리 크나?" |
20%+ → 고성장 |
| 부채비율 | 총부채 ÷ 자기자본 "빚이 감당할 만한가?" |
100% 미만 → 안정적 |
기업을 고르는 관점은 크게 5가지입니다. AI에게 "어떤 기준으로 찾아줘"라고 말할 때 이 분류를 사용합니다.
| 스타일 | 핵심 기준 | AI에게 물어볼 질문 |
|---|---|---|
| 저평가 | 낮은 PER/PBR + 안정적 수익 |
"시장이 놓치고 있는 싼 주식은?" |
| 고배당 | 배당수익률 3%+ + 배당 지속성 |
"매년 꾸준히 배당 주는 기업은?" |
| 고성장 | 매출/이익 20%+ 성장률 |
"지금 폭발적으로 크는 기업은?" |
| 턴어라운드 | 적자→흑자 전환 or 구조조정 |
"바닥 찍고 올라오는 기업은?" |
| 업종 대장 | 시장점유율 1위 + 해자 |
"이 업종에서 안 망할 기업은?" |
아래 두 프롬프트를 직접 AI에 넣어보세요. 차이가 확 느껴집니다.
| 요소 | 설명 | 예시 |
|---|---|---|
| ① 역할 부여 | AI에게 전문가 역할을 줌 | "너는 한국 주식 시장 전문 애널리스트야" |
| ② 기준 제시 | 분석할 항목을 번호로 나열 | "PER, PBR, ROE를 업종 평균과 비교해줘" |
| ③ 출력 형식 | 답변 형태를 지정 | "표 형태로 정리하고 ★5점 평가해줘" |
{기업명} 부분만 바꿔서 어떤 기업이든 분석할 수 있습니다.
"시장이 놓치고 있는 좋은 기업"을 AI에게 찾아달라고 하는 방법입니다.
"매년 꾸준히 현금을 주는 기업"을 찾는 프롬프트입니다. 은퇴 준비나 안정적 수익에 적합합니다.
"지금 폭발적으로 크고 있는 기업"을 찾는 프롬프트입니다. 수익 극대화를 원할 때 사용합니다.
"바닥을 찍고 반등하는 기업"을 찾는 프롬프트입니다. 높은 수익을 노리지만 리스크도 가장 큽니다.
개별 기업 전에 "어떤 업종이 좋은지"부터 파악하는 탑다운 접근법입니다.
각 AI마다 강점이 다릅니다. 상황에 맞는 도구를 골라 쓰세요.
| AI 도구 | 기업 분석 강점 | 약점 | 추천 용도 |
|---|---|---|---|
| Perplexity | 실시간 데이터 출처 링크 제공 |
깊은 분석은 약함 | 최신 실적/뉴스 확인 |
| ChatGPT | 분석 구조화 잘함 GPTs 활용 가능 |
실시간 데이터 부정확 가능 | 분석 프레임 만들기 |
| Gemini | 구글 검색 연동 실시간 강점 |
한국 주식 데이터 가끔 부정확 | 최신 뉴스 + 실적 확인 |
| Claude | 논리적 분석 표 정리 최고 |
실시간 데이터 제한적 | 분석 보고서 작성 |
| 상황 | 추가할 문장 |
|---|---|
| 답변이 너무 길 때 | "핵심만 표 1개로 요약해줘" |
| 답변이 너무 일반적일 때 | "구체적 숫자와 근거를 포함해줘" |
| 리스크를 알고 싶을 때 | "이 기업에 투자하면 안 되는 이유 3가지도 알려줘" |
| 비교가 필요할 때 | "경쟁사 대비 장단점을 표로 비교해줘" |
| 초보자 눈높이로 | "주식 초보자도 이해할 수 있게 쉽게 설명해줘" |
| 최신 정보가 필요할 때 | "2024년 최신 실적 기준으로 분석해줘" |
| 후속 분석이 필요할 때 | "위 분석에서 1위 기업을 더 깊이 분석해줘" |
종목명만 입력하면 AI가 자동으로 120점 만점 분석을 수행합니다. 수업에서는 ★5점으로 간소화해서 봅니다.
| 평가 영역 | 배점 | 세부 항목 |
|---|---|---|
| 시장 지배력 | 20점 | 시장점유율, 업종 내 순위, 경쟁 강도 |
| 수익성 | 20점 | 영업이익률, ROE, 순이익 추이 |
| 밸류에이션 | 20점 | PER·PBR 업종 대비, 역사적 밴드 위치 |
| 성장성 | 15점 | 매출·이익 성장률, 신사업·파이프라인 |
| 재무 안정성 | 15점 | 부채비율, 유동비율, 현금흐름 |
| 배당 매력 | 10점 | 배당수익률, 배당 지속성·성장성 |
| 🌟 보너스 | +20점 | 기술 해자(+10), 얼리버드 저평가(+10) |
| 합계 | 120점 |
| 등급 | 점수 범위 | ★ 환산 | 의미 |
|---|---|---|---|
| S | 100~120점 | ★★★★★ | 최상위 종목 — 강력 관심 |
| A | 85~99점 | ★★★★☆ | 우수 종목 — 관심 목록 |
| B | 70~84점 | ★★★☆☆ | 양호 — 조건부 관심 |
| C | 55~69점 | ★★☆☆☆ | 보통 — 개선 필요 |
| D | 54점 이하 | ★☆☆☆☆ | 부진 — 주의 필요 |
아래 프롬프트를 GPTs, Gems, Projects에 시스템 지침으로 넣으면 종목명만 입력해도 자동 분석됩니다.
스코어링 프롬프트를 각 AI에 "기본 지침"으로 세팅하면, 이후엔 종목명만 입력해도 자동 분석됩니다.
| 항목 | ChatGPT | Gemini | Claude | Grok |
|---|---|---|---|---|
| 설정 기능 | GPTs | Gems | Projects | 없음 (매번 입력) |
| 저장 여부 | ✅ 영구 저장 | ✅ 영구 저장 | ✅ 영구 저장 | ❌ 대화마다 |
| 실시간 검색 | ✅ 웹 검색 | ✅ 구글 연동 | ✅ 웹 검색 | ✅ X + 웹 |
| 공유 가능 | ✅ 링크 공유 | ❌ | ❌ | ❌ |
| 무료 사용 | ⚠️ 제한적 | ✅ 무료 가능 | ⚠️ 제한적 | ✅ 무료 가능 |
| 추천 용도 | 구조화 분석 | 실시간 데이터 | 깊은 논리 분석 | 시장 심리 분석 |
설정 완료 후 실제로 어떻게 사용하는지, 입력과 출력 예시를 보여드립니다.
| 평가 영역 | 점수 | 배점 | 근거 |
|---|---|---|---|
| 시장 지배력 | 18 | /20 | 메모리 반도체 글로벌 1위 |
| 수익성 | 14 | /20 | ROE 8%, 영업이익률 12% |
| 밸류에이션 | 14 | /20 | PER 업종 평균 수준, PBR 1.2 |
| 성장성 | 12 | /15 | HBM·AI 반도체 수요 급증 |
| 재무 안정성 | 13 | /15 | 부채비율 35%, FCF 양수 |
| 배당 매력 | 6 | /10 | 배당수익률 약 2%, 꾸준한 배당 |
| 🌟 기술 해자 | +8 | /10 | EUV 공정, HBM3E 양산 기술 |
| 합계 | 85점 | /120 | A등급 ★★★★☆ |
| 상황 | 후속 질문 (복사해서 사용) |
|---|---|
| 점수 근거가 궁금할 때 | "수익성 점수가 낮은 이유를 더 자세히 설명해줘" |
| 적정 주가를 알고 싶을 때 | "PER 밴드 기준으로 적정 주가를 추정해줘" |
| 경쟁사와 비교하고 싶을 때 | "같은 업종 1위, 2위, 3위 기업과 스코어를 비교해줘" |
| 리스크를 더 알고 싶을 때 | "이 기업에 투자하면 안 되는 이유 5가지를 알려줘" |
| 최근 이슈 반영 | "최근 3개월 뉴스/이슈를 반영해서 점수를 재평가해줘" |
| 포트폴리오 구성 | "위 분석 기준으로 1,000만원 포트폴리오를 구성해줘" |
| Grok 전용 질문 | "X(트위터)에서 이 종목에 대한 최근 투자자 반응도 분석해줘" |
결과물을 확인하고 스크립트를 복사하세요.
// =============================================
// 📊 PlanX 시황 요약 리포트 v3 (최종)
//
// 시세: 네이버증권(국내) + GOOGLEFINANCE(해외/환율)
// 분석: Gemini AI (Google Search grounding)
//
// [사용법]
// 1. Google 스프레드시트에서 확장프로그램 → Apps Script
// 2. 이 코드 전체를 붙여넣기
// 3. CONFIG에서 API키와 이메일만 수정
// 4. testFetchData 먼저 실행 → 시세 확인
// 5. testRun 실행 → 이메일 발송
// =============================================
var CONFIG = {
GEMINI_API_KEY: '여기에_API키_붙여넣기',
MODEL: 'gemini-2.5-flash',
SEND_TO: '여기에_이메일_입력',
};
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 1단계: 시세 데이터 수집
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 네이버 증권 API (국내 지수)
function fetchNaverIndex(market) {
try {
var url = 'https://m.stock.naver.com/api/index/' + market + '/basic';
var res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
var j = JSON.parse(res.getContentText());
return {
value: j.closePrice || '',
change: j.compareToPreviousClosePrice || '',
changePercent: j.fluctuationsRatio || '',
date: j.localTradedAt || ''
};
} catch(e) {
return {value: '-', change: '-', changePercent: '-'};
}
}
// Google Sheets GOOGLEFINANCE (해외 지수, 환율)
function fetchGoogleFinance(ticker) {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tempSheet = ss.getSheetByName('_temp_finance');
if (!tempSheet) {
tempSheet = ss.insertSheet('_temp_finance');
}
// 현재가
tempSheet.getRange('A1').setFormula('=GOOGLEFINANCE("' + ticker + '","price")');
// 전일 종가
tempSheet.getRange('A2').setFormula('=GOOGLEFINANCE("' + ticker + '","closeyest")');
SpreadsheetApp.flush();
Utilities.sleep(2000); // 데이터 로딩 대기
var price = tempSheet.getRange('A1').getValue();
var prevClose = tempSheet.getRange('A2').getValue();
// #N/A 또는 에러 체크
if (!price || typeof price === 'string' && price.indexOf('N/A') >= 0) {
tempSheet.clear();
return {value: '-', change: '-', changePercent: '-'};
}
var change = 0;
var changePct = 0;
if (price && prevClose && prevClose !== 0) {
change = price - prevClose;
changePct = (change / prevClose) * 100;
}
// 정리
tempSheet.clear();
return {
value: typeof price === 'number' ? price.toFixed(2) : String(price),
change: change >= 0 ? '+' + change.toFixed(2) : change.toFixed(2),
changePercent: changePct >= 0 ? '+' + changePct.toFixed(2) : changePct.toFixed(2)
};
} catch(e) {
return {value: '-', change: '-', changePercent: '-'};
}
}
// 전체 시세 수집
function fetchMarketData() {
var data = {};
// 국내 (네이버 증권 - 정확)
data.kospi = fetchNaverIndex('KOSPI');
data.kosdaq = fetchNaverIndex('KOSDAQ');
// 해외 (GOOGLEFINANCE)
data.sp500 = fetchGoogleFinance('.INX');
data.nasdaq = fetchGoogleFinance('.IXIC');
data.dji = fetchGoogleFinance('.DJI');
// 환율 (여러 방법 시도)
data.usdkrw = fetchExchangeRate();
// 투자자별 매매동향 (네이버)
data.investor = fetchInvestorFlow();
return data;
}
// 환율 수집 (네이버 시장지표 크롤링)
function fetchExchangeRate() {
// 방법1: 네이버 시장지표 HTML 크롤링
try {
var url = 'https://finance.naver.com/marketindex/';
var res = UrlFetchApp.fetch(url, {
muteHttpExceptions: true,
headers: {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
});
var html = res.getContentText();
// 원/달러 환율 추출 (첫 번째 blind 값이 USD/KRW)
var matches = html.match(/class="value">[0-9,\.]+/g);
if (matches && matches.length > 0) {
var val = matches[0].replace('class="value">', '');
// 등락폭 추출
var changeMatches = html.match(/class="change">[0-9,\.]+/g);
var chg = changeMatches && changeMatches[0] ? changeMatches[0].replace('class="change">', '') : '';
// 상승/하락 판단
var isDown = html.indexOf('ico_down') < html.indexOf('ico_up') || html.indexOf('downdown') > -1;
return {
value: val,
change: chg ? (isDown ? '-' + chg : '+' + chg) : '',
changePercent: ''
};
}
} catch(e) { Logger.log('네이버 환율 크롤링 실패: ' + e.message); }
// 방법2: 두나무 API
try {
var url2 = 'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD';
var res2 = UrlFetchApp.fetch(url2, {muteHttpExceptions: true});
if (res2.getResponseCode() === 200) {
var j = JSON.parse(res2.getContentText());
if (j && j[0]) {
return {
value: String(j[0].basePrice || ''),
change: String(j[0].change || ''),
changePercent: String(j[0].changePercent || '')
};
}
}
} catch(e) {}
// 방법3: GOOGLEFINANCE
var tickers = ['USDKRW', 'KRW=X', 'CURRENCY:USDKRW'];
for (var i = 0; i < tickers.length; i++) {
try {
var r = fetchGoogleFinance(tickers[i]);
if (r.value && r.value !== '-' && String(r.value).indexOf('N/A') < 0) return r;
} catch(e) {}
}
return {value: 'Gemini 검색', change: '-', changePercent: '-'};
}
function fetchInvestorFlow() {
try {
var url = 'https://m.stock.naver.com/api/index/KOSPI/investorBuySellDaily';
var res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
var json = JSON.parse(res.getContentText());
if (json && json.length > 0) {
var latest = json[0];
return {
foreigner: latest.foreignerPureBuyQuant || 0,
institution: latest.institutionPureBuyQuant || 0,
individual: latest.individualPureBuyQuant || 0,
date: latest.tradingDate || ''
};
}
} catch(e) {}
// 실패 시 Gemini에게 맡기기
return {foreigner: 'API 미제공', institution: 'API 미제공', individual: 'API 미제공', date: ''};
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 2단계: Gemini 분석
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
function callGemini(prompt) {
if (!prompt) throw new Error('프롬프트가 비어있습니다.');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/'
+ CONFIG.MODEL + ':generateContent?key=' + CONFIG.GEMINI_API_KEY;
var res = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({
contents: [{parts: [{text: String(prompt)}]}],
generationConfig: {temperature: 0.3},
tools: [{google_search: {}}]
}),
muteHttpExceptions: true
});
var code = res.getResponseCode();
var raw = res.getContentText();
if (code !== 200) throw new Error('Gemini 오류 (' + code + '): ' + raw.substring(0, 300));
var data = JSON.parse(raw);
var text = '';
var parts = data.candidates[0].content.parts || [];
for (var i = 0; i < parts.length; i++) {
if (parts[i].text) text += parts[i].text;
}
text = text.replace(/```json/g, '').replace(/```/g, '').trim();
var jsonStart = text.indexOf('{');
var jsonEnd = text.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
text = text.substring(jsonStart, jsonEnd + 1);
}
try {
return JSON.parse(text);
} catch(e) {
throw new Error('JSON 파싱 실패. 응답: ' + text.substring(0, 300));
}
}
function buildPrompt(md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E)');
var invText = '';
if (md.investor.foreigner !== 'API 미제공') {
var f = Number(md.investor.foreigner);
var inst = Number(md.investor.institution);
var ind = Number(md.investor.individual);
invText = '외국인: ' + (f >= 0 ? '순매수 ' : '순매도 ') + Math.abs(Math.round(f/100000000)).toLocaleString() + '억원';
invText += ' / 기관: ' + (inst >= 0 ? '순매수 ' : '순매도 ') + Math.abs(Math.round(inst/100000000)).toLocaleString() + '억원';
invText += ' / 개인: ' + (ind >= 0 ? '순매수 ' : '순매도 ') + Math.abs(Math.round(ind/100000000)).toLocaleString() + '억원';
} else {
invText = '(수급 데이터 API 미제공. 반드시 구글 검색으로 오늘 코스피 외국인/기관/개인 순매수 순매도 금액을 KRX/네이버증권에서 확인하여 정확한 수치를 제공하라.)';
}
var p = '';
p += '[필수 지침]\n';
p += '1. 모든 응답은 반드시 한국어. 영어 절대 금지.\n';
p += '2. 아래 실시간 시세는 네이버증권/구글파이낸스에서 가져온 정확한 데이터다. 그대로 사용.\n';
p += '3. 분석/해석/종목추천만 네가 담당. 시세 수치는 절대 변경하지 마라.\n';
p += '4. 구글 검색으로 오늘 주요 뉴스, 공시, 증권사 리포트를 확인하여 반영.\n';
p += '5. 증권사 리포트 (미래에셋, 삼성, KB, 한투, NH) 분석 참조.\n';
p += '6. 매크로(금리/환율/유가) + 수급 + 테마 관점 종합 분석.\n';
p += '7. 반드시 오늘(' + today + ') 데이터만 사용. 전일 데이터를 오늘로 혼동 금지.\n\n';
p += '[날짜] ' + today + '\n\n';
p += '[실시간 시세 데이터]\n';
p += '코스피: ' + md.kospi.value + ' (전일대비 ' + md.kospi.change + ', ' + md.kospi.changePercent + '%) [기준: ' + (md.kospi.date||today) + ']\n';
p += '코스닥: ' + md.kosdaq.value + ' (전일대비 ' + md.kosdaq.change + ', ' + md.kosdaq.changePercent + '%) [기준: ' + (md.kosdaq.date||today) + ']\n';
p += 'S&P500: ' + md.sp500.value + ' (' + md.sp500.change + ', ' + md.sp500.changePercent + '%)\n';
p += '나스닥: ' + md.nasdaq.value + ' (' + md.nasdaq.change + ', ' + md.nasdaq.changePercent + '%)\n';
p += '다우: ' + md.dji.value + ' (' + md.dji.change + ', ' + md.dji.changePercent + '%)\n';
var fxText = md.usdkrw.value;
if (!fxText || fxText === 'Gemini 검색' || fxText === '-') {
p += '원/달러: (구글 검색으로 확인)\n';
} else {
p += '원/달러: ' + md.usdkrw.value + ' (' + md.usdkrw.change + ')\n';
}
p += '수급: ' + invText + '\n\n';
p += '[역할] 증권사 수석 스트래티지스트. 기관 투자자 대상 시황 리포트.\n\n';
p += '[분석 요구사항]\n';
p += '- 시장 분위기: 공포/탐욕/관망/혼조 판단 + 근거 (VIX, 풋콜비율 등)\n';
p += '- 수급 해석: 외국인/기관/개인 각각의 매매 의미와 향후 수급 전망\n';
p += '- 주목 종목 3개: 증권사 리포트/수급/테마 기반, 구체적 매매 전략 포함\n';
p += '- 키워드 3개: 오늘 시장을 움직인 핵심 테마 + 관련 종목 체인\n';
p += '- 총평 3문장: (1)시장 핵심 (2)수급 해석 (3)내일 전략\n\n';
p += 'JSON으로만 응답:\n{\n';
p += ' "one_line": "시장 한줄 요약 (위 시세 수치 반드시 포함)",\n';
p += ' "mood": "공포/탐욕/관망/혼조",\n';
p += ' "mood_score": 50,\n';
p += ' "trend": "상승/하락/횡보",\n';
p += ' "investor_flow": {\n';
p += ' "foreigner": {"action":"순매수/순매도","amount":"X,XXX억원","analysis":"외국인 매매 배경과 의미 + 향후 전망 2문장"},\n';
p += ' "institution": {"action":"순매수/순매도","amount":"X,XXX억원","analysis":"기관 매매 배경과 의미 2문장"},\n';
p += ' "individual": {"action":"순매수/순매도","amount":"X,XXX억원","analysis":"개인 매매 특징과 시사점 2문장"}\n';
p += ' },\n';
p += ' "hot_picks": [\n';
p += ' {"name":"종목명","reason":"구체적 이유 (수급+실적+테마 근거)","strategy":"매수/관망/주의 + 목표가 또는 기준가"},\n';
p += ' {"name":"종목명","reason":"구체적 이유","strategy":"매수/관망/주의"},\n';
p += ' {"name":"종목명","reason":"구체적 이유","strategy":"매수/관망/주의"}\n';
p += ' ],\n';
p += ' "keywords": [\n';
p += ' {"keyword":"테마 키워드","related":"관련 종목 3~5개","catalyst":"촉매 이벤트"},\n';
p += ' {"keyword":"테마 키워드","related":"관련 종목","catalyst":"촉매"},\n';
p += ' {"keyword":"테마 키워드","related":"관련 종목","catalyst":"촉매"}\n';
p += ' ],\n';
p += ' "summary": ["총평1 (시장 핵심 흐름 + 수치)","총평2 (수급 분석 + 의미)","총평3 (내일 전략 + 주목 포인트)"]\n';
p += '}';
p += '\n\n위 JSON 형식으로만 응답하라. JSON 외에 어떤 텍스트도 포함하지 마라. 순수 JSON만 출력.';
return p;
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 3단계: HTML 이메일 빌더
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
function buildHTML(ai, md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E) HH:mm');
var moodColors = {'공포':'#DC2626','탐욕':'#16A34A','관망':'#78716C','혼조':'#D97706'};
var moodColor = moodColors[ai.mood] || '#78716C';
function chgColor(v) { return String(v||'').indexOf('-')>=0 ? '#DC2626' : '#16A34A'; }
function sec(icon, title) { return '<div style="background:#0B1426;color:#F5A623;padding:8px 14px;font-size:14px;font-weight:bold;border-radius:6px;margin-top:18px;">'+icon+' '+title+'</div>'; }
function th(cols) {
var h='<tr style="background:#1E293B;color:#CBD5E1;">';
for(var i=0;i<cols.length;i++) h+='<th style="padding:7px 12px;text-align:left;font-weight:500;">'+cols[i]+'</th>';
return h+'</tr>';
}
// 지수 테이블 (실제 데이터)
var indices = [
{n:'코스피',d:md.kospi}, {n:'코스닥',d:md.kosdaq},
{n:'S&P500',d:md.sp500}, {n:'나스닥',d:md.nasdaq},
{n:'다우',d:md.dji}, {n:'원/달러',d:md.usdkrw}
];
var idxR = '';
for(var i=0;i<indices.length;i++){
var x=indices[i], d=x.d||{}, bg=i%2===0?'#fff':'#F8FAFC', cc=chgColor(d.change);
idxR+='<tr style="background:'+bg+';"><td style="padding:8px 12px;font-weight:600;border-bottom:1px solid #E2E8F0;">'+x.n+'</td>';
idxR+='<td style="padding:8px 12px;font-weight:bold;border-bottom:1px solid #E2E8F0;">'+(d.value||'-')+(d.date?' <span style="font-size:10px;color:#94A3B8;">('+d.date.substring(5)+')</span>':'')+'</td>';
idxR+='<td style="padding:8px 12px;color:'+cc+';font-weight:bold;border-bottom:1px solid #E2E8F0;">'+(d.change||'-')+'</td>';
idxR+='<td style="padding:8px 12px;color:'+cc+';border-bottom:1px solid #E2E8F0;">'+(d.changePercent?d.changePercent+'%':'-')+'</td></tr>';
}
// 수급 테이블 (API 데이터 또는 AI 분석) — 카드형 디자인
var inv = md.investor||{};
var aiInv = ai.investor_flow||{};
var monR = '';
function fmtB(v){
var n=Number(v); if(isNaN(n)) return String(v);
var b=Math.round(n/100000000);
var formatted = Math.abs(b).toLocaleString ? Math.abs(b).toLocaleString() : Math.abs(b);
return (b>=0?'+':'-') + formatted + '억원';
}
var investorList = [
{name:'외국인', emoji:'🌍', apiVal:inv.foreigner, aiData:aiInv.foreigner},
{name:'기관', emoji:'🏦', apiVal:inv.institution, aiData:aiInv.institution},
{name:'개인', emoji:'👤', apiVal:inv.individual, aiData:aiInv.individual}
];
for(var i=0;i<investorList.length;i++){
var item=investorList[i];
var action, amount, analysis, actionColor, actionBg;
if(item.apiVal && item.apiVal !== 'API 미제공') {
var n = Number(item.apiVal);
action = n >= 0 ? '순매수' : '순매도';
actionColor = n >= 0 ? '#16A34A' : '#DC2626';
actionBg = n >= 0 ? '#DCFCE7' : '#FEE2E2';
amount = fmtB(item.apiVal);
analysis = (item.aiData && item.aiData.analysis) ? item.aiData.analysis : '';
} else {
var ad = item.aiData || {};
action = ad.action || '-';
actionColor = (action.indexOf('매수')>=0) ? '#16A34A' : (action.indexOf('매도')>=0) ? '#DC2626' : '#78716C';
actionBg = (action.indexOf('매수')>=0) ? '#DCFCE7' : (action.indexOf('매도')>=0) ? '#FEE2E2' : '#F3F4F6';
amount = ad.amount || '(AI 추정)';
analysis = ad.analysis || '';
}
monR+='<div style="display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-bottom:1px solid #F1F5F9;'+(i===0?'':'')+ '">';
monR+='<div style="flex-shrink:0;width:44px;height:44px;border-radius:10px;background:'+actionBg+';display:flex;align-items:center;justify-content:center;font-size:20px;">'+item.emoji+'</div>';
monR+='<div style="flex:1;min-width:0;">';
monR+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;">';
monR+='<span style="font-size:15px;font-weight:700;color:#0F172A;">'+item.name+'</span>';
monR+='<span style="background:'+actionBg+';color:'+actionColor+';padding:2px 10px;border-radius:12px;font-size:12px;font-weight:700;">'+action+'</span>';
monR+='<span style="font-size:15px;font-weight:800;color:'+actionColor+';">'+amount+'</span>';
monR+='</div>';
if(analysis) {
monR+='<div style="font-size:13px;color:#64748B;line-height:1.6;">'+analysis+'</div>';
}
monR+='</div></div>';
}
// 종목 — 카드형
var picks=ai.hot_picks||[], pkR='';
var strategyStyle = {
'매수': 'background:#DCFCE7;color:#16A34A;',
'관망': 'background:#FEF3C7;color:#92400E;',
'주의': 'background:#FEE2E2;color:#DC2626;'
};
for(var i=0;i<picks.length;i++){
var p=picks[i];
var sStyle = strategyStyle[p.strategy] || 'background:#EFF6FF;color:#1D4ED8;';
pkR+='<div style="padding:14px 16px;border-bottom:1px solid #F1F5F9;">';
pkR+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">';
pkR+='<span style="background:#0B1426;color:#F5A623;width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;flex-shrink:0;">'+(i+1)+'</span>';
pkR+='<span style="font-size:16px;font-weight:800;color:#0F172A;">'+p.name+'</span>';
pkR+='<span style="'+sStyle+'padding:2px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-left:auto;">'+p.strategy+'</span>';
pkR+='</div>';
pkR+='<div style="font-size:13px;color:#475569;line-height:1.7;padding-left:34px;">'+p.reason+'</div>';
pkR+='</div>';
}
// 키워드 — 태그형
var kws=ai.keywords||[], kwH='';
var kwColors = ['#1a73e8','#7C3AED','#059669','#D97706','#DC2626'];
for(var i=0;i<kws.length;i++){
var kc = kwColors[i % kwColors.length];
kwH+='<div style="padding:10px 14px;border-left:4px solid '+kc+';background:#F8FAFC;border-radius:0 8px 8px 0;margin:6px 0;">';
kwH+='<span style="font-size:14px;font-weight:700;color:'+kc+';">'+kws[i].keyword+'</span>';
kwH+='<span style="color:#94A3B8;margin:0 6px;">→</span>';
kwH+='<span style="font-size:13px;color:#475569;">'+kws[i].related+'</span>';
kwH+='</div>';
}
// 총평 — 번호 카드형
var sums=ai.summary||[], smH='';
var sumIcons = ['📊','💰','🔮'];
for(var i=0;i<sums.length;i++){
smH+='<div style="display:flex;gap:10px;padding:10px 0;'+(i<sums.length-1?'border-bottom:1px solid #F1F5F9;':'')+'">';
smH+='<div style="flex-shrink:0;width:32px;height:32px;border-radius:8px;background:#EFF6FF;display:flex;align-items:center;justify-content:center;font-size:16px;">'+(sumIcons[i]||'📌')+'</div>';
smH+='<div style="font-size:14px;color:#334155;line-height:1.8;">'+sums[i]+'</div>';
smH+='</div>';
}
// 조립
var h='<div style="max-width:680px;margin:16px auto;font-family:Arial,sans-serif;">';
// 헤더
h+='<div style="background:linear-gradient(135deg,#0B1426,#1E3A5F);padding:20px 24px;border-radius:12px 12px 0 0;">';
h+='<div style="display:inline-block;background:#1a73e8;color:white;font-size:12px;font-weight:900;padding:3px 12px;border-radius:5px;letter-spacing:1.5px;">PlanX</div>';
h+='<div style="color:#F5A623;font-size:20px;font-weight:bold;margin-top:10px;">📊 시황 요약 리포트</div>';
h+='<div style="color:#94A3B8;font-size:12px;margin-top:2px;">'+today+' · 네이버증권+구글파이낸스 실시간</div>';
h+='<div style="margin-top:10px;"><span style="background:'+moodColor+';color:white;padding:4px 16px;border-radius:20px;font-size:14px;font-weight:bold;">'+(ai.mood||'')+' '+(ai.mood_score||'')+'점</span>';
h+='<span style="color:#94A3B8;font-size:12px;margin-left:10px;">'+(ai.trend||'')+'</span></div>';
h+='<div style="background:rgba(255,255,255,0.08);border-radius:8px;padding:12px 16px;margin-top:14px;"><span style="color:#F5A623;font-size:15px;font-weight:bold;">💡 '+(ai.one_line||'')+'</span></div>';
h+='</div>';
// 본문
h+='<div style="background:#fff;border:1px solid #E2E8F0;padding:20px;">';
h+=sec('📈','주요 지수 (실시간)');
h+='<table style="width:100%;border-collapse:collapse;font-size:13px;margin-top:2px;">'+th(['지수','현재','전일대비','등락률'])+idxR+'</table>';
h+=sec('💰','투자자별 매매동향');
h+='<div style="background:#FFFFFF;border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">'+monR+'</div>';
h+=sec('🔥','주목 종목 (AI 분석)');
h+='<div style="background:#FFFFFF;border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">'+pkR+'</div>';
h+=sec('🏷️','핵심 키워드');
h+='<div style="padding:2px 0;">'+kwH+'</div>';
h+=sec('📝','총평');
h+='<div style="padding:8px 12px;">'+smH+'</div>';
h+='</div>';
// 푸터
h+='<div style="background:#F8FAFC;padding:12px 20px;border-radius:0 0 12px 12px;border:1px solid #E2E8F0;border-top:none;text-align:center;">';
h+='<div style="color:#94A3B8;font-size:11px;">⚠️ AI 분석 참고자료 · 투자 판단과 책임은 본인에게 있습니다</div>';
h+='<div style="color:#94A3B8;font-size:10px;margin-top:2px;">시세: 네이버증권+GOOGLEFINANCE · 분석: Gemini</div>';
h+='<div style="color:#FCD34D;font-size:11px;font-weight:600;margin-top:4px;">© 2026 PlanX</div>';
h+='</div></div>';
return h;
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 실행 함수들
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
function run_market() {
Logger.log('📡 1단계: 시세 수집...');
var md = fetchMarketData();
Logger.log('코스피: ' + md.kospi.value + ' / 코스닥: ' + md.kosdaq.value);
Logger.log('S&P500: ' + md.sp500.value + ' / 나스닥: ' + md.nasdaq.value);
Logger.log('원달러: ' + md.usdkrw.value);
Logger.log('🤖 2단계: Gemini 분석...');
var prompt = buildPrompt(md);
var ai = callGemini(prompt);
Logger.log('분위기: ' + ai.mood + ' ' + ai.mood_score + '점');
Logger.log('📧 3단계: 이메일 발송...');
var html = buildHTML(ai, md);
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy-MM-dd');
MailApp.sendEmail({
to: CONFIG.SEND_TO,
subject: '[PlanX] 📊 시황요약 (' + today + ')',
htmlBody: html
});
Logger.log('✅ 완료!');
}
// ▶ 이 함수를 실행하세요
function testRun() {
run_market();
}
// 시세만 테스트 (API키 없이도 실행 가능)
function testFetchData() {
var d = fetchMarketData();
Logger.log('=== 국내 (네이버증권) ===');
Logger.log('코스피: ' + d.kospi.value + ' (' + d.kospi.change + ', ' + d.kospi.changePercent + '%)');
Logger.log('코스닥: ' + d.kosdaq.value + ' (' + d.kosdaq.change + ', ' + d.kosdaq.changePercent + '%)');
Logger.log('=== 해외 (GOOGLEFINANCE) ===');
Logger.log('S&P500: ' + d.sp500.value + ' (' + d.sp500.change + ', ' + d.sp500.changePercent + '%)');
Logger.log('나스닥: ' + d.nasdaq.value + ' (' + d.nasdaq.change + ', ' + d.nasdaq.changePercent + '%)');
Logger.log('다우: ' + d.dji.value + ' (' + d.dji.change + ', ' + d.dji.changePercent + '%)');
Logger.log('=== 환율 ===');
Logger.log('원/달러: ' + d.usdkrw.value + ' (' + d.usdkrw.change + ')');
Logger.log('=== 수급 ===');
Logger.log('외국인: ' + d.investor.foreigner);
Logger.log('기관: ' + d.investor.institution);
Logger.log('개인: ' + d.investor.individual);
}
// ═══════════════════════════════════════
// 🏭 섹터 분석 리포트
// 네이버증권 업종 데이터 + Gemini 분석
// ═══════════════════════════════════════
var CONFIG = {
GEMINI_API_KEY: '여기에_API키_붙여넣기',
MODEL: 'gemini-2.5-flash',
SEND_TO: '본인이메일입력',
};
function callGemini(prompt) {
if (!prompt) throw new Error('프롬프트 비어있음');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/' + CONFIG.MODEL + ':generateContent?key=' + CONFIG.GEMINI_API_KEY;
var res = UrlFetchApp.fetch(url, {
method: 'post', contentType: 'application/json',
payload: JSON.stringify({contents:[{parts:[{text:String(prompt)}]}], generationConfig:{temperature:0.3}, tools:[{google_search:{}}]}),
muteHttpExceptions: true
});
var code = res.getResponseCode(), raw = res.getContentText();
if (code !== 200) throw new Error('API ' + code + ': ' + raw.substring(0,300));
var data = JSON.parse(raw);
var text = '';
var parts = data.candidates[0].content.parts || [];
for (var i = 0; i < parts.length; i++) {
if (parts[i].text) text += parts[i].text;
}
text = text.replace(/```json/g,'').replace(/```/g,'').trim();
var jsonStart = text.indexOf('{');
var jsonEnd = text.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
text = text.substring(jsonStart, jsonEnd + 1);
}
try {
return JSON.parse(text);
} catch(e) {
throw new Error('JSON 파싱 실패. 응답: ' + text.substring(0, 300));
}
}
function fetchNaverIndex(market) {
try {
var res = UrlFetchApp.fetch('https://m.stock.naver.com/api/index/' + market + '/basic', {muteHttpExceptions:true});
var j = JSON.parse(res.getContentText());
return {value:j.closePrice||'', change:j.compareToPreviousClosePrice||'', changePercent:j.fluctuationsRatio||'', date:j.localTradedAt||''};
} catch(e) { return {value:'-',change:'-',changePercent:'-'}; }
}
function fetchSectorData() {
var kospi = fetchNaverIndex('KOSPI');
var kosdaq = fetchNaverIndex('KOSDAQ');
return {kospi:kospi, kosdaq:kosdaq};
}
function buildSectorPrompt(md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E)');
var p = '';
p += '[필수 지침]\n';
p += '1. 모든 응답 한국어. 영어 절대 금지.\n';
p += '2. 구글 검색으로 오늘(' + today + ') KRX/네이버증권 업종별 등락률을 반드시 확인하라.\n';
p += '3. 데이터 출처: KRX 업종지수 > 네이버증권 업종 > 증권사 섹터 리포트 (미래에셋, 삼성, KB, 한투)\n';
p += '4. 각 섹터의 등락률은 반드시 실제 수치를 사용. 불확실하면 "추정" 표기.\n';
p += '5. 외국인/기관 순매매 금액도 실제 데이터 기반.\n';
p += '6. 반드시 오늘(' + today + ') 데이터만 사용. 전일 데이터 혼동 금지.\n\n';
p += '[실시간 데이터]\n코스피: ' + md.kospi.value + ' (' + md.kospi.changePercent + '%)\n코스닥: ' + md.kosdaq.value + ' (' + md.kosdaq.changePercent + '%)\n\n';
p += '[역할] 증권사 수석 섹터 애널리스트. 기관 투자자 대상 섹터 분석 리포트 수준.\n\n';
p += '[분석 요구사항]\n';
p += '- 강세 섹터: KRX 업종지수 기준 상위 5개 섹터의 정확한 등락률, 외국인/기관 수급, 대장주, 상승 촉매(catalyst)\n';
p += '- 약세 섹터: 하위 3개 섹터의 정확한 등락률, 하락 원인, 반등 가능성 판단\n';
p += '- 섹터 로테이션: 최근 1주 자금 흐름 방향성 (성장주↔가치주, 대형↔중소형 등)\n';
p += '- 다음 주목 섹터: 증권사 리포트/정책/글로벌 이슈 기반 근거 제시\n\n';
p += 'JSON:\n{\n';
p += ' "sector_ranking": [\n';
p += ' {"rank":1,"name":"섹터명","change":"+X.XX%","foreign":"순매수 X,XXX억","institution":"순매수 X,XXX억","top_stock":"대장주명","catalyst":"상승 촉매 구체적으로","outlook":"단기 전망 1문장"},\n';
p += ' {"rank":2,"name":"섹터명","change":"+X.XX%","foreign":"순매수/매도","institution":"순매수/매도","top_stock":"대장주","catalyst":"촉매","outlook":"전망"},\n';
p += ' {"rank":3,"name":"섹터명","change":"+X.XX%","foreign":"동향","institution":"동향","top_stock":"대장주","catalyst":"촉매","outlook":"전망"},\n';
p += ' {"rank":4,"name":"섹터명","change":"+X.XX%","foreign":"동향","institution":"동향","top_stock":"대장주","catalyst":"촉매","outlook":"전망"},\n';
p += ' {"rank":5,"name":"섹터명","change":"+X.XX%","foreign":"동향","institution":"동향","top_stock":"대장주","catalyst":"촉매","outlook":"전망"}\n';
p += ' ],\n';
p += ' "worst_sectors": [\n';
p += ' {"name":"섹터명","change":"-X.XX%","reason":"하락 원인 구체적으로","rebound_chance":"반등 가능성 높음/낮음 + 근거","outlook":"향후 1~2주 전망"}\n';
p += ' ],\n';
p += ' "rotation": {"direction":"자금 이동 방향 (예: IT→방어주)","reason":"로테이션 원인","duration":"지속 예상 기간"},\n';
p += ' "next_hot": {"sector":"다음 주목 섹터","reason":"근거 (정책/실적/글로벌)","timing":"진입 시점 제안","related_stocks":"관련 종목 3~5개"},\n';
p += ' "summary": ["섹터 총평1 (오늘 핵심 흐름)","섹터 총평2 (수급 특징)","섹터 총평3 (다음주 전략 제안)"]\n';
p += '}';
p += '\n\n위 JSON 형식으로만 응답하라. JSON 외에 어떤 텍스트도 포함하지 마라. 순수 JSON만 출력.';
return p;
}
function buildSectorHTML(ai, md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E) HH:mm');
var h = '<div style="max-width:680px;margin:16px auto;font-family:Arial,sans-serif;">';
h += '<div style="background:linear-gradient(135deg,#1E3A5F,#0B1426);padding:20px 24px;border-radius:12px 12px 0 0;">';
h += '<div style="display:inline-block;background:#1a73e8;color:white;font-size:12px;font-weight:900;padding:3px 12px;border-radius:5px;">PlanX</div>';
h += '<div style="color:#60A5FA;font-size:20px;font-weight:bold;margin-top:10px;">🏭 섹터 분석 리포트</div>';
h += '<div style="color:#94A3B8;font-size:12px;">' + today + '</div>';
h += '<div style="display:flex;gap:12px;margin-top:12px;">';
h += '<div style="background:rgba(255,255,255,0.1);border-radius:8px;padding:8px 16px;text-align:center;"><div style="font-size:11px;color:#94A3B8;">코스피</div><div style="font-size:18px;font-weight:bold;color:' + (String(md.kospi.change).indexOf('-')>=0?'#F87171':'#4ADE80') + ';">' + md.kospi.value + '</div></div>';
h += '<div style="background:rgba(255,255,255,0.1);border-radius:8px;padding:8px 16px;text-align:center;"><div style="font-size:11px;color:#94A3B8;">코스닥</div><div style="font-size:18px;font-weight:bold;color:' + (String(md.kosdaq.change).indexOf('-')>=0?'#F87171':'#4ADE80') + ';">' + md.kosdaq.value + '</div></div>';
h += '</div></div>';
h += '<div style="background:#fff;border:1px solid #E2E8F0;padding:20px;">';
// 강세 섹터
h += '<div style="background:#0B1426;color:#F5A623;padding:8px 14px;font-weight:bold;border-radius:6px;">🏆 강세 섹터 TOP 3</div>';
h += '<div style="border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">';
var ranks = ai.sector_ranking || [];
var medals = ['🥇','🥈','🥉'];
for (var i=0; i<ranks.length; i++) {
var r = ranks[i];
var cc = String(r.change||'').indexOf('-')>=0 ? '#DC2626' : '#16A34A';
h += '<div style="padding:14px 16px;border-bottom:' + (i<ranks.length-1?'1px solid #F1F5F9':'none') + ';">';
h += '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">';
h += '<span style="font-size:20px;">' + (medals[i]||'') + '</span>';
h += '<span style="font-size:16px;font-weight:800;color:#0F172A;">' + r.name + '</span>';
h += '<span style="color:' + cc + ';font-size:15px;font-weight:800;margin-left:auto;">' + r.change + '</span>';
h += '</div>';
h += '<div style="display:flex;gap:8px;padding-left:28px;font-size:12px;">';
h += '<span style="background:#EFF6FF;color:#1D4ED8;padding:2px 8px;border-radius:10px;">대장: ' + r.top_stock + '</span>';
h += '<span style="background:#F8FAFC;color:#64748B;padding:2px 8px;border-radius:10px;">외국인: ' + r.foreign + '</span>';
h += '</div>';
h += '<div style="font-size:13px;color:#475569;padding-left:28px;margin-top:4px;">' + r.reason + '</div>';
h += '</div>';
}
h += '</div>';
// 약세 섹터
h += '<div style="background:#0B1426;color:#F87171;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📉 약세 섹터</div>';
var worsts = ai.worst_sectors || [];
for (var i=0; i<worsts.length; i++) {
var w = worsts[i];
h += '<div style="background:#FEF2F2;border-left:4px solid #DC2626;border-radius:0 8px 8px 0;padding:12px 16px;margin:6px 0;">';
h += '<div style="display:flex;align-items:center;gap:8px;"><span style="font-weight:700;color:#DC2626;">' + w.name + '</span><span style="color:#DC2626;font-weight:800;">' + w.change + '</span></div>';
h += '<div style="font-size:13px;color:#64748B;margin-top:4px;">' + w.reason + '</div>';
h += '<div style="font-size:12px;color:#92400E;margin-top:2px;">전망: ' + (w.outlook||'') + '</div>';
h += '</div>';
}
// 로테이션
h += '<div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;padding:14px 16px;margin-top:18px;">';
h += '<div style="font-size:13px;font-weight:700;color:#1D4ED8;">🔄 섹터 로테이션</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:4px;">' + (ai.rotation||'') + '</div>';
h += '</div>';
// 다음 주목
h += '<div style="background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;padding:14px 16px;margin-top:8px;">';
h += '<div style="font-size:13px;font-weight:700;color:#16A34A;">🔮 다음 주목 섹터</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:4px;">' + (ai.next_hot||'') + '</div>';
h += '</div>';
// 총평
h += '<div style="background:#0B1426;color:#F5A623;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📝 총평</div>';
var sums = ai.summary || [];
for (var i=0; i<sums.length; i++) {
h += '<div style="padding:8px 12px;font-size:14px;line-height:1.8;">' + (i+1) + '. ' + sums[i] + '</div>';
}
h += '</div>';
h += '<div style="background:#F8FAFC;padding:12px;border-radius:0 0 12px 12px;border:1px solid #E2E8F0;border-top:none;text-align:center;">';
h += '<div style="color:#94A3B8;font-size:11px;">⚠️ AI 참고자료 · 투자권유 아님</div>';
h += '<div style="color:#FCD34D;font-size:11px;font-weight:600;margin-top:2px;">© 2026 PlanX</div>';
h += '</div></div>';
return h;
}
function run_sector() {
var md = fetchSectorData();
var ai = callGemini(buildSectorPrompt(md));
var html = buildSectorHTML(ai, md);
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy-MM-dd');
MailApp.sendEmail({to:CONFIG.SEND_TO, subject:'[PlanX] 🏭 섹터분석 (' + today + ')', htmlBody:html});
}
function testRun() { run_sector(); }
// ═══════════════════════════════════════
// 🔍 종목 분석 리포트 (120점 스코어링)
// 네이버증권 재무 데이터 + Gemini 분석
// ═══════════════════════════════════════
var CONFIG = {
GEMINI_API_KEY: '여기에_API키_붙여넣기',
MODEL: 'gemini-2.5-flash',
SEND_TO: '본인이메일입력',
TICKER: 'SK하이닉스', // ← 분석할 종목명
};
function callGemini(prompt) {
if (!prompt) throw new Error('프롬프트 비어있음');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/' + CONFIG.MODEL + ':generateContent?key=' + CONFIG.GEMINI_API_KEY;
var res = UrlFetchApp.fetch(url, {
method:'post', contentType:'application/json',
payload: JSON.stringify({contents:[{parts:[{text:String(prompt)}]}], generationConfig:{temperature:0.3}, tools:[{google_search:{}}]}),
muteHttpExceptions:true
});
var code=res.getResponseCode(), raw=res.getContentText();
if(code!==200) throw new Error('API '+code+': '+raw.substring(0,300));
var data = JSON.parse(raw);
var text = '';
var parts = data.candidates[0].content.parts || [];
for (var i = 0; i < parts.length; i++) {
if (parts[i].text) text += parts[i].text;
}
text = text.replace(/```json/g,'').replace(/```/g,'').trim();
// JSON 블록만 추출 (앞뒤 텍스트 제거)
var jsonStart = text.indexOf('{');
var jsonEnd = text.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
text = text.substring(jsonStart, jsonEnd + 1);
}
try {
return JSON.parse(text);
} catch(e) {
throw new Error('JSON 파싱 실패. 응답: ' + text.substring(0, 300));
}
}
function buildStockPrompt() {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일');
var ticker = CONFIG.TICKER;
var p = '';
p += '[필수 지침]\n';
p += '1. 모든 응답 한국어. 영어 금지.\n';
p += '2. 구글 검색으로 ' + ticker + '의 최신 시세, 재무제표(최근 4분기), 증권사 목표가/투자의견을 반드시 확인.\n';
p += '3. 데이터 출처: KRX > 네이버증권 > 전자공시(DART) > 증권사 리포트 (미래에셋, 삼성, KB, 한투, NH)\n';
p += '4. 모든 수치는 실제 데이터. 불확실하면 "추정" 표기. 거짓 금지.\n';
p += '5. 증권사 컨센서스(목표가 평균/최고/최저, 매수/중립/매도 비율) 반드시 포함.\n\n';
p += '[날짜] ' + today + '\n[종목] ' + ticker + '\n\n';
p += '[120점 스코어링 기준 - 엄격 적용]\n';
p += '시장지배력(20점): 글로벌/국내 점유율 %, 경쟁사 대비 지위, 진입장벽\n';
p += '수익성(20점): ROE, ROA, 영업이익률, 순이익률 - 최근 4분기 추이\n';
p += '밸류에이션(20점): PER/PBR/PSR 업종 평균 대비, PEG, EV/EBITDA\n';
p += '성장성(15점): 매출/영업이익 성장률(YoY), 신사업 매출 비중, R&D 투자\n';
p += '재무안정성(15점): 부채비율, 유동비율, 이자보상배율, FCF 추이\n';
p += '배당(10점): 배당수익률, 배당성향, 배당성장률 3개년\n';
p += '보너스-기술해자(10점): 특허/독점기술/전환비용 - 구체적 기술명 명시\n';
p += '보너스-얼리버드(10점): 현재 PER이 업종 대비 저평가 + 성장 모멘텀 존재시\n\n';
p += 'JSON:\n{\n';
p += ' "company": {\n';
p += ' "name":"정확한 종목명","code":"6자리 종목코드","sector":"업종",\n';
p += ' "market_cap":"시가총액 (조원)","current_price":"현재 주가",\n';
p += ' "change":"오늘 등락률","52w_high":"52주 최고가","52w_low":"52주 최저가"\n';
p += ' },\n';
p += ' "financials": {\n';
p += ' "revenue":"최근 연간 매출","revenue_growth":"매출 성장률 YoY",\n';
p += ' "operating_profit":"영업이익","op_margin":"영업이익률",\n';
p += ' "net_income":"순이익","roe":"ROE","per":"PER","pbr":"PBR",\n';
p += ' "debt_ratio":"부채비율","fcf":"잉여현금흐름(양수/음수)"\n';
p += ' },\n';
p += ' "consensus": {\n';
p += ' "target_avg":"증권사 평균 목표가","target_high":"최고 목표가","target_low":"최저 목표가",\n';
p += ' "buy_ratio":"매수 의견 비율","analyst_count":"분석 증권사 수"\n';
p += ' },\n';
p += ' "scores": [\n';
p += ' {"area":"시장지배력","score":0,"max":20,"reason":"점유율 X%, 경쟁지위 구체적"},\n';
p += ' {"area":"수익성","score":0,"max":20,"reason":"ROE X%, 영업이익률 X%, 추이"},\n';
p += ' {"area":"밸류에이션","score":0,"max":20,"reason":"PER X배 (업종 X배), PBR X배"},\n';
p += ' {"area":"성장성","score":0,"max":15,"reason":"매출성장률 X%, 신사업 비중 X%"},\n';
p += ' {"area":"재무안정성","score":0,"max":15,"reason":"부채비율 X%, FCF X억"},\n';
p += ' {"area":"배당","score":0,"max":10,"reason":"배당수익률 X%, 배당성향 X%"}\n';
p += ' ],\n';
p += ' "bonus": {\n';
p += ' "tech_moat":{"score":0,"max":10,"reason":"구체적 기술/특허명"},\n';
p += ' "early_bird":{"score":0,"max":10,"reason":"저평가 근거"}\n';
p += ' },\n';
p += ' "total": 0,\n';
p += ' "grade": "S(100+)/A(85~99)/B(70~84)/C(55~69)/D(55미만)",\n';
p += ' "bull_points": ["투자포인트1 (구체적 수치/사실 포함)","투자포인트2","투자포인트3"],\n';
p += ' "risks": ["리스크1 (확률/영향도 포함)","리스크2","리스크3"],\n';
p += ' "verdict": "최종 투자 의견 3~4문장 (현재가 대비 목표가 괴리율, 진입 시점 제안 포함)"\n';
p += '}';
p += '\n\n위 JSON 형식으로만 응답하라. JSON 외에 어떤 텍스트도 포함하지 마라. 순수 JSON만 출력.';
return p;
}
function buildStockHTML(ai) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 HH:mm');
var c = ai.company || {};
var gradeColors = {'S':'#F59E0B','A':'#7C3AED','B':'#1a73e8','C':'#64748B','D':'#DC2626'};
var gc = gradeColors[ai.grade] || '#64748B';
var h = '<div style="max-width:680px;margin:16px auto;font-family:Arial,sans-serif;">';
h += '<div style="background:linear-gradient(135deg,#4C1D95,#7C3AED);padding:20px 24px;border-radius:12px 12px 0 0;">';
h += '<div style="display:inline-block;background:#1a73e8;color:white;font-size:12px;font-weight:900;padding:3px 12px;border-radius:5px;">PlanX</div>';
h += '<div style="color:white;font-size:20px;font-weight:bold;margin-top:10px;">🔍 종목 분석: ' + (c.name||'') + '</div>';
h += '<div style="color:rgba(255,255,255,0.7);font-size:12px;">' + (c.code||'') + ' · ' + (c.sector||'') + ' · 시총 ' + (c.market_cap||'') + '</div>';
h += '<div style="display:flex;gap:12px;margin-top:12px;">';
h += '<div style="background:rgba(255,255,255,0.15);border-radius:8px;padding:8px 16px;text-align:center;"><div style="font-size:11px;color:rgba(255,255,255,0.6);">현재가</div><div style="font-size:20px;font-weight:bold;color:white;">' + (c.current_price||'') + '</div></div>';
h += '<div style="background:rgba(255,255,255,0.15);border-radius:8px;padding:8px 16px;text-align:center;"><div style="font-size:11px;color:rgba(255,255,255,0.6);">등락</div><div style="font-size:20px;font-weight:bold;color:' + (String(c.change||'').indexOf('-')>=0?'#F87171':'#4ADE80') + ';">' + (c.change||'') + '</div></div>';
h += '<div style="background:' + gc + ';border-radius:8px;padding:8px 16px;text-align:center;"><div style="font-size:11px;color:rgba(255,255,255,0.6);">등급</div><div style="font-size:24px;font-weight:900;color:white;">' + (ai.grade||'') + '</div></div>';
h += '</div></div>';
h += '<div style="background:#fff;border:1px solid #E2E8F0;padding:20px;">';
// 스코어 카드
h += '<div style="background:#4C1D95;color:#C4B5FD;padding:8px 14px;font-weight:bold;border-radius:6px;">📐 스코어 카드 (' + (ai.total||0) + '점/120점)</div>';
// 점수 바
var pct = Math.round(((ai.total||0)/120)*100);
h += '<div style="background:#E2E8F0;border-radius:8px;height:16px;margin:8px 0;overflow:hidden;"><div style="background:' + gc + ';height:100%;width:' + pct + '%;border-radius:8px;"></div></div>';
h += '<table style="width:100%;border-collapse:collapse;font-size:13px;">';
h += '<tr style="background:#1E293B;color:#CBD5E1;"><th style="padding:7px 12px;text-align:left;">영역</th><th style="width:60px;">점수</th><th style="width:50px;">배점</th><th>근거</th></tr>';
var scores = ai.scores || [];
for (var i=0; i<scores.length; i++) {
var s = scores[i], bg = i%2===0?'#fff':'#F8FAFC';
h += '<tr style="background:'+bg+';"><td style="padding:8px 12px;font-weight:600;border-bottom:1px solid #E2E8F0;">'+s.area+'</td>';
h += '<td style="padding:8px 12px;font-weight:bold;color:'+gc+';border-bottom:1px solid #E2E8F0;text-align:center;">'+s.score+'</td>';
h += '<td style="padding:8px 12px;color:#94A3B8;border-bottom:1px solid #E2E8F0;text-align:center;">/'+s.max+'</td>';
h += '<td style="padding:8px 12px;font-size:12px;color:#475569;border-bottom:1px solid #E2E8F0;">'+s.reason+'</td></tr>';
}
// 보너스
var bn = ai.bonus || {};
if (bn.tech_moat && bn.tech_moat.score > 0) {
h += '<tr style="background:#FEF3C7;"><td style="padding:8px 12px;font-weight:600;">🌟 기술해자</td><td style="padding:8px 12px;font-weight:bold;color:#D97706;text-align:center;">+'+bn.tech_moat.score+'</td><td style="padding:8px 12px;color:#94A3B8;text-align:center;">/'+bn.tech_moat.max+'</td><td style="padding:8px 12px;font-size:12px;">'+bn.tech_moat.reason+'</td></tr>';
}
if (bn.early_bird && bn.early_bird.score > 0) {
h += '<tr style="background:#DBEAFE;"><td style="padding:8px 12px;font-weight:600;">🐦 얼리버드</td><td style="padding:8px 12px;font-weight:bold;color:#1D4ED8;text-align:center;">+'+bn.early_bird.score+'</td><td style="padding:8px 12px;color:#94A3B8;text-align:center;">/'+bn.early_bird.max+'</td><td style="padding:8px 12px;font-size:12px;">'+bn.early_bird.reason+'</td></tr>';
}
// 합계
h += '<tr style="background:#F5F3FF;font-weight:bold;font-size:15px;"><td style="padding:10px 12px;">합계</td><td style="padding:10px 12px;color:'+gc+';text-align:center;">'+ai.total+'점</td><td style="padding:10px 12px;text-align:center;">/120</td>';
h += '<td style="padding:10px 12px;"><span style="background:'+gc+';color:white;padding:3px 14px;border-radius:6px;font-size:14px;">'+ai.grade+'등급</span></td></tr>';
h += '</table>';
// 투자포인트 / 리스크
h += '<div style="display:flex;gap:12px;margin-top:18px;flex-wrap:wrap;">';
h += '<div style="flex:1;min-width:200px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;padding:14px;">';
h += '<div style="font-weight:700;color:#16A34A;margin-bottom:6px;">✅ 투자 포인트</div>';
var bulls = ai.bull_points || [];
for(var i=0;i<bulls.length;i++) h += '<div style="font-size:13px;padding:3px 0;color:#334155;">' + (i+1) + '. ' + bulls[i] + '</div>';
h += '</div>';
h += '<div style="flex:1;min-width:200px;background:#FEF2F2;border:1px solid #FECACA;border-radius:8px;padding:14px;">';
h += '<div style="font-weight:700;color:#DC2626;margin-bottom:6px;">⚠️ 리스크</div>';
var risks = ai.risks || [];
for(var i=0;i<risks.length;i++) h += '<div style="font-size:13px;padding:3px 0;color:#334155;">' + (i+1) + '. ' + risks[i] + '</div>';
h += '</div></div>';
// 목표가 + 최종의견
h += '<div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;padding:14px 16px;margin-top:14px;">';
h += '<div style="font-weight:700;color:#1D4ED8;">🎯 증권사 목표가: ' + (ai.target_price||'') + '</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:6px;line-height:1.8;">' + (ai.verdict||'') + '</div>';
h += '</div>';
h += '</div>';
h += '<div style="background:#F8FAFC;padding:12px;border-radius:0 0 12px 12px;border:1px solid #E2E8F0;border-top:none;text-align:center;">';
h += '<div style="color:#94A3B8;font-size:11px;">⚠️ AI 참고자료 · 투자권유 아님</div>';
h += '<div style="color:#FCD34D;font-size:11px;font-weight:600;margin-top:2px;">© 2026 PlanX</div></div></div>';
return h;
}
function run_stock() {
var ai = callGemini(buildStockPrompt());
var html = buildStockHTML(ai);
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy-MM-dd');
MailApp.sendEmail({to:CONFIG.SEND_TO, subject:'[PlanX] 🔍 '+CONFIG.TICKER+' 분석 ('+today+')', htmlBody:html});
}
function testRun() { run_stock(); }
// ═══════════════════════════════════════
// 🌅 장전 기대 섹터 리포트
// GOOGLEFINANCE 해외시세 + Gemini 분석
// 매일 오전 8시 트리거 추천
// ═══════════════════════════════════════
var CONFIG = {
GEMINI_API_KEY: '여기에_API키_붙여넣기',
MODEL: 'gemini-2.5-flash',
SEND_TO: '본인이메일입력',
};
function callGemini(prompt) {
if (!prompt) throw new Error('프롬프트 비어있음');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/' + CONFIG.MODEL + ':generateContent?key=' + CONFIG.GEMINI_API_KEY;
var res = UrlFetchApp.fetch(url, {
method:'post', contentType:'application/json',
payload: JSON.stringify({contents:[{parts:[{text:String(prompt)}]}], generationConfig:{temperature:0.3}, tools:[{google_search:{}}]}),
muteHttpExceptions:true
});
var code=res.getResponseCode(), raw=res.getContentText();
if(code!==200) throw new Error('API '+code+': '+raw.substring(0,300));
var data = JSON.parse(raw);
var text = '';
var parts = data.candidates[0].content.parts || [];
for (var i = 0; i < parts.length; i++) {
if (parts[i].text) text += parts[i].text;
}
text = text.replace(/```json/g,'').replace(/```/g,'').trim();
// JSON 블록만 추출 (앞뒤 텍스트 제거)
var jsonStart = text.indexOf('{');
var jsonEnd = text.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
text = text.substring(jsonStart, jsonEnd + 1);
}
try {
return JSON.parse(text);
} catch(e) {
throw new Error('JSON 파싱 실패. 응답: ' + text.substring(0, 300));
}
}
function fetchGoogleFinance(ticker) {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('_temp_finance');
if (!sh) sh = ss.insertSheet('_temp_finance');
sh.getRange('A1').setFormula('=GOOGLEFINANCE("' + ticker + '","price")');
sh.getRange('A2').setFormula('=GOOGLEFINANCE("' + ticker + '","closeyest")');
SpreadsheetApp.flush(); Utilities.sleep(2000);
var price = sh.getRange('A1').getValue();
var prev = sh.getRange('A2').getValue();
sh.clear();
if (!price || String(price).indexOf('N/A')>=0) return {value:'-',change:'-',changePct:'-'};
var chg = price-prev, pct = prev?((chg/prev)*100):0;
return {value:Number(price).toFixed(2), change:(chg>=0?'+':'')+chg.toFixed(2), changePct:(pct>=0?'+':'')+pct.toFixed(2)+'%'};
} catch(e) { return {value:'-',change:'-',changePct:'-'}; }
}
function fetchPremarketData() {
return {
sp500: fetchGoogleFinance('.INX'),
nasdaq: fetchGoogleFinance('.IXIC'),
dji: fetchGoogleFinance('.DJI'),
sox: fetchGoogleFinance('SOXX'),
vix: fetchGoogleFinance('.VIX')
};
}
function buildPrePrompt(md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E)');
var p = '';
p += '[필수 지침]\n';
p += '1. 모든 응답 한국어. 영어 금지.\n';
p += '2. 아래 해외 시세는 GOOGLEFINANCE 실시간 데이터. 그대로 사용.\n';
p += '3. 구글 검색으로 오늘 장 개장 전 주요 뉴스, 증권사 모닝브리프를 확인하라.\n';
p += '4. 미국 개별 종목 등락이 한국 관련주에 미치는 영향을 구체적으로 연결하라.\n';
p += '5. NXT 프리마켓(08:00~) 동향도 가능하면 반영.\n';
p += '6. 반드시 오늘(' + today + ') 데이터만 사용. 전일 데이터 혼동 금지.\n\n';
p += '[날짜] ' + today + ' 오전 장 개장 전\n\n';
p += '[해외 시세 (전일 마감)]\n';
p += 'S&P500: ' + md.sp500.value + ' (' + md.sp500.changePct + ')\n';
p += '나스닥: ' + md.nasdaq.value + ' (' + md.nasdaq.changePct + ')\n';
p += '다우: ' + md.dji.value + ' (' + md.dji.changePct + ')\n';
p += 'SOX(반도체지수): ' + md.sox.value + ' (' + md.sox.changePct + ')\n';
p += 'VIX(공포지수): ' + md.vix.value + '\n\n';
p += '[역할] 증권사 모닝브리프 작성자. 기관 투자자 대상 장전 전략 리포트.\n\n';
p += '[분석 요구사항]\n';
p += '- 해외 시장 마감 핵심: 주요 이슈 2~3개, 특정 종목 등락(엔비디아/TSMC/애플 등)이 한국에 미칠 영향\n';
p += '- 기대 섹터 TOP 3: 해외 연동 근거 + 한국 관련주 3~5개씩 + 구체적 진입 전략\n';
p += '- 주의 섹터: 해외 악재로 영향 받을 섹터 + 회피 이유\n';
p += '- 오프닝 전략: 갭상승/갭하락 시나리오별 대응, 분할매수 기준가 제안\n\n';
p += 'JSON:\n{\n';
p += ' "overnight_summary": "해외 시장 마감 핵심 요약 2~3문장 (수치 포함)",\n';
p += ' "key_movers": [\n';
p += ' {"us_stock":"미국 종목명","change":"+X.X%","korea_impact":"한국 관련주와 예상 영향"}\n';
p += ' ],\n';
p += ' "korea_expected": "오늘 코스피/코스닥 예상 방향 + 예상 등락폭 + 근거",\n';
p += ' "hot_sectors": [\n';
p += ' {"rank":1,"name":"섹터명","reason":"기대 이유 (해외 연동 + 국내 촉매)","stocks":["종목1","종목2","종목3"],"entry_strategy":"진입 전략 구체적","risk":"주의할 점"},\n';
p += ' {"rank":2,"name":"섹터명","reason":"기대 이유","stocks":["종목1","종목2"],"entry_strategy":"진입 전략","risk":"주의점"},\n';
p += ' {"rank":3,"name":"섹터명","reason":"기대 이유","stocks":["종목1","종목2"],"entry_strategy":"진입 전략","risk":"주의점"}\n';
p += ' ],\n';
p += ' "risk_sectors": [{"name":"섹터명","reason":"회피 이유 구체적","affected_stocks":"영향 받을 종목"}],\n';
p += ' "opening_strategy": {\n';
p += ' "gap_up": "갭상승 시 대응 전략",\n';
p += ' "gap_down": "갭하락 시 대응 전략",\n';
p += ' "key_levels": "코스피 주요 지지/저항 수준"\n';
p += ' }\n';
p += '}';
p += '\n\n위 JSON 형식으로만 응답하라. JSON 외에 어떤 텍스트도 포함하지 마라. 순수 JSON만 출력.';
return p;
}
function buildPreHTML(ai, md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E) HH:mm');
var h = '<div style="max-width:680px;margin:16px auto;font-family:Arial,sans-serif;">';
h += '<div style="background:linear-gradient(135deg,#D97706,#F59E0B);padding:20px 24px;border-radius:12px 12px 0 0;">';
h += '<div style="display:inline-block;background:#1a73e8;color:white;font-size:12px;font-weight:900;padding:3px 12px;border-radius:5px;">PlanX</div>';
h += '<div style="color:white;font-size:20px;font-weight:bold;margin-top:10px;">🌅 장전 기대 섹터</div>';
h += '<div style="color:rgba(255,255,255,0.8);font-size:12px;">' + today + ' · 장 개장 전 분석</div></div>';
h += '<div style="background:#fff;border:1px solid #E2E8F0;padding:20px;">';
// 해외 시세 카드
h += '<div style="background:#92400E;color:#FDE68A;padding:8px 14px;font-weight:bold;border-radius:6px;">🌍 해외 마감 시세</div>';
h += '<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;">';
var world = [{n:'S&P500',d:md.sp500},{n:'나스닥',d:md.nasdaq},{n:'다우',d:md.dji},{n:'SOX',d:md.sox},{n:'VIX',d:md.vix}];
for(var i=0;i<world.length;i++){
var w=world[i],d=w.d||{},cc=String(d.change||'').indexOf('-')>=0?'#DC2626':'#16A34A';
if(w.n==='VIX') cc = Number(d.value)>20?'#DC2626':'#16A34A';
h+='<div style="flex:1;min-width:100px;background:#F8FAFC;border:1px solid #E2E8F0;border-radius:8px;padding:10px;text-align:center;">';
h+='<div style="font-size:11px;color:#64748B;">'+w.n+'</div>';
h+='<div style="font-size:18px;font-weight:bold;color:'+cc+';margin:2px 0;">'+(d.value||'-')+'</div>';
h+='<div style="font-size:11px;color:'+cc+';">'+(d.changePct||d.change||'')+'</div>';
h+='</div>';
}
h+='</div>';
// 한국 예상
h += '<div style="background:#FEF3C7;border:1px solid #FDE68A;border-radius:8px;padding:14px;margin-top:14px;">';
h += '<div style="font-weight:700;color:#92400E;">🇰🇷 오늘 한국 장 예상</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:4px;">' + (ai.korea_expected||'') + '</div></div>';
// 기대 섹터
h += '<div style="background:#92400E;color:#FDE68A;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">🔥 기대 섹터 TOP 3</div>';
h += '<div style="border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">';
var secs = ai.hot_sectors || [];
for(var i=0;i<secs.length;i++){
var s=secs[i];
var stStyle = s.strategy==='매수'?'background:#DCFCE7;color:#16A34A;':s.strategy==='주의'?'background:#FEE2E2;color:#DC2626;':'background:#FEF3C7;color:#92400E;';
h+='<div style="padding:14px 16px;border-bottom:'+(i<secs.length-1?'1px solid #F1F5F9;':'none')+'">';
h+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">';
h+='<span style="background:#F59E0B;color:white;width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;">'+s.rank+'</span>';
h+='<span style="font-size:16px;font-weight:800;">'+s.name+'</span>';
h+='<span style="'+stStyle+'padding:2px 10px;border-radius:10px;font-size:11px;font-weight:700;margin-left:auto;">'+s.strategy+'</span></div>';
h+='<div style="font-size:13px;color:#475569;padding-left:32px;">'+s.reason+'</div>';
h+='<div style="padding-left:32px;margin-top:4px;">';
var stocks = s.stocks||[];
for(var j=0;j<stocks.length;j++) h+='<span style="background:#EFF6FF;color:#1D4ED8;padding:2px 8px;border-radius:10px;font-size:11px;margin-right:4px;">'+stocks[j]+'</span>';
h+='</div></div>';
}
h+='</div>';
// 주의 섹터
var risks = ai.risk_sectors || [];
if (risks.length > 0) {
h += '<div style="background:#FEF2F2;border-left:4px solid #DC2626;border-radius:0 8px 8px 0;padding:12px 16px;margin-top:12px;">';
h += '<div style="font-weight:700;color:#DC2626;margin-bottom:4px;">⚠️ 주의 섹터</div>';
for(var i=0;i<risks.length;i++) h+='<div style="font-size:13px;color:#64748B;">'+risks[i].name+' — '+risks[i].reason+'</div>';
h+='</div>';
}
// 전략
h += '<div style="background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;padding:14px;margin-top:14px;">';
h += '<div style="font-weight:700;color:#16A34A;">🎯 오프닝 전략</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:4px;line-height:1.8;">' + (ai.opening_strategy||'') + '</div></div>';
h += '</div>';
h += '<div style="background:#F8FAFC;padding:12px;border-radius:0 0 12px 12px;border:1px solid #E2E8F0;border-top:none;text-align:center;">';
h += '<div style="color:#94A3B8;font-size:11px;">⚠️ AI 참고자료 · 투자권유 아님</div>';
h += '<div style="color:#FCD34D;font-size:11px;font-weight:600;margin-top:2px;">© 2026 PlanX</div></div></div>';
return h;
}
function run_premarket() {
var md = fetchPremarketData();
var ai = callGemini(buildPrePrompt(md));
MailApp.sendEmail({to:CONFIG.SEND_TO, subject:'[PlanX] 🌅 장전 기대섹터 ('+Utilities.formatDate(new Date(),'Asia/Seoul','yyyy-MM-dd')+')', htmlBody:buildPreHTML(ai,md)});
}
function testRun() { run_premarket(); }
// ═══════════════════════════════════════
// 🌙 장후 특징 섹터 & 종목 리포트
// 네이버증권 실시간 + Gemini 분석
// 매일 오후 4시 트리거 추천
// ═══════════════════════════════════════
var CONFIG = {
GEMINI_API_KEY: '여기에_API키_붙여넣기',
MODEL: 'gemini-2.5-flash',
SEND_TO: '본인이메일입력',
};
function callGemini(prompt) {
if (!prompt) throw new Error('프롬프트 비어있음');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/' + CONFIG.MODEL + ':generateContent?key=' + CONFIG.GEMINI_API_KEY;
var res = UrlFetchApp.fetch(url, {
method:'post', contentType:'application/json',
payload: JSON.stringify({contents:[{parts:[{text:String(prompt)}]}], generationConfig:{temperature:0.3}, tools:[{google_search:{}}]}),
muteHttpExceptions:true
});
var code=res.getResponseCode(), raw=res.getContentText();
if(code!==200) throw new Error('API '+code+': '+raw.substring(0,300));
var data = JSON.parse(raw);
var text = '';
var parts = data.candidates[0].content.parts || [];
for (var i = 0; i < parts.length; i++) {
if (parts[i].text) text += parts[i].text;
}
text = text.replace(/```json/g,'').replace(/```/g,'').trim();
// JSON 블록만 추출 (앞뒤 텍스트 제거)
var jsonStart = text.indexOf('{');
var jsonEnd = text.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
text = text.substring(jsonStart, jsonEnd + 1);
}
try {
return JSON.parse(text);
} catch(e) {
throw new Error('JSON 파싱 실패. 응답: ' + text.substring(0, 300));
}
}
function fetchNaverIndex(market) {
try {
var res = UrlFetchApp.fetch('https://m.stock.naver.com/api/index/' + market + '/basic', {muteHttpExceptions:true});
var j = JSON.parse(res.getContentText());
return {value:j.closePrice||'', change:j.compareToPreviousClosePrice||'', changePercent:j.fluctuationsRatio||'', date:j.localTradedAt||''};
} catch(e) { return {value:'-',change:'-',changePercent:'-'}; }
}
function fetchPostData() {
return {
kospi: fetchNaverIndex('KOSPI'),
kosdaq: fetchNaverIndex('KOSDAQ')
};
}
function buildPostPrompt(md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E)');
var p = '';
p += '[필수 지침]\n';
p += '1. 모든 응답 한국어. 영어 금지.\n';
p += '2. 아래 종가 데이터는 네이버증권 실시간. 그대로 사용.\n';
p += '3. 구글 검색으로 오늘 급등/급락 종목 TOP 5, 특징 섹터, 거래대금 상위 종목을 확인하라.\n';
p += '4. 각 종목의 급등/급락 사유를 뉴스/공시/수급 기반으로 구체적 설명.\n';
p += '5. 거래량 폭증 여부, 신고가/신저가 돌파 여부도 체크.\n';
p += '6. 반드시 오늘(' + today + ') 데이터만 사용. 전일/전주 데이터를 오늘로 혼동하지 마라.\n\n';
p += '[날짜] ' + today + ' 장 마감 후\n\n';
p += '[종가 데이터 (네이버증권 기준)]\n';
p += '코스피: ' + md.kospi.value + ' (' + md.kospi.change + ', ' + md.kospi.changePercent + '%) — 기준일: ' + (md.kospi.date || today) + '\n';
p += '코스닥: ' + md.kosdaq.value + ' (' + md.kosdaq.change + ', ' + md.kosdaq.changePercent + '%) — 기준일: ' + (md.kosdaq.date || today) + '\n\n';
p += '[역할] 증권사 장후 마켓리뷰 작성자. 기관 투자자 대상.\n\n';
p += '[분석 요구사항]\n';
p += '- 강세 섹터: 상위 3개 섹터의 등락률, 수급(외국인/기관), 상승 촉매, 대장주\n';
p += '- 약세 섹터: 하위 2개 섹터의 등락률, 하락 원인, 내일 반등 가능성\n';
p += '- 급등 TOP 5: 종목명, 등락률, 급등 사유(공시/뉴스/테마), 거래량 변화, 내일 연속성 판단\n';
p += '- 급락 TOP 5: 종목명, 등락률, 급락 사유, 손절/보유 판단\n';
p += '- 내일 전망: 야간 선물, 글로벌 이벤트 기반 내일 시장 방향 + 주목 섹터/종목\n\n';
p += 'JSON:\n{\n';
p += ' "market_summary": "오늘 장 핵심 한줄 요약 (수치 포함)",\n';
p += ' "hot_sectors": [\n';
p += ' {"name":"섹터명","change":"+X.XX%","reason":"상승 촉매 구체적","top_stock":"대장주","foreign":"외국인 순매수/매도 금액","volume_note":"거래량 특이사항"}\n';
p += ' ],\n';
p += ' "cold_sectors": [\n';
p += ' {"name":"섹터명","change":"-X.XX%","reason":"하락 원인 구체적","rebound":"내일 반등 가능성 + 근거"}\n';
p += ' ],\n';
p += ' "top_gainers": [\n';
p += ' {"name":"종목명","code":"종목코드","change":"+XX.X%","reason":"급등 사유 (공시/뉴스/테마 구체적)","volume":"거래량 전일대비 X배","continuation":"내일 연속 상승 가능성 높음/낮음 + 근거"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","volume":"거래량","continuation":"연속성"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","volume":"거래량","continuation":"연속성"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","volume":"거래량","continuation":"연속성"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","volume":"거래량","continuation":"연속성"}\n';
p += ' ],\n';
p += ' "top_losers": [\n';
p += ' {"name":"종목명","code":"코드","change":"-XX.X%","reason":"급락 사유 구체적","action":"손절/보유/추가매수 판단 + 근거"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","action":"판단"},\n';
p += ' {"name":"종목명","code":"코드","change":"등락","reason":"사유","action":"판단"}\n';
p += ' ],\n';
p += ' "tomorrow_watch": {\n';
p += ' "direction":"내일 예상 방향 (상승/하락/횡보) + 근거",\n';
p += ' "key_events":"내일 주요 이벤트/발표",\n';
p += ' "watch_sectors":"주목 섹터 2~3개",\n';
p += ' "watch_stocks":"주목 종목 3~5개 + 이유"\n';
p += ' },\n';
p += ' "summary": ["장후 총평1 (오늘 핵심)","장후 총평2 (수급 분석)","장후 총평3 (내일 전략)"]\n';
p += '}';
p += '\n\n위 JSON 형식으로만 응답하라. JSON 외에 어떤 텍스트도 포함하지 마라. 순수 JSON만 출력.';
return p;
}
function buildPostHTML(ai, md) {
var today = Utilities.formatDate(new Date(), 'Asia/Seoul', 'yyyy년 MM월 dd일 (E) HH:mm');
var kc = String(md.kospi.change||'').indexOf('-')>=0;
var kdc = String(md.kosdaq.change||'').indexOf('-')>=0;
var h = '<div style="max-width:680px;margin:16px auto;font-family:Arial,sans-serif;">';
// 헤더
h += '<div style="background:linear-gradient(135deg,#0F172A,#1E293B);padding:20px 24px;border-radius:12px 12px 0 0;">';
h += '<div style="display:inline-block;background:#1a73e8;color:white;font-size:12px;font-weight:900;padding:3px 12px;border-radius:5px;">PlanX</div>';
h += '<div style="color:white;font-size:20px;font-weight:bold;margin-top:10px;">🌙 장후 특징 섹터 & 종목</div>';
h += '<div style="color:#94A3B8;font-size:12px;">' + today + ' · 네이버증권 종가 기준</div>';
h += '<div style="display:flex;gap:12px;margin-top:12px;">';
h += '<div style="background:rgba(255,255,255,0.1);border:1px solid '+(kc?'#F87171':'#4ADE80')+';border-radius:8px;padding:8px 20px;text-align:center;"><div style="font-size:11px;color:#94A3B8;">코스피</div><div style="font-size:20px;font-weight:bold;color:'+(kc?'#F87171':'#4ADE80')+';">'+md.kospi.value+'</div><div style="font-size:12px;color:'+(kc?'#F87171':'#4ADE80')+';">'+md.kospi.change+' ('+md.kospi.changePercent+'%)</div></div>';
h += '<div style="background:rgba(255,255,255,0.1);border:1px solid '+(kdc?'#F87171':'#4ADE80')+';border-radius:8px;padding:8px 20px;text-align:center;"><div style="font-size:11px;color:#94A3B8;">코스닥</div><div style="font-size:20px;font-weight:bold;color:'+(kdc?'#F87171':'#4ADE80')+';">'+md.kosdaq.value+'</div><div style="font-size:12px;color:'+(kdc?'#F87171':'#4ADE80')+';">'+md.kosdaq.change+' ('+md.kosdaq.changePercent+'%)</div></div>';
h += '</div>';
h += '<div style="background:rgba(255,255,255,0.08);border-radius:8px;padding:10px 16px;margin-top:12px;color:#F5A623;font-size:14px;font-weight:bold;">💡 '+(ai.market_summary||'')+'</div>';
h += '</div>';
h += '<div style="background:#fff;border:1px solid #E2E8F0;padding:20px;">';
// 강세 섹터
h += '<div style="background:#0F172A;color:#4ADE80;padding:8px 14px;font-weight:bold;border-radius:6px;">🔥 강세 섹터</div>';
h += '<div style="border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">';
var hots = ai.hot_sectors||[];
for(var i=0;i<hots.length;i++){
var s=hots[i];
h+='<div style="display:flex;align-items:flex-start;gap:10px;padding:14px 16px;border-bottom:'+(i<hots.length-1?'1px solid #F1F5F9;':'none')+'">';
h+='<div style="flex-shrink:0;width:40px;height:40px;border-radius:8px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;font-size:16px;">🏆</div>';
h+='<div style="flex:1;">';
h+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;"><span style="font-weight:800;font-size:15px;">'+s.name+'</span><span style="color:#16A34A;font-weight:800;font-size:15px;">'+s.change+'</span></div>';
h+='<div style="font-size:13px;color:#475569;line-height:1.6;">'+s.reason+'</div>';
h+='<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;">';
h+='<span style="background:#EFF6FF;color:#1D4ED8;padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;">대장: '+(s.top_stock||'')+'</span>';
if(s.foreign) h+='<span style="background:#F8FAFC;color:#64748B;padding:2px 10px;border-radius:10px;font-size:11px;">외국인: '+s.foreign+'</span>';
if(s.volume_note) h+='<span style="background:#FEF3C7;color:#92400E;padding:2px 10px;border-radius:10px;font-size:11px;">'+s.volume_note+'</span>';
h+='</div></div></div>';
}
h+='</div>';
// 약세 섹터
var colds = ai.cold_sectors||[];
if(colds.length>0){
h += '<div style="background:#0F172A;color:#F87171;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📉 약세 섹터</div>';
for(var i=0;i<colds.length;i++){
var c=colds[i];
h+='<div style="background:#FEF2F2;border-left:4px solid #DC2626;border-radius:0 8px 8px 0;padding:12px 16px;margin:6px 0;">';
h+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;">';
h+='<span style="font-weight:700;color:#DC2626;font-size:15px;">'+c.name+'</span>';
h+='<span style="color:#DC2626;font-weight:800;font-size:15px;">'+c.change+'</span></div>';
h+='<div style="font-size:13px;color:#64748B;">'+c.reason+'</div>';
if(c.rebound) h+='<div style="font-size:12px;color:#92400E;margin-top:4px;background:#FEF3C7;padding:4px 8px;border-radius:4px;display:inline-block;">반등 전망: '+c.rebound+'</div>';
h+='</div>';
}
}
// 급등 종목 TOP 5
h += '<div style="background:#0F172A;color:#4ADE80;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📈 급등 종목 TOP 5</div>';
h += '<div style="border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">';
var gainers = ai.top_gainers||[];
for(var i=0;i<gainers.length;i++){
var g=gainers[i];
h+='<div style="padding:14px 16px;border-bottom:'+(i<gainers.length-1?'1px solid #F1F5F9;':'none')+'">';
h+='<div style="display:flex;align-items:center;gap:8px;">';
h+='<span style="background:#DCFCE7;color:#16A34A;width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;">'+(i+1)+'</span>';
h+='<span style="font-weight:800;font-size:15px;">'+g.name+'</span>';
if(g.code) h+='<span style="font-size:11px;color:#94A3B8;">('+g.code+')</span>';
h+='<span style="color:#16A34A;font-weight:800;font-size:16px;margin-left:auto;">'+g.change+'</span></div>';
h+='<div style="font-size:13px;color:#475569;padding-left:34px;margin-top:4px;line-height:1.6;">'+g.reason+'</div>';
h+='<div style="display:flex;flex-wrap:wrap;gap:6px;padding-left:34px;margin-top:4px;">';
if(g.volume) h+='<span style="background:#F8FAFC;color:#64748B;padding:2px 8px;border-radius:10px;font-size:11px;">거래량: '+g.volume+'</span>';
if(g.continuation) {
var contColor = g.continuation.indexOf('높음')>=0 ? '#16A34A' : g.continuation.indexOf('낮음')>=0 ? '#DC2626' : '#64748B';
var contBg = g.continuation.indexOf('높음')>=0 ? '#DCFCE7' : g.continuation.indexOf('낮음')>=0 ? '#FEE2E2' : '#F8FAFC';
h+='<span style="background:'+contBg+';color:'+contColor+';padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">내일: '+g.continuation+'</span>';
}
h+='</div></div>';
}
h+='</div>';
// 급락 종목 TOP 5
h += '<div style="background:#0F172A;color:#F87171;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📉 급락 종목 TOP 5</div>';
h += '<div style="border:1px solid #E2E8F0;border-radius:8px;margin-top:4px;">';
var losers = ai.top_losers||[];
for(var i=0;i<losers.length;i++){
var l=losers[i];
h+='<div style="padding:14px 16px;border-bottom:'+(i<losers.length-1?'1px solid #F1F5F9;':'none')+'">';
h+='<div style="display:flex;align-items:center;gap:8px;">';
h+='<span style="background:#FEE2E2;color:#DC2626;width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;">'+(i+1)+'</span>';
h+='<span style="font-weight:800;font-size:15px;">'+l.name+'</span>';
if(l.code) h+='<span style="font-size:11px;color:#94A3B8;">('+l.code+')</span>';
h+='<span style="color:#DC2626;font-weight:800;font-size:16px;margin-left:auto;">'+l.change+'</span></div>';
h+='<div style="font-size:13px;color:#475569;padding-left:34px;margin-top:4px;line-height:1.6;">'+l.reason+'</div>';
if(l.action) {
var actColor = l.action.indexOf('손절')>=0 ? '#DC2626' : l.action.indexOf('추가매수')>=0 ? '#16A34A' : '#D97706';
var actBg = l.action.indexOf('손절')>=0 ? '#FEE2E2' : l.action.indexOf('추가매수')>=0 ? '#DCFCE7' : '#FEF3C7';
h+='<div style="padding-left:34px;margin-top:4px;"><span style="background:'+actBg+';color:'+actColor+';padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">'+l.action+'</span></div>';
}
h+='</div>';
}
h+='</div>';
// 내일 주목 (객체 처리)
var tw = ai.tomorrow_watch || {};
if (typeof tw === 'string') {
// 문자열이면 그대로
h += '<div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;padding:14px;margin-top:18px;">';
h += '<div style="font-weight:700;color:#1D4ED8;font-size:15px;">🔮 내일 주목 포인트</div>';
h += '<div style="font-size:14px;color:#334155;margin-top:4px;line-height:1.8;">'+tw+'</div></div>';
} else {
// 객체면 각 항목 표시
h += '<div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;padding:16px;margin-top:18px;">';
h += '<div style="font-weight:700;color:#1D4ED8;font-size:15px;margin-bottom:10px;">🔮 내일 주목 포인트</div>';
if(tw.direction) h += '<div style="display:flex;gap:8px;margin-bottom:8px;"><span style="background:#1D4ED8;color:white;padding:2px 10px;border-radius:10px;font-size:12px;font-weight:600;flex-shrink:0;">방향</span><span style="font-size:14px;color:#334155;">'+tw.direction+'</span></div>';
if(tw.key_events) h += '<div style="display:flex;gap:8px;margin-bottom:8px;"><span style="background:#D97706;color:white;padding:2px 10px;border-radius:10px;font-size:12px;font-weight:600;flex-shrink:0;">이벤트</span><span style="font-size:14px;color:#334155;">'+tw.key_events+'</span></div>';
if(tw.watch_sectors) h += '<div style="display:flex;gap:8px;margin-bottom:8px;"><span style="background:#16A34A;color:white;padding:2px 10px;border-radius:10px;font-size:12px;font-weight:600;flex-shrink:0;">섹터</span><span style="font-size:14px;color:#334155;">'+tw.watch_sectors+'</span></div>';
if(tw.watch_stocks) h += '<div style="display:flex;gap:8px;"><span style="background:#7C3AED;color:white;padding:2px 10px;border-radius:10px;font-size:12px;font-weight:600;flex-shrink:0;">종목</span><span style="font-size:14px;color:#334155;">'+tw.watch_stocks+'</span></div>';
h += '</div>';
}
// 총평
h += '<div style="background:#0F172A;color:#F5A623;padding:8px 14px;font-weight:bold;border-radius:6px;margin-top:18px;">📝 총평</div>';
var sums=ai.summary||[];
var sumIcons = ['📊','💰','🔮'];
for(var i=0;i<sums.length;i++){
h+='<div style="display:flex;gap:10px;padding:10px 12px;'+(i<sums.length-1?'border-bottom:1px solid #F1F5F9;':'')+'">';
h+='<div style="flex-shrink:0;font-size:16px;">'+(sumIcons[i]||'📌')+'</div>';
h+='<div style="font-size:14px;color:#334155;line-height:1.8;">'+sums[i]+'</div></div>';
}
h += '</div>';
h += '<div style="background:#F8FAFC;padding:12px 20px;border-radius:0 0 12px 12px;border:1px solid #E2E8F0;border-top:none;text-align:center;">';
h += '<div style="color:#94A3B8;font-size:11px;">⚠️ AI 분석 참고자료 · 투자 판단과 책임은 본인에게 있습니다</div>';
h += '<div style="color:#94A3B8;font-size:10px;margin-top:2px;">시세: 네이버증권 · 분석: Gemini + Google Search</div>';
h += '<div style="color:#FCD34D;font-size:11px;font-weight:600;margin-top:4px;">© 2026 PlanX</div>';
h += '</div></div>';
return h;
}
function run_postmarket() {
var md = fetchPostData();
var ai = callGemini(buildPostPrompt(md));
MailApp.sendEmail({to:CONFIG.SEND_TO, subject:'[PlanX] 🌙 장후특징 ('+Utilities.formatDate(new Date(),'Asia/Seoul','yyyy-MM-dd')+')', htmlBody:buildPostHTML(ai,md)});
}
function testRun() { run_postmarket(); }