Commit Graph

227 Commits

Author SHA1 Message Date
55f0c6844c Merge branch 'dev' into main — 파라미터 3중 소스 정합(alembic 0012) + 리스크 가드 복원
운영 DB 가 옛 값으로 돌고 있던 문제를 해소한다:
  MEAN_REVERSION_RSI_THRESHOLD 30.0 → 46.0  (RSI<46 후보 95종목 vs RSI<30 32종목)
  STOP_LOSS_PERCENT           -6.0 → -7.0
  DAY_TRADE_MAX_POSITIONS        3 → 5
  DAY_TRADE_PER_STOCK_RATIO   0.10 → 0.06
  ANALYSIS_STOCK_COUNT         150 → 300
  CONSECUTIVE_LOSS_BAN_DAYS     30 → 42
alembic 0012 가 운영자 커스텀 값은 보존하고 옛 기본값과 같은 행만 갱신한다.

KIS_MODE=virtual (모의투자). alembic 현재 리비전 0011 → 0012 적용.
2026-09-28 09:31:31 +09:00
1b9257c4d2 fix: 매매 파라미터 3중 소스 정합 + 리스크 가드 복원 + 죽은 코드 2,695줄 제거
## 배경
"전략이 혼란스럽고 현재와 맞지 않는다"를 조사한 결과, 전략 설계가 아니라
전략을 검증했다는 도구와 파라미터 소스가 어긋나 있었다.

## 파라미터 소스 단일화
- 우선순위가 adaptive > strategy_cfg(DB) > config.py 인데 문서는 맨 마지막인
  config.py 를 기술하고 있었다. 11곳이 불일치(BULL RSI 46 vs DB 30, SL -7% vs -6%).
- DEFAULT_PARAMS 를 config.py 에 정합 + alembic 0012 로 기존 DB 행 갱신
  (운영자가 UI 로 바꾼 값은 보존, 옛 기본값과 같은 행만 갱신, 테이블 가드·멱등)

## 백테스트를 실전 로직에 정합
- calculate_adx_simple 이 정확히 period 배(14.0×) 발산 → Aggressive 게이트
  `ADX>25` 가 전 레짐 100% 통과. 전환 빈도 5.6%→2.0%(-65%), 성과 상방 편향
- 백테스트 RSI 는 SMA, 실전은 Wilder → 진입 판정 10~11% 불일치
- us_backtest_engine RSI 1봉 지연 제거 (룩어헤드 없음을 절단 테스트로 증명)
- backend/backtest_results/ 25건은 폐기 대상 (§8.4)

## 리스크 가드 복원
- 4% 절대 상한이 try 블록 안에 있어 예외 시 적응형 6~7% 가 통과 → try 밖으로
- 청산을 매수보다 먼저 평가 (보유 종목이 추천에 들면 손절 대신 물타기가 나갔다)
- 리스크 가드 4개를 매수 전용화 → MDD -10%·보유한도 도달 시에도 손절 작동
- MDD 갱신 5회 연속 실패 시 신규 진입 차단 fail-safe (청산은 계속 허용)
- 스윙 청산이 DT 보유분까지 매도하던 문제, sell_pct 기준 혼재 정정

## 죽은 코드 제거 (2,695줄)
- short_term_strategy, kosdaq_scanner, app/strategy/ 패키지
- app/trading/ + korea_investment_api_adapter (real_market_api 정본은
  korea_investment_api.py:1482, adapter 는 import 0건)

## 지표·사이징 단일화 (수치 무변경 증명)
- indicators_core.py: 3,274건 비교 최대 오차 0.000e+00
- sizing_core.py: 42,105건 비교 불일치 0
- 미국 전략 상수 13개 config 편입 (hex 까지 동일, 3,168 케이스 전수 비교)

