[2026-05-28] 진입 지연 및 섹터 필터 수정
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ Claude Code가 이 파일을 읽고 필요시 수정함
|
|||||||
|
|
||||||
# ── 변동성 돌파 ──
|
# ── 변동성 돌파 ──
|
||||||
STRATEGY_K = 0.5
|
STRATEGY_K = 0.5
|
||||||
ENTRY_START = "09:15"
|
ENTRY_START = "09:20"
|
||||||
ENTRY_END = "14:30"
|
ENTRY_END = "14:30"
|
||||||
FORCE_EXIT = "14:50" # 절대 변경 불가
|
FORCE_EXIT = "14:50" # 절대 변경 불가
|
||||||
TP1_PCT = 0.020 # 1차 익절 +2.0% → 70% 매도
|
TP1_PCT = 0.020 # 1차 익절 +2.0% → 70% 매도
|
||||||
|
|||||||
+50
@@ -93,6 +93,7 @@ class StockBot:
|
|||||||
self.positions = {} # ticker → {name, entry, qty, tp1_done, entry_time}
|
self.positions = {} # ticker → {name, entry, qty, tp1_done, entry_time}
|
||||||
self.universe = [] # 감시 종목 리스트
|
self.universe = [] # 감시 종목 리스트
|
||||||
self.ticker_names = {} # ticker → 종목명 캐시
|
self.ticker_names = {} # ticker → 종목명 캐시
|
||||||
|
self.ticker_sectors = {} # ticker -> sector name cache
|
||||||
self.sl_tickers = set() # 당일 SL 당한 종목 — 재진입 차단
|
self.sl_tickers = set() # 당일 SL 당한 종목 — 재진입 차단
|
||||||
self.risk = None # RiskManager (잔고 확인 후 초기화)
|
self.risk = None # RiskManager (잔고 확인 후 초기화)
|
||||||
self.running = False
|
self.running = False
|
||||||
@@ -524,6 +525,18 @@ class StockBot:
|
|||||||
# ETF/ETN/인버스/레버리지 종목 필터
|
# ETF/ETN/인버스/레버리지 종목 필터
|
||||||
_ETF_KEYWORDS = ('인버스', '레버리지', '선물', 'KODEX', 'TIGER', 'KBSTAR',
|
_ETF_KEYWORDS = ('인버스', '레버리지', '선물', 'KODEX', 'TIGER', 'KBSTAR',
|
||||||
'HANARO', 'ARIRANG', 'KOSEF', 'SOL', 'ACE', 'RISE', 'PLUS')
|
'HANARO', 'ARIRANG', 'KOSEF', 'SOL', 'ACE', 'RISE', 'PLUS')
|
||||||
|
_SECTOR_FIELDS = (
|
||||||
|
"sector",
|
||||||
|
"sector_name",
|
||||||
|
"bstp_kor_isnm",
|
||||||
|
"bstp_cls_name",
|
||||||
|
"bstp_name",
|
||||||
|
)
|
||||||
|
_AVOID_SECTOR_NAME_HINTS = (
|
||||||
|
("\uac74\uc124", ("\uac74\uc124", "\ub300\uc6b0\uac74\uc124", "\ud604\ub300\uac74\uc124", "GS\uac74\uc124", "DL\uc774\uc564\uc528", "HDC\ud604\ub300\uc0b0\uc5c5\uac1c\ubc1c")),
|
||||||
|
("\uc804\uae30\uac00\uc2a4", ("\ud55c\uad6d\uc804\ub825", "\ud55c\uc804", "\ud55c\uad6d\uac00\uc2a4", "\uc9c0\uc5ed\ub09c\ubc29", "\uc804\uae30", "\uac00\uc2a4")),
|
||||||
|
("\uc8fc\ub958", ("\ud558\uc774\ud2b8\uc9c4\ub85c", "\ub86f\ub370\uce60\uc131", "\ubb34\ud559", "\ubcf4\ud574\uc591\uc870", "\uad6d\uc21c\ub2f9", "\uc8fc\ub958")),
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_etf(ticker: str, name: str) -> bool:
|
def _is_etf(ticker: str, name: str) -> bool:
|
||||||
@@ -531,6 +544,35 @@ class StockBot:
|
|||||||
return True
|
return True
|
||||||
return any(kw in name for kw in StockBot._ETF_KEYWORDS)
|
return any(kw in name for kw in StockBot._ETF_KEYWORDS)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sector_from_rank_row(cls, row: dict) -> str:
|
||||||
|
for field in cls._SECTOR_FIELDS:
|
||||||
|
value = row.get(field)
|
||||||
|
if value:
|
||||||
|
return str(value).strip()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _infer_avoid_sector_from_name(self, name: str) -> str:
|
||||||
|
compact_name = (name or "").replace(" ", "")
|
||||||
|
if not compact_name:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
for sector in self.strategy.context.get("avoid_sectors", []):
|
||||||
|
sector_name = str(sector or "").strip()
|
||||||
|
if not sector_name:
|
||||||
|
continue
|
||||||
|
compact_sector = sector_name.replace(" ", "")
|
||||||
|
|
||||||
|
for key, hints in self._AVOID_SECTOR_NAME_HINTS:
|
||||||
|
if key in compact_sector and any(hint in compact_name for hint in hints):
|
||||||
|
return sector_name
|
||||||
|
|
||||||
|
generic_token = compact_sector.replace("\uc5c5", "")
|
||||||
|
if len(generic_token) >= 2 and generic_token in compact_name:
|
||||||
|
return sector_name
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
async def update_universe(self):
|
async def update_universe(self):
|
||||||
"""종목 풀 갱신 + 전일 데이터 수집"""
|
"""종목 풀 갱신 + 전일 데이터 수집"""
|
||||||
logger.info("유니버스 갱신 시작")
|
logger.info("유니버스 갱신 시작")
|
||||||
@@ -541,6 +583,9 @@ class StockBot:
|
|||||||
# 종목명 캐시 갱신 + ETF 필터
|
# 종목명 캐시 갱신 + ETF 필터
|
||||||
for r in rank:
|
for r in rank:
|
||||||
self.ticker_names[r["ticker"]] = r["name"]
|
self.ticker_names[r["ticker"]] = r["name"]
|
||||||
|
sector = self._sector_from_rank_row(r)
|
||||||
|
if sector:
|
||||||
|
self.ticker_sectors[r["ticker"]] = sector
|
||||||
tickers = [r["ticker"] for r in rank
|
tickers = [r["ticker"] for r in rank
|
||||||
if not self._is_etf(r["ticker"], r["name"])]
|
if not self._is_etf(r["ticker"], r["name"])]
|
||||||
|
|
||||||
@@ -732,11 +777,16 @@ class StockBot:
|
|||||||
price_info = await self.kis.get_price(ticker) # rate limiter가 자동 throttle
|
price_info = await self.kis.get_price(ticker) # rate limiter가 자동 throttle
|
||||||
current = price_info["current"]
|
current = price_info["current"]
|
||||||
name = self.ticker_names.get(ticker, ticker)
|
name = self.ticker_names.get(ticker, ticker)
|
||||||
|
sector = (
|
||||||
|
self.ticker_sectors.get(ticker, "")
|
||||||
|
or self._infer_avoid_sector_from_name(name)
|
||||||
|
)
|
||||||
|
|
||||||
signal = self.strategy.check_entry(
|
signal = self.strategy.check_entry(
|
||||||
ticker=ticker,
|
ticker=ticker,
|
||||||
name=name,
|
name=name,
|
||||||
current_price=current,
|
current_price=current,
|
||||||
|
sector=sector,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not signal["signal"]:
|
if not signal["signal"]:
|
||||||
|
|||||||
Reference in New Issue
Block a user