#!/usr/bin/env bash
# 08편 — daily_stock_analysis(25) 의 `관망` 을 반사실 시험 주문 두 건으로 채점한다.
#
#   ┌─ 설계 원칙 ────────────────────────────────────────────────────────────┐
#   │ **의도는 도구가 갖는다.** weltiq 는 집행 기록만 갖고, 판정(`관망`)·확신도(`중간`)· │
#   │ 근거(MACD 데드크로스·이격 +3.82%)는 **저장하지 않는다.**                       │
#   │ 서버로 가는 것은 client_order_id · client_tracking_id · (문자열) metadata 뿐이고, │
#   │ 판정과 근거는 tool_log.csv 에만 쓴다. 둘을 잇는 것은 **추적 ID 하나**다.          │
#   └───────────────────────────────────────────────────────────────────────┘
#
# 이 레시피가 채우는 빈칸:
#   25 는 `관망` 과 함께 가격 네 칸을 낸다 — ideal_buy 219.01 · secondary_buy 219.16 ·
#   stop_loss 218.90 · take_profit 230.00. **이관에서는 이 네 칸이 주문 세트로 안 선다**:
#   ideal_buy 219.01 과 stop_loss 218.90 이 0.05% 붙어 있고, 2회차에서는 손절(219.36)이
#   매수가(219.01) **위**로 올라왔다. 그런데 **반사실로는 선다** — 진입을 현재가로 옮기는 순간
#   218.90 은 손절로, 230.00 은 목표로 제자리를 찾는다. 이게 이 레시피의 요지다.
#
# 쓰는 법:
#   export WELTIQ_API_KEY='wk_...'        # scope: run-forward-tests, read-data
#   export RUN_ID='dsa-cf-1'
#   ./08-trial-order-daily-stock-analysis-wait.sh
#
# 주의: 시험 주문은 **월 한도**를 쓴다 (진입 접수 1건 = 1회). 아래 PLAN 은 2건이다.
#       **us-equity 는 세션이 있다.** 폐장에 내면 접수는 201 이고 배치에서
#       `rejected:market_closed` 로 되돌아온다 — 그때 한도 1회는 **환불된다**.
#       ⓪ 에서 세션을 먼저 보고, 닫혀 있으면 **그대로 멈춘다.** 시각을 속이지 않는다.
#       **실행하지 않았다.** 이 파일은 `bash -n` 까지만 확인했다.
set -euo pipefail

: "${WELTIQ_BASE_URL:=https://weltiq.ai}"
: "${WELTIQ_API_KEY:?WELTIQ_API_KEY 를 설정하세요 (pro 계정 API 키, scope: run-forward-tests read-data)}"
: "${RUN_ID:=dsa-cf-$(date +%Y%m%d-%H%M%S)}"
: "${SYMBOL:=NVDA}"
: "${VENUE:=us-equity}"
WORK="${WORK_DIR:-./cf-08-$RUN_ID}"

command -v jq >/dev/null || { echo 'jq 가 필요합니다 (brew install jq)'; exit 2; }
mkdir -p "$WORK"
CALLS=0
api() { CALLS=$((CALLS + 1)); curl -sS -H "Authorization: Bearer $WELTIQ_API_KEY" \
        -H 'Content-Type: application/json' "$@"; }

# ── ⓪ 세션 먼저 — 닫혀 있으면 안 낸다 ───────────────────────────────────────
# 폐장에 내도 접수는 된다(201). 하지만 체결이 없으니 손익이 없고, 판정은 **미판정**이다.
# 미판정을 0 으로 채우면 표본이 오염된다. 그래서 여기서 멈추는 쪽이 옳다.
echo '⓪ 세션 확인'
api "$WELTIQ_BASE_URL/v1/capabilities" > "$WORK/capabilities.json"
OPEN=$(jq -r --arg v "$VENUE" '.markets[]? | select(.venue == $v) | .session.is_open // "unknown"' \
       "$WORK/capabilities.json")
