Compare commits

...

6 Commits

Author SHA1 Message Date
agentson
7aa5fedc12 fix: market_data에 unrealized_pnl_pct/holding_days 추가하여 SELL 시나리오 정상화 (#259)
Some checks failed
CI / test (pull_request) Has been cancelled
trading_cycle()의 market_data에 보유 포지션 정보가 없어
Condition requires 'unrealized_pnl_pct' but key missing from market_data 경고 발생.
보유 종목(NVDA 등)의 take-profit/stop-loss 시나리오가 평가 불가하여 HOLD(confidence=0) 고착.

- get_open_position()에 timestamp 컬럼 추가
- market_data 구성 시 open_position 조회 후 아래 키 추가:
  - unrealized_pnl_pct: (current_price - entry_price) / entry_price * 100
  - holding_days: 매수일로부터 경과 일수

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:23:28 +09:00
agentson
3e777a5ab8 fix: mock_settings에 MODE='paper' 명시하여 paper 모드 테스트 실패 수정 (#261)
mock_settings fixture에 MODE 미지정 시 .env의 MODE=live가 적용되어
paper TR_ID를 검증하는 테스트 3개가 실패.

- test_buy_market_order: VTTT1002U 기대 → TTTT1002U 실제
- test_sell_limit_order: VTTT1001U 기대 → TTTT1006U 실제
- test_us_paper_uses_vttt1004u: VTTT1004U 기대 → TTTT1004U 실제

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:23:01 +09:00
f87c4dc2f0 Merge pull request 'fix: ranking API 필수 파라미터 KEYB 추가 및 GUBN 값 수정 (#258)' (#260) from feature/issue-258-ranking-api-keyb-param into main
Some checks failed
CI / test (push) Has been cancelled
Reviewed-on: #260
2026-02-26 00:20:58 +09:00
agentson
8af5f564c3 fix: ranking API 필수 파라미터 KEYB 추가 및 GUBN 값 수정 (#258)
Some checks failed
CI / test (pull_request) Has been cancelled
KIS 공식 문서(20260221) 기준 KEYB(NEXT KEY BUFF)는 Required=Y이나
누락되어 있어 항상 rt_cd=2 오류 발생, fallback 경로로만 실행됨.

- fluctuation/volume 양쪽 params에 KEYB: '' 추가
- GUBN 주석 수정: 0=하락율, 1=상승율 (문서 기준)
- GUBN 값 0→1 수정: 상승율 기준으로 변동성 급등 종목 스캔

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:19:55 +09:00
06e4fc5597 Merge pull request 'fix: run_overnight.sh --mode=paper → --mode=live 수정 (#256)' (#257) from feature/issue-256-fix-overnight-live-mode into main
Some checks failed
CI / test (push) Has been cancelled
Reviewed-on: #257
2026-02-26 00:06:50 +09:00
agentson
b697b6d515 fix: run_overnight.sh --mode=paper → --mode=live 수정 (#256)
Some checks failed
CI / test (pull_request) Has been cancelled
실전투자 API 설정(.env: 실전 BASE_URL, 계좌번호)을 사용하면서
--mode=paper로 실행하여 TR_ID 불일치 발생.

실전투자 서버에 모의투자 TR_ID(VTTS3012R)를 날려
EGW02004: 실전투자 TR 이 아닙니다. 오류로 해외 거래 전부 실패.

APP_CMD 기본값을 --mode=live로 변경하여 실전투자 TR_ID(TTTS3012R) 사용.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:03:51 +09:00
5 changed files with 26 additions and 5 deletions

View File

@@ -23,7 +23,7 @@ if [ -z "${APP_CMD:-}" ]; then
dashboard_port="${DASHBOARD_PORT:-8080}"
APP_CMD="DASHBOARD_PORT=$dashboard_port $PYTHON_BIN -m src.main --mode=paper --dashboard"
APP_CMD="DASHBOARD_PORT=$dashboard_port $PYTHON_BIN -m src.main --mode=live --dashboard"
fi
mkdir -p "$LOG_DIR"

View File

@@ -121,6 +121,7 @@ class OverseasBroker:
tr_id = self._broker._settings.OVERSEAS_RANKING_VOLUME_TR_ID
path = self._broker._settings.OVERSEAS_RANKING_VOLUME_PATH
params: dict[str, str] = {
"KEYB": "", # NEXT KEY BUFF — Required, 공백
"AUTH": "",
"EXCD": ranking_excd,
"MIXN": "0",
@@ -130,10 +131,11 @@ class OverseasBroker:
tr_id = self._broker._settings.OVERSEAS_RANKING_FLUCT_TR_ID
path = self._broker._settings.OVERSEAS_RANKING_FLUCT_PATH
params = {
"KEYB": "", # NEXT KEY BUFF — Required, 공백
"AUTH": "",
"EXCD": ranking_excd,
"NDAY": "0",
"GUBN": "0", # 0=전체(상승+하락), 1=상승 — 변동성 스캐너는 전체 필요
"GUBN": "1", # 0=하락, 1=상승 — 변동성 스캐너는 급등 종목 우선
"VOL_RANG": "0",
}

View File

@@ -254,7 +254,7 @@ def get_open_position(
"""Return open position if latest trade is BUY, else None."""
cursor = conn.execute(
"""
SELECT action, decision_id, price, quantity
SELECT action, decision_id, price, quantity, timestamp
FROM trades
WHERE stock_code = ?
AND market = ?
@@ -266,7 +266,7 @@ def get_open_position(
row = cursor.fetchone()
if not row or row[0] != "BUY":
return None
return {"decision_id": row[1], "price": row[2], "quantity": row[3]}
return {"decision_id": row[1], "price": row[2], "quantity": row[3], "timestamp": row[4]}
def get_recent_symbols(

View File

@@ -576,6 +576,22 @@ async def trading_cycle(
market_data["rsi"] = candidate.rsi
market_data["volume_ratio"] = candidate.volume_ratio
# Enrich market_data with holding info for SELL/HOLD scenario conditions
open_pos = get_open_position(db_conn, stock_code, market.code)
if open_pos and current_price > 0:
entry_price = safe_float(open_pos.get("price"), 0.0)
if entry_price > 0:
market_data["unrealized_pnl_pct"] = (
(current_price - entry_price) / entry_price * 100
)
entry_ts = open_pos.get("timestamp")
if entry_ts:
try:
entry_date = datetime.fromisoformat(entry_ts).date()
market_data["holding_days"] = (datetime.now(UTC).date() - entry_date).days
except (ValueError, TypeError):
pass
# 1.3. Record L7 real-time context (market-scoped keys)
timeframe = datetime.now(UTC).isoformat()
context_store.set_context(

View File

@@ -28,6 +28,7 @@ def mock_settings() -> Settings:
KIS_APP_SECRET="test_secret",
KIS_ACCOUNT_NO="12345678-01",
GEMINI_API_KEY="test_gemini_key",
MODE="paper", # Explicitly set to avoid .env MODE=live override
)
@@ -122,9 +123,10 @@ class TestFetchOverseasRankings:
params = call_args[1]["params"]
assert "/uapi/overseas-stock/v1/ranking/updown-rate" in url
assert params["KEYB"] == "" # Required by KIS API spec
assert params["EXCD"] == "NAS"
assert params["NDAY"] == "0"
assert params["GUBN"] == "0" # 0=전체(상승+하락), 변동성 스캐너에 필요
assert params["GUBN"] == "1" # 1=상승율 — 변동성 스캐너는 급등 종목 우선
assert params["VOL_RANG"] == "0"
overseas_broker._broker._auth_headers.assert_called_with("HHDFS76290000")
@@ -157,6 +159,7 @@ class TestFetchOverseasRankings:
params = call_args[1]["params"]
assert "/uapi/overseas-stock/v1/ranking/volume-surge" in url
assert params["KEYB"] == "" # Required by KIS API spec
assert params["EXCD"] == "NYS"
assert params["MIXN"] == "0"
assert params["VOL_RANG"] == "0"