## 테스트 안전 (실 KIS 호출 차단)
- 유닛 테스트가 실제 KIS 에 매도 주문을 2회 전송하고 있었다. 리팩터링이
  close_stale_positions 의 대상을 _execute_exit → _execute_exit_stale 로
  바꿨는데 목이 전자에 걸려 빗나갔다. test_api_endpoints 도 캡처된 싱글톤
  참조 누수로 실제 잔고 조회를 타 141초를 소비했다.
- backend/tests/conftest.py 신설: 주문·토큰·시세·잔고를 호출 즉시 예외로 차단.
  예외는 BaseException 파생(프로덕션 except Exception 폴백에 삼켜지지 않게),
  클래스 메서드에 적용(캡처된 참조까지 덮음), DATABASE_URL `_test` 가드 강제
- 전체 유닛 스위트: 365초/6 failed → 11.8초/0 failed (소켓 차단 하 559 passed)

## 신규 (둘 다 플래그 OFF, 백테스트 미검증)
- range_trading_strategy.py — SIDEWAYS 담당 전략이 없던 빈 칸. 미배선
- check_bear_buy_signal_v2 — BEAR 4중 AND(발화율 2.3%) → 점수제(12.4%)

## 문서
- CLAUDE.md 를 실제 코드 기준으로 재작성. §0 파라미터 우선순위, §4.1 테스트 규약,
  §6.4 문서화되지 않았던 진입 차단 게이트 9종, §8.4 백테스트 폐기 근거,
  §8.5 미해결 19항목
- 루트 md 6개 → 2개(docs/ 이동), 중복 배치 문서 병합, 스테일 worktree 제거

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 08:58:18 +09:00
96704cc86f Merge branch 'dev' into main — 배포 실패 수정 2026-09-23 15:02:29 +09:00
b4608e5abf fix(ci): bind mount 디렉터리 사전 생성 — token_cache 미존재로 배포 전면 중단
docker-compose.yml 에 ./token_cache 볼륨을 추가했는데 배포 디렉터리에 없어
'Bind mount failed: /volume1/docker/autostock/token_cache does not exist' 로
DB Migration 단계부터 실패, Deploy 가 스킵돼 2026-09-23 11:30 이후 변경분이
서버에 반영되지 않았다(컨테이너는 옛 이미지로 계속 구동).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 15:02:27 +09:00
9301350c61 Merge branch 'dev' into main — 스윙 상한 70% 2026-09-23 14:48:36 +09:00
78ab3d7d4e chore: 스윙 자본 상한 70%로 상향 (스윙:DT = 70:30)
스윙이 이미 47%라 신규 매수가 swing_capital_cap 에 막혀(오늘 60건) 표본이 쌓이지 않았다.
- SWING_MAX_CAPITAL_RATIO 0.50 → 0.70
- DAY_TRADE_MAX_RATIO_DT 0.50 → 0.30, DAY_TRADE_MAX_POSITIONS 8 → 5 (5×6%=30%)
- 두 상한의 합이 100%를 넘지 않는지 테스트로 고정

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 14:48:34 +09:00
c4748a97b6 Merge branch 'dev' into main — 최소주문금액·종목당 비중 조정 2026-09-23 14:40:19 +09:00
9774c1249b fix: 진입이 최소주문금액에 막혀 거래가 안 나던 문제 — 15만원으로 하향 + 종목당 4%
로그 집계(2026-09-23): 진입 차단 141건이 min_amount_guard. 최소 주문금액 50만원이
종목당 예산(총자산 982만×6%≈59만)과 거의 같아, 신뢰도·현금 보정으로 20~30만원이 되면
전부 탈락했다(기아 230,800원·엘앤에프 217,000원 등).
- MIN_BUY_AMOUNT_KRW 500,000 → 150,000
- MAX_POSITION_SIZE 0.06 → 0.04 (종목당 금액을 낮추고 동시 보유 종목 수를 늘려 표본 확보)
- AI 분석 매수 추천 금액의 신뢰도 하한 0.5 → 0.8 (과도 축소 방지)
- 설정 관계를 테스트로 고정(최소주문 ≤ 종목당 예산의 50%, 스윙 상한 내 10종목 이상)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 14:40:18 +09:00
7c94bda6e1 Merge branch 'dev' into main — AI 분석 목표가·손절가 2026-09-23 13:35:01 +09:00
f480eac25c feat: AI 투자 분석에 목표가·손절가 표시
CLAUDE.md §5.2 기준으로 계산해 매수 추천 금액 아래에 함께 표시.
  · 손절 = ATR(2.5×)과 -7% 중 타이트한 쪽(가격 높은 쪽), 적응형 모드면 그 값 사용
  · 목표 = 진입가 + 2R (R = 진입가 − 손절가) — +2R 50% 분할 매도 지점
