{
  "ticker": "010950.KS",
  "ticker_name": "S-Oil",
  "status": "success",
  "trade_date": "2026-04-14",
  "analysis_date": "2026-04-14",
  "decision": "{\n  \"rating\": \"NO_TRADE\",\n  \"portfolio_stance\": \"BULLISH\",\n  \"entry_action\": \"WAIT\",\n  \"setup_quality\": \"DEVELOPING\",\n  \"confidence\": 0.68,\n  \"time_horizon\": \"medium\",\n  \"entry_logic\": \"010950.KS는 방향성으로는 우호적이다. 2026-04-13 종가 KRW 122000이 10일 EMA 약 KRW 116027, 50일 SMA 약 KRW 111480, 200일 SMA 약 KRW 81069 위에 있고, MACD가 2026-04-03 -402.90에서 2026-04-13 2116.87로 전환됐으며 히스토그램도 1134.54로 확대됐다. RSI 58.29는 회복 모멘텀은 확인하지만 과열권은 아니다. Q1 2026 영업이익 KRW 500bn 수준 기대, Q4 영업현금흐름 KRW 1.86tn, 자유현금흐름 KRW 613bn, 순차입금의 Q3 KRW 6.38tn에서 Q4 KRW 5.70tn 감소도 턴어라운드 근거다. 다만 즉시 진입은 보류한다. 3월 초 KRW 176551 부근까지 급등한 뒤 KRW 105000-112000대로 되밀린 이력이 있고 ATR이 약 KRW 7683, 가격 대비 약 6.3%로 높아 단일 종가 기준 추격 매수의 위험보상이 충분하지 않다. 신규 진입은 KRW 124500 상향 돌파가 최근 4월 거래량을 명확히 웃도는 거래량과 함께 확인되거나, KRW 116000-118000 조정 구간에서 지지와 RSI 중립 이상, MACD 양호가 동시에 확인될 때만 정당화된다. 조건 충족 시에는 스타터 진입이 가능하지만 현재는 WAIT이다.\",\n  \"exit_logic\": \"이미 보유 중이면 KRW 116000 일봉 종가 이탈과 MACD 히스토그램 둔화가 동반될 때 축소 또는 청산한다. 신규 스타터 진입 후 같은 조건이 발생하면 논쟁 없이 손절을 우선한다. 50일 SMA 부근인 KRW 111500 이탈은 단기 회복 구조 실패로 보고 강세 관점을 재평가한다. Q1 2026 영업이익이 KRW 500bn 기대를 크게 밑돌거나, Q2 정제마진 약화, 원가 부담, 운전자본 압박, 순차입금 재확대 신호가 나오면 강세 논리를 철회한다.\",\n  \"position_sizing\": \"현재 신규 포지션은 열지 않는다. 조건이 충족되면 초기 스타터는 정상 비중의 약 1/3로 제한한다. 1/2 비중은 KRW 124500 돌파 후 지지 재확인, 또는 Q1 실적과 Q2 마진 전망이 모두 우호적으로 확인된 뒤에만 검토한다. 높은 ATR, 3월 거래량 충격, 총부채 KRW 7.91tn, 순차입금 KRW 5.70tn, 유동비율 0.706, 약 KRW 3.60tn의 음의 운전자본을 감안해 추격 증액은 피한다.\",\n  \"risk_limits\": \"초기 위험선은 KRW 116000 아래로 둔다. 진입가 기준 약 1 ATR보다 포트폴리오 손실 한도가 더 타이트하면 더 보수적인 기준을 적용한다. KRW 116000 아래에서는 물타기 금지다. KRW 111500 부근 50일 SMA 이탈 시 전체 강세 시나리오를 재검토한다. 정유 업황의 순환성, 고유가에 따른 재고이익과 운전자본 부담의 양면성, 건설중인자산 약 KRW 8.60tn에 따른 자금 소요를 고려해 총 익스포저는 작게 유지한다.\",\n  \"catalysts\": [\n    \"Q1 2026 영업이익이 KRW 500bn 수준 이상으로 확인되고 전년 대비 흑자 전환이 명확해지는 경우\",\n    \"정제마진과 원유·제품 스프레드가 견조하게 유지되면서 수요 둔화나 정책 부담이 나타나지 않는 경우\",\n    \"National Pension 지분 확대가 저PBR 및 가치 재평가 논리를 계속 지지하는 경우\",\n    \"KRW 124500 상향 돌파가 최근 4월 거래량을 웃도는 거래량과 함께 확인되는 경우\",\n    \"주가가 10일 EMA 약 KRW 116027과 50일 SMA 약 KRW 111480 위에서 안정적으로 유지되는 경우\"\n  ],\n  \"invalidators\": [\n    \"KRW 116000 일봉 종가 이탈과 MACD 히스토그램 둔화가 함께 나타나는 경우\",\n    \"50일 SMA 부근 KRW 111500을 이탈해 단기 회복 구조가 실패하는 경우\",\n    \"Q1 2026 실적이 KRW 500bn 영업이익 기대를 하회하거나 Q2 마진 약화 가이던스가 제시되는 경우\",\n    \"유가 USD 100 이상 환경이 재고이익보다 수요 파괴, 운전자본 부담, 정책 리스크로 더 크게 작용하는 경우\",\n    \"순차입금, 유동성, 자유현금흐름이 다시 악화되고 대규모 capex 부담이 확대되는 경우\"\n  ],\n  \"watchlist_triggers\": [\n    \"010950.KS가 KRW 124500 위로 일봉 마감하고 거래량이 최근 4월 범위를 명확히 상회하는지 확인\",\n    \"KRW 116000-118000 조정 구간에서 지지되고 RSI가 중립 이상, MACD가 양호하게 유지되는지 확인\",\n    \"Q1 2026 영업이익이 KRW 500bn 기대에 부합하거나 상회하는지 확인\",\n    \"Q1 실적 이후 Q2 정제마진, 재고이익, 운전자본, 순차입금 관련 코멘트 확인\",\n    \"National Pension 또는 다른 기관투자자 지분 변화 확인\",\n    \"ATR이 현 수준에서 축소되어 손절과 포지션 관리가 더 명확해지는지 확인\"\n  ],\n  \"data_coverage\": {\n    \"company_news_count\": 5,\n    \"disclosures_count\": 1,\n    \"social_source\": \"news_derived\",\n    \"macro_items_count\": 1\n  }\n}",
  "started_at": "2026-04-14T11:12:28.201017+09:00",
  "finished_at": "2026-04-14T11:25:16.330808+09:00",
  "duration_seconds": 768.13,
  "metrics": {
    "llm_calls": 59,
    "tool_calls": 22,
    "tokens_in": 0,
    "tokens_out": 0,
    "tokens_available": false,
    "calls_by_model": {
      "gpt-5.4": 59
    }
  },
  "tool_telemetry": {
    "total_tool_calls": 22,
    "vendor_calls": {
      "yfinance": 15,
      "naver": 3,
      "ecos": 1,
      "opendart": 1,
      "alpha_vantage": 2
    },
    "fallback_count": 4,
    "events": [
      {
        "method": "get_stock_data",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_indicators",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_social_sentiment",
        "vendor": "naver",
        "status": "fallback",
        "fallback": true,
        "note": "naver: empty or unusable result"
      },
      {
        "method": "get_company_news",
        "vendor": "naver",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_social_sentiment",
        "vendor": "yfinance",
        "status": "fallback",
        "fallback": true,
        "note": "yfinance: empty or unusable result"
      },
      {
        "method": "get_macro_news",
        "vendor": "ecos",
        "status": "fallback",
        "fallback": true,
        "note": "ecos: ECOS API key is not configured."
      },
      {
        "method": "get_disclosures",
        "vendor": "opendart",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_macro_news",
        "vendor": "alpha_vantage",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_company_news",
        "vendor": "naver",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_cashflow",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_balance_sheet",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_income_statement",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_insider_transactions",
        "vendor": "yfinance",
        "status": "fallback",
        "fallback": true,
        "note": "yfinance: empty or unusable result"
      },
      {
        "method": "get_fundamentals",
        "vendor": "yfinance",
        "status": "success",
        "fallback": false,
        "note": null
      },
      {
        "method": "get_insider_transactions",
        "vendor": "alpha_vantage",
        "status": "success",
        "fallback": false,
        "note": null
      }
    ]
  },
  "quality_flags": [
    "token_usage_unavailable"
  ],
  "report_writer": {
    "status": "fallback",
    "scope": "ticker",
    "provider": "codex",
    "model": "gpt-5.4",
    "reason": "writer_failed",
    "error": "1 validation error for CodexChatModel\ncodex_workspace_dir\n  Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]\n    For further information visit https://errors.pydantic.dev/2.12/v/string_type"
  },
  "provider": "codex",
  "models": {
    "quick_model": "gpt-5.4",
    "deep_model": "gpt-5.4",
    "output_model": "gpt-5.4"
  }
}