docs: restructure audit docs and create loss recovery action plan (#331)
- Clean up 80_implementation_audit.md: remove review history (6.1/6.2), extract SQL queries, condense data quality section - Create 85_loss_recovery_action_plan.md with 13 action items across 3 phases (Phase 1: stop bleeding, Phase 2: data integrity + v2, Phase 3: v3 session optimization) - Extract standard audit SQL queries to scripts/audit_queries.sql - Update docs/ouroboros/README.md with 85_ link - Create Gitea issues #318-#330 for all 13 action items Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -112,14 +112,13 @@ Updated: 2026-02-28
|
||||
|
||||
### 3.2 일별 손익
|
||||
|
||||
> 주의: 아래 초기 집계 건수는 DB 실측치와 일부 불일치 (02-26: 문서 10건 vs DB 14건, 02-27: 문서 21건 vs DB 22건).
|
||||
> 정확한 재현은 3.8 표준 집계 SQL 참조.
|
||||
|
||||
| 날짜 | 매도 수 (초기 집계) | 승 | 패 | 일간 손익 |
|
||||
|------|---------------------|----|----|-----------|
|
||||
| 날짜 | 매도 수 | 승 | 패 | 일간 손익 |
|
||||
|------|---------|----|----|-----------|
|
||||
| 02-25 | 9 | 8 | 1 | +63.21 (USD, 미세 수익) |
|
||||
| 02-26 | 10 | 5 | 5 | **-32,083.40** (KR 대량 손실) |
|
||||
| 02-27 | 21 | 5 | 16 | **-20,461.11** (고빈도 매매, 대부분 손실) |
|
||||
| 02-26 | 14 | 5 | 5 | **-32,083.40** (KR 대량 손실) |
|
||||
| 02-27 | 22 | 5 | 16 | **-20,461.11** (고빈도 매매, 대부분 손실) |
|
||||
|
||||
> 정확한 재현: `scripts/audit_queries.sql` 참조.
|
||||
|
||||
### 3.3 시장별 손익
|
||||
|
||||
@@ -185,208 +184,21 @@ Updated: 2026-02-28
|
||||
결론적으로 USD 구간의 플러스 성과는 실질적으로 `CRCA` 이상치 1건 영향이 지배적이며,
|
||||
해당 거래를 무결성 필터로 제외하면 USD 성과는 손실 구간으로 전환된다.
|
||||
|
||||
### 3.7 데이터 품질 이슈 및 집계 정의
|
||||
### 3.7 데이터 품질 이슈 요약
|
||||
|
||||
#### 3.7.1 기간/건수 표기 (반영 완료)
|
||||
|
||||
- 3.1에 UTC 기준 기간 명시 + SELL 45건(기간 외 1건 제외) 주석 추가.
|
||||
- 3.2 일별 표에 DB 실측치 불일치 주의 문구 추가. 정확한 재현은 3.8 SQL 참조.
|
||||
|
||||
#### 3.7.2 승률 정의 (반영 완료)
|
||||
|
||||
- 종합 승률 39.1%(18/46): 0손익 포함 기준 — 3.1에 명시.
|
||||
- KR 시장 승률 38.5%(5/13): 0손익 제외 기준 — 3.3에 명시.
|
||||
|
||||
#### 3.7.3 startup-sync 중복 기록
|
||||
|
||||
- `BUY + [startup-sync]`가 76건 기록됨(동일 종목 반복 동기화 다수).
|
||||
- `BUY price=0`도 38건 존재해, PnL 매칭 시 원가 기준이 흔들릴 여지가 큼.
|
||||
- 성과 집계 시 `startup-sync`는 별도 레이어(초기 포지션 인식 이벤트)로 분리 저장 권장.
|
||||
- 3.5에서 startup-sync 분리 집계 제공.
|
||||
|
||||
#### 3.7.4 티커-거래소 드리프트 (ROOT-7 반영 완료)
|
||||
|
||||
- 예: `CCUP/CRCA/FIGS/LLY` 등 동일 티커가 `US_AMEX/US_NASDAQ/US_NYSE`에 혼재 기록.
|
||||
- 포지션 키를 `(ticker)`로만 쓰면 오매칭 위험 → ROOT-7으로 등재.
|
||||
|
||||
#### 3.7.5 FX PnL 분리 항목 미활성 (1.2 반영 완료)
|
||||
|
||||
- 스키마상 `strategy_pnl`, `fx_pnl` 컬럼이 있으나 SELL 전체 기준 `fx_pnl`은 전부 0.
|
||||
- 1.2에서 REQ-V3-007 상태를 "⚠️ 코드 완료 / 운영 미반영"으로 변경.
|
||||
- **startup-sync 중복**: BUY 76건 반복 동기화, price=0 38건 → PnL 매칭 왜곡 가능. 분리 집계는 3.5 참조.
|
||||
- **티커-거래소 드리프트**: 동일 티커가 다중 거래소에 혼재 기록 → ROOT-7 참조.
|
||||
- **FX PnL 미활성**: 스키마 존재, 운영 데이터 전부 0 → REQ-V3-007 참조.
|
||||
|
||||
### 3.8 표준 집계 SQL (재현용)
|
||||
|
||||
아래 SQL을 기준 쿼리로 고정하면, 성과표를 항상 같은 규칙으로 재생산할 수 있다.
|
||||
성과표 재현을 위한 기준 쿼리는 [`scripts/audit_queries.sql`](../../scripts/audit_queries.sql)에 분리되어 있다.
|
||||
|
||||
```sql
|
||||
-- Base: 기간 + LIVE + SELL + 직전 BUY 메타 매칭
|
||||
WITH base AS (
|
||||
SELECT *
|
||||
FROM trades
|
||||
WHERE mode='live'
|
||||
AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT
|
||||
s.id,
|
||||
s.timestamp,
|
||||
s.stock_code,
|
||||
s.market,
|
||||
s.exchange_code,
|
||||
s.quantity AS sell_qty,
|
||||
s.price AS sell_price,
|
||||
s.pnl,
|
||||
COALESCE((
|
||||
SELECT b.rationale
|
||||
FROM trades b
|
||||
WHERE b.mode='live'
|
||||
AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code
|
||||
AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC
|
||||
LIMIT 1
|
||||
), '') AS prev_buy_rationale,
|
||||
(
|
||||
SELECT b.quantity
|
||||
FROM trades b
|
||||
WHERE b.mode='live'
|
||||
AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code
|
||||
AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC
|
||||
LIMIT 1
|
||||
) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT * FROM labeled;
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Q1) 통화 분리 손익 (혼합 금지)
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Q2) 기존 보유(startup-sync) 제외 성과
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
WHERE prev_buy_rationale NOT LIKE '[startup-sync]%'
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Q3) 수량 일치 체결만 포함(무결성 필터)
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
WHERE prev_buy_qty = sell_qty
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Q4) 이상치 목록 (수량 불일치)
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.id, s.timestamp, s.stock_code, s.market, s.quantity AS sell_qty, s.pnl,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
id, timestamp, stock_code, market, sell_qty, prev_buy_qty, ROUND(pnl,2) AS pnl
|
||||
FROM labeled
|
||||
WHERE prev_buy_qty IS NOT NULL
|
||||
AND prev_buy_qty != sell_qty
|
||||
ORDER BY ABS(pnl) DESC;
|
||||
```
|
||||
- **Base**: 기간 + LIVE + SELL + 직전 BUY 메타 매칭
|
||||
- **Q1**: 통화 분리 손익 (KRW/USD 혼합 금지)
|
||||
- **Q2**: 기존 보유(startup-sync) 제외 성과
|
||||
- **Q3**: 수량 일치 체결만 포함 (무결성 필터)
|
||||
- **Q4**: 이상치 목록 (수량 불일치)
|
||||
|
||||
---
|
||||
|
||||
@@ -519,6 +331,8 @@ Phase 3 (중기): v3 세션 최적화
|
||||
- ✅ 블랙아웃 관리 (`test_blackout_manager.py`)
|
||||
- ✅ 주문 정책 저유동 거부 (`test_order_policy.py`)
|
||||
- ✅ FX 손익 분리 (`test_db.py`)
|
||||
- ✅ 블랙아웃 복구 후 유효 intent 실행 (`tests/test_main.py:5811`)
|
||||
- ✅ 블랙아웃 복구 후 정책 거부 intent 드롭 (`tests/test_main.py:5851`)
|
||||
|
||||
### 테스트 미존재
|
||||
|
||||
@@ -529,35 +343,12 @@ Phase 3 (중기): v3 세션 최적화
|
||||
- ❌ 블랙아웃 복구 주문의 DB 기록 검증
|
||||
- ❌ SELL PnL 계산 시 수량 불일치 케이스
|
||||
|
||||
### 테스트 존재 (재점검으로 확인)
|
||||
---
|
||||
|
||||
- ✅ 블랙아웃 복구 후 유효 intent 실행 (`tests/test_main.py:5811`)
|
||||
- ✅ 블랙아웃 복구 후 정책 거부 intent 드롭 (`tests/test_main.py:5851`)
|
||||
## 7. 후속 문서
|
||||
|
||||
### 6.1 재점검 반영 이력 (2026-02-28)
|
||||
|
||||
아래 코멘트들은 코드 대조 검증 후 본문에 반영 완료됨:
|
||||
|
||||
1. ROOT-5: “완전 미통합” → “부분 통합 + 실효성 부족”으로 정정 (본문 반영)
|
||||
2. GAP-4: “재검증 없음” → “부분 해소 + DB 기록 미구현”으로 정정 (본문 반영)
|
||||
3. 블랙아웃 복구 DB 미기록: GAP-4에 통합 + 개선 방안 5.2에 P0 추가
|
||||
4. SELL PnL buy_qty 버그: ROOT-6으로 신규 등재 (CRITICAL)
|
||||
5. BUY 매칭 exchange_code 누락: ROOT-7로 신규 등재 (HIGH)
|
||||
6. 경로 표기: `main.py` → `src/main.py`로 정규화 완료
|
||||
7. 테스트 섹션: 블랙아웃 복구 테스트 존재 확인, “테스트 존재 (재점검)” 항목으로 이동
|
||||
|
||||
### 6.2 정밀 검토 반영 이력 (2026-02-28)
|
||||
|
||||
아래 코멘트들은 검증 후 본문에 반영 완료됨:
|
||||
|
||||
1. 기간 기준 통일: 3.1에 UTC 기준 명시 + SELL 45건(기간 외 1건 제외) 주석 추가
|
||||
2. ROOT-1 ↔ ROOT-5 정합성: ROOT-1 문구를 “staged exit 호출되나 hard_stop 편향”으로 정정
|
||||
3. REQ-V3-007 2단 표기: “⚠️ 코드 완료 / 운영 미반영”으로 상태 변경
|
||||
4. ROOT-7 톤 조정: “잠재 오매칭 리스크”로 표현 변경, 확정 버그 → 구조 리스크로 재분류
|
||||
5. 3.6 USD 손익 표에 환산 KRW(가정 환율 1,450) + KRW 합산 참고값 병기
|
||||
6. 3.2 일별 표에 DB 실측치 불일치 주의 문구 추가
|
||||
7. 3.3 KR 승률에 “0손익 제외” 기준 명시
|
||||
8. 3.7 코멘트들을 세부 항목(3.7.1~3.7.5)으로 정리, 각 항목에 반영 상태 표기
|
||||
- **실행 계획**: [85_loss_recovery_action_plan.md](./85_loss_recovery_action_plan.md) — ROOT/GAP 해소를 위한 Phase별 작업 분해 및 Gitea 이슈 연결
|
||||
- **표준 집계 SQL**: [scripts/audit_queries.sql](../../scripts/audit_queries.sql)
|
||||
|
||||
---
|
||||
|
||||
|
||||
392
docs/ouroboros/85_loss_recovery_action_plan.md
Normal file
392
docs/ouroboros/85_loss_recovery_action_plan.md
Normal file
@@ -0,0 +1,392 @@
|
||||
<!--
|
||||
Doc-ID: DOC-ACTION-085
|
||||
Version: 1.0.0
|
||||
Status: active
|
||||
Owner: strategy
|
||||
Updated: 2026-02-28
|
||||
-->
|
||||
|
||||
# 손실 복구 실행 계획
|
||||
|
||||
작성일: 2026-02-28
|
||||
기반 문서: [80_implementation_audit.md](./80_implementation_audit.md) (ROOT 7개 + GAP 5개)
|
||||
|
||||
---
|
||||
|
||||
## 1. 요약
|
||||
|
||||
### 1.1 목표
|
||||
|
||||
80_implementation_audit.md에서 식별된 7개 근본 원인(ROOT-1~7)과 5개 구현 갭(GAP-1~5)을 해소하여 실거래 손실 구간에서 탈출한다.
|
||||
|
||||
### 1.2 성공 기준 (정량)
|
||||
|
||||
| 지표 | 현재 | 목표 |
|
||||
|------|------|------|
|
||||
| KR 시장 승률 | 38.5% | >= 50% |
|
||||
| 동일 종목 반복 매매 (일간) | 최대 4회 | <= 2회 |
|
||||
| US 페니스탁($5 이하) 진입 | 무제한 | 0건 |
|
||||
| SELL PnL 수량 불일치 건 | 존재 | 0건 |
|
||||
| 블랙아웃 복구 주문 DB 누락 | 존재 | 0건 |
|
||||
| session_id 누락 거래 로그 | 다수 | 0건 |
|
||||
| 진화 전략 syntax 오류율 | 100% (확인된 3건 모두) | 0% |
|
||||
|
||||
---
|
||||
|
||||
## 2. Phase별 작업 분해
|
||||
|
||||
### Phase 1: 즉시 — 손실 출혈 차단
|
||||
|
||||
가장 큰 손실 패턴(노이즈 손절, 반복 매매, 페니스탁)을 즉시 제거한다.
|
||||
|
||||
---
|
||||
|
||||
#### ACT-01: KR 손절선 ATR 기반 동적 확대
|
||||
|
||||
- **ROOT 참조**: ROOT-1 (hard_stop_pct -2%가 KR 소형주 변동성 대비 과소)
|
||||
- **Gitea 이슈**: feat: KR 손절선 ATR 기반 동적 확대 (-2% → ATR 적응형)
|
||||
- **Gitea 이슈 번호**: #318
|
||||
- **변경 대상 파일**: `src/main.py`, `src/strategy/exit_rules.py`, `src/config.py`
|
||||
- **현재 동작**: `hard_stop_pct = -2.0` 고정값으로 모든 시장에 동일 적용
|
||||
- **목표 동작**: KR 시장은 ATR(14) 기반 동적 손절선 적용. 최소 -2%, 최대 -7%, 기본값은 `k * ATR / entry_price * 100` (k=2.0)
|
||||
- **수용 기준**:
|
||||
- ATR 값이 존재할 때 동적 손절선이 계산됨
|
||||
- ATR 미제공 시 기존 -2% 폴백
|
||||
- KR 이외 시장은 기존 동작 유지
|
||||
- **테스트 계획**:
|
||||
- 단위: ATR 기반 손절선 계산 로직 테스트 (경계값: ATR=0, ATR=극단값)
|
||||
- 통합: 백테스트 파이프라인에서 KR 종목 손절 빈도 비교
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-02: 손절 후 동일 종목 재진입 쿨다운
|
||||
|
||||
- **ROOT 참조**: ROOT-2 (동일 종목 반복 매매)
|
||||
- **Gitea 이슈**: feat: 손절 후 동일 종목 재진입 쿨다운 (1~2시간)
|
||||
- **Gitea 이슈 번호**: #319
|
||||
- **변경 대상 파일**: `src/main.py`, `src/config.py`
|
||||
- **현재 동작**: 손절 후 동일 종목 즉시 재매수 가능
|
||||
- **목표 동작**: 손절(SELL with pnl < 0) 후 동일 종목은 `COOLDOWN_MINUTES` (기본 120분) 동안 매수 차단
|
||||
- **수용 기준**:
|
||||
- 손절 기록이 있는 종목에 대해 쿨다운 시간 내 BUY 시도 시 거부
|
||||
- 쿨다운 경과 후 정상 진입 허용
|
||||
- 익절(pnl >= 0)에는 쿨다운 미적용
|
||||
- **테스트 계획**:
|
||||
- 단위: 쿨다운 시간 내/외 매수 시도 테스트
|
||||
- 통합: 229000 유사 패턴 백테스트 시나리오
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-03: US $5 이하 종목 진입 차단 필터
|
||||
|
||||
- **ROOT 참조**: ROOT-3 (미국 페니스탁 무분별 진입)
|
||||
- **Gitea 이슈**: feat: US $5 이하 종목 진입 차단 필터
|
||||
- **Gitea 이슈 번호**: #320
|
||||
- **변경 대상 파일**: `src/main.py`, `src/config.py`
|
||||
- **현재 동작**: 가격 제한 없이 모든 US 종목 진입 가능
|
||||
- **목표 동작**: US 시장 BUY 시 현재가 $5 이하이면 진입 차단. 임계값은 `US_MIN_PRICE` 환경변수로 설정 가능
|
||||
- **수용 기준**:
|
||||
- $5 이하 종목 BUY 시도 시 거부 + 로그 기록
|
||||
- $5 초과 종목은 기존 동작 유지
|
||||
- KR 등 다른 시장에는 미적용
|
||||
- **테스트 계획**:
|
||||
- 단위: 가격별 필터 동작 테스트 (경계값: $4.99, $5.00, $5.01)
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-04: 진화 전략 코드 생성 시 syntax 검증 추가
|
||||
|
||||
- **ROOT 참조**: ROOT-4 (진화 전략 문법 오류)
|
||||
- **Gitea 이슈**: fix: 진화 전략 코드 생성 시 syntax 검증 추가
|
||||
- **Gitea 이슈 번호**: #321
|
||||
- **변경 대상 파일**: `src/evolution/optimizer.py`
|
||||
- **현재 동작**: 생성된 Python 코드를 검증 없이 파일로 저장
|
||||
- **목표 동작**: `ast.parse()` + `compile()` 로 syntax 검증 후 통과한 코드만 저장. 실패 시 로그 경고 + 기존 전략 유지
|
||||
- **수용 기준**:
|
||||
- syntax 오류가 있는 코드는 저장되지 않음
|
||||
- 검증 실패 시 기존 전략으로 폴백
|
||||
- 검증 실패 로그가 기록됨
|
||||
- **테스트 계획**:
|
||||
- 단위: 정상 코드/오류 코드 검증 테스트
|
||||
- 기존 `v20260227_*_evolved.py` 파일로 회귀 테스트
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 단기 — 데이터 정합성 + v2 실효화
|
||||
|
||||
손익 계산 정확도를 확보하고, v2 청산 로직을 실효화한다.
|
||||
|
||||
---
|
||||
|
||||
#### ACT-05: SELL PnL 계산을 sell_qty 기준으로 수정
|
||||
|
||||
- **ROOT 참조**: ROOT-6 (CRITICAL — PnL 계산이 buy_qty 사용)
|
||||
- **Gitea 이슈**: fix(critical): SELL PnL 계산을 sell_qty 기준으로 수정
|
||||
- **Gitea 이슈 번호**: #322
|
||||
- **변경 대상 파일**: `src/main.py` (line 1658-1663, 2755-2760)
|
||||
- **현재 동작**: `trade_pnl = (trade_price - buy_price) * buy_qty` — 직전 BUY 수량 사용
|
||||
- **목표 동작**: `trade_pnl = (trade_price - buy_price) * sell_qty` — 실제 매도 수량 사용
|
||||
- **수용 기준**:
|
||||
- 부분청산 시 매도 수량 기준 PnL 계산
|
||||
- 기존 전량 매도(buy_qty == sell_qty) 케이스는 동일 결과
|
||||
- CRCA 유사 이상치 재발 불가
|
||||
- **테스트 계획**:
|
||||
- 단위: 전량 매도, 부분 매도, 수량 불일치 케이스별 PnL 검증
|
||||
- DB: Q4 쿼리(`scripts/audit_queries.sql`)로 이상치 0건 확인
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-06: BUY 매칭 키에 exchange_code 추가
|
||||
|
||||
- **ROOT 참조**: ROOT-7 (BUY 매칭 키에 exchange_code 미포함)
|
||||
- **Gitea 이슈**: fix: BUY 매칭 키에 exchange_code 추가
|
||||
- **Gitea 이슈 번호**: #323
|
||||
- **변경 대상 파일**: `src/db.py` (line 292-313)
|
||||
- **현재 동작**: `get_latest_buy_trade()`가 `(stock_code, market)`만으로 매칭
|
||||
- **목표 동작**: `exchange_code`가 존재할 때 매칭 키에 포함. NULL인 경우 기존 동작 유지 (하위 호환)
|
||||
- **수용 기준**:
|
||||
- 동일 티커 다중 거래소 기록 시 정확한 BUY 매칭
|
||||
- exchange_code가 NULL인 레거시 데이터에서도 정상 동작
|
||||
- **테스트 계획**:
|
||||
- 단위: 동일 티커 다중 exchange 매칭 테스트
|
||||
- 단위: exchange_code NULL 하위 호환 테스트
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-07: 블랙아웃 복구 주문에 log_trade() 추가
|
||||
|
||||
- **ROOT 참조**: GAP-4 (블랙아웃 복구 주문 DB 미기록)
|
||||
- **Gitea 이슈**: fix: 블랙아웃 복구 주문에 log_trade() 추가
|
||||
- **Gitea 이슈 번호**: #324
|
||||
- **변경 대상 파일**: `src/main.py` (line 694-791, 블랙아웃 복구 실행 경로)
|
||||
- **현재 동작**: 블랙아웃 복구 주문이 실행되나 `log_trade()` 호출 없음 → DB에 기록 안 됨
|
||||
- **목표 동작**: 복구 주문 실행 후 `log_trade()` 호출하여 DB에 기록. rationale에 `[blackout-recovery]` prefix 추가
|
||||
- **수용 기준**:
|
||||
- 블랙아웃 복구 주문이 trades 테이블에 기록됨
|
||||
- rationale로 복구 주문 식별 가능
|
||||
- 성과 리포트에 복구 주문 포함
|
||||
- **테스트 계획**:
|
||||
- 단위: 복구 주문 실행 후 DB 기록 존재 확인
|
||||
- 통합: 블랙아웃 시나리오 end-to-end 테스트
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-08: v2 staged exit에 실제 피처 공급
|
||||
|
||||
- **ROOT 참조**: ROOT-5 (v2 청산 로직 실효성 부족)
|
||||
- **Gitea 이슈**: feat: v2 staged exit에 실제 피처(ATR, pred_down_prob) 공급
|
||||
- **Gitea 이슈 번호**: #325
|
||||
- **변경 대상 파일**: `src/main.py` (line 500-583), `src/strategy/exit_rules.py`, `src/analysis/technical.py`
|
||||
- **현재 동작**: `atr_value=0.0`, `pred_down_prob=0.0`으로 공급 → hard stop만 발동
|
||||
- **목표 동작**:
|
||||
- `atr_value`: 보유 종목의 ATR(14) 실시간 계산하여 공급
|
||||
- `pred_down_prob`: 최소한 RSI 기반 하락 확률 추정값 공급 (추후 ML 모델로 대체 가능)
|
||||
- `be_arm_pct`/`arm_pct`: 독립 파라미터로 설정 가능 (take_profit_pct * 0.4 기계적 파생 제거)
|
||||
- **수용 기준**:
|
||||
- `evaluate_exit()` 호출 시 atr_value > 0 (ATR 계산 가능한 종목)
|
||||
- ATR trailing stop이 실제 발동 가능
|
||||
- be_arm_pct/arm_pct 독립 설정 가능
|
||||
- **테스트 계획**:
|
||||
- 단위: 피처 공급 경로별 값 검증
|
||||
- 통합: 상태기계 전이 시나리오 (HOLDING→BE_LOCK→ARMED→EXITED)
|
||||
- **의존성**: ACT-01 (ATR 계산 인프라 공유)
|
||||
|
||||
---
|
||||
|
||||
#### ACT-09: session_id를 거래/의사결정 로그에 명시적 전달
|
||||
|
||||
- **ROOT 참조**: GAP-1 (DecisionLogger session_id 미포함), GAP-2 (log_trade session_id 미전달)
|
||||
- **Gitea 이슈**: feat: session_id를 거래/의사결정 로그에 명시적 전달
|
||||
- **Gitea 이슈 번호**: #326
|
||||
- **변경 대상 파일**: `src/logging/decision_logger.py`, `src/main.py` (line 1625, 1682, 2769), `src/db.py`
|
||||
- **현재 동작**:
|
||||
- `log_decision()`: session_id 파라미터 없음
|
||||
- `log_trade()`: session_id 미전달, 시장 코드 기반 자동 추론에 의존
|
||||
- **목표 동작**:
|
||||
- `log_decision()`: session_id 파라미터 추가, 로그에 기록
|
||||
- `log_trade()` 호출 시 런타임 session_id 명시적 전달
|
||||
- **수용 기준**:
|
||||
- 모든 SELL/BUY 로그에 session_id 필드 존재
|
||||
- 의사결정 로그에 session_id 필드 존재
|
||||
- session_id가 실제 런타임 세션과 일치
|
||||
- **테스트 계획**:
|
||||
- 단위: log_decision() session_id 캡처 테스트
|
||||
- 단위: log_trade() session_id 전달 테스트
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 중기 — v3 세션 최적화
|
||||
|
||||
세션 경계 처리와 운영 거버넌스를 강화한다.
|
||||
|
||||
---
|
||||
|
||||
#### ACT-10: 세션 전환 시 리스크 파라미터 동적 재로딩
|
||||
|
||||
- **ROOT 참조**: GAP-3 (세션 전환 시 리스크 파라미터 재로딩 없음)
|
||||
- **Gitea 이슈**: feat: 세션 전환 시 리스크 파라미터 동적 재로딩
|
||||
- **Gitea 이슈 번호**: #327
|
||||
- **변경 대상 파일**: `src/main.py`, `src/config.py`
|
||||
- **현재 동작**: 리스크 파라미터가 시작 시 한 번만 로딩
|
||||
- **목표 동작**: 세션 경계 변경 이벤트 시 해당 세션의 리스크 파라미터를 재로딩. 세션별 프로파일 지원
|
||||
- **수용 기준**:
|
||||
- NXT_AFTER → KRX_REG 전환 시 파라미터 재로딩 확인
|
||||
- 재로딩 이벤트 로그 기록
|
||||
- 재로딩 실패 시 기존 파라미터 유지 (안전 폴백)
|
||||
- **테스트 계획**:
|
||||
- 단위: 세션 전환 훅 콜백 테스트
|
||||
- 단위: 재로딩 실패 시 폴백 테스트
|
||||
- **의존성**: ACT-09 (session_id 인프라)
|
||||
|
||||
---
|
||||
|
||||
#### ACT-11: 블랙아웃 복구 시 가격/세션 재검증 강화
|
||||
|
||||
- **ROOT 참조**: GAP-4 잔여 (가격 유효성, 세션 변경 재적용 미구현)
|
||||
- **Gitea 이슈**: feat: 블랙아웃 복구 시 가격/세션 재검증 강화
|
||||
- **Gitea 이슈 번호**: #328
|
||||
- **변경 대상 파일**: `src/main.py` (line 694-791), `src/core/blackout_manager.py`
|
||||
- **현재 동작**: stale BUY/SELL 드롭 + order_policy 검증만 수행
|
||||
- **목표 동작**:
|
||||
- 복구 시 현재 시세 조회하여 가격 유효성 검증 (진입가 대비 급등/급락 시 드롭)
|
||||
- 세션 변경 시 새 세션의 파라미터로 재검증
|
||||
- **수용 기준**:
|
||||
- 블랙아웃 전후 가격 변동 > 임계값(예: 5%) 시 주문 드롭
|
||||
- 세션 변경 시 새 세션 파라미터로 재평가
|
||||
- **테스트 계획**:
|
||||
- 단위: 가격 변동 시나리오별 드롭/실행 테스트
|
||||
- 통합: 블랙아웃 + 세션 전환 복합 시나리오
|
||||
- **의존성**: ACT-07 (복구 주문 DB 기록), ACT-10 (세션 파라미터 재로딩)
|
||||
|
||||
---
|
||||
|
||||
#### ACT-12: Triple Barrier 시간장벽을 캘린더 시간(분) 기반으로 전환
|
||||
|
||||
- **ROOT 참조**: GAP-5 (시간장벽이 봉 개수 고정)
|
||||
- **Gitea 이슈**: feat: Triple Barrier 시간장벽을 캘린더 시간(분) 기반으로 전환
|
||||
- **Gitea 이슈 번호**: #329
|
||||
- **변경 대상 파일**: `src/analysis/triple_barrier.py`
|
||||
- **현재 동작**: `max_holding_bars` (고정 봉 수) 사용
|
||||
- **목표 동작**: `max_holding_minutes` (캘린더 시간) 기반으로 전환. 봉 주기와 무관하게 일정 시간 경과 시 장벽 도달
|
||||
- **수용 기준**:
|
||||
- 분 단위 시간장벽이 봉 주기 변경에도 일관 동작
|
||||
- 기존 max_holding_bars 하위 호환 (deprecated 경고)
|
||||
- **테스트 계획**:
|
||||
- 단위: 다양한 봉 주기(1분, 5분, 15분)에서 시간장벽 일관성 테스트
|
||||
- 기존 triple_barrier 테스트 회귀 확인
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
#### ACT-13: CI 자동 검증 (정책 레지스트리 + TASK-REQ 매핑)
|
||||
|
||||
- **ROOT 참조**: REQ-OPS-002 (정책 변경 시 레지스트리 업데이트 강제), REQ-OPS-003 (TASK-REQ 매핑 강제)
|
||||
- **Gitea 이슈**: infra: CI 자동 검증 (정책 레지스트리 + TASK-REQ 매핑)
|
||||
- **Gitea 이슈 번호**: #330
|
||||
- **변경 대상 파일**: `.gitea/workflows/`, `scripts/validate_governance_assets.py`
|
||||
- **현재 동작**: CI 자동 검증 없음. 문서 검증은 수동 실행
|
||||
- **목표 동작**:
|
||||
- PR 시 정책 레지스트리(`01_requirements_registry.md`) 변경 여부 자동 검증
|
||||
- TASK/이슈가 REQ-ID를 참조하는지 자동 검증
|
||||
- **수용 기준**:
|
||||
- 정책 파일 변경 시 레지스트리 미업데이트면 CI 실패
|
||||
- 새 이슈/PR에 REQ-ID 미참조 시 경고
|
||||
- **테스트 계획**:
|
||||
- CI 파이프라인 자체 테스트 (정상/실패 케이스)
|
||||
- **의존성**: 없음
|
||||
|
||||
---
|
||||
|
||||
## 3. 검증 계획
|
||||
|
||||
### 3.1 단위 테스트
|
||||
|
||||
- 모든 ACT 항목에 대해 개별 테스트 작성
|
||||
- 커버리지 >= 80% 유지
|
||||
- 기존 551개 테스트 전체 통과 확인
|
||||
|
||||
### 3.2 통합 테스트
|
||||
|
||||
- 백테스트 파이프라인: Phase 1 적용 전후 KR 시장 손절 빈도, 반복 매매 횟수, 승률 비교
|
||||
- 상태기계 통합: Phase 2 피처 공급 후 4중 청산 로직 end-to-end 시나리오
|
||||
- 블랙아웃 복합: Phase 3 세션 전환 + 블랙아웃 복구 시나리오
|
||||
|
||||
### 3.3 실환경 검증
|
||||
|
||||
- Paper trading은 실환경과 괴리가 커 검증 신뢰도 부족 → **소액 live 운용**으로 검증
|
||||
- Phase별 투입 기준: 단위/통합 테스트 통과 → 소액 live (1~2일) → 모니터링 → 정상 확인 후 본운용
|
||||
|
||||
---
|
||||
|
||||
## 4. 의존성 그래프
|
||||
|
||||
```
|
||||
Phase 1 (병렬 실행 가능)
|
||||
ACT-01 #318 ─┐
|
||||
ACT-02 #319 │ (모두 독립)
|
||||
ACT-03 #320 │
|
||||
ACT-04 #321 ─┘
|
||||
|
||||
Phase 2
|
||||
ACT-05 #322 ─┐
|
||||
ACT-06 #323 │ (대부분 독립)
|
||||
ACT-07 #324 │
|
||||
ACT-09 #326 ─┘
|
||||
ACT-08 #325 ←── ACT-01 #318 (ATR 인프라 공유)
|
||||
|
||||
Phase 3
|
||||
ACT-10 #327 ←── ACT-09 #326 (session_id 인프라)
|
||||
ACT-11 #328 ←── ACT-07 #324, ACT-10 #327
|
||||
ACT-12 #329 (독립)
|
||||
ACT-13 #330 (독립)
|
||||
```
|
||||
|
||||
### Phase 간 관계
|
||||
|
||||
- Phase 1 → Phase 2: Phase 1 완료가 Phase 2의 전제 조건은 아니나, Phase 1로 출혈 차단 후 Phase 2 진행 권장
|
||||
- Phase 2 → Phase 3: ACT-09(session_id)가 ACT-10(세션 재로딩)의 전제, ACT-07+ACT-10이 ACT-11의 전제
|
||||
|
||||
---
|
||||
|
||||
## 5. 롤백 계획
|
||||
|
||||
### Phase 1 롤백
|
||||
|
||||
- 각 ACT는 독립적이므로 개별 revert 가능
|
||||
- 손절선(ACT-01): 기존 -2% 고정값으로 복원
|
||||
- 쿨다운(ACT-02): 쿨다운 체크 제거
|
||||
- 가격 필터(ACT-03): 필터 조건 제거
|
||||
- syntax 검증(ACT-04): 검증 스킵, 기존 저장 로직 복원
|
||||
|
||||
### Phase 2 롤백
|
||||
|
||||
- PnL 수정(ACT-05): buy_qty 기준으로 복원 (단, 데이터 정합성 후퇴 감수)
|
||||
- exchange_code(ACT-06): 매칭 키에서 제거
|
||||
- 블랙아웃 DB(ACT-07): log_trade() 호출 제거
|
||||
- 피처 공급(ACT-08): 0.0 공급으로 복원
|
||||
- session_id(ACT-09): 파라미터 제거, 자동 추론 복원
|
||||
|
||||
### Phase 3 롤백
|
||||
|
||||
- 세션 재로딩(ACT-10): 시작 시 1회 로딩으로 복원
|
||||
- 블랙아웃 재검증(ACT-11): 기존 stale 드롭만 유지
|
||||
- 시간장벽(ACT-12): max_holding_bars로 복원
|
||||
- CI(ACT-13): CI 워크플로우 제거
|
||||
|
||||
### 롤백 절차
|
||||
|
||||
1. 해당 ACT의 PR branch에서 `git revert` 수행
|
||||
2. 기존 테스트 전체 통과 확인
|
||||
3. 실환경 투입 전 소액 live 검증
|
||||
|
||||
---
|
||||
|
||||
*끝.*
|
||||
@@ -23,6 +23,7 @@ Updated: 2026-02-26
|
||||
9. 저장소 강제 설정 체크리스트: [60_repo_enforcement_checklist.md](./60_repo_enforcement_checklist.md)
|
||||
10. 메인 에이전트 아이디에이션 백로그: [70_main_agent_ideation.md](./70_main_agent_ideation.md)
|
||||
11. v2/v3 구현 감사 및 수익률 분석: [80_implementation_audit.md](./80_implementation_audit.md)
|
||||
12. 손실 복구 실행 계획: [85_loss_recovery_action_plan.md](./85_loss_recovery_action_plan.md)
|
||||
|
||||
## 운영 규칙
|
||||
|
||||
|
||||
184
scripts/audit_queries.sql
Normal file
184
scripts/audit_queries.sql
Normal file
@@ -0,0 +1,184 @@
|
||||
-- audit_queries.sql
|
||||
-- 용도: 80_implementation_audit.md 성과표 재현을 위한 표준 집계 SQL
|
||||
-- 대상 DB: trading.db (SQLite)
|
||||
-- 기간: 2026-02-25 ~ 2026-02-28 (UTC)
|
||||
-- 참조: docs/ouroboros/80_implementation_audit.md Section 3
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Base: 기간 + LIVE + SELL + 직전 BUY 메타 매칭
|
||||
------------------------------------------------------------------------
|
||||
-- 모든 후속 쿼리의 기반이 되는 CTE.
|
||||
-- prev_buy_rationale: 직전 BUY의 rationale (startup-sync 분류용)
|
||||
-- prev_buy_qty: 직전 BUY 수량 (수량 일치 무결성 필터용)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
WITH base AS (
|
||||
SELECT *
|
||||
FROM trades
|
||||
WHERE mode='live'
|
||||
AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT
|
||||
s.id,
|
||||
s.timestamp,
|
||||
s.stock_code,
|
||||
s.market,
|
||||
s.exchange_code,
|
||||
s.quantity AS sell_qty,
|
||||
s.price AS sell_price,
|
||||
s.pnl,
|
||||
COALESCE((
|
||||
SELECT b.rationale
|
||||
FROM trades b
|
||||
WHERE b.mode='live'
|
||||
AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code
|
||||
AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC
|
||||
LIMIT 1
|
||||
), '') AS prev_buy_rationale,
|
||||
(
|
||||
SELECT b.quantity
|
||||
FROM trades b
|
||||
WHERE b.mode='live'
|
||||
AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code
|
||||
AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC
|
||||
LIMIT 1
|
||||
) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT * FROM labeled;
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Q1) 통화 분리 손익 (KRW/USD 혼합 금지)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Q2) 기존 보유(startup-sync) 제외 성과
|
||||
------------------------------------------------------------------------
|
||||
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
WHERE prev_buy_rationale NOT LIKE '[startup-sync]%'
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Q3) 수량 일치 체결만 포함 (무결성 필터)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.*,
|
||||
s.quantity AS sell_qty,
|
||||
COALESCE((SELECT b.rationale FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1), '') AS prev_buy_rationale,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
CASE WHEN market='KR' THEN 'KRW' ELSE 'USD' END AS ccy,
|
||||
COUNT(*) AS sells,
|
||||
ROUND(SUM(pnl),2) AS pnl_sum
|
||||
FROM labeled
|
||||
WHERE prev_buy_qty = sell_qty
|
||||
GROUP BY ccy
|
||||
ORDER BY ccy;
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Q4) 이상치 목록 (수량 불일치)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
WITH base AS (
|
||||
SELECT * FROM trades
|
||||
WHERE mode='live' AND action='SELL'
|
||||
AND timestamp >= '2026-02-25T00:00:00+00:00'
|
||||
AND timestamp < '2026-02-28T00:00:00+00:00'
|
||||
),
|
||||
labeled AS (
|
||||
SELECT s.id, s.timestamp, s.stock_code, s.market, s.quantity AS sell_qty, s.pnl,
|
||||
(SELECT b.quantity FROM trades b
|
||||
WHERE b.mode='live' AND b.action='BUY'
|
||||
AND b.stock_code=s.stock_code AND b.market=s.market
|
||||
AND b.timestamp < s.timestamp
|
||||
ORDER BY b.timestamp DESC, b.id DESC LIMIT 1) AS prev_buy_qty
|
||||
FROM base s
|
||||
)
|
||||
SELECT
|
||||
id, timestamp, stock_code, market, sell_qty, prev_buy_qty, ROUND(pnl,2) AS pnl
|
||||
FROM labeled
|
||||
WHERE prev_buy_qty IS NOT NULL
|
||||
AND prev_buy_qty != sell_qty
|
||||
ORDER BY ABS(pnl) DESC;
|
||||
Reference in New Issue
Block a user