{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c46a0b87",
   "metadata": {
    "papermill": {
     "duration": 0.001108,
     "end_time": "2026-10-29T21:45:23.714051+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:23.712943+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# 찌짐이 방지 — 저점매수 / 매수금지 점수\n",
    "\n",
    "**용도**: 관심 종목별 저점매수(매도금지) 점수와 과열 매수금지 점수를 비교합니다.\n",
    "**종목 리스트**: `bottom_tickers.json` (웹 UI '🧾 저점 종목' 버튼으로 편집)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "470e6ba9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-29T21:45:23.715961Z",
     "iopub.status.busy": "2026-10-29T21:45:23.715867Z",
     "iopub.status.idle": "2026-10-29T21:45:23.747547Z",
     "shell.execute_reply": "2026-10-29T21:45:23.747203Z"
    },
    "papermill": {
     "duration": 0.033242,
     "end_time": "2026-10-29T21:45:23.747977+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:23.714735+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[CONFIG] /Users/loonshots/룬샤시_공시정리/태버지/bottom_tickers.json\n"
     ]
    }
   ],
   "source": [
    "import json, sys\n",
    "from pathlib import Path\n",
    "\n",
    "TICKERS_FILE = Path('/Users/loonshots/룬샤시_공시정리/태버지/bottom_tickers.json')\n",
    "DEFAULT_GROUPS = {\n",
    "    '지수': [\n",
    "        {'ticker': '^KS11', 'name': 'KOSPI'},\n",
    "        {'ticker': '^KQ11', 'name': 'KOSDAQ'},\n",
    "    ],\n",
    "    '국장': [\n",
    "        {'ticker': '005930', 'name': '삼성전자'},\n",
    "        {'ticker': '000660', 'name': 'SK하이닉스'},\n",
    "        {'ticker': '009150', 'name': '삼성전기'},\n",
    "    ],\n",
    "    '해외': [],\n",
    "}\n",
    "\n",
    "sys.path.insert(0, '/Users/loonshots/projects/predict/OHLCV 기반')\n",
    "try:\n",
    "    from stock_map import resolve_stock, code_to_name\n",
    "except Exception:\n",
    "    resolve_stock = None\n",
    "    def code_to_name(code):\n",
    "        return code\n",
    "\n",
    "print(f'[CONFIG] {TICKERS_FILE}')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "be32c9ec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-29T21:45:23.749783Z",
     "iopub.status.busy": "2026-10-29T21:45:23.749717Z",
     "iopub.status.idle": "2026-10-29T21:45:24.202275Z",
     "shell.execute_reply": "2026-10-29T21:45:24.201982Z"
    },
    "papermill": {
     "duration": 0.454158,
     "end_time": "2026-10-29T21:45:24.202925+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:23.748767+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "분석 종목:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>group</th>\n",
       "      <th>name</th>\n",
       "      <th>ticker</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>지수</td>\n",
       "      <td>KOSPI</td>\n",
       "      <td>^KS11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>지수</td>\n",
       "      <td>KOSDAQ</td>\n",
       "      <td>^KQ11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>국장</td>\n",
       "      <td>삼성전자</td>\n",
       "      <td>005930</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>국장</td>\n",
       "      <td>SK하이닉스</td>\n",
       "      <td>000660</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>국장</td>\n",
       "      <td>삼성전기</td>\n",
       "      <td>009150</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  group    name  ticker\n",
       "0    지수   KOSPI   ^KS11\n",
       "1    지수  KOSDAQ   ^KQ11\n",
       "2    국장    삼성전자  005930\n",
       "3    국장  SK하이닉스  000660\n",
       "4    국장    삼성전기  009150"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import yfinance as yf\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from IPython.display import display\n",
    "\n",
    "pd.set_option('display.max_columns', None)\n",
    "pd.set_option('display.width', 200)\n",
    "\n",
    "def load_ticker_groups():\n",
    "    if not TICKERS_FILE.exists():\n",
    "        return DEFAULT_GROUPS\n",
    "    try:\n",
    "        data = json.loads(TICKERS_FILE.read_text(encoding='utf-8'))\n",
    "        return data if isinstance(data, dict) else DEFAULT_GROUPS\n",
    "    except Exception as exc:\n",
    "        print(f'[WARN] bottom_tickers.json 로드 실패: {exc}')\n",
    "        return DEFAULT_GROUPS\n",
    "\n",
    "def normalize_entry(entry, group_name=''):\n",
    "    if isinstance(entry, str):\n",
    "        ticker, name = entry.strip(), entry.strip()\n",
    "    elif isinstance(entry, dict):\n",
    "        ticker = str(entry.get('ticker') or '').strip()\n",
    "        name = str(entry.get('name') or '').strip()\n",
    "    else:\n",
    "        return None\n",
    "    if not ticker:\n",
    "        return None\n",
    "\n",
    "    upper = ticker.upper()\n",
    "    raw_code = upper[:-3] if upper.endswith(('.KS', '.KQ')) else upper\n",
    "    if raw_code.isdigit() and len(raw_code) == 6:\n",
    "        return {'input': ticker, 'ticker': raw_code, 'name': name or code_to_name(raw_code), 'group': group_name}\n",
    "    if resolve_stock and not upper.startswith('^') and not ticker.isascii():\n",
    "        code = resolve_stock(ticker)\n",
    "        if code:\n",
    "            return {'input': ticker, 'ticker': code, 'name': name or code_to_name(code), 'group': group_name}\n",
    "    return {'input': ticker, 'ticker': upper, 'name': name or upper, 'group': group_name}\n",
    "\n",
    "def flatten_ticker_groups(groups):\n",
    "    entries = []\n",
    "    seen = set()\n",
    "    for group_name, items in groups.items():\n",
    "        if not isinstance(items, list):\n",
    "            continue\n",
    "        for item in items:\n",
    "            entry = normalize_entry(item, group_name)\n",
    "            if not entry:\n",
    "                continue\n",
    "            key = entry['ticker'].upper()\n",
    "            if key in seen:\n",
    "                continue\n",
    "            seen.add(key)\n",
    "            entries.append(entry)\n",
    "    return entries\n",
    "\n",
    "def yahoo_candidates(entry):\n",
    "    ticker = entry['ticker'].strip().upper()\n",
    "    if ticker.startswith('^'):\n",
    "        return [ticker]\n",
    "    if ticker.endswith(('.KS', '.KQ')):\n",
    "        code, suffix = ticker[:-3], ticker[-3:]\n",
    "        other = '.KQ' if suffix == '.KS' else '.KS'\n",
    "        return [ticker, code + other]\n",
    "    if ticker.isdigit() and len(ticker) == 6:\n",
    "        return [ticker + '.KS', ticker + '.KQ']\n",
    "    return [ticker]\n",
    "\n",
    "ticker_groups = load_ticker_groups()\n",
    "ticker_entries = flatten_ticker_groups(ticker_groups)\n",
    "print('분석 종목:')\n",
    "display(pd.DataFrame(ticker_entries)[['group', 'name', 'ticker']] if ticker_entries else pd.DataFrame(columns=['group', 'name', 'ticker']))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "31b866a1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-29T21:45:24.205016Z",
     "iopub.status.busy": "2026-10-29T21:45:24.204909Z",
     "iopub.status.idle": "2026-10-29T21:45:24.208864Z",
     "shell.execute_reply": "2026-10-29T21:45:24.208477Z"
    },
    "papermill": {
     "duration": 0.005435,
     "end_time": "2026-10-29T21:45:24.209252+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:24.203817+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def percentile_score(value, history, lower_is_better=True):\n",
    "    history = history.dropna()\n",
    "    if len(history) == 0:\n",
    "        return np.nan\n",
    "    rank = (history < value).mean() * 100\n",
    "    return 100 - rank if lower_is_better else rank\n",
    "\n",
    "def atr(df, period=14):\n",
    "    tr = pd.concat([\n",
    "        df['High'] - df['Low'],\n",
    "        (df['High'] - df['Close'].shift()).abs(),\n",
    "        (df['Low'] - df['Close'].shift()).abs()\n",
    "    ], axis=1).max(axis=1)\n",
    "    return tr.rolling(period).mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "13bdcb1a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-29T21:45:24.211407Z",
     "iopub.status.busy": "2026-10-29T21:45:24.211350Z",
     "iopub.status.idle": "2026-10-29T21:45:24.218811Z",
     "shell.execute_reply": "2026-10-29T21:45:24.218398Z"
    },
    "papermill": {
     "duration": 0.009142,
     "end_time": "2026-10-29T21:45:24.219264+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:24.210122+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def download_first_available(entry):\n",
    "    last_error = None\n",
    "    for yahoo_ticker in yahoo_candidates(entry):\n",
    "        try:\n",
    "            df = yf.download(yahoo_ticker, period='1y', auto_adjust=True, progress=False)\n",
    "            if df is not None and not df.empty:\n",
    "                return yahoo_ticker, df\n",
    "        except Exception as exc:\n",
    "            last_error = exc\n",
    "    if last_error:\n",
    "        raise last_error\n",
    "    return None, pd.DataFrame()\n",
    "\n",
    "def analyze_ticker(entry):\n",
    "    yahoo_ticker, df = download_first_available(entry)\n",
    "    if df.empty:\n",
    "        return None\n",
    "    if isinstance(df.columns, pd.MultiIndex):\n",
    "        df.columns = df.columns.get_level_values(0)\n",
    "    df['MA5'] = df['Close'].rolling(5).mean()\n",
    "    df['MA30'] = df['Close'].rolling(30).mean()\n",
    "    df['MA50'] = df['Close'].rolling(50).mean()\n",
    "    df['DIS30'] = df['Close'] / df['MA30'] * 100\n",
    "    df['DIS50'] = df['Close'] / df['MA50'] * 100\n",
    "    df['ATR14'] = atr(df)\n",
    "    df['HIGH120'] = df['Close'].rolling(120).max()\n",
    "    df['DROP_FROM_120H_PCT'] = (df['Close'] / df['HIGH120'] - 1) * 100\n",
    "    df['DOWN_ATR30'] = (df['Close'] - df['MA30']) / df['ATR14']\n",
    "    df['VOL_MA5'] = df['Volume'].rolling(5).mean()\n",
    "    df['VOL_MA20'] = df['Volume'].rolling(20).mean()\n",
    "    df['VOL_RATIO'] = df['Volume'] / df['VOL_MA20']\n",
    "    df['RET3'] = df['Close'].pct_change(3)\n",
    "    df['DAY_RETURN'] = (df['Close'] - df['Open']) / df['Open']\n",
    "    df = df.dropna()\n",
    "    if df.empty:\n",
    "        return None\n",
    "    latest = df.iloc[-1]\n",
    "\n",
    "    # 1. 저점매수(=매도금지) 점수\n",
    "    score_dis30 = percentile_score(latest['DIS30'], df['DIS30'], True) * 0.30\n",
    "    score_dis50 = percentile_score(latest['DIS50'], df['DIS50'], True) * 0.20\n",
    "    score_high120 = percentile_score(latest['DROP_FROM_120H_PCT'], df['DROP_FROM_120H_PCT'], True) * 0.20\n",
    "    score_down_atr30 = percentile_score(latest['DOWN_ATR30'], df['DOWN_ATR30'], True) * 0.10\n",
    "    score_vol_dry = 10 if latest['VOL_MA5'] < latest['VOL_MA20'] else 0\n",
    "    score_vol_rebound = 10 if (latest['Volume'] > latest['VOL_MA5'] and latest['Volume'] > df['Volume'].shift(1).iloc[-1]) else 0\n",
    "    bottom_score = min(score_dis30 + score_dis50 + score_high120 + score_down_atr30 + score_vol_dry + score_vol_rebound, 100)\n",
    "\n",
    "    # 2. 매수금지(과열) 점수\n",
    "    buy_stop_score = 0\n",
    "    if latest['DIS30'] > df['DIS30'].quantile(0.95): buy_stop_score += 25\n",
    "    elif latest['DIS30'] > df['DIS30'].quantile(0.90): buy_stop_score += 20\n",
    "    elif latest['DIS30'] > df['DIS30'].quantile(0.80): buy_stop_score += 10\n",
    "    if latest['DIS50'] > df['DIS50'].quantile(0.95): buy_stop_score += 15\n",
    "    elif latest['DIS50'] > df['DIS50'].quantile(0.90): buy_stop_score += 10\n",
    "    elif latest['DIS50'] > df['DIS50'].quantile(0.80): buy_stop_score += 5\n",
    "    if latest['DOWN_ATR30'] > 3: buy_stop_score += 20\n",
    "    elif latest['DOWN_ATR30'] > 2: buy_stop_score += 15\n",
    "    elif latest['DOWN_ATR30'] > 1: buy_stop_score += 10\n",
    "    if latest['RET3'] > 0.10: buy_stop_score += 15\n",
    "    elif latest['RET3'] > 0.07: buy_stop_score += 10\n",
    "    elif latest['RET3'] > 0.05: buy_stop_score += 5\n",
    "    if latest['VOL_RATIO'] > 2.5: buy_stop_score += 15\n",
    "    elif latest['VOL_RATIO'] > 2.0: buy_stop_score += 10\n",
    "    elif latest['VOL_RATIO'] > 1.5: buy_stop_score += 5\n",
    "    if latest['DAY_RETURN'] > 0.05: buy_stop_score += 10\n",
    "    elif latest['DAY_RETURN'] > 0.03: buy_stop_score += 7\n",
    "    elif latest['DAY_RETURN'] > 0.02: buy_stop_score += 5\n",
    "    buy_stop_score = min(buy_stop_score, 100)\n",
    "\n",
    "    if bottom_score >= 80: bottom_signal = '저점매수 후보 / 비중축소 금지'\n",
    "    elif bottom_score >= 65: bottom_signal = '매도 보류 / 분할매수 관심'\n",
    "    elif bottom_score >= 50: bottom_signal = '절반만 축소 / 관찰'\n",
    "    else: bottom_signal = '기존 원칙 적용'\n",
    "    if buy_stop_score >= 80: buy_signal = '신규매수 금지'\n",
    "    elif buy_stop_score >= 65: buy_signal = '추격매수 자제'\n",
    "    elif buy_stop_score >= 50: buy_signal = '일부만 매수'\n",
    "    else: buy_signal = '과열 아님'\n",
    "\n",
    "    return {\n",
    "        '그룹': entry.get('group', ''), '종목': entry['name'], '입력': entry['input'], 'Yahoo티커': yahoo_ticker,\n",
    "        '현재가': round(latest['Close'], 2),\n",
    "        '30일이격도': round(latest['DIS30'], 2), '50일이격도': round(latest['DIS50'], 2),\n",
    "        '120일고점대비(%)': round(latest['DROP_FROM_120H_PCT'], 2),\n",
    "        '30일선대비ATR': round(latest['DOWN_ATR30'], 2),\n",
    "        '3일수익률(%)': round(latest['RET3'] * 100, 2),\n",
    "        '당일수익률(%)': round(latest['DAY_RETURN'] * 100, 2),\n",
    "        '거래량비율': round(latest['VOL_RATIO'], 2),\n",
    "        '거래량감소': bool(latest['VOL_MA5'] < latest['VOL_MA20']),\n",
    "        '거래량재증가': bool(latest['Volume'] > latest['VOL_MA5']),\n",
    "        '저점매수점수': round(bottom_score, 1), '매도금지점수': round(bottom_score, 1),\n",
    "        '매수금지점수': round(buy_stop_score, 1),\n",
    "        '판단': bottom_signal, '매수판단': buy_signal,\n",
    "    }\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "1587729a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-29T21:45:24.221322Z",
     "iopub.status.busy": "2026-10-29T21:45:24.221255Z",
     "iopub.status.idle": "2026-10-29T21:45:26.891666Z",
     "shell.execute_reply": "2026-10-29T21:45:26.890951Z"
    },
    "papermill": {
     "duration": 2.672514,
     "end_time": "2026-10-29T21:45:26.892537+00:00",
     "exception": false,
     "start_time": "2026-10-29T21:45:24.220023+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>그룹</th>\n",
       "      <th>종목</th>\n",
       "      <th>입력</th>\n",
       "      <th>Yahoo티커</th>\n",
       "      <th>현재가</th>\n",
       "      <th>30일이격도</th>\n",
       "      <th>50일이격도</th>\n",
       "      <th>120일고점대비(%)</th>\n",
       "      <th>30일선대비ATR</th>\n",
       "      <th>3일수익률(%)</th>\n",
       "      <th>당일수익률(%)</th>\n",
       "      <th>거래량비율</th>\n",
       "      <th>거래량감소</th>\n",
       "      <th>거래량재증가</th>\n",
       "      <th>저점매수점수</th>\n",
       "      <th>매도금지점수</th>\n",
       "      <th>매수금지점수</th>\n",
       "      <th>판단</th>\n",
       "      <th>매수판단</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>국장</td>\n",
       "      <td>삼성전자</td>\n",
       "      <td>005930</td>\n",
       "      <td>005930.KS</td>\n",
       "      <td>232000.00</td>\n",
       "      <td>94.08</td>\n",
       "      <td>91.67</td>\n",
       "      <td>-35.93</td>\n",
       "      <td>-0.95</td>\n",
       "      <td>8.92</td>\n",
       "      <td>-3.73</td>\n",
       "      <td>1.39</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>80.4</td>\n",
       "      <td>80.4</td>\n",
       "      <td>10</td>\n",
       "      <td>저점매수 후보 / 비중축소 금지</td>\n",
       "      <td>과열 아님</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>지수</td>\n",
       "      <td>KOSPI</td>\n",
       "      <td>^KS11</td>\n",
       "      <td>^KS11</td>\n",
       "      <td>6012.44</td>\n",
       "      <td>94.18</td>\n",
       "      <td>91.69</td>\n",
       "      <td>-34.03</td>\n",
       "      <td>-1.28</td>\n",
       "      <td>8.33</td>\n",
       "      <td>4.31</td>\n",
       "      <td>0.85</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>80.2</td>\n",
       "      <td>80.2</td>\n",
       "      <td>17</td>\n",
       "      <td>저점매수 후보 / 비중축소 금지</td>\n",
       "      <td>과열 아님</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>지수</td>\n",
       "      <td>KOSDAQ</td>\n",
       "      <td>^KQ11</td>\n",
       "      <td>^KQ11</td>\n",
       "      <td>771.28</td>\n",
       "      <td>93.76</td>\n",
       "      <td>93.57</td>\n",
       "      <td>-36.45</td>\n",
       "      <td>-1.41</td>\n",
       "      <td>2.61</td>\n",
       "      <td>1.90</td>\n",
       "      <td>1.05</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>72.4</td>\n",
       "      <td>72.4</td>\n",
       "      <td>0</td>\n",
       "      <td>매도 보류 / 분할매수 관심</td>\n",
       "      <td>과열 아님</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>국장</td>\n",
       "      <td>삼성전기</td>\n",
       "      <td>009150</td>\n",
       "      <td>009150.KS</td>\n",
       "      <td>1293000.00</td>\n",
       "      <td>94.76</td>\n",
       "      <td>93.81</td>\n",
       "      <td>-43.04</td>\n",
       "      <td>-0.68</td>\n",
       "      <td>15.65</td>\n",
       "      <td>-1.30</td>\n",
       "      <td>1.56</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>68.5</td>\n",
       "      <td>68.5</td>\n",
       "      <td>20</td>\n",
       "      <td>매도 보류 / 분할매수 관심</td>\n",
       "      <td>과열 아님</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>국장</td>\n",
       "      <td>SK하이닉스</td>\n",
       "      <td>000660</td>\n",
       "      <td>000660.KS</td>\n",
       "      <td>1526000.00</td>\n",
       "      <td>96.58</td>\n",
       "      <td>93.77</td>\n",
       "      <td>-47.71</td>\n",
       "      <td>-0.50</td>\n",
       "      <td>19.69</td>\n",
       "      <td>0.99</td>\n",
       "      <td>1.27</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>64.8</td>\n",
       "      <td>64.8</td>\n",
       "      <td>15</td>\n",
       "      <td>절반만 축소 / 관찰</td>\n",
       "      <td>과열 아님</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   그룹      종목      입력    Yahoo티커         현재가  30일이격도  50일이격도  120일고점대비(%)  30일선대비ATR  3일수익률(%)  당일수익률(%)  거래량비율  거래량감소  거래량재증가  저점매수점수  매도금지점수  매수금지점수                 판단   매수판단\n",
       "0  국장    삼성전자  005930  005930.KS   232000.00   94.08   91.67       -35.93      -0.95      8.92     -3.73   1.39   True    True    80.4    80.4      10  저점매수 후보 / 비중축소 금지  과열 아님\n",
       "1  지수   KOSPI   ^KS11      ^KS11     6012.44   94.18   91.69       -34.03      -1.28      8.33      4.31   0.85   True    True    80.2    80.2      17  저점매수 후보 / 비중축소 금지  과열 아님\n",
       "2  지수  KOSDAQ   ^KQ11      ^KQ11      771.28   93.76   93.57       -36.45      -1.41      2.61      1.90   1.05   True    True    72.4    72.4       0    매도 보류 / 분할매수 관심  과열 아님\n",
       "3  국장    삼성전기  009150  009150.KS  1293000.00   94.76   93.81       -43.04      -0.68     15.65     -1.30   1.56  False    True    68.5    68.5      20    매도 보류 / 분할매수 관심  과열 아님\n",
       "4  국장  SK하이닉스  000660  000660.KS  1526000.00   96.58   93.77       -47.71      -0.50     19.69      0.99   1.27  False    True    64.8    64.8      15        절반만 축소 / 관찰  과열 아님"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "results = []\n",
    "for entry in ticker_entries:\n",
    "    try:\n",
    "        r = analyze_ticker(entry)\n",
    "        if r:\n",
    "            results.append(r)\n",
    "        else:\n",
    "            print(f\"{entry['name']} ({entry['ticker']}) 데이터 없음\")\n",
    "    except Exception as e:\n",
    "        print(f\"{entry['name']} ({entry['ticker']}) 오류: {e}\")\n",
    "\n",
    "result_df = pd.DataFrame(results)\n",
    "if not result_df.empty:\n",
    "    result_df = result_df.sort_values(['저점매수점수','매수금지점수'], ascending=[False, True]).reset_index(drop=True)\n",
    "    display(result_df)\n",
    "else:\n",
    "    print('결과 없음 — 티커/네트워크 확인')\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.13"
  },
  "papermill": {
   "default_parameters": {},
   "duration": 4.216117,
   "end_time": "2026-10-29T21:45:27.111954+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/Users/seog-yejun/룬샤시_공시정리/태버지/10. 찌짐이 방지 (저점매수·매수금지 점수).ipynb",
   "output_path": "/Users/seog-yejun/룬샤시_공시정리/태버지/10. 찌짐이 방지 (저점매수·매수금지 점수).ipynb",
   "parameters": {},
   "start_time": "2026-10-29T21:45:22.895837+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}