ATR 은 stock_analysis.atr_14 우선, 없으면 DB 일봉으로 계산(KIS API 미사용). 테스트 4건 추가.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:34:59 +09:00
ba900c1bdb Merge branch 'dev' into main — AI 분석 매수 추천 금액 2026-09-23 13:33:11 +09:00
acbd9f35ea feat: AI 투자 분석에 매수 추천 금액 표시
BUY 신호일 때 실제 자동매매와 동일한 기준으로 매수 금액·수량을 계산해 패널 상단에 표시.
  · 종목당 상한 = 총자산 × MAX_POSITION_SIZE(적응형 모드면 그 값)
  · 신뢰도 보정 = 예산 × (confidence/0.7), 0.5~1.2배
  · 현금 여력(최소 현금보유 제외)·스윙 합산 상한(SWING_MAX_CAPITAL_RATIO) 잔여로 클램프
BUY 가 아니거나 예산이 1주 미만이면 금액 대신 사유를 표시. 테스트 6건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:33:06 +09:00
7b97029d68 Merge branch 'dev' into main — 시간손절 조기발동 수정 2026-09-23 12:54:24 +09:00
5706a8971c fix: 시각 기준 불일치로 시간손절이 즉시 발동해 포지션이 조기 청산되던 문제
재배포로 DB 복원된 데이트레이딩 포지션의 entry_time 이 UTC(구 기본값)라 KST now 와의
차이가 +540분이 되어, 진입 25분 만에 '시간 손절: 565분 경과'로 청산됐다
(2026-09-23 산일전기 -0.5%, 미스토홀딩스 -0.4% — 익절 조건까지 가보지도 못함).
_elapsed_minutes(): 경과가 음수이거나 정규장 길이(390분)를 넘으면 기준 불일치로 보고 무시.
시간손절·트레일링 최소보유 판정 모두 이 헬퍼를 사용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 12:54:22 +09:00
bdc5dd9468 Merge branch 'dev' into main — 시간대 통일·토큰 캐시 2026-09-23 11:24:17 +09:00
ec2fa2b423 fix: DB 타임스탬프 KST 통일 + KIS 토큰 캐시 영속화
시간대 혼재: 모델 기본값은 datetime.utcnow(UTC), 수동 매매 등 일부 경로는 _get_kst_now(KST)로
저장해 같은 테이블에 9시간 차가 섞였다(화면 매매시각 02:14 등). 조회 구간(utcnow - days)도
KST 저장분과 비교돼 어긋났고, 추가매수 쿨다운은 (UTC now - KST created_at)이 음수라 상시
발동했다. app.core.timeutil.now_kst() 도입 후 모델 기본값 27곳과 app/ 전역 utcnow() 호출을
모두 KST 기준으로 통일(잔존 0건, 테스트로 고정).

