Files
kis_trader/remove/legacy_root/export_sniper.py
Your Name 6d2a706a48 커밋 1 — 실매 가격 TTL 구멍 (본체)
왜: 체결이 없어도 마지막가는 유지인데, TTL로 None 만들고 매도/EOD를 건너뛰어 8/5 돌파·금요일 leftover가 남음. 호가필터 TTL 구멍과 같은 병.

넣을 파일

신규: kis_trader/engine/live_sell_price.py
kis_trader/strategies/base.py (_ws_last_quote, _resolve_sell_price)
전략: momentum.py scalping.py tail_catch.py breakout.py range_break.py dart_strategy.py updow_strategy.py updown_feed.py us_momentum.py
WS: ws_manager.py kis_ws.py kiwoom_ws.py ls_ws.py kis_ws_overseas.py
kis_trader/web/live_config_schema.py (WS_PRICE_MAX_AGE_SEC 기본 0)
database.py (키 주석 + legacy/ sys.path)
kis_trader/execution/order_manager.py (잔고 있는데 40240000 ghost_purge 금지 — 같은 EOD 사고)
EOD가 min_hold에 안 막히게 손본 momentum_hts_logic.py / scalping_engine.py / tail_engine.py (이 대화에서 손본 부분만 확인 후)
문서: docs/like_mcp.md/db_erd.md code_architecture.md (가격 TTL 문구)
메시지 초안

fix: 매수·매도 현재가를 TTL로 버리지 않음 (마지막 RAM)
횡보·체결 공백을 죽은 캐시로 오인해 None 처리하면 손절·EOD가 스킵된다.
호가필터와 같이 나이는 무시하고 마지막 체결가를 유지한다. EOD는 매수가 폴백.
영향: 실매 O / 백테·옵투나 봉 경로 거의 무관 (엔진 식 변경 아님)

커밋 2 — 루트 정리 (remove/ vs legacy/)
왜: 루트 단독봇·테스트는 지울 보관함으로. 웹·알람이 아직 쓰는 모듈은 remove에 두면 나중에 폴더째 삭제 때 깨짐.

넣을 파일

이동: 미사용 → remove/legacy_root/ (래퍼, ETF/키움 옛봇, 테스트, kiwoom_rest_api 등)
이동: 사용 중 → legacy/ (holding_bot kis_holding_ver1 news_analyzer kis_long_ver1/2)
신규: kis_trader/utils/legacy_root.py legacy/README.md remove/README.md
import 경로: backtest_web.py mm_butler.py mm_remote.py updow_holding_cfg.py dbband_stock_cfg.py param_search_updow*.py dbband_param_search.py param_search_apply_snapshot.py verify_three_paths.py
docs/like_mcp.md/code_architecture.md 수동 노트
메시지 초안

chore: 미사용 루트는 remove/, 웹·알람 구모듈은 legacy/
remove는 나중에 통째 삭제 예정. holding_bot·news_analyzer·kis_long은
ensure_legacy_root로 legacy/만 본다.
빼기: scratch/set_ws_price_max_age_zero.py (일회성)
2026-08-18 00:13:17 +09:00

133 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
관세청 수출 호재 감지 스나이퍼
- 네이버 금융 속보에서 '수출/반도체' 관련 뉴스 실시간 감시
- 호재 종목을 즉시 후보군에 추가
"""
import requests
from bs4 import BeautifulSoup
import time
import datetime
import logging
import re
from typing import List, Dict, Optional
logger = logging.getLogger("ExportSniper")
class ExportSniper:
"""관세청 수출 호재 감지기"""
def __init__(self):
# 네이버 금융 실시간 속보 URL
self.target_url = "https://finance.naver.com/news/news_list.naver?mode=LSS2D&section_id=101&section_id2=258"
self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
self.keywords = ['관세청', '수출', '반도체', '잠정', '무역수지', '수출입']
self.seen_news = set() # 이미 본 뉴스는 중복 출력 방지
# 종목명 매칭용 (반도체 관련)
self.semiconductor_stocks = {
'삼성전자': '005930',
'SK하이닉스': '000660',
'삼성SDI': '006400',
'LG화학': '051910',
'LG에너지솔루션': '373220',
'포스코홀딩스': '005490',
'포스코케미칼': '003670',
}
def fetch_news(self) -> List[Dict]:
"""네이버 금융 속보에서 뉴스 가져오기"""
try:
res = requests.get(self.target_url, headers=self.headers, timeout=5)
soup = BeautifulSoup(res.text, 'html.parser')
# 뉴스 리스트 파싱
news_items = soup.select('ul.realtimeNewsList li dl')
found_news = []
for item in news_items:
title_tag = item.select_one('a')
if not title_tag:
continue
title = title_tag.text.strip()
link = "https://finance.naver.com" + title_tag.get('href', '')
# 중복 체크
if title in self.seen_news:
continue
self.seen_news.add(title)
# 키워드 감지
if any(keyword in title for keyword in self.keywords):
# 긍정적 키워드 체크 (증가, 급증, 상승 등)
positive_keywords = ['증가', '급증', '상승', '호조', '기록', '최대', '신고']
is_positive = any(pk in title for pk in positive_keywords)
if is_positive:
found_news.append({
'title': title,
'link': link,
'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})
logger.info(f"🔥 [호재 포착] {title}")
return found_news
except Exception as e:
logger.error(f"❌ 뉴스 크롤링 실패: {e}")
return []
def extract_stocks_from_news(self, news_title: str) -> List[str]:
"""
뉴스 제목에서 관련 종목 코드 추출
:param news_title: 뉴스 제목
:return: 종목 코드 리스트
"""
stocks = []
# 반도체 관련 종목 매칭
for name, code in self.semiconductor_stocks.items():
if name in news_title:
stocks.append(code)
# "반도체" 키워드가 있으면 주요 반도체 종목 모두 추가
if '반도체' in news_title:
stocks.extend(['005930', '000660']) # 삼성전자, SK하이닉스
return list(set(stocks)) # 중복 제거
def get_hot_stocks(self) -> List[Dict]:
"""
호재 뉴스에서 관련 종목 추출
:return: [{'code': '005930', 'name': '삼성전자', 'reason': '뉴스 제목'}, ...]
"""
news_list = self.fetch_news()
hot_stocks = []
for news in news_list:
codes = self.extract_stocks_from_news(news['title'])
for code in codes:
# 종목명 찾기
name = None
for stock_name, stock_code in self.semiconductor_stocks.items():
if stock_code == code:
name = stock_name
break
if name:
hot_stocks.append({
'code': code,
'name': name,
'reason': news['title'],
'timestamp': news['timestamp']
})
return hot_stocks