echo "   $VENUE is_open=$OPEN"
if [ "$OPEN" != "true" ]; then
  echo "   → 폐장이다. 여기서 멈춘다. 22:30~05:00 KST 에 같은 스크립트를 그대로 돌려라."
  echo "   (굳이 내 보고 싶으면 SKIP_SESSION_GATE=1 을 주면 된다 — 결과는 rejected:market_closed 다.)"
  [ "${SKIP_SESSION_GATE:-0}" = 1 ] || exit 0
fi

# ── ① 심기 — 도구의 네 칸을 반사실 두 건으로 옮긴다 ─────────────────────────
# **롱** — 도구는 "219.01 까지 내려오면 사라, 지금(227.38)은 아니다"라고 했다.
#          그 반대로 **지금 시장가로 산다.** 청산 조건은 도구가 낸 숫자 그대로:
#          stop 218.90 / target 230.00. 역할을 바꾸지 않았다.
# **숏**  — 이 도구는 **명시적으로 하락을 시사했다**: MACD DIF 1.4828 < DEA 1.5377(데드크로스) ·
#          역배열 · MA5 대비 이격 +3.82% · "추격 금지: 5일선 대비 +5% 초과 시 매수 안 함".
#          그래서 숏을 따로 한 건 낸다. 목표 219.01 = 도구의 ideal_buy(= MA5, 도구가 지목한
#          눌림 도착점), 손절 234.50 = 도구의 저항(채팅 3/3 동일). **역할만 뒤집었고 숫자는 도구 것이다.**
#
# 숏 주의: us-equity 숏은 **대차수수료가 0 으로 계산된다**(프로필의 유일한 비보수적 편향).
#          10분짜리라 영향이 작지만, 오래 들고 있으면 기록이 시장보다 유리해진다.
#
# 한 줄 = client_order_id | side | take_profit | stop_loss | 청산까지 몇 초
#   (롱은 목표 > 손절, 숏은 목표 < 손절. 반대로 주면 422 다.)
PLAN='
'"$RUN_ID"'-long|long|230.00|218.90|600
'"$RUN_ID"'-short|short|219.01|234.50|600
'

echo 'client_order_id,tracking,verdict,confidence,side,reason' > "$WORK/tool_log.csv"
echo "① 심기 — tracking=$RUN_ID"
while IFS='|' read -r COID SIDE TP SL CLOSE_S; do
  [ -n "${COID:-}" ] || continue
  # ↓ 서버로 가는 본문. 판정도 근거도 **여기 없다**.
  #   metadata 에 **float 을 넣으면 500** 이다 (canonical_json 이 결정론 규칙으로 float 을 거부).
  #   숫자를 실을 일이 있으면 **문자열**로: {"ma5": "219.01"}.
  BODY=$(jq -n --arg v "$VENUE" --arg s "$SYMBOL" --arg c "$COID" --arg t "$RUN_ID" \
               --arg sd "$SIDE" --arg tp "$TP" --arg sl "$SL" --argjson sec "$CLOSE_S" \
    '{venue:$v, symbol:$s, client_order_id:$c, side:$sd,
      client_tracking_id:$t, tags:["counterfactual","a2-08"],
      exit_plan:{close_after_seconds:$sec, take_profit:$tp, stop_loss:$sl},
      max_duration_hours:1,
      metadata:{verdict:"hold", advice:"관망", confidence:"중간",
                ma5:"219.01", tool:"25-daily-stock-analysis"}}')
  RESP=$(api -X POST "$WELTIQ_BASE_URL/v1/trial-buys" -d "$BODY")
  echo "   $COID ($SIDE) → $(echo "$RESP" | jq -r '.test_id // .error // "?"')"
  # ↓ 도구 쪽 로그. **서버에는 안 보낸다.**
  printf '%s,%s,hold,중간,%s,"MACD DIF 1.4828 < DEA 1.5377 · 이격 +3.82%%"\n' \
    "$COID" "$RUN_ID" "$SIDE" >> "$WORK/tool_log.csv"
done <<< "$PLAN"

echo "   (체결·청산이 돌 시간을 준다: 660초)"; sleep "${SETTLE_S:-660}"