KIS 토큰 캐시(~/.cache/auto-stock)가 컨테이너에만 있어 배포마다 사라지고 토큰을 새로
발급받다 KIS가 EGW00103(유효하지 않은 AppKey)로 거부 → 502·잔고 0 유발. 볼륨 마운트로
재배포 후에도 재사용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 11:24:14 +09:00
09ba76f88b Merge branch 'dev' into main — 예수금·프론트 복원력 2026-09-23 11:15:50 +09:00
e2d4b607cb fix: 예수금 음수 표시 + 배포 후 화면이 영구 정지하던 문제
- 예수금: dnca_tot_amt(D+2 기준)를 먼저 읽어 당일 매수가 많으면 -913,965원처럼 음수가
  표시됐다. 주문가능현금(_ord_psbl_cash) → prvs_rcdl_excc_amt → nxdy_excc_amt 순으로
  첫 양수를 사용한다(데이트레이딩 엔진과 동일 기준).
- 프론트: /auth/me 가 한 번 실패하면(배포 중 502) useTenantMode.mode 가 undefined 로 남아
  포트폴리오·매매 쿼리가 전부 enabled:false 로 멈췄다(오늘 26분간 API 호출 0건 확인).
  auth-me 를 실패 시 10초 간격 재시도로 바꾸고, queryClient 기본값에 retry 3회/백오프,
  refetchOnReconnect, networkMode:'always' 를 적용해 백엔드 복구 시 자동으로 되살아나게 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 11:15:48 +09:00
