← 목록
기타 2026-07-31 19KB 읽기 17분

멀티계정 관제 아키텍처 — 여러 창 · 여러 브로커 계정 동시/독립 운용

2026-07-31 · KPS 트레이딩 도구 · 설계 + 골격 구현 대상: Tradovate 계정 다수 (APEX 연동 / Lucid 연동 / LifeUp 연동) 이번 산출 범위 = 설계 + 한선씨 골격 + 자체시험. 실주문·신규 인스턴스 기동은 범위 밖(사장님 승인 사항).

0. 현재 도달점 (실측 기준선)

  • 크라우니AI 브라우저(WKWebView) 1인스턴스(:9847) 가 Tradovate 웹에서 LifeUp 5계좌 조작
  • 단일계좌 실증 5거래 완주 2회 / 5계좌 동시 진입·진출 1사이클 성공(진입 5/5 · 진출 5/5 · 잔존 0)
  • 손익 정본 = 브로커 실현손익 델타. 규칙엔진은 3거래마다 4상 고도화
  • 현 규칙셋 수익성 없음 (최근 실증 -$7.50 / 5거래) → 전략도구로 채택/폐기를 판정 가능하게 만드는 것이 이번 설계의 목적 중 하나

1. 인스턴스 모델

크라우니AI 인스턴스 1개  =  창 1개  =  탭 1개  =  브로커 로그인 세션 1개  =  CROWNY_CTL_PORT 1개
        │
        └─ 그 로그인 아래의 계좌 N개(LifeUp 5계좌처럼)는 "계좌전환" 클릭으로 순차 조작

  • 제어서버 계약(정본 /Users/ef/CrownyBrowser/native/crowny-ai-control.m):