# ── ② 회수 — tracking= 으로 계정 전체에서 내 런만 ────────────────────────────
echo "② 회수 — tracking=$RUN_ID"
api "$WELTIQ_BASE_URL/v1/forward-tests?tracking=$RUN_ID&limit=200" > "$WORK/list.json"
jq -r '.tests[].test_id' "$WORK/list.json" > "$WORK/test_ids.txt"
echo "   시험 $(wc -l < "$WORK/test_ids.txt" | tr -d ' ')건"

# ── ③ 집행 기록 — 계정 전체 CSV 를 한 번에 ───────────────────────────────────
# 자동 청산 행이 진입의 추적 ID·태그를 물려받으므로 `tracking=` 한 번에 {enter, exit} 가 같이 온다.
echo '③ 집행 기록 — 계정 전체 CSV 한 번에'
api "$WELTIQ_BASE_URL/v1/forward-tests/orders?format=csv&tracking=$RUN_ID" > "$WORK/all.csv"
echo "   $(($(wc -l < "$WORK/all.csv") - 1))행"

# ── ④ 조인 + 판정 ───────────────────────────────────────────────────────────
# CSV 에 인용된 쉼표(metadata·exit_plan)가 있어 awk 로 자르면 깨진다. 조인은 python3 csv 모듈로.
echo '④ 조인 — all.csv × tool_log.csv'
python3 - "$WORK" <<'PY'
import csv, sys
w = sys.argv[1]
log = {(r['tracking'], r['client_order_id']): r for r in csv.DictReader(open(f'{w}/tool_log.csv'))}
rows = list(csv.DictReader(open(f'{w}/all.csv')))
cols = (list(rows[0]) if rows else []) + ['verdict', 'confidence', 'reason', 'join']
out = csv.DictWriter(open(f'{w}/joined.csv', 'w', newline=''), cols); out.writeheader()
per, matched = {}, 0
for r in rows:
    m = log.get((r['client_tracking_id'], r['client_order_id']))
    r.update(verdict=m['verdict'] if m else '', confidence=m['confidence'] if m else '',
             reason=m['reason'] if m else '', join='matched' if m else 'unmatched')
    matched += bool(m)
    out.writerow(r)
    t = per.setdefault(r['test_id'], {'side': '', 'enter': None, 'exit': None, 'reason': ''})
    if m:
        t['side'] = m['side']
    if r.get('close_reason'):
        t['reason'] = r['close_reason']
    if r['fill_qty']:
        t['enter' if r['action'] == 'enter' else 'exit'] = (
            float(r['fill_qty']), float(r['fill_px']), float(r['fee'] or 0))
print(f'   조인됨 {matched} / 안 됨 {len(rows) - matched}')
# unmatched = 서버가 만든 자동 청산 행(client_order_id 가 "exit-plan:…"). 정상이다.

print(f'\n   {"방향":<7}{"청산사유":>24}{"순손익":>18}{"손익분기%":>11}')
for t in per.values():
    if not t['side']:
        continue
    if not (t['enter'] and t['exit']):
        # **미판정은 미판정이라고 적는다.** 폐장이면 여기 `rejected:market_closed` 가 찍힌다.
        print(f'   {t["side"]:<7}{t["reason"] or "미체결":>24}{"미판정":>18}{"—":>11}')
        continue
    fee = t['enter'][2] + t['exit'][2]
    notional = t['enter'][0] * t['enter'][1]
    sign = 1 if t['side'] == 'long' else -1
    net = sign * (t['exit'][0] * t['exit'][1] - notional) - fee
    print(f'   {t["side"]:<7}{t["reason"]:>24}{net:>18.10f}{abs(net) / notional * 100:>11.4f}')
print('   (손익분기% = |순손익| / 진입 금액. 고정 시장에서는 이것이 곧 왕복 비용이고,')
print('    「그 관망이 틀리려면 그 창 안에 최소 얼마가 움직였어야 하나」의 답이다.)')
print('   (숏은 대차수수료 0 으로 계산된다 — 길게 들면 기록이 시장보다 유리해진다.)')
PY

echo
echo "끝. HTTP 호출 $CALLS 회 · 산출물 $WORK/{tool_log,all,joined}.csv"
echo "   회수는 목록 1 + 전체 CSV 1 = 2회다 (시험 수와 무관)."