2c4df46a06 Merge branch 'dev' into main — 감사 잔여 항목 2026-09-23 11:11:22 +09:00
85cb5cbc09 fix: 감사 잔여 항목 — 잔량 출처 DB화·ORB 영속화·미국 부분청산·듀얼전략 태깅·NULL 가격
- portfolio_cache_bridge: 잔량 산출을 Portfolio(DB) 기준으로. 캐시는 sync 실패 시 최대
  5분 낡아 실제와 다른 잔량이 기록될 수 있었음(감사 #14). 조회 실패 시 캐시 폴백.
- ORB 캐시 Redis 영속화(save/load_orb_cache) — 재배포 시 인메모리가 날아가 당일 ORB
  전략이 통째로 불가능했음. 수집 후 저장, 스케줄러 기동·수집 시 복원.
- us_market_scheduler: 부분청산이 Portfolio.quantity 를 갱신하지 않던 문제 → reflect_sell 연동.
- 듀얼 전략(MOMENTUM_BREAKOUT/PULLBACK_BUY)이 ActiveTrade.strategy_name 에 저장되지 않아
  CLAUDE.md §8.4 모멘텀 전용 청산(+10%/+20% 분할)이 한 번도 실행된 적 없었음 → 태깅 전달.
  매도 시에는 실제 행의 strategy_name 으로 close (swing 으로 닫으면 유령 행 잔존).
- 수동매도: current_price NULL 시 TypeError 로 주문 후 상태 갱신이 통째로 누락되던 엣지 수정.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 11:11:20 +09:00
f6a0a351f8 Merge branch 'dev' into main — P2 감사 수정 2026-09-23 10:57:42 +09:00
8971a648e2 fix(P2): 주문 멱등성·취소 보호·다중행·v2/수동매도 정합
- korea_investment_api: 주문 계열 엔드포인트는 내부 재시도 금지(_NON_IDEMPOTENT_ENDPOINTS).
  상위 _place_order_with_retry 와 중첩돼 타임아웃 시 동일 주문 최대 12회 전송 가능했음.
  get_todays_orders() 추가.
- trade_executor: 예외 후 재시도 전에 당일 주문내역으로 접수 여부 확인(_find_recent_order)
  → 접수됐으면 재주문하지 않고 기존 주문 반환.
- daytrading/exit_service: 주문 체결 이후 상태 반영을 _finalize_exit 로 분리하고
  asyncio.shield 로 보호 — 스케줄러 단계 타임아웃이 이 구간을 취소하면 브로커는 팔았는데
  인메모리는 그대로라 다음 사이클이 재매도.
- swing/position_manager: active 행이 2개면 MultipleResultsFound 로 해당 종목 청산이
  통째로 스킵됐음(손절 미작동) → 최신 행 사용.
- swing/exit_service: 큐 지연·DLQ 시 같은 분할매도가 반복되지 않도록 180초 인메모리 쿨다운.
- portfolio_v2: 부분매도 ActiveTrade 잔량 갱신 + 전략명 매핑(daytrading→day_trade,
  us_swing→us_mean_reversion) — 매칭 실패로 active 행이 영구 잔존했음.
- portfolio.sell_stock: safe_place_order 경유(세마포어·분산락) + DT 인메모리 잔량 반영.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:57:40 +09:00
9f5cb2bff9 Merge branch 'dev' into main — P1 감사 수정 2026-09-23 10:53:15 +09:00
9cdd12b41e fix(P1): MDD 축소 수량 반영·ActiveTrade 전략명 필수화·스윙 매도에서 DT 보유분 제외
- mdd_manager: 강제 축소가 TradeHistory/remaining_pct 만 갱신하고 실제 수량은 어디도
  줄이지 않아 다음 청산이 축소 전 수량으로 매도했음 → _reflect_reduction()으로
  Portfolio·DT 인메모리 반영, ActiveTrade.quantity 도 잔량으로 갱신 (두 경로 모두)
- swing/exit_service: close/upsert 에 strategy_name 미전달 시 db_worker 가 최신 active
  행(=DT)을 닫거나 트레일링 고점을 덮어씀 → 항상 전략명 명시
- trade_executor._execute_sell: Portfolio 행(스윙+DT 합계)을 그대로 매도 대상으로 삼아
  DT 보유분까지 팔았음 → 엔진 보유 수량을 제외한 자기 전략 보유분만 대상, upsert 에 전략명
- portfolio.sync: DT 태깅이 해외 루프에만 적용돼 국내 DT 매수분이 swing 으로 저장되면
  스윙 엔진이 손절 → 국내에도 적용. _dt_qty 정의를 output1 블록 밖으로 올려 NameError 방지

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:53:13 +09:00
7758eb6569 Merge branch 'dev' into main — P0 감사 수정 2026-09-23 10:51:03 +09:00
70def8fe0b fix(P0): 인증 누락·레거시 DT 엔진 병존·혼재 종목 재태깅·자정 후 스테일 청산 무력화
감사 CRITICAL 4건:
- /daytrading/{watchlist,execute,close-all,cycle} 에 require_api_key 누락 → 인증 없이
  전량 청산 가능했음 (CLAUDE.md §4 위반)
- 레거시 day_trading._positions 가 엔진 day_trading_state 와 별도로 기동 시 복원돼
  같은 포지션이 두 엔진에 존재 → /close-all 시 이중 매도. 복원 제거 + close-all/cycle 을
  엔진에 위임, /execute 실매수는 410(엔진 미관리 포지션 방지)
- reflect_buy 가 기존 행을 daytrading 으로 재태깅 → 같은 종목 스윙 보유분이
  get_swing_holdings 에서 빠져 손절·익절 정지. 신규 행일 때만 태깅
- close_stale_positions 가 인메모리만 순회 → 자정 reset 후 대상이 사라져 무력.
  ActiveTrade(day_trade/active/전일) 조회 기반으로 변경하고 인메모리가 비어도 실행

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:49:30 +09:00
36fcd1ef28 Merge branch 'dev' into main — 외부 유입 포지션 자동 등록 2026-09-23 10:42:42 +09:00
6207d13487 feat: 외부 유입 포지션 ActiveTrade 자동 등록 — 손절·분할익절 관리 편입
다른 인스턴스/증권사 앱에서 매수돼 동기화로 들어온 포지션은 ActiveTrade 행이 없어
손절·분할익절·트레일링 관리를 전혀 받지 못했다(2026-09-22 카카오뱅크가 -13%까지
방치되다 청산). 동기화 시 보유 중인데 active 행이 없으면 평균단가를 진입가로 등록하고
손절가를 STOP_LOSS_PERCENT 기준으로 설정한다. 데이트레이딩 태깅 종목은 제외
(DT 엔진이 당일 청산 대상으로 복원해버리므로).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:42:40 +09:00
8456b00dfa Merge branch 'dev' into main — ActiveTrade 정합 2026-09-23 10:38:26 +09:00
ee4f8afa8b fix: ActiveTrade 잔량 자동 정합 + PATCH 수량 교정 지원
수동/분할 매도 누락으로 ActiveTrade 잔량이 실제 보유량과 어긋나면 다음 청산이
보유량보다 많이 매도한다. 동기화 시 종목당 active 행이 1개뿐일 때 실제 보유량으로
교정하고, 보유가 없으면 closed 처리(스윙+DT 혼재 종목은 자동 교정 제외).
운영 교정용으로 PATCH /portfolio/active-trades/{code}/status 에 quantity 필드 추가.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:38:24 +09:00
863b464d99 Merge branch 'dev' into main — 분할매도 잔량·ActiveTrade 정리 2026-09-23 10:36:02 +09:00
b933e23777 fix: 분할매도 잔량 미갱신 — 스윙·국내 executor·미국 경로 동일 패턴 + 수동매도 ActiveTrade 정리
DT에서 찾은 '분할매도 후 ActiveTrade.quantity 미갱신' 패턴이 3곳 더 있었음:
- us_market_scheduler: total_qty = trade.quantity 를 매도량 산정에 직접 사용 →
  분할매도 후 다음 사이클이 원래 수량 기준으로 팔아 과다매도(US_TRADING_ENABLED=true 상태)
- swing/exit_service, trade_executor._execute_sell: remaining_pct만 갱신
또한 수동 매도(/portfolio/sell)가 ActiveTrade를 전혀 정리하지 않아 청산된 종목이 active로
잔존(삼아알미늄·LG 등) → 재매수 시 옛 remaining_pct가 살아나 1차 익절이 건너뛰어짐.
전량 매도 시 close, 분할 시 잔량 갱신으로 수정.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:36:00 +09:00
15080a6fbd Merge branch 'dev' into main — 로그 기반 버그 4건 수정 2026-09-23 10:24:47 +09:00
f32bf412f3 fix: 로그 분석으로 찾은 버그 4건 (과다매도·수량 0 오염·LSTM 무력화·sync 충돌)
1) DT 분할매도가 ActiveTrade.quantity를 갱신하지 않아 재기동 복원 시 원래 수량이 되살아남
   → 잔량 4주인데 7주 매도되어 같은 종목 스윙 보유분 3주까지 청산됨(2026-09-23 LG씨엔에스).
