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>
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-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>
모든 스케줄러가 장 마감 중 고정 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>
데이트레이딩 스케줄러는 장 마감 시 무조건 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>
- 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>
- 프론트: 토스트 알림 시스템(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>
- 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>
/daytrading/{status,watchlist,positions}(v1)는 main.py가 실행하는 엔진
(services/daytrading)과 공유되지 않는 별개 인메모리 dict를 읽어 항상 0을 반환했음.
조회는 entry_service 감시목록·day_trading_state 기준으로 응답하고, POST /watchlist는
엔진에도 반영. 프론트 보유종목 패널 하단에 감시/ORB/포지션/청산/당일손익·현재 단계
(ORB 수집중·신호 대기·쿨다운 등)를 30초 폴링으로 표시.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세션 로그인 사용자는 프론트가 v2(/portfolio/v2)를 쓰는데, 멀티테넌트가 꺼져 있으면
v2 컨텍스트가 legacy로 떨어져 KIS 동기화 없이 DB 스냅숏만 반환 → 현재가·손익이 갱신되지 않음.
/auth/me 에 multi_tenant_enabled 를 추가하고, 프론트는 세션 로그인 AND 플래그 true 일 때만 v2 사용.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
동기화로 유입된 포지션(ActiveTrade 행 없음)에 1차 익절(50%)이 매 사이클
반복 실행돼 전량 청산되는 문제 수정 (2026-09-22 두산에너빌리티 18→9→0주).
- exit_service: 부분매도 시 ActiveTrade 없으면 필수 키 포함해 생성, Portfolio 잔량 즉시 감소
- db_worker: 필수 키 없는 ActiveTrade 신규 upsert는 재시도 대신 경고 후 스킵
- portfolio.py sync 청산 경로 동일 수정
- 회귀 테스트 추가
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
POST /auth/admin/tenant/assign-env-credentials {username}: 동일 앱키 1사용자 제약을 지키며
현 보유자(부트스트랩 admin)에서 대상 사용자로 이동, 대상의 기존 자격증명 교체, 컨텍스트 캐시 무효화.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
로그인 사용자명(관리자 배지, API 키면 '운영자') 표시 → /account 링크.
로그아웃은 localStorage api_key 제거 후 로그인 화면으로.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- models.User + alembic 0008 (users: username unique, bcrypt password_hash, is_active, is_admin)
- auth.py: POST /auth/register, /auth/login, GET /auth/me, 관리자용 GET /auth/users,
POST /auth/users/{id}/approve|revoke. bcrypt 는 asyncio.to_thread 로 오프로드,
미존재 계정도 더미 해시 검증(타이밍 균등화), IP당 실패 잠금 유지
- 정책: 첫 가입자 = 관리자·즉시 활성, 이후 가입자는 관리자 승인 필요
(SIGNUP_AUTO_APPROVE=true 로 해제, SIGNUP_ENABLED=false 로 가입 차단). API 키는 관리자 취급
- config: ADMIN_USERNAME/ADMIN_PASSWORD(_HASH) 제거 → SIGNUP_ENABLED / SIGNUP_AUTO_APPROVE
- Login.tsx: 로그인/회원가입 탭, 비밀번호 확인, 승인 안내 배너, 상태코드별 메시지,
첫 가입자는 가입 직후 자동 로그인
- DB 접근: 회원가입/로그인은 sync_portfolio 와 같은 단일 트랜잭션 예외로 엔드포인트 직접 접근
(컨테이너 Postgres 대상 통합 테스트 19건 통과)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- auth.py 신규: POST /api/v1/auth/login → JWT 세션 토큰(HS256, SECRET_KEY 서명, 24h)
GET /api/v1/auth/me 자격 확인. IP당 5회 실패 시 300초 잠금(인메모리)
- config: ADMIN_USERNAME / ADMIN_PASSWORD / ADMIN_PASSWORD_HASH(bcrypt) /
SESSION_TOKEN_EXPIRE_HOURS / LOGIN_MAX_FAILURES / LOGIN_LOCKOUT_SECONDS
- security: create_session_token / verify_session_token / is_valid_credential 추가.
require_api_key·optional_api_key 는 시그니처 유지, X-API-Key 값으로 API 키 또는
세션 토큰 모두 수용 → 프론트 api.ts·useWebSocket·MCP 서버 호출 경로 무변경
- main.py WebSocket 인증도 is_valid_credential 로 통일
- bcrypt 검증은 passlib 대신 bcrypt 직접 사용 (passlib 1.7.4 ↔ bcrypt 5.0 비호환)
- Login.tsx: 아이디/비밀번호 폼, 401/429/503 별 안내 메시지
- .env.example 에 관리자 계정 항목 추가
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Login: 카드형 레이아웃, 키 아이콘·표시/숨김 토글, 인라인 에러(aria), 로딩 스피너,
입력 시 에러 자동 해제, 그라디언트 텍스트 제거
- Layout 헤더: 태그라인 삭제, 브랜드명 단일 표기
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DS1517+(Atom C2538)에서 빌드가 30분 이상 걸리던 원인 두 가지를 제거.
1. PyTorch CPU 전용 휠 사용 (backend)
PyPI 기본 torch 휠은 nvidia-cublas/cudnn 등 CUDA 패키지를 수 GB
함께 내려받는데 NAS에는 GPU가 없어 전부 불필요했다.
download.pytorch.org/whl/cpu 에서 먼저 설치하면 requirements.txt 의
torch 줄은 충족되어 건너뛴다. ml_ensemble 은 이미 지연 임포트라 동작 동일.
2. pip/npm 캐시를 빌드 간 재사용 (backend, mcp-server, frontend)
--no-cache-dir 제거하고 RUN --mount=type=cache 로 전환.
requirements.txt 가 바뀌어도 받아둔 휠은 재다운로드하지 않는다.
Jenkinsfile 에 DOCKER_BUILDKIT/COMPOSE_DOCKER_CLI_BUILD 추가.
부수: frontend Dockerfile 의 FromAsCasing 경고 정리 (as build → AS build).
세 Dockerfile 모두 docker build --check 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NAS 복구·배포 점검 과정에서 사용한 읽기 전용 명령을 허용목록에 추가.
- docker-compose 포트 매핑 조회 (frontend 3001 확인)
- frontend/nginx.conf·deploy/nginx-reverse-proxy.conf 라우팅 조회
- stock.shium.net 응답 및 /api 경로 확인
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"매수 실행" 결정 후 조용히 스킵되던 경우의 사유를 로그로 남겨, 왜 매수가
안 나가는지 즉시 파악 가능하게 함. (이번 세션에서 이 원인 추적에 15턴+ 소요)
- execute_auto_trade_background: execute_auto_trade가 BUY/SELL이 아닌 결과를
반환하면 종목코드+action+reason을 INFO 로그로 출력.
- _execute_buy: 수량 0 스킵 시 배율(플레이북 FOMC·국면·뉴스)로 인한 진입차단
vs 현금부족을 구분해 사유 반환 + [BUY_SKIP] 로그.
- 최종 HOLD(진입조건 미충족)에 reason="no_buy_signal" 부여(기존 '미상' 제거).
매매 판단 로직은 변경 없음(로깅·사유 문자열만 추가).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBmzf1HDnvFHLraJ8eaaKJ
매수 시점에 "왜 샀는지"를 기술적 신호뿐 아니라 뉴스·실적·전망까지 담아
entry_reason에 기록. 매매 판단은 바꾸지 않음(기록 전용).
- naver_finance.get_stock_fundamentals(): 네이버 m.stock integration API로
PER/EPS/목표주가/투자의견 조회. 10분 TTL 캐시, 실패/타임아웃 시 None.
- trade_executor._execute_buy: 주문 접수 후(주문 지연·차단 없음) 기술+뉴스+
실적+전망을 한 스토리로 조립해 entry_reason 기록. 각 부분 try/except로
감싸 실패 시 기술 스토리로 폴백(entry_reason 항상 세팅, 매매 영향 0).
예: "기술: RSI과매도, BB하단 (점수 5) | 실적: PER 11.3배, EPS 22,292원
| 전망: 목표가 487,045(+93%), 투자의견 4.0/5"
Phase 2(실적을 매매 기준에 반영)는 OpenDART point-in-time 데이터 + 백테스트
검증 후 별도 진행 예정(CLAUDE.md §8.1-4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBmzf1HDnvFHLraJ8eaaKJ
## CRITICAL/HIGH (S1-S8)
- S1: Aggressive 모드 %B 스케일링 캡 1.3 (BB 중앙선 위 매수 방지)
- S2: ML scaler train-only fit (데이터 누수 제거)
- S3: SIDEWAYS = ADX<20 AND slope≈0 (트렌드 초기 차단 해소)
- S4: EOD 청산 실패 시 재시도 + CRITICAL 로그
- S5: 일일 손실 한도에 미실현 P&L 포함
- S6: trailing stop 고점 매 사이클 갱신
- S7: 섹터 필터 미매핑 종목 "기타" 분류 (max 3 적용)
- S8: 스캐너 RSI 기준 25→46, BB 점수 -3%~+1.5% 구간
## MEDIUM (M1-M7)
- M1: 스캐너 최소 거래량 100만→10만주
- M2: ADX 히스테리시스 18/22 (국면 플리핑 방지)
- M3: 분할매도 최소 수익 1.0%→0.5%
- M4: ORB 갭 필터 4% + 레인지 폭 0.3% 최소
- M5: 쿨다운 스트릭 부분매도 시 미변경
- M6: MDD 강제매도 loss_tracker 기록 제거 (허위 ban 방지)
- M7: RSI Wilder 스무딩으로 통일
## 인프라/보안
- 인증 누락 엔드포인트 수정 (analysis, journals)
- nginx 보안 헤더 location 블록 누락 수정
- Alembic 마이그레이션 IF NOT EXISTS 가드 추가
- SellRequest/UsOrderRequest 입력 검증 강화
- 차트 네이버 폴백 추가 (DB 데이터 없을 때)
- 서버 이전 시 자동 DB 마이그레이션 + KRX 초기 수집
- CLAUDE.md 섹션 5.1 백테스트 값 동기화 (4%→6%, -6%→-7%)
- MAX_SWING_POSITIONS 25→20 (CLAUDE.md 준수)
- mdd_manager failed_count에서 skip 종목 제외
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>