거래 탭 사용법¶
거래 탭은 실시간 운용 화면입니다. 현재 상태 확인과 즉시 액션(수동 주문/취소/전략 제어)에 집중합니다.
이 화면은 무엇을 하는 곳인가요?¶
- 대시보드: 계좌 전체 흐름 확인
- 거래 탭: 지금 조치해야 할 종목/전략 관리
처음 거래 탭에 접속했을 때¶
전략이 없는 상태에서 거래 탭을 열면 인라인 안내 카드가 전략 테이블 위에 표시됩니다. 카드는 스튜디오에서 전략을 먼저 작성하고 백테스트로 검증한 뒤 이 탭에서 등록하도록 안내합니다. 전략을 등록하면 카드가 자동으로 사라집니다.
처음 전략 운영을 시작할 때¶
거래 탭에서 전략을 처음 실행하는 순서입니다.
- 업비트 인증 확인: 설정 탭 → 업비트 인증 섹션에서 access key / secret key가 등록돼 있는지 확인합니다. Upbit는 별도의 paper-trading 환경이 없으므로 처음에는 잔고가 작은 별도 계정으로 시작해 시세 흐름·알림·체결 통보를 검증한 뒤 본 자본금에 단계적으로 적용하는 것을 권장합니다.
- 전략 추가: 우측 편집 패널에서 새 전략을 만들거나, 스튜디오에서 가져온 전략을 선택합니다.
- 종목 설정: 전략에 감시할 종목을 추가합니다(예:
KRW-BTC,KRW-ETH). - 전략 활성화: 전략 테이블에서 토글을 켭니다. 활성화 즉시 해당 종목 감시가 시작됩니다.
- 상태 확인: 하단 운영 스트립에서 활성 전략 수와 엔진 상태를 확인합니다.
처음에는 작은 자본금으로 시작하세요
Upbit는 별도의 paper-trading 환경이 없어 실거래 키를 등록하면 즉시 실제 주문이 나갈 수 있습니다. 처음에는 잔고가 작은 별도 계정으로 시작해 전략 동작을 검증하고, 익숙해진 뒤에 자본금을 키우세요. 운영 전 백테스트는 스튜디오 백테스트 패널에서 충분히 검증한 뒤 활성화합니다.
화면 구성¶
전략 테이블 (상단)¶
전략 단위 상태를 확인하고 제어합니다.
- 켜기/끄기
- 편집/삭제
- 보유/주문/손익 요약
전략을 선택하면 아래 종목 테이블이 해당 전략 기준으로 좁혀집니다.
종목 테이블 (하단)¶
종목 단위로 상태를 확인하고 액션을 실행합니다.
- 보유수량, 현재가, 손익, 대기주문
- 행 클릭 시 상세 패널 열기
- 상세에서 차트/주문 이력/정책 상태 확인
보기 모드¶
전체 보기¶
- 모든 전략 종목 + 미귀속 종목을 함께 표시
전략별 보기¶
- 선택한 전략 종목만 표시
미귀속 종목¶
미귀속은 현재 어떤 전략에도 연결되지 않은 종목/주문입니다. 미귀속 종목이 쌓이면 손익 추적이 부정확해질 수 있습니다.
예를 들어, 업비트 앱·웹에서 직접 매수한 코인이나 Quantiq 외부에서 처리된 주문이 여기에 표시될 수 있습니다.
- 자동매매 전략 대상이 아닙니다.
- 사용자가 수동으로 확인·정리해야 합니다.
- 전체 보기에서만 확인하세요.
수동 액션¶
수동 매수/매도¶
종목 행의 작업 메뉴에서 실행합니다.
주문 전 확인 항목:
- 현재가
- 보유수량/가능수량
- 시장가/지정가
- 주문수량
대기 주문 취소¶
- 종목 상세 패널의 주문 이력에서 취소합니다.
- 이미 체결/거부/실패된 주문은 취소할 수 없습니다.
- 취소 요청은 Upbit
DELETE /v1/order(uuid)로 라우팅됩니다. 브로커가 해당 주문을 모르는 경우(uuid 만료/이미 정리됨) 로컬 상태도 자동으로 종료됩니다. - 엔진을 정지/일시정지하면 활성 주문도 같은 Upbit 취소 경로로 일괄 취소를 시도합니다.
차트 사용¶
- 종목 상세에서 차트를 확인할 수 있습니다.
- 전략 종목은 전략 오버레이/마커를 함께 봅니다.
- 미귀속 종목은 기본 시세 차트 중심으로 확인합니다.
전역 UI¶
탑바¶
- 엔진 상태
- 브로커 연결 상태 — 키 저장 여부에 따라
브로커 연결/브로커 미연결배지가 표시됩니다(업비트 키를 저장하면 즉시 배지가 켜지며 레거시 KIS 설정은 더 이상 영향을 주지 않습니다 — #87). 브로커 미연결 상태에서 주문이 시도되면 화면 상단에브로커 연결 끊김배너가 떠 설정 탭에서 인증 정보를 확인하라고 안내합니다. - 서버 실시간 연결 상태 표시 — 서버 실시간 연결 여부를 표시합니다. 회색 점은 아직 연결 전이거나 연결 중, 주황색 배지는 연결 끊김을 나타냅니다.
- 활성 전략 수
하단 운영 스트립¶
- 엔진 상태
- 추적 보유/평가금액/손익
- 대기주문 수/오늘 체결
거래 로그 / 알림¶
- 거래 로그: 최근 주요 이벤트(주문, 체결, 전략 상태 변경 등) 이력을 확인합니다.
- 즉시 알림(토스트): 화면 하단에 짧게 표시되는 알림으로, 주문 접수·체결·경고·연결 오류 등이 실시간으로 나타납니다.
전략 시작 시 과거 봉 로딩¶
전략을 활성화하거나 엔진을 시작하면 과거 캔들 데이터를 로딩한 뒤 DSL 평가가 시작됩니다. 로딩이 완료되기 전 틱이 도착하면 해당 틱은 조용히 건너뜁니다(알림 없음). 데이터 로딩이 완료되면 자동으로 재개되므로 별도 조작이 필요하지 않습니다.
엔진 시작/중지가 의미하는 것¶
엔진의 실행 / 정지는 거래(주문 발사) 권한 스위치입니다. 런타임 서버 자체나 데이터 수집 인프라와는 분리되어 동작합니다.
- 엔진 실행 중: 전략 평가 결과에 따라 실제 주문이 브로커로 발사됩니다.
- 엔진 정지(STOPPED): 주문 발사만 차단됩니다. 다음 인프라는 계속 가동을 유지합니다.
- 캔들 boundary timer(가격/봉 갱신)
- 스크리너(
@screener) 평가 및 모니터링 결과 갱신 - 캔들 사전 워밍업
- 실시간 시세 WebSocket과 stale ticker 갱신을 위한 REST fallback
- DSL 평가, 차트 오버레이 갱신
- 따라서 탑바와 하단 운영 스트립의 엔진 상태 라벨은 정지일 때
정지 · 스크리너·모니터링 가동 중형태로 표시됩니다. "정지" 단독 표기가 아니므로 사용자는 데이터 수집이 멈췄다고 오해할 필요가 없습니다. - 엔진을 정지해도 런타임 서버는 그대로 살아있고 운영 화면은 유지됩니다(엔진 정지 → "런타임 기동 대기 중" 화면으로 회귀하던 회귀는 차단되어 있습니다).
- 새 키 저장, 자동 업데이트 적용, 백테스트 시작 같은 일부 동작은 엔진 실행 중에는 차단되며 정지 상태에서만 허용됩니다.
- 엔진 시작/중지 요청 후에는 탑바 버튼이 잠시 진행 중 라벨로 바뀝니다. 네트워크 지연 등으로 상태 확인이 20초가 지나도 갱신되지 않으면 "엔진 상태 확인이 지연되고 있습니다. 잠시 후 자동으로 갱신됩니다." 안내 토스트가 표시됩니다. 안내 토스트가 떠도 동일 제어 버튼은 응답이 도착하거나 엔진 상태가 정상 전이될 때까지 잠금 상태를 유지합니다(중복 클릭으로 인한 상태 충돌을 막기 위함). 다른 컨트롤은 그 동안에도 정상 사용할 수 있습니다.
- 엔진을 중지하면 그 시점에 남아 있던 미체결 주문을 자동으로 취소 요청합니다. 결과는 즉시 토스트로 안내됩니다.
- 미체결이 없는 경우: 토스트는 뜨지 않습니다. 거래 로그에 "엔진 중지 시점 정리할 미체결 주문 없음" 항목이 남아 이력으로 확인할 수 있습니다.
- 모든 취소 요청이 수락된 경우: "미체결 주문 N건 취소 요청을 보냈습니다." (info)
- 일부가 거절된 경우: "미체결 주문 N건 취소 시도 — M건이 거절되었습니다. 알림 패널에서 확인하세요." (warning) — 거절 사유는 우상단 알림 벨 패널에서 상세 확인합니다.
전략 편집 패널¶
- 전략 생성/편집은 우측 패널에서 진행합니다.
- 새 전략은 보통 비활성 상태로 저장 후, 필요 시 테이블에서 활성화합니다.
- 전략 종목 구성은 패널에서 직접 입력하거나 종목 검색(KRW + BTC + USDT 마켓 전체)으로 추가합니다.
- 손절/익절 값은 퍼센트 기준으로 입력합니다. 예:
3은 3%를 의미합니다. - 손절(
stop_loss_pct)과 익절(take_profit_pct) 규칙은 항상 청산(liquidate) 액션으로만 동작합니다.
KIS 조건식 / 관심종목 그룹 / 브로커 연결 테스트 제거 (PR-2b, 2026-05-01)
국내주식(KIS) 시절에 있던 조건식 가져오기 버튼과 관심종목 그룹 가져오기 모달은 Upbit 피벗에서 제거되었습니다(PR-1). 업비트 종목 구성은 검색창(/api/symbols/search)에서 코드(KRW-BTC 등) 또는 한글명(비트코인 등)을 입력해 추가하거나, 스크립트의 @screener / @universe.metric 데코레이터로 cross-ticker 조건을 직접 표현합니다.
#32 PR-2b 추가 정리 (2026-05-01): 백엔드 KIS 전용 라우트(/api/conditions, /api/user-int-symbol-groups*, /api/broker/test, /api/broker/test-rate-limit)를 server-side에서 일괄 제거했습니다. 브로커 연결 테스트는 설정 탭 > 업비트 인증의 "연결 테스트" 버튼 (/api/upbit/test)을 사용하세요. AI 어시스트(LLM Studio)의 KIS 조건식·관심종목 그룹 조회 툴(condition_inventory_lookup, user_int_symbol_group_lookup 등)은 빈 결과를 반환하도록 변경되었습니다 — 업비트 환경에서는 @screener/@universe.metric 데코레이터로 조건을 표현하세요.
고급 리스크 룰 위치¶
- 계좌 단위 고급 리스크 룰은 전략 패널이 아니라 설정 탭 > 리스크 관리에서 관리합니다.
- 계좌 리스크 룰은
일일 손실 한도,누적 손실 한도와 액션(주문 차단,엔진 중지,알림만)을 기준으로 동작합니다. rule.*계열 주문(손절·익절 등)은 시장가로만 실행됩니다.trailing_stop은 DSL 전용 기능(rule.trailing_stop)이며, 설정 탭 리스크 룰에서는 사용할 수 없습니다.
시그널 전용 모드¶
라이브 시장 데이터로 전략을 실행하되 실제 주문은 발사하지 않고 시그널만 관찰하고 싶을 때 사용합니다. 백테스트(과거 데이터, 시간 압축)와 라이브 시작(실시간 데이터 + 실제 주문) 사이의 중간 검증 단계로 활용할 수 있습니다.
시작 방법¶
탑바의 엔진 시작 버튼 오른쪽 ▼ 을 클릭해 드롭다운을 열고 시그널 전용 시작을 선택하세요.
- 엔진이
시그널 전용모드로 시작됩니다. - 탑바 배지에 시그널 전용 표시가 나타납니다.
- 시그널이 발생하면 알림은 정상적으로 전송됩니다.
동작 차이¶
| 라이브 시작 | 시그널 전용 시작 | |
|---|---|---|
| 시장 데이터 | 실시간 | 실시간 |
| DSL 평가 | 정상 | 정상 |
| 알림 | 발송 | 발송 |
| 주문 발사 | O (실제 주문) | X (가상 체결만) |
| 잔고/포지션 영향 | O | X (격리) |
| entitlement | can_live 필요 |
모든 사용자 허용 |
가상 체결 격리¶
시그널 전용 모드의 가상 체결(is_virtual=true, fill_mode=signal_only)은 실제 주문 흐름과 완전히 격리됩니다.
- 포지션, 잔고, PnL, 리스크 가이드는 가상 체결에 의해 변경되지 않습니다.
- 가상 체결 기록은 거래 내역에 쌓이지만 실제 계좌에 반영되지 않습니다.
언제 쓰나요?¶
- 새 전략의 시그널 타이밍·빈도를 실제 시장에서 관찰하고 싶을 때
- 자본 위험 없이 알림·체결 플로우가 올바른지 확인하고 싶을 때
- 라이브 시작 전 최종 드라이런 단계
백테스트와 시그널 전용의 차이
백테스트는 과거 데이터를 시간 압축해 빠르게 돌려봅니다. 시그널 전용은 지금 이 순간 실제 시장 데이터를 기반으로 전략이 어떤 시그널을 내는지 실시간으로 확인합니다. "지금 시장에서 내 전략이 언제 BUY를 부르는지" 보고 싶을 때는 시그널 전용 모드가 더 적합합니다.
24/7 운영 정책¶
Upbit는 24시간 거래되는 거래소이므로 KRX(국내주식)의 장 시작·마감 시각이 적용되지 않습니다.
- 엔진은 항상 동작합니다 — 야간/주말에도 신호가 발생하면 그대로 평가됩니다.
- 사용자 결정 BUY/SELL은 KST 특정 시각으로 자동 차단되지 않습니다. 이전 버전의
15:20 이후 주문 차단게이트는 #29에서 제거되었습니다. - 시간대별 정책이 필요하면 DSL 안에서
market.time(KST"HH:MM") 문자열 비교로 사용자가 직접 정의하세요. 예:if market.time > "23:00": hold(tag="late_night"). - 한 번 청산(release)된 종목은 같은 전략에서 60분간 자동 재진입 대상에서 제외됩니다 (
RELEASE_COOLDOWN_MINUTES = 60고정). 즉시 다시 잡고 싶다면 종목을 수동으로 다시 추가하세요. - 봉 마감 타이머는 24시간 동작합니다 — 거래량이 0인 시간대에도 분/시간 단위 봉이 자동으로 마감되어
barstate(scale).is_confirmed가 정확히 잡힙니다. - DSL 표면 변화:
rule.close_before(minutes)장마감 청산 helper와market.minutes_until_close/market.minutes_since_open필드는 제거되었습니다.
업비트 IP 등록 안내 모달 (no_authorization_ip 401)¶
업비트 Open API는 사용자가 등록한 공인 IP에서 호출된 인증 API만 처리합니다. 등록되지 않은 IP에서 자산 조회/주문/잔고 호출이 발생하면 모두 HTTP 401 no_authorization_ip로 차단되며, Quantiq는 이 상황에서 한 곳에 모인 안내 모달을 띄워 사용자가 직접 복구할 수 있게 합니다.
발동 조건:
- REST 자산/주문/잔고 호출 또는 Private 웹소켓 재연결 중 401 응답의
error.name이no_authorization_ip일 때 자동으로 모달이 열립니다. - 짧은 시간 안에 동일 401이 여러 번 발생해도(예: REST와 WS reconnect가 거의 동시에 실패) 30초 윈도우로 묶여 한 번만 노출됩니다. 사용자가 모달을 닫고 한참 뒤 다시 401이 발생하면 모달도 다시 열립니다.
모달이 안내하는 절차:
- 현재 공인 IPv4 확인 — 모달 상단에 브라우저가 직접 조회한 공인 IP가 표시됩니다. 외부 IP 조회 API 호출이 실패하면 "확인 불가" 메시지가 떠서, 직접
whatismyip.akamai.com또는 네이버 "내 IP" 검색으로 확인하라는 안내가 같이 노출됩니다. - 클립보드 복사 버튼 — 표시된 IP를 그대로 복사해 다음 단계 입력 시간을 줄입니다.
- 업비트 IP 관리 페이지 열기 버튼 —
https://upbit.com/mypage/open_api_management페이지가 외부 브라우저(Electron 빌드 기준) 또는 새 탭(웹 빌드)으로 열립니다. 해당 페이지에서 키별로 등록된 IP 목록을 직접 갱신해야 합니다. - 모바일/카페 환경 경고 — 외부 와이파이·모바일 핫스팟 등 공인 IP가 자주 바뀌는 환경에서는 등록된 IP가 빠르게 무효화되므로 운영 환경으로 권장하지 않습니다. 업비트는 키당 최대 10개까지 IP를 등록할 수 있어, 자주 사용하는 회선 몇 개를 미리 등록해 두는 것이 안전합니다.
- 외부 브라우저 실행 실패 시 폴백 — 데스크톱 빌드에서 OS 기본 브라우저 핸들러가 없거나 실행이 실패하면 모달 내부에 "외부 브라우저를 열 수 없습니다" 안내가 추가로 노출되며, 그 자리에 업비트 IP 관리 페이지 URL을 그대로 복사할 수 있는 버튼이 함께 표시됩니다. 사용자는 그 URL을 직접 복사해 별도 브라우저 주소창에 붙여 넣어 이동하면 됩니다.
복구 후 추가 동작은 필요 없습니다 — IP가 정상 등록되면 다음 호출부터 자동으로 인증이 통과합니다.
운영자 참고:
- 자동 안전모드, IP 헬스 배지, IP 프로필 관리자 같은 부가 보호 로직은 의도적으로 제공하지 않습니다(어차피 모든 인증 호출이 401로 차단되어 추가 차단 로직이 막을 게 없기 때문).
- QA/UI 미리보기가 필요한 환경에서는
QUANTIQ_DEV_UPBIT_IP_MODAL=true환경 변수로 런타임을 띄우면POST /api/v1/runtime/_dev/upbit-ip-blocked트리거가 활성화되어 모달을 강제 표시할 수 있습니다(production 빌드에서는 기본 비활성).
브로커 모드 (업비트 단일)¶
런타임은 업비트 broker stack 단일 owner로 동작합니다. 사용자가 키를 저장했는지에 따라 주문 라우팅이 활성화됩니다.
KRX 24/7 시장 시간 잔재 제거 (PR-2c-γ, 2026-05-04)
이전까지 백엔드/프론트엔드에 남아 있던 KRX 정규장(09:00~15:20) 기반 시간 표면이 모두 제거되었습니다.
- 백엔드:
console_chart_utils의is_market_open/regular_order_session_gate/minutes_until_market_close/filter_market_hours/is_trading_day헬퍼와 KRX 거래일/장시간 상수가 모두 spec-out 되었습니다. 수동 주문 핸들러의 09:00~15:20 컷오프 게이트도 제거되어 24시간 어느 시점에서도 주문이 발행됩니다(엔진 페이즈/리스크 게이트는 그대로 유효). - 응답 페이로드:
/api/state와/api/aux응답에서market_status필드("장중"/"장 전"/"장 마감"/"휴장"라벨러)가 제거되었습니다. 클라이언트는 더 이상 이 필드를 기대하지 않습니다. - 운영 스트립: 운영 화면 하단 스트립의 "장 상태" 컬럼이 사라졌습니다. Upbit는 항상 거래되므로 별도 라벨이 필요 없습니다. 탑바의 작은 "장중/장 마감" 라벨도 함께 제거되었습니다.
- 개발 모드 시간 강제:
QUANTIQ_DEV_MARKET_OPEN=true환경변수로 dev 환경에서 시간을 강제 장중으로 오버라이드하던 블록이 제거되었습니다. 테스트는set_time_override(datetime)헬퍼로 직접 시계를 freeze 합니다.
이로써 KIS 한국투자증권 시장 시간 가정이 백엔드/프론트엔드/응답 계약에서 모두 사라졌습니다. 잔여 KIS SSOT spec mop-up은 PR-2c-δ(예정)에서 진행됩니다.
KIS 표면 spec-out 완료 (PR-2c-β3, 2026-05-03)
이전 KIS 한국투자증권 시절의 백엔드/프론트엔드 잔여 표면이 모두 제거되었습니다.
- 백엔드:
POST /api/credentials(KIS 키 저장 deprecation stub) +POST /broker/mode(paper/live 모드 전환 deprecation stub)가 라우터에서 사라졌습니다. 이전에는 410 응답을 반환하는 placeholder였습니다. - 설정 탭: 증권사 인증 섹션(KIS HTS ID / app_key / app_secret / 실전 계좌번호 / 모의투자 계좌번호 입력 폼)이 사라졌습니다. 인증은 업비트 인증 섹션 한 곳에서만 처리합니다.
- 탑바: 거래 모드(모의계좌/실전계좌) 전환 토글 버튼이 제거되었습니다. Upbit는 별도의 paper-trading 환경이 없어 등가 기능이 없으며, 운영 검증은 백테스트 + 작은 자본금으로 시작하는 흐름으로 대체됐습니다.
- 백테스트 패널: KIS 시절 분봉 12개월 retention 경고가 사라졌습니다(Upbit는 동일 제한이 없음). 분봉 시작일 입력에서
min제약도 제거됐습니다. - 알림 모달: KIS rate-limit 안내 모달(
KisRateLimitNoticeModal)과 설정 탭 안내 박스(KisRateLimitInfoBox)도 모두 제거됐습니다.
이로써 사용자가 보는 표면에는 KIS 흔적이 남아 있지 않습니다. 잔여 KIS 관련 SSOT 문서/cloud docs 정리는 PR-2c-δ(예정)에서 진행됩니다.
Paper/live 토글 잔재 정리 (#85, 2026-05-04)
KIS 시절의 paper/live 토글은 PR-2c-β3에서 UI/라우트가 사라졌지만, 백엔드 잔재(EngineMode.PAPER, BrokerStateModel.mode/is_paper, config["upbit"]["is_paper"], Settings의 "모의 거래용 키 (paper)" 체크박스, _active_account_no의 virt_account_no 분기)가 남아 있어 #85에서 일괄 제거되었습니다. Upbit는 별도 paper 키 발급 환경이 없어 paper/live 구분 자체가 의미 없습니다.
사용자가 직접 마주하는 변화:
- 엔진 시작 동작:
엔진 시작버튼은 항상 live 모드로 시작합니다. 모드 선택 UI(슬라이더/드롭다운/페이로드)는 모두 사라졌습니다. 클라우드 구독/라이선스가 주문을 차단하면현재 구독/라이선스에서는 주문을 시작할 수 없습니다메시지로 거부됩니다(이전엔현재 구독/라이선스로는 실전투자를 시작할 수 없습니다). - 설정 탭 — 업비트 인증: "모의 거래용 키 (paper)" 체크박스가 사라졌습니다. 저장 페이로드도
is_paper필드를 받지 않습니다. 업비트 키 한 종류만 등록하면 됩니다. - 설정 탭 — 권한 카드: "실전투자 권한" 카드가 "주문 권한"으로 라벨 변경되었습니다. 안내 문구도
현재 라이선스로 live mode를 사용할 수 있습니다→현재 라이선스로 주문을 발사할 수 있습니다로 정합화됐습니다. cloud subscription의can_liveentitlement 자체는 변하지 않습니다(서버 schema 그대로). - broker 상태 응답 (
/api/state.broker,/api/aux.broker):mode필드와is_paper필드가 사라지고configured/connected두 필드만 남았습니다.broker.mode === 'live'같은 클라이언트 분기는 모두 항상 true였던 셈이며, 새 클라이언트는 이 두 필드를 사용하지 않습니다.
라이브 시장 데이터로 시그널만 받고 실제 주문은 발사하지 않으려면 시그널 전용 시작 옵션을 사용합니다(#94). 자세한 내용은 아래 시그널 전용 모드 섹션을 참고하세요.
관련 문서: app-overview.md(앱 화면 구성), execution-model.md(실행 모델).
KIS 잔재 표면 정리 (PR-2c-α, 2026-05-03)
탑바의 브로커 연결 배지는 이전 KIS 연결 / KIS 미연결 라벨에서 일반화된 브로커 연결 / 브로커 미연결로 정리되었습니다. 연결 끊김 알림 카탈로그도 kis_disconnected / kis_reconnected / kis_auth_failed / kis_rate_limited 4종을 제거하고, 폴링 기반 브로커 연결 상태 배너용 단일 broker_disconnected 코드로 대체했습니다 (배너 본문은 "브로커 API와의 연결이 끊어진 상태입니다." + "설정에서 인증 정보를 확인하세요."). 프런트엔드 WebSocket 핸들러도 code="broker_disconnected" 알림이 도착하면 connectionStatus.broker를 즉시 disconnected로 동기화해, 폴링과 WS 둘 중 빠른 신호로 배너가 켜집니다. 온보딩 토스트와 설정 탭 상단 안내도 KIS 한국투자증권 API 키 표현에서 업비트 키(access key / secret key) 안내로 바뀌었습니다. 백엔드 docstring/주석의 KIS 잔재(약 20개 파일)도 함께 정리되었으며, KIS broker 폼/KisRateLimitNotice 모달//api/credentials 410 stub 정리는 PR-2c-β에서 일괄 처리됩니다.
KIS broker layer 제거 (PR-2b'-δ, 2026-05-02)
이전까지 런타임은 KIS와 업비트 두 broker stack을 동시에 준비하고 키 저장 여부에 따라 fallback했습니다. KIS broker 본체(5개 모듈 + KISClientService)는 #32 PR-2b'-δ에서 제거되었으며, 다음 항목도 함께 spec-out 되었습니다.
POST /api/credentials(KIS 키 저장 endpoint) — 업비트 키는POST /api/upbit/credentials(아래 절차)로만 저장합니다.POST /broker/mode의 paper/live 모드 전환 — 업비트는 별도paper환경이 없으므로 의미 변경 예정(PR-2c).- KIS HTS 조건검색식 자동 새로고침 path(
refresh_all_conditions) — 업비트 환경에서는@screener데코레이터가 동등 역할을 담당합니다. - KIS 체결통보 단일 connection — 업비트 Private WS dispatcher(
myOrder채널)가 단일 owner. - 텔레그램 봇의 "위탁계좌 보유 포지션 import" 액션 — KIS-shape
fetch_balance/output1응답에 의존했으며, 업비트 동등 import 흐름은 #70에서 정의 예정. 현재는 안내 메시지를 반환합니다. /api/state응답의 계좌 스냅샷(account_summary/positions일부) — KIS-shape_refresh_account_cache_from_brokerpath가 사라져 과도기에는 비어 있을 수 있습니다. 업비트get_accounts()기반 동등 구현은 #70 범위.- 전략 저장 시 KIS 시장 segmentation 기반 종목 사전 검증(ETN/ELW/SPAC/KONEX 등) — #70 PR-B에서 통째 제거. 전략 우측 패널의 "위험 종목 필터링" 섹션도 프론트엔드에서 제거됨. 거래 대상은 전략 편집 패널의 거래 대상 섹션에서 두 모드 라디오 —
선택한 종목만/제외한 종목 외 모두— 중 하나를 골라 직접 지정합니다 (둘을 동시에 입력하지 않으며, 모드를 토글하면 다른 쪽 입력값은 그대로 보존돼 다시 돌아와도 복원됩니다). 전략 활성화 여부와 무관하게 ranking 평가의 후보 풀(universe.in_top등)은 KRW 마켓 전체로 유지되므로,선택한 종목만모드에서도 ranking 조건이 KRW 전체 기준으로 정상 동작합니다. - 워크스페이스 row의
change_pct(등락률) 기준 시가 수정 (#69) — 기존에는 캔들 캐시에서 가장 오래된 캔들의 open 값을 사용했습니다(1T 캐시 200개이면 ~3.3시간 전 시가, 1D이면 수일 전). 현재는 가장 최신 캔들의 open 값을 사용합니다. 1D 캔들이 있으면 현재 24시간 봉 시가(오늘 시장 시작 시점), 분봉만 있으면 마지막 분봉 시가.change_pct와change_abs값이 이전보다 더 최근 기준을 반영합니다.
작동 방식:
- 업비트 키가 저장되어 있으면 모든 주문은 업비트 경로(
UpbitBroker)로 흐릅니다. - 키가 미저장이면 주문 시도는 즉시
BROKER_NOT_CONFIGURED로 거절되며, 거래 탭에 동일한 사유 코드가 노출됩니다. 키를 저장하면 다음 주문부터 정상 라우팅됩니다(런타임 재시작 불필요). - mid-session에 키를 저장하거나 변경하면 다음 주문 시도에서 새 키가 적용됩니다. 이미 진행 중인 주문은 기존 경로로 마무리됩니다.
- mid-session 활성화 (#96 PR-B): 런타임 시작 시 키가 미저장이었더라도 설정 탭에서 키를 처음 저장하면 즉시 다음이 활성화됩니다(런타임 재시작 불필요): (1) 업비트 Private WebSocket 체결 통보 수신, (2)
universe.*DSL 표면과@universe.metric사용자 정의 메트릭 평가, (3) 이미 enabled인 전략의 메트릭 자동 등록. 활성화는 첫 저장 시점 1회만 일어나며 (이후 키 회전은 바로 broker 경로에만 반영) 활성화 자체가 실패해도 키 저장은 성공으로 응답합니다 — 다음 저장에서 자동 재시도. 활성화가 실패한 경우 런타임 로그에upbit activation hook failed또는post-activation strategy metric sync failed메시지가 남습니다.
업비트 모드 부가 동작:
universe.*DSL 표면(universe.in_top,universe.metric등)과@screenercadence freeze는 업비트 모드에서만 동작합니다. 사용자 정의 metric은 전략별로 등록되며, 등록이 없을 때는 백그라운드 metric sweep 루프가 cheap한 idle 상태로만 돕니다.- metric sweep 폴링 주기는 환경 변수
QUANTIQ_METRIC_SWEEP_INTERVAL_SEC(기본 1.0초)로 조정할 수 있습니다. 일반적으로 기본값을 유지합니다.
업비트 키 등록 / 연결 테스트 (설정 탭):
- 설정 탭 → "업비트 인증" 섹션: 별도 Section으로 표시됩니다. 키가 저장되어 있지 않으면 "미등록" 상태이고 클릭해 펼쳐야 합니다.
- Access Key / Secret Key 입력: 업비트 Open API 페이지에서 발급받은 두 키를 입력하고 "저장" 버튼을 누릅니다. 키는 로컬에서 Fernet 대칭키로 암호화되어 보관되며 클라우드로 전송되지 않습니다. 엔진이 실행 중이면 거절되므로 키를 변경할 때는 먼저 엔진을 중지하세요. 둘 중 하나라도 비어 있으면 저장이 거절됩니다("Access Key와 Secret Key가 모두 필요합니다") — 빈 키로 저장이 통과되면 모든 주문이 401로 실패하기 때문입니다. 업비트는 별도 paper 키 발급 환경이 없어 모의 거래용 토글은 제공하지 않습니다(#85).
- 연결 테스트 버튼:
GET /v1/accounts로 업비트 인증을 검증합니다. 응답이 정상이면 "업비트 연결 성공 (계좌 N건)"이 표시됩니다. 실패 시 다음과 같이 안내합니다: - 공인 IP 미등록: "등록되지 않은 공인 IP에서 호출되었습니다" 메시지 + 자동으로 IP 안내 모달이 함께 열립니다(이전 섹션 참조). 모달의 절차에 따라 IP를 등록한 뒤 다시 테스트하세요.
- API 키 오류: "API 키가 잘못되었거나 만료되었습니다". 업비트 Open API 페이지에서 키 상태를 다시 확인하고 필요 시 새 키를 발급받으세요.
- 타임아웃 / 네트워크 오류: 일시적 장애일 가능성이 높으므로 잠시 후 다시 시도합니다.
- 저장 후 동작: 저장 즉시
upbit_service.clear_cache()가 호출되어 다음 주문부터 새 키로 라우팅됩니다(런타임 재시작 불필요).
운영자 참고:
- 응답 페이로드에서
access_key/secret_key값은 항상 마지막 4자리만 노출되도록 redaction 모듈이 마스킹합니다(설정 화면 새로고침 시 화면에 마스킹된 값이 보이는 것은 정상 동작). - 자격증명 저장 시 폼이 마스킹된 값을 그대로 다시 보내는 경우가 있어, 백엔드는 마스킹된 값이 들어오면 기존 키를 그대로 보존합니다(필드를 비워서 다시 저장하지 않는 한 키가 사라지지 않음).
전략 활성화와 universe.* metric 자동 등록¶
@universe.metric 데코레이터로 등록한 사용자 정의 metric은 전략을 저장 / 활성화 / 비활성화 / 삭제할 때마다 엔진과 자동으로 동기화됩니다.
작동 방식:
- 전략을 활성화(enabled=true) 하고 그 스크립트에
@universe.metric정의가 있으면, 다음 스윕 사이클부터 엔진이 universe의 모든 ticker × 등록 metric을 refresh 주기로 평가합니다.universe.in_top/universe.metric/universe.rank같은 호출은 그 캐시를 즉시 사용합니다. - 전략을 비활성화 / 삭제 하면 등록이 자동 해제되고, 다음 스윕 사이클부터 해당 전략의 metric은 평가되지 않습니다.
- 스크립트를 수정해 새 metric을 추가하거나 제거해도 저장 시점에 자동 reconcile됩니다.
- 컴파일 오류가 있는 전략은 등록되지 않으며, 다른 전략의 metric 등록에는 영향이 없습니다.
scope 결정 규칙(어떤 ticker × metric을 평가할지):
- 전략에
symbols가 명시되어 있으면 그 명시 리스트가 그대로 scope입니다(예:["KRW-BTC", "KRW-ETH"]). KRW 외에 BTC, USDT 마켓 ticker(예:BTC-ETH,USDT-BTC)도 명시 가능합니다. symbols가 비어 있으면 자동으로 업비트 KRW 마켓 전체(/v1/market/all에서KRW-접두 ticker만)가 scope이 됩니다. 이는 "거래대금 상위 N" 같은 screener 패턴의 자연스러운 기본값입니다. BTC-/USDT- 마켓은 거래대금이 작은 종목이 많아 빈symbols로는 자동 포함하지 않으므로, 필요하면 명시적으로 나열해야 합니다.- 시장 목록은 KRW + BTC + USDT 전체가 한 번에 캐시되어(상장/상장폐지가 있을 때만
reset_markets_cache()또는 재기동으로 갱신), 차트/심볼 검색 등 다른 표면도 같은 캐시를 공유합니다. 빈번한 전략 저장이 업비트 Quotation rate-limit을 압박하지 않습니다.
전략 활성화와 @screener 자동 모니터링¶
@screener 데코레이터로 작성한 스크리너 스크립트를 가진 전략은 활성화 직후부터 cache.scope에 포함된 모든 ticker가 자동으로 평가됩니다. 별도로 symbols를 지정하지 않아도 매칭된 ticker가 모니터링 패널에 자동으로 추가되고, screen() 호출이 발생한 ticker는 binding이 잡혀 @trade 매수/매도 결정의 대상이 됩니다.
작동 방식:
- 전략을 활성화(enabled=true) 하면 엔진이 그 전략의 cache.scope(빈
symbols시 KRW 마켓 전체)를 cadence 주기(@screener(refresh="..."))로 평가합니다.screen()이 호출되어 state가in으로 전환된 ticker는 자동으로 binding이 생성됩니다 — 사용자가 ticker를 일일이 등록할 필요가 없습니다. release()가 호출되거나 cadence 도래 시screen()이 호출되지 않으면 state가out으로 전환되며,on_drop정책에 따라 binding이 해제됩니다 (deactivate= 보유 포지션이 없을 때 release,ignore= 유지).- 전략을 비활성화 / 삭제 하면 sweep 등록이 해제되고, 다음 사이클부터 평가가 멈춥니다.
@screener미사용 전략(패턴 1·2)은 영향을 받지 않습니다 — 기존처럼symbols에 명시한 ticker만 평가/모니터링됩니다.
refresh cadence:
refresh="tick": 매 sweep 사이클(1초 간격)마다 평가됩니다. 가벼운 지표 1-2개의 screener에 적합합니다. 큰 universe + 무거운 지표 조합에서는 무거울 수 있어 명시적인 interval을 권장합니다.refresh="5s"/"30s"/"1m"/"5m": 마지막 평가 후 지정 시간 경과 시점에 다시 평가합니다. screener 부하를 직관적으로 제어할 수 있습니다.refresh="candle:5T"/"candle:1h"/"candle:1D"등: 봉 마감 시점에 동기화되어 평가합니다 (예:candle:5T는 09:05:00 / 09:10:00 / 09:15:00 ...). EMA·RSI 등 confirmed bar 기준 지표를 사용하는 screener에 적합합니다. 봉이 도달하지 않은 종목은 자동으로 다음 sweep 사이클(약 1초 후)에 재시도되므로 사용자가 따로 처리할 필요가 없습니다. 활성화 시점이 봉 중간이면 첫 평가는 다음 봉 마감에서 시작합니다 (예: 09:02:30에candle:5T전략을 활성화하면 첫 평가는 09:05:00). 미완성 봉으로 평가되어 잘못된 신호가 나가는 일을 막기 위한 동작입니다.
부하 특성: universe-wide 평가지만 cadence-driven이라 refresh="1m"은 tick 대비 60배, refresh="candle:5T"는 300배 적은 평가 횟수로 동작합니다. cache.scope가 KRW 마켓 전체(~200종목)일 때 N개 screener 전략 기준 부하 차이가 유의미합니다.
캔들 워밍업 readiness gate¶
자격증명 등록 직후 또는 재기동 직후에는 KRW 마켓 전체 × 9개 봉 스케일 캔들을 사전 적재하는 캔들 워밍업이 항상 가동됩니다. 이 기간 동안 라이브 평가·주문이 일시적으로 지연되며, UI에는 다음과 같이 노출됩니다:
- 운영 스트립 진행률 (모든 페이지 하단): "전체 종목 워밍업 중" 펄스 배지 +
loaded_pairs / total_pairs (pct%)(KRW 약 200종목 × 사용 스케일 기준 수백~1,800쌍).candle_warmup_progressWS 이벤트가 push될 때마다 자동 갱신되고,pct >= 1이 되면 자동으로 사라집니다. - 전략 PENDING 배지: 활성화된(enabled=true) 전략은 strategy row에 PENDING 배지가 표시됩니다 — "활성화돼 있으나 캔들 데이터 준비 중"임을 알리는 표식입니다. 워밍업 완료 시 추가 사용자 액션 없이 자동으로 평가가 재개되며 배지가 사라집니다.
- Studio 스크리너 테스트: 워밍업 중 sweep 호출은 503 + "워밍업 진행 중" PENDING 카드로 안내됩니다. 카드 안의 진행률 bar는 운영 스트립과 동일한 WS 스트림에 reactive로 묶여 있어 사용자가 새로고침을 다시 누르지 않아도 매 push마다 갱신됩니다(새로고침 버튼은 활성 유지 — 100% 도달 후 한 번 더 눌러 sweep을 시작합니다).
차단되지 않는 경로:
- 백테스트 (
/api/backtest/full-report): 자체 히스토리 빌드 경로를 사용하므로 게이트와 무관합니다. - 유니버스 ticker 메트릭: 캔들 워밍업이 진행 중이어도 WS ticker 프레임(현재가/거래대금) 갱신은 정상 동작합니다 — 운영 화면 종목 표는 그대로 그려집니다.
예상 소요 시간:
- 첫 기동(콜드): 약 200초.
- 재기동(웜): 디스크 캔들 캐시(
UpbitCandleStore) fast-path로 수 초 내 완료.
자세한 내부 동작은 워밍업 모드를 참고하세요.
실시간 체결 통보 (Upbit Private WebSocket)¶
업비트 키가 등록된 상태로 런타임을 시작하면 자동으로 Upbit Private WebSocket(myOrder + myAsset)에 연결되어 체결 통보가 실시간으로 들어옵니다.
- 연결이 끊기고 다시 붙을 때마다
list_open_orders스냅샷과 런타임이 알고 있는 미체결 주문을 비교해, WS down 동안 일어난 FILL/CANCEL은 자동으로 reconcile됩니다. - 비정상 종료된 경우 5초 백오프 후 재연결을 시도합니다.
- IP가 등록되지 않은 환경에서는 WS handshake가 401로 실패하고, REST 401과 동일한 IP 안내 모달이 자동으로 노출됩니다(이전 섹션 참조).
Mid-session 활성화 제한: 엔진을 시작한 뒤에 업비트 키를 처음 저장하면 자동 주문 라우팅은 즉시 적용되지만 Private WebSocket 태스크와 universe 메트릭 평가 인프라는 다음 재시작까지 활성화되지 않습니다. 키를 새로 등록한 직후에는 한 번 런타임을 재기동하는 것을 권장합니다.
업비트 소수점 체결 알림 (피벗 진행 중 한시 제한)¶
업비트 피벗 진행 단계에서는 주문 수량 단위가 정수만 안전하게 처리됩니다. 따라서 KRW-BTC 등 일부 코인에서 소수점 단위(예: 0.5 BTC) 체결이 발생하면, 해당 체결은 자동 반영되지 않고 다음과 같이 동작합니다.
- 해당 주문은 체결 직전 상태로 거래 탭에 그대로 남습니다(자동으로
부분체결/체결완료로 진행되지 않습니다). - 거래 로그에
upbit dispatch: dropped fractional fill ...또는upbit recovery: ... skipped (fractional ...)형태의 에러 로그가 기록됩니다. - 같은 시점의 다른 주문(정수 수량)은 영향 없이 정상 반영됩니다.
권장 대응:
- 정수 수량으로 체결 가능한 마켓(예: KRW-XRP, KRW-DOGE 등 KRW 알트코인)을 주로 사용하세요.
- 소수점 체결이 발생한 경우 거래 로그를 확인한 뒤 거래소 화면에서 실제 체결 내역을 직접 검토하고, 필요 시 수동으로 후속 주문/정산을 처리하세요.
이 제한은 정수 기반 주문 FSM 위에서 임시로 적용되며, Decimal 기반 주문 FSM 리팩터(KIS 제거 단계)가 완료되면 자동으로 해제됩니다.
기억할 점¶
- 거래 탭은 실시간 운용 조치 화면입니다. 전략 설계는 스튜디오에서, 실행 관리는 여기서 합니다.
- 미귀속 종목은 숨기지 말고 주기적으로 확인하세요.
- 주문/취소 이후 상태가 즉시 바뀌지 않으면 잠시 기다린 뒤 새로고침 없이 다시 확인하세요.