2) reflect_sell이 '전략 포지션 잔량'을 Portfolio 행 잔량으로 사용 → DT 전량청산이 스윙
   보유분까지 0으로 만듦. 행 잔량은 항상 (행 수량 - 매도 수량)으로 계산.
3) LSTM 체크포인트에 numpy 배열이 들어가 weights_only=True 로드가 매번 실패 → 앙상블에서
   LSTM이 조용히 빠진 채 예측 중이었음. 저장을 list로 바꾸고, 기존 파일은 MODEL_DIR 내부일
   때만 완화 재시도.
4) 수동 매도(행 삭제)와 sync 동시 실행 시 StaleDataError로 sync 전체 실패 → 전량 매도는
   행 삭제 대신 수량 0, sync는 충돌 시 실패 기록 없이 다음 주기 재시도.
   추가로 비강제 sync는 락 점유 중이면 30초 대기 없이 캐시 반환.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:24:45 +09:00
9a8c843237 Merge branch 'dev' into main — 스케줄러 단계 타임아웃 2026-09-23 10:18:02 +09:00
d8550a2241 fix: 스케줄러 단계별 타임아웃 — KIS 무응답 시 EOD 청산 누락 방지
로그 분석(2026-09-22): 15:03:46 진입 스캔 이후 데이트레이딩 스케줄러가 16:52 재배포까지
1시간 49분간 아무 로그도 남기지 않음 = KIS API 무응답(500/Server disconnected 연속)으로
await가 반환되지 않아 루프 정지 → 15:20 EOD 강제청산 미실행 → 포지션 익일 보유.
- _guard(): 각 단계를 asyncio.wait_for로 감싸 초과 시 취소 후 다음 사이클 진행
- 단계별 상한: MDD 30s / watchlist 120s / ORB·EOD청산 300s / 청산·진입 180s
- MDD 판단 불가 시 보수적으로 신규 진입 차단
- 테스트 3건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:18:00 +09:00
92cceb2cae Merge branch 'dev' into main — 전일 미청산 포지션 안전장치 2026-09-23 10:01:02 +09:00
a659de09e3 feat: 전일 미청산 데이트레이딩 포지션 즉시 청산 안전장치
15:20 EOD 강제청산이 누락/실패하면 포지션이 하룻밤 갭 리스크에 노출된 채 다음 날
일반 청산 로직을 탄다(2026-09-22 LG씨엔에스 7주 → 익일 09:30 분할매도).
- exit_service.close_stale_positions(): 진입일이 오늘이 아닌 포지션을 장중 시장가 전량 청산,
  현재가 조회 실패 시 진입가 폴백, 실패 시 다음 30초 사이클에서 재시도
