crowny-dex 매칭엔진 + 차트/오더북 UI
개요
dex.crowny.org(포트 9402) 트레이딩 탭에 없던 지정가 매칭엔진과 차트/오더북 UI를 구현했다.
서버 로직은
DEX서버.한선(hanseonc_high 고수준), 웹은 기존 정적 SPA 관례대로
web/index.html에 순수 JS로 이식(로직 아닌 UI 이식이라 예외 적용).
무엇을 했는지
1. 매칭엔진 (DEX서버.한선)
- 가격-시간 우선 매칭: 신규 주문 등록 시 반대편 open/partial 주문 중 교차 가능한 것을 라운드마다 최적가(매수=최저매도가, 매도=최고매수가)로 1건씩 찾아 체결(
주문매칭시도()). 체결가=먼저 등록된(메이커) 주문 가격.
- 체결 시: 양측
주문잔량 갱신, 완전체결="filled" / 부분체결="partial". 매수자는 CRD 차감(주문시 자기 지정가 기준 선차감) 후 매칭되면 CRN 수령 + (지정가-체결가)×수량 만큼 CRD 환급. 매도자는 CRN 차감(주문시 선차감) 후 매칭되면 CRD 수령.
- 체결 원장:
거래체결ID/시각/가/수량/매수유저/매도유저 병렬배열(로그캡 3500 도달 시 회전, 기존 기부/스왑 로그와 동일 패턴). 감사로그(trade_fill)도 매수/매도 양측 기록.
- 신규 API:
POST /api/orders/cancel {userId,orderId} — 본인 소유 open/partial 주문만 취소 가능, 잔량에 해당하는 잠금 자금(매수=CRD, 매도=CRN) 환불.
- 신규 API:
GET /api/orderbook — open/partial 주문을 매수(가격 내림차순)/매도(가격 오름차순)로 집계, {price,qty} 리스트.
- 신규 API:
GET /api/trades — 최근 체결 최대 200건, 최신순.
2. 웹 UI (web/index.html renderTrade() 재작성)
- 순수
<canvas> 라인차트(drawChart()) — /api/trades 실데이터만 그림. 체결이 없으면 캔버스 숨기고 "체결 없음" 안내(가짜 데이터 생성 없음).
- 오더북 패널 —
/api/orderbook 3초 폴링, 매수(초록)/매도(빨강) 리스트.
- 내 미체결 주문 패널 —
/api/orders 결과를 클라이언트에서 본인 userId+open/partial로 필터, 각 주문에 취소 버튼(cancelOrder() → /api/orders/cancel).
- 주문 실행 후 즉시체결/부분체결/미체결 피드백 메시지로 구분 표시.
- 기존 CSS 변수(
--상승/--하락/--그린배경/--적갈배경 등)만 사용, 새 색 없음. 트레이딩 탭 진입 시에만 폴링 시작(startTradePolling/stopTradePolling).
VM 함정 (신규 실측)
계속은 예약어(반복문 continue류 추정) — bare 변수명으로 쓰면 "줄 877: 변수명 기대, '계속' 발견" 컴파일 에러. 루프계속으로 우회.
- 매칭 로직은 함수 지역 배열을 반환하지 않고(기존 배열재사용 함정 회피) 전역 주문 배열을 직접 인덱스로 갱신 + 스칼라(총체결수량)만 반환하도록 설계해 회피.
- 오더북 정렬은 버블정렬을 수동 구현(가격+수량 병렬배열 동시 스왑) —
정렬() 내장함수는 단일 배열 오름차순만 지원하고 병렬배열 동반 정렬 기능이 없어서.
체결 실측 (curl, localhost:9821 QA 후 9402 프로덕션 재검증)
- 신규 매도 40 CRN@19.5 등록 → 신규 매수 5 CRN@22 → 즉시 전량체결(filled), 체결가=19.5(메이커가), 매도 잔량 35로 감소, orderbook에 ask 19.5×35만 남음.
- 매도 50 CRN@20 vs 매수 30 CRN@25 → 부분체결: 매수측 filled(30 전량), 매도측 partial(잔량 20). 정산: 매수자 CRD 750 선차감 후 (25-20)30=150 환급→순지출 600, CRN+30. 매도자 CRN 50 선차감 후 CRD+600(=2030). wallet API로 수치 일치 확인.
- 취소: 매도 잔여 20 주문 취소 → CRN 20 환불(950→970) 확인. 재취소 시도 →
{"error":"취소 불가 상태: cancelled"} 422 확인.
- 프로덕션(9402) 재기동 후 동일 스모크(매도10@20 vs 매수10@21) → 즉시 전량체결,
/api/trades에 T-1 기록 확인.
- 웹 UI:
크라우니AI검증.sh shot으로 #트레이딩 탭 스크린샷 — 차트(실거래 2건 라인), 오더북(매도 19.5×35), 주문폼, 미체결주문 패널 모두 정상 렌더 확인(빈 화면 아님).
관련 파일
/Users/ef/crowny-dex/DEX서버.한선 — 매칭엔진/취소/오더북/체결내역 로직 및 라우트 추가
/Users/ef/crowny-dex/web/index.html — renderTrade() 차트+오더북+미체결주문 UI, drawChart/loadOrderbook/loadTrades/loadMyOrders/cancelOrder 함수
/Users/ef/crowny-dex/build.toau — 재컴파일 산출물(배포됨)
/Users/ef/Library/LaunchAgents/org.crowny.dex.plist — 재기동으로 반영
- 스크린샷:
/tmp/dex_trade_shot.png
잔여 이슈
- 체결가 규칙은 "메이커(먼저 등록된 주문) 가격" 단일 규칙만 구현 — 거래량가중평균(VWAP) 등 대안 규칙은 미검토.
- 오더북/체결 API는 인증 없이 전체 공개(기존 서버 전체가 공개 API 패턴이라 일관성 유지, 별도 이슈 아님).
- 배열 4095 상한 crash 리스크는 거래체결 로그도 기존 로그캡(3500) 회전 패턴을 그대로 적용해 완화했으나, 24h+ 장기구동 시 회전 빈도 자체는 검증하지 않음(기존 CLAUDE.md 잔여 이슈와 동일 계열).
- 시장가 주문(market order) 미구현 — 지정가만 지원(작업 지시 범위 내).