feat: add velocity entry, fast-poll thread, tighten BEAR threshold

- Add velocity-based entry signal in strategy.py (VELOCITY_THRESHOLD=0.10,
  VELOCITY_MIN_MOVE=0.5%, VELOCITY_MIN_AGE_M=5)
- Add fast-poll thread in daemon/runner.py (SIGNAL_POLL_INTERVAL=15s)
  for sub-minute velocity event detection
- Add vol_ratio tiered condition and get_active_signals() to strategy.py
- Change BEAR_THRESHOLD -1.0 → -0.5 in market_regime.py to catch
  slow downtrends earlier (weighted 2h score)
- Expand sell_reason VARCHAR2(500) in price_db.py DDL
- Add velocity_backtest.py and sim10m.py for strategy experimentation
- Update STRATEGY.md: correct regime algorithm description (weighted 2h
  score, not BTC 1h ±5%), add fast-poll/velocity sections, add backtest
  section D, add change history table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-03 10:17:08 +09:00
parent 612055162e
commit 673ce08d84
7 changed files with 986 additions and 36 deletions

View File

@@ -1,21 +1,60 @@
"""매수 기회 스캔 루프 - 60초마다 전체 시장 스캔."""
"""매수 기회 스캔 루프 - 60초마다 전체 시장 스캔 + 신호 종목 빠른 폴링."""
import logging
import os
import threading
import time
from core import trader
from core.market import get_top_tickers
from core.market_regime import get_regime
from core.strategy import should_buy
from core.strategy import get_active_signals, should_buy
logger = logging.getLogger(__name__)
SCAN_INTERVAL = 60 #
SCAN_INTERVAL = 60 # 전체 시장 스캔 주기 (초)
SIGNAL_POLL_INTERVAL = int(os.getenv("SIGNAL_POLL_INTERVAL", "15")) # 신호 종목 빠른 감시 주기 (초)
def _fast_poll_loop() -> None:
"""활성 신호 종목을 SIGNAL_POLL_INTERVAL 초마다 빠르게 체크.
신호 감지 후 전체 스캔 60초를 기다리지 않고, 신호 종목만 빠르게 감시하여
목표 임계값 도달 시 즉시 매수한다.
"""
logger.info(f"신호 감시 시작 (주기={SIGNAL_POLL_INTERVAL}초)")
while True:
try:
signals = get_active_signals()
if signals:
regime = get_regime()
if regime["name"] != "bear":
positions = trader.get_positions()
for ticker in list(signals):
if ticker in positions:
continue
if len(positions) >= trader.MAX_POSITIONS:
break
try:
if should_buy(ticker):
logger.info(f"[빠른감시] 매수 신호: {ticker}")
trader.buy(ticker)
time.sleep(0.1)
except Exception as e:
logger.error(f"[빠른감시] 오류 {ticker}: {e}")
except Exception as e:
logger.error(f"신호 감시 루프 오류: {e}")
time.sleep(SIGNAL_POLL_INTERVAL)
def run_scanner() -> None:
"""메인 스캔 루프."""
logger.info(f"스캐너 시작 (주기={SCAN_INTERVAL}초)")
# 신호 종목 빠른 감시 스레드 시작
t = threading.Thread(target=_fast_poll_loop, daemon=True, name="signal-fast-poll")
t.start()
logger.info(f"스캐너 시작 (스캔={SCAN_INTERVAL}초 | 신호감시={SIGNAL_POLL_INTERVAL}초)")
while True:
try:
# 포지션 꽉 찼으면 스캔 스킵