/health /navigate /eval /find /click_text /screenshot /read_page /tabs /console
  • env 게이트: CROWNY_CTL=1 또는 CROWNY_CTL_PORT 지정 시에만 리슨(기본 off)
  • CROWNY_CTL_TOKEN 설정 시 X-Crowny-Token 헤더 강제(멀티 인스턴스에서는 인스턴스별 토큰 권장)
  • 런처(정본 /Users/ef/CrownyBrowser/크라우니AI제어.app/Contents/MacOS/launcher)가 이미 멀티 인스턴스에 필요한 요소를 전부 갖고 있다:
  • bash  PORT="${CROWNY_CTL_PORT:-9847}"
      URL="${CROWNY_CTL_URL:-https://trader.tradovate.com}"
      # 같은 포트로 이미 살아있으면 새로 안 띄움(중복 방지)
      export CROWNY_CTL=1 CROWNY_CTL_PORT="$PORT"
      
    런처 수정 없이 CROWNY_CTL_PORT=9848 launcher 형태로 두 번째 인스턴스를 띄울 수 있다. 단 /tmp/crowny-ai-ctl.port 단일 파일에 마지막 포트를 덮어쓰는 부분은 멀티에서 의미를 잃으므로, 포트의 단일 원본은 이 문서의 레지스트리 PSV로 승격한다.

    1.1 왜 인스턴스를 분리하는가 (실측 근거)

    근거내용
    쿠키 단위 세션 충돌Tradovate 웹은 브라우저 쿠키/로컬스토리지 1벌에 로그인 1세션. 같은 WKWebView 인스턴스(공유 데이터스토어)에서 두 번째 계정으로 로그인하면 첫 세션이 로그아웃된다. 계정 2개 동시 유지 불가.
    DOM 계약이 "현재 로그인" 전제브라우저어댑터.한선상태읽기()·계좌전환()li.selected[data-testid^=account-item-]화면 1개를 읽는다. 한 창에 두 브로커를 얹으면 이 계약이 성립하지 않는다.
    세션 4상 판정의 단위세션감시.한선POSITION+MNQ+선택계좌 유무로 티/옴/타/음을 낸다 — 이 판정 자체가 인스턴스 1개 단위다. 멀티 계정을 한 창에 넣으면 경보 대상이 특정 불가.
    실패 격리한 브로커 세션이 만료·리다이렉트돼도 다른 브로커는 영향받지 않아야 한다. 프로세스 분리가 가장 싼 격리.
    확인된 사실: 제어서버는 이미 인스턴스별 포트를 지원하고, 런처는 이미 포트별 중복기동을 막는다. 즉 멀티 인스턴스는 새 기능 구현이 아니라 "관제 계층"만 추가하면 되는 상태다.

    1.2 포트 할당 규약

    • 이 포트들은 로컬 전용 제어포트(127.0.0.1 바인드, 게이트웨이 도메인 아님).
    • crowny-ports.sh check 로 충돌만 확인했고, set(gateway.yaml 변경)은 하지 않았다 — 공유 인프라 무접촉 원칙.
    사장님 승인 후 "로컬 예약" 목적으로 등록할지 결정 필요(§7).

    포트상태(2026-07-31 실측)배정안
    9847사용 중 (PID 94980 CrownyBrowser, 사장님 로그인 세션)LifeUp (기존, 종료·네비게이션 금지)
    9848미등록·비어 있음APEX
    9849미등록·비어 있음Lucid
    9850불가 — monitoring/defence/monitor.crowny.org 점유사용 금지
    9851불가 — churck.crowny.org사용 금지
    9852불가 — crowny-ai.crowny.org사용 금지
    9853~9856미등록·비어 있음예비(4번째~7번째 인스턴스)
    연속 대역이 9850~9852에서 끊긴다. 9847·9848·9849 + 9853~9856 이 실제 가용 집합.


    2. 레지스트리 (단일 원본)

    파일: /tmp/kps/인스턴스.psv — 1줄 = 1인스턴스

    인스턴스ID|포트|브로커|계정군|전략ID|상태|비고
    

    필드의미
    인스턴스IDINST_LIFEUP 등 고유 식별자. 로그·패널·플래그 키
    포트CROWNY_CTL_PORT
    브로커LifeUp / APEX / Lucid
    계정군혼합 팬아웃의 그룹 키(예 LIFEUP5, APEX, LUCID)
    전략ID전략.psv 의 전략 참조. 미배정 가능
    상태4상 — 1=티(가동) 0=옴(대기/미확인) -1=타(정지, 팬아웃 대상 제외) -9=음(무응답)
    비고자유 텍스트(구분자·개행 자동 안전화)
    초기 생성값(파일이 없으면 관제기가 자동 시딩):

    INST_LIFEUP|9847|LifeUp|LIFEUP5|S_기본|1|사장님 로그인 세션 - 종료금지
    INST_APEX|9848|APEX|APEX|미배정|0|미기동 - 사장님 승인 후 런처 기동
    INST_LUCID|9849|Lucid|LUCID|미배정|0|미기동 - 사장님 승인 후 런처 기동
    


    3. 팬아웃 3모드

    모드대상 선정용도
    동시상태≠타(정지) 인 전 인스턴스하나의 시그널을 전 브로커에 동시 진입/진출. 기존 다계좌동시.한선 의 5계좌 사이클을 인스턴스 축으로 한 단계 확장
    독립전 인스턴스를 열거만 하고 명령을 주입하지 않음인스턴스별로 배정된 전략이 각자 판단·실행. 관제기는 헬스·집계·안전정지만 담당
    혼합계정군 == 그룹 인 인스턴스만"APEX 그룹만 청산", "LIFEUP5만 관망" 같은 그룹 단위 지시
    • 실패 격리: 루프는 인스턴스 실패에서 끊기지 않는다. {대상, 성공, 실패} 로 집계 반환 + 인스턴스별 로그.
    • 상태 -1(타/정지)인 인스턴스는 모든 모드에서 자동 제외 → 사장님이 특정 창을 손으로 쓰는 동안 관제 제외 가능.

    실측 드라이런 (2026-07-31)

    [관제팬아웃] 모드=동시 그룹= 명령=ENTER|ALL|매수|지정가|0|SYMBOL=MNQU6
    [관제팬아웃] 대상=3 성공=0 실패=3          ← KJ_LIVE 미설정 → fail-closed 로 전부 거부(의도된 결과)
    [관제팬아웃] 모드=혼합 그룹=LIFEUP5 명령=STATUS|ALL
    [관제팬아웃] 대상=1 성공=1 실패=0          ← 읽기전용 STATUS 는 통과, position=0 확인
    [관제팬아웃] 모드=독립
    [관제팬아웃] 대상=3 성공=3 실패=0          ← 명령주입 없이 열거만
    


    4. 안전 설계 (최우선)

    4.1 실주문 3중 게이트 (fail-closed)

    1. DRY_RUN활성 — 브라우저어댑터 전역, 기본 1. 1이면 주문/청산 실호출 없음(기록만)
    2. KJ_LIVE 환경변수 — "1" 이 아니면 주문계열 명령(ENTER/COPY/CLOSE_POSITION/CANCEL) 자체를 거부
    3. BA_미니차단(심볼) — MNQ 계열 외 전부 차단. DRY_RUN 무관 항상 동작
    읽기 경로(/health, 조회용 /eval)는 게이트와 무관하게 허용 — 무해하고, 안전 판단에 필수이기 때문.

    4.2 전체청산 (최우선 안전 기능)

    전체청산() 은 전 인스턴스를 순회하며:

    1. /health 로 생존 확인 → 죽어 있으면 "무응답" 집계하고 다음 인스턴스로 계속
    2. CLOSE_POSITION|ALL 요청 (게이트 통과 시에만 실호출, 아니면 드라이런 기록)
    3. /evalPOSITION 재판독 → 0 이면 검증통과
    4. 0 이 아니거나 판독 실패(NA)면 인스턴스별 잔존 경보
    잔존 경보는 인스턴스별로 분리한다:
    /tmp/kps/포지션잔존_<포트>.flag     ← 인스턴스별 플래그 (기존 단일 포지션잔존.flag 와 별도)
    
    + 세션감시.한선세션경보_포지션잔존() 재사용(macOS 알림·로그 채널 그대로).

    4.3 세션 4상

    인스턴스별로 T/A/O/U 를 판정해 세션_티_정상(1)/세션_타_만료(-1)/세션_옴_전환중(0)/세션_음_무응답(-9) 로 매핑. 판정 JS는 세션감시.한선 의 것과 동일 계약. (전역 BA_제어포트 를 함수 안에서 갈아끼우면 한선씨 "전역 스칼라 함수내 재대입" 함정에 걸리므로, 관제기는 포트를 인자로 받는 자체 호출 경로를 쓴다.)


    5. 전략도구

    파일: /Users/ef/CrownyDoc/projects/kps-trading-tool/전략도구.한선

    전략 = 규칙셋 + 파라미터 묶음. /tmp/kps/전략.psv

    전략ID|이름|규칙셋경로|목표포인트|손절포인트|쿨다운초|최대거래|일한도센트|상태|비고
    

    • 규칙셋경로기존 M8 규칙 PSV 를 가리킨다(서브5-전략엔진-v4.한선 / 실증실행기.한선 스키마 그대로):
    id|이름|슬롯|비교연산|기준값|가중치|상태|발화수|승기여|패기여|손익합 전략도구는 규칙을 경로로만 참조한다 — M8_규칙파싱 을 재정의하지 않아 파일간 함수명 충돌(하드에러)을 원천 회피.
    • 파라미터는 실증실행기의 하드 상수(EX_목표포인트=12, EX_손절포인트=8, EX_쿨다운초=20, EX_거래캡=5, EX_일목표상한센트=6000)와 1:1 대응 → 전략별로 이 값들을 바꿔 A/B 가능.

    전략평가 — 4상 판정

    입력 = 장부배열(거래ID|시그널|발화IDs|손익센트|손익유효), 손익유효==1 인 줄만 사용(실현손익 델타 미확보분 = 오염원 제외).

    지표산식
    승률승 × 100 / 표본 (정수 %)
    손익합실현손익 델타 합(센트)
    거래당평균손익합 / 표본
    기대값(승합/표본) − (패합/표본)
    MDD누적곡선 최고점 대비 최대 낙폭(센트)
    판정조건
    음(-9) 데이터부족표본 < 10
    타(-1) 폐기손익합 < 0
    티(1) 채택손익합 > 0 그리고 승률 ≥ 45% 그리고 MDD ≤ 손익합×3
    옴(0) 관찰그 외(손익합=0 포함)
    현 규칙셋(-$7.50/5거래)을 이 판정에 넣으면 표본 5 → 음(데이터부족). 즉 "실패"가 아니라 "아직 판정할 표본이 없다"가 정직한 결론이다. 최소 10거래가 판정 기준선.

    모든 나눗셈은 ST_나눔()(절단 나눗셈)으로 — 한선씨 자연반올림(|r|≤|b|/2)으로 음수 손익이 왜곡되는 함정 회피(자체시험으로 검증).


    6. 멘토패널 (설계 + 데이터 계약 — UI 구현은 이번 범위 밖)

    6.1 목적

    사람이 도구 상태를 한눈에 보고 개입하는 화면. 자동화가 사람을 대체하는 게 아니라, 사람이 전체청산·전략교체·인스턴스 정지를 즉시 누를 수 있게 하는 것이 목적.

    6.2 재사용 가능한 기존 자산 (조사 결과)

    crowny-trader:7741 에 이미 KPS 전용 자산이 있다:

    자산경로재사용 판정
    KPS 화면/Users/ef/crowny-trader/public/kps.html (226줄)재사용 가능 — 카드/표 레이아웃 골격 그대로 인스턴스 카드로 전환
    SSE 스트림/Users/ef/crowny-trader/routes/kps.js GET /api/kps/stream재사용 가능kpsClients 집합이 autotrade SSE와 완전 분리돼 있고, ~25s 주석 핑 하트비트로 프록시 idle 끊김·죽은 소켓 정리까지 이미 구현됨
    기존 엔드포인트/api/kps/livelock /edge /condition /journal /nt8 /config /mentor-trade /mentor-edge부분 재사용 — 이미 mentor-* 네이밍이 있어 멘토 개념이 선점돼 있음. 인스턴스 축은 신규 /api/kps/instances 로 추가하는 편이 충돌이 없다
    결론: 멘토패널은 신규 서버를 세우지 말고 crowny-trader:7741 안에 얹는다. 포트 추가 0개.

    6.3 데이터 계약 (JSON)

    관제기가 /tmp/kps/멘토패널.json 을 주기 갱신 → trader 서버는 이 파일을 읽어 SSE 로 밀기만 한다. 폴링 부하 최소화: 브라우저는 폴링하지 않고 SSE 수신만, 서버는 파일 mtime 변화 시에만 브로드캐스트.

    json{
      "갱신시각": 1785475099,
      "인스턴스수": 3,
      "인스턴스": [
        {
          "인스턴스ID": "INST_LIFEUP",
          "포트": 9847,
          "브로커": "LifeUp",
          "계정군": "LIFEUP5",
          "계정수": 5,
          "생존": 1,
          "세션상태": 1,
          "세션라벨": "티(로그인정상)",
          "url": "https://trader.tradovate.com/",
          "포지션": "0",
          "전략ID": "S_기본",
          "상태": 1,
          "최근거래": []
        }
      ]
    }
    

    • 최근거래 = 인스턴스별 /tmp/kps/거래_<인스턴스ID>.psv마지막 3줄(장부 PSV 원문 문자열 배열)
    • 당일실현손익 은 현재 계약에 미포함 — 인스턴스별 일일 손익 누적기가 아직 없기 때문(§8 미해결).
    거짓 0을 넣지 않고 필드를 비워두는 편이 정직하다. 배선 후 "당일실현손익센트" 로 추가.
    • 경보 도 미포함 — 현재는 파일 플래그(포지션잔존_<포트>.flag)가 정본. 패널은 이 파일 존재 여부로 표시하면 되고,
    JSON 필드로 승격할지는 UI 구현 시 결정.

    6.4 갱신 주기 제안

    상황주기
    장중·포지션 보유5초
    장중·무포지션15초
    장외60초 또는 수동
    멘토패널갱신() 1회 비용 = 인스턴스당 /health 1 + /eval 2(세션·포지션). 3인스턴스 기준 9회 로컬 HTTP. 전부 --connect-timeout 2 -m 6 이라 죽은 인스턴스도 최악 2초에 끊긴다.


    7. 사장님이 결정해야 할 항목

    1. 포트 확정 — 9848=APEX, 9849=Lucid 로 확정할지. (9850~9852는 다른 서비스 점유로 사용 불가, 예비는 9853~9856)
    2. 포트 정식 등록 여부crowny-ports.sh set 은 gateway.yaml(공유 인프라)을 건드리므로 실행하지 않았다. 로컬 예약 목적으로 등록할지 결정 필요.
    3. 브로커별 계정 수 — LifeUp은 5계좌 실측 확정. APEX·Lucid는 각 로그인 아래 계좌가 몇 개인지 미확인. 계좌 ID 목록은 인스턴스별로 달라야 하는데 현재 BA_계좌목록LifeUp 5개가 하드코딩돼 있다(§8-1).
    4. 인스턴스 기동 승인 — 새 크라우니AI 인스턴스는 띄우지 않았다. 기동 = CROWNY_CTL_PORT=9848 /Users/ef/CrownyBrowser/크라우니AI제어.app/Contents/MacOS/launcher + 사장님이 직접 브로커 로그인(자동 로그인 시도 금지).
    5. 전략 배정 — 인스턴스별로 같은 전략(동시 모드)인지, 각기 다른 전략(독립 모드)인지. 현 규칙셋은 표본 부족으로 미검증 상태라, 초기엔 한 인스턴스에서만 라이브 + 나머지는 드라이런 병주(shadow) 로 비교하는 편이 안전.
    6. 제어토큰 — 인스턴스가 늘면 CROWNY_CTL_TOKEN 을 인스턴스별로 다르게 줄지(오발사 방지 효과).

    8. 미해결 / 다음 작업

    1. 계좌목록 인스턴스별 분리브라우저어댑터.한선 BA_초기화() 가 LifeUp 5계좌를 하드코딩. 멀티 브로커에는
    /tmp/kps/계좌_<인스턴스ID>.psv 같은 인스턴스별 계좌 파일이 필요하다. (어댑터 수정 필요 — 이번 범위 밖)
    1. 어댑터의 포트 파라미터화BA_제어포트 가 전역 상수 9847. 관제기는 자체 호출 경로로 우회했지만,
    실제 주문 실행을 인스턴스별로 하려면 어댑터가 CTL_PORT 환경변수를 읽거나, 인스턴스별 어댑터 프로세스로 분리해야 한다. 현재 KJ_인스턴스명령() 의 주문 경로는 게이트 통과 시 "위임" 로그만 남기는 미배선 스텁이다(허위배선 방지 — 실호출하는 척하지 않음).
    1. 인스턴스별 일일 실현손익 누적 — 멘토패널 당일실현손익 필드의 전제. 실증실행기의 EX_일일손익센트 를 인스턴스 축으로 확장 필요.
    2. 전략도구 ↔ 실증실행기 배선 — 현재 실증실행기는 파라미터를 파일 상수로 갖는다. 전략.psv 에서 읽어오도록 배선하면 전략 교체가 무재컴파일로 가능.
    3. 레지스트리 4095/문자열 캡 — 인스턴스 수가 수십 개가 될 일은 없어 현재 구조로 충분(캡 무관).
    4. 멘토패널 UI — 이번 범위 밖. §6.2 자산 위에 인스턴스 카드 + 전체청산 버튼부터.

    9. 산출 파일

    파일내용
    /Users/ef/CrownyDoc/projects/kps-trading-tool/관제기.한선레지스트리·전체헬스·관제팬아웃·전체청산·전략배정·멘토패널갱신
    /Users/ef/CrownyDoc/projects/kps-trading-tool/전략도구.한선전략 CRUD + 4상 전략평가(승률·기대값·MDD·거래당평균)
    /Users/ef/CrownyDoc/projects/2026-07-31-멀티계정-관제-아키텍처.md본 문서

    실행

    bashcd /Users/ef/CrownyOS/crownyc
    
    # 관제기 (BA_SKIP_RUN=1 필수 — 어댑터 top-level 배치처리 억제)
    ./hanseonc_high /Users/ef/CrownyDoc/projects/kps-trading-tool/관제기.한선 > /tmp/kj.toau
    env BA_SKIP_RUN=1 KJ_TEST=1   ./crownyc run /tmp/kj.toau   # 자체시험 12/12 (무네트워크)
    env BA_SKIP_RUN=1 KJ_HEALTH=1 ./crownyc run /tmp/kj.toau   # 읽기전용 전체헬스
    env BA_SKIP_RUN=1 KJ_PANEL=1  ./crownyc run /tmp/kj.toau   # 멘토패널.json 갱신
    
    # 전략도구 (독립 — 네트워크·브라우저 무접촉)
    ./hanseonc_high /Users/ef/CrownyDoc/projects/kps-trading-tool/전략도구.한선 > /tmp/st.toau
    env ST_TEST=1 ./crownyc run /tmp/st.toau                   # 자체시험 10/10
    

    검증 결과 (2026-07-31 실측)

    항목결과
    관제기 컴파일CROWNY_STRICT=1 경고 0, 49915 큐브
    관제기 자체시험12 / 12 PASS
    전략도구 컴파일CROWNY_STRICT=1 경고 0, 11384 큐브
    전략도구 자체시험10 / 10 PASS
    라이브 읽기전용 헬스9847 생존 · 세션 티(로그인정상) · position=0 / 9848·9849 음(무응답, 미기동)
    실주문0건 — DRY_RUN=1 · KJ_LIVE 미설정으로 주문계열 전부 fail-closed 거부 확인
    9847 인스턴스종료·네비게이션 없음(POST /health, 조회용 /eval 만 호출)