카피트레이딩은 개념이 단순합니다. 리더가 사면 팔로어도 산다. 그런데 직접 만들어 보면 어려운 곳은 ‘복사’가 아니라 복사가 실패했을 때의 상태 관리라는 것을 알게 됩니다.

전체 구조

구성 요소는 네 개입니다.

  1. 리더 감시: 리더 계좌의 체결을 실시간으로 받습니다. WebSocket 구독이 적합하고, 폴링은 지연 때문에 진입가 차이가 커집니다.
  2. 배분 계산: 팔로어별 수량을 계산합니다. 여기가 가장 많이 틀립니다.
  3. 주문 실행: 팔로어 계좌마다 주문을 넣습니다.
  4. 상태 대조: 리더와 팔로어의 포지션이 실제로 일치하는지 주기적으로 확인합니다. 이 네 번째가 없으면 며칠 안에 어긋납니다.

수량 환산: 랏을 그대로 복사하면 안 됩니다

리더가 1.0랏을 매수했다고 팔로어도 1.0랏을 넣으면, 자금이 10분의 1인 팔로어 계좌는 즉시 위험해집니다. 실무에서 쓰는 방식은 세 가지입니다.

  • 자산 비례: 팔로어 수량 = 리더 수량 × (팔로어 자산 / 리더 자산). 가장 일반적입니다.
  • 고정 배수: 팔로어가 직접 배수를 지정합니다. 단순하지만 자산 변동을 반영하지 못합니다.
  • 위험 기준: 손절폭과 계좌 자산으로 수량을 역산합니다. 가장 안전하지만 리더가 손절을 설정해야 성립합니다.

어느 방식이든 브로커의 최소 주문 단위와 단위 증분으로 반올림해야 합니다. 계산 결과가 0.037랏이면 대개 0.04 또는 0.03으로 맞춰야 하고, 최소 단위보다 작으면 주문을 건너뛰고 그 사실을 기록해야 합니다. 조용히 버리면 나중에 포지션 불일치의 원인을 찾을 수 없습니다.

함정 1: 부분 체결과 슬리피지

리더가 1.0랏을 한 번에 체결했더라도 팔로어 주문은 나뉘어 체결되거나 다른 가격에 들어갈 수 있습니다. 그래서 “리더와 같은 포지션”이라는 상태는 정확히는 존재하지 않고, 허용 오차 범위 내에서 같다는 상태만 존재합니다.

따라서 시스템은 ‘같다/다르다’가 아니라 ‘오차가 임계치 이내인가’로 판단해야 합니다. 오차 임계치는 설정값으로 두고, 넘으면 경고합니다.

함정 2: 팔로어가 중간에 실패했을 때

팔로어 계좌 하나가 마진 부족으로 진입에 실패하면 어떻게 할까요. 선택지는 세 가지이고, 무엇을 선택하든 미리 정해두어야 합니다.

  • 해당 팔로어만 건너뛰고 나머지는 진행한다(가장 일반적)
  • 전체를 취소한다(리더와의 정합성을 최우선할 때)
  • 가능한 수량만 축소해 진입한다(부분 추종)

정하지 않은 상태로 운영하면 팔로어마다 다른 상태가 되고, 청산 시점에 무엇을 얼마나 닫아야 하는지 알 수 없게 됩니다.

함정 3: 청산 대응

리더가 부분 청산을 하면 팔로어도 같은 비율로 줄여야 합니다. 이때 ‘랏 수’가 아니라 ‘비율’로 계산해야 합니다. 리더가 1.0랏 중 0.3랏을 닫았다면 30%를 닫는 것이고, 팔로어의 0.3랏을 닫는 것이 아닙니다.

또한 팔로어가 수동으로 포지션을 건드릴 수 있습니다. 이 경우 비율 계산의 기준이 깨지므로, 대조 단계에서 ‘예상 포지션’과 ‘실제 포지션’을 비교해 차이가 나면 자동 추종을 멈추고 알리는 편이 안전합니다.

함정 4: 재동기화

서버가 재시작되거나 네트워크가 끊긴 뒤 다시 붙었을 때, 그동안 리더가 낸 주문을 소급 적용해야 할까요. 대부분의 경우 소급하지 않는 것이 맞습니다. 이미 가격이 달라졌기 때문에 뒤늦은 진입은 전략과 무관한 포지션이 됩니다.

대신 재접속 시점에 현재 포지션만 대조하고, 리더에게는 있는데 팔로어에게 없는 포지션은 ‘누락’으로 기록만 남기는 방식이 안전합니다. 이 규칙을 명시하지 않으면 재접속 때마다 예상하지 못한 진입이 발생합니다.

대조 루프는 반드시 만드세요

# 5분마다 리더/팔로어 포지션 대조
leader = positions(LEADER_ID)
for f in FOLLOWERS:
    mine = positions(f["id"])
    expected = {p["symbol"]: scale(p["volume"], f) for p in leader}
    actual   = {p["symbol"]: p["volume"] for p in mine}
    for sym, want in expected.items():
        have = actual.get(sym, 0)
        if abs(have - want) > f["tolerance"]:
            notify(f"[{f['name']}] {sym} 불일치: 기대 {want} / 실제 {have}")

이 루프가 있으면 어긋남을 사람이 발견하기 전에 시스템이 알려줍니다. 카피트레이딩에서 신뢰를 잃는 대부분의 사고는 어긋난 사실을 며칠 뒤에 알게 되는 상황에서 발생합니다.

운영 관점의 준비

팔로어가 늘어나면 계좌 수가 곧 시스템 규모가 됩니다. 계좌 단위 과금 구조면 팔로어 모집이 그대로 비용 증가가 되므로, 계좌 수와 무관한 플랜인지 확인해야 합니다. 요금제에서 기준을 볼 수 있습니다.

실시간 체결 수신은 WebSocket이 전제입니다. 폴링으로 만들면 진입가 차이가 누적되어 팔로어 성과가 리더와 벌어집니다.

정리

카피트레이딩 구축에서 코드량의 대부분은 복사가 아니라 예외 처리입니다. 수량 반올림, 실패 정책, 부분 청산 비율, 재동기화 규칙, 대조 루프 — 이 다섯 개를 먼저 정의하면 나머지는 단순한 주문 호출입니다.

자주 묻는 질문

카피트레이딩에서 팔로어 수량은 어떻게 계산하나요?

가장 일반적인 방식은 자산 비례입니다. 리더 수량에 팔로어 자산과 리더 자산의 비율을 곱하고, 브로커의 최소 주문 단위로 반올림합니다. 최소 단위보다 작으면 건너뛰고 기록을 남겨야 합니다.

서버가 끊겼다가 복구되면 그동안의 주문을 따라가야 하나요?

대개 소급하지 않는 것이 안전합니다. 가격이 이미 달라졌기 때문입니다. 복구 시점에는 현재 포지션만 대조하고 누락은 기록으로 남기는 방식이 일반적입니다.

MT4·MT5 계좌를 코드로 연결하기

계정을 만들고 API 키를 받으면 30분 안에 첫 계좌 조회와 주문을 실행할 수 있습니다.

계정 만들기 → API 문서 보기