- 청산 사이클(check_and_exit_positions) 및 스케줄러 기동 직후 양쪽에서 호출
- 테스트 5건, CLAUDE.md §7.3 반영

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:01:00 +09:00
e631d6d38f Merge branch 'dev' into main — DT 보유 종목 표시 2026-09-23 09:33:16 +09:00
fd8a0f1188 fix: 데이트레이딩 보유 종목이 화면에 안 보이던 문제 — 엔진 포지션 직접 표시
Portfolio 테이블은 stock_code가 unique라 같은 종목을 스윙+데이트레이딩으로 동시 보유하면
한 행에 합쳐지고 strategy_type도 하나만 남는다(LG씨엔에스 스윙 8 + DT 4 = 12주 'swing').
- 프론트: /daytrading/positions(엔진 인메모리)를 직접 조회해 '데이트레이딩' 그룹으로 표시하고,
  스윙 행에서는 그 수량을 차감
- 백엔드 동기화: 전량이 DT 포지션일 때만 'daytrading' 태깅 (부분 혼재 시 스윙 청산 대상 유지)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 09:33:14 +09:00
a7fd6e8564 Merge branch 'dev' into main — 개장 전 분석 선행·스케줄러 기상 2026-09-23 09:07:59 +09:00
9fd02932fa fix: 개장 시 추천 종목 지연 — 배치 분석 개장 전 선행 실행 + 스케줄러 개장 전 기상
모든 스케줄러가 장 마감 중 고정 1800초를 자서 09:00 개장을 최대 30분 건너뛰었고,
배치 분석은 개장 후에야 시작돼 추천 종목이 09:00에 비어 있었음.
- market_schedule.is_pre_market_window() 추가 (08:30~09:00, 거래일만)
- batch_analysis_scheduler: 개장 전 준비 구간에도 분석 실행 → stock_analysis·Redis 추천
  캐시를 미리 채워 09:00에 추천 즉시 표시
- batch/auto_trade/swing 스케줄러: 마감 중 sleep = min(1800, 개장까지 남은 시간)
- 테스트 1건 추가 (총 5건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 09:07:57 +09:00
9c63d04eac Merge branch 'dev' into main — 개장 전 기상·ORB 수집 보장 2026-09-23 09:05:38 +09:00
546cf17edb fix: 장 마감 중 30분 sleep이 09:00 개장을 건너뛰어 ORB 수집 누락
데이트레이딩 스케줄러는 장 마감 시 무조건 1800초를 자서, 08:31~09:00 사이에 잠들면
09:01~09:30에 깨어나 ORB 수집 구간(09:00~09:30)을 통째로 놓쳤음(2026-09-23 감시종목 0 확인).
- market_schedule.seconds_until_market_open() 추가 (주말·공휴일 건너뜀)
- 스케줄러: 마감 중 sleep = min(1800, 개장까지 남은 시간), 개장 3분 전 watchlist 선구성
- 회귀 테스트 4건

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 09:05:36 +09:00
2c1de1653f Merge branch 'dev' into main — DT 포지션 표시·매도 즉시 반영 2026-09-22 16:49:49 +09:00
a57c720559 feat: 데이트레이딩 포지션 화면 표시 + 모든 매도 즉시 화면 반영
- portfolio_cache_bridge 신설: 매매 엔진 체결 시 Portfolio(db_worker 큐) + 인메모리 캐시 즉시 갱신.
  DT 매수는 strategy_type='daytrading'으로 태깅되어 보유 종목 패널의 '데이트레이딩' 그룹에 표시되고
  스윙 청산 로직(strategy_type='swing' 대상)에서 제외됨. (기존엔 KIS 동기화로 'swing'으로 유입돼
  DT 포지션이 안 보이고 스윙 엔진이 같이 관리하는 문제)
- DT 매도·스윙 자동 매도도 캐시에서 즉시 제거/차감 + 최근매도 기록(동기화 되살림 방지).
- 동기화 시 DT 엔진 보유 종목은 'daytrading'으로 태깅.
- 프론트: 수동 매도 성공 시 낙관적 갱신으로 서버 재조회 전 즉시 목록에서 제거.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:49:46 +09:00
1882eadded Merge branch 'dev' into main — 수동매도 반영·토스트·가용현금 2026-09-22 14:40:45 +09:00
496529782b fix: 수동 매도 즉시 반영 + 매도/매수 결과 토스트 + 당일 매도대금 가용현금 반영
- 프론트: 토스트 알림 시스템(lib/toast + ToastHost) 추가, 매도/매수 성공·실패를 화면에 표시
  (기존엔 console.log만, 실패는 블로킹 alert). 성공 토스트에 손익 표시.
- 매도 직후 예수금/평가액 요약 즉시 갱신(apply_sell), 최근 5분 내 수동 매도 종목은
  KIS 잔고 반영 지연으로 동기화가 되살리지 않도록 스킵(recent_sells).
- 예수금이 D+2 결제 기준(dnca_tot_amt)이라 당일 매도대금이 화면·데이트레이딩 가용현금에
  반영되지 않던 문제: 매수가능조회(inquire-psbl-order) ord_psbl_cash 를 우선 사용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 14:40:39 +09:00
15018ddd72 Merge branch 'dev' into main — DT ORB 복구·50:50·시간대 완화 2026-09-22 13:29:04 +09:00
e0178aaf0d feat: 데이트레이딩 ORB 복구 + 스윙:DT 자본 50:50 + 시간대 제한 완화
- ORB: 09:00에 당일 분석이 없으면 최근 분석일 결과로 watchlist 선채움 → ORB 수집 가능.
  자동 목록은 30분마다 갱신(당일 분석 완료 시 교체), 수동 설정 목록은 덮어쓰지 않음.
- 자본 배분: DAY_TRADE_MAX_RATIO_DT 0.25→0.50, 종목당 4%→6%, 동시 3→8종목.
  SWING_MAX_CAPITAL_RATIO=0.50 신설 — 스윙 합산 투자액 초과 시 스윙 신규 매수 차단(trade_executor).
- 시간대: 점심(11:30~13:00) VWAP/RSI_BB 제한 제거, 하드코딩 14:30 마감 → 설정값 15:00.
- 회귀 테스트 3건, CLAUDE.md §6/§7.3/§8.2 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 13:29:02 +09:00