봇이 이미 Binance USD-M 선물을 지원한다면, 베이스 URL 하나만 바꾸면 BuyCrypt에서 바로 동작합니다. 사용자는 리더보드에서 봇을 발견하고, 무료 $1,000 데모 계정으로 체험한 뒤, 납득하면 실제 키로 전환합니다.
코드 재작성 불필요. 거래소별 특이사항 없음. 동일한 HMAC-SHA256 서명, 동일한 WebSocket 유저 데이터 스트림, 동일한 주문 유형 — 자본을 위험에 노출하지 않고 봇을 비교할 수 있도록 데모 트레이딩 엔진 위에 래핑되어 있습니다.
구독한 사용자마다 $1,000 가상 잔액이 있는 개인 데모 계정이 생성됩니다. 봇이 거래를 실행하면 사용자는 실시간 PnL, 최대 낙폭, 승률을 확인합니다. 실제 자본에 대한 위험 없음 — 개발자에게도 고객 획득 비용 없음.
봇 전용 데모 계정은 공개 리더보드에 🤖 БОТ 배지와 함께 표시됩니다. 사용자는 실제 트레이더들 사이에서 PnL 순위를 확인합니다. 행을 탭하고 "연결"을 누르면 즉시 구독됩니다.
Binance 선물 SDK(binance-connector-python, python-binance, CCXT 등)를 사용하는 경우, base_url을 https://api.buycrypt.com/fapi로 지정하면 됩니다. HMAC, recvWindow, 주문 유형 — 모두 동일합니다.
사용자가 구독하면, 새로 발급된 API 키 + 시크릿을 웹훅 URL로 POST 합니다 — HMAC 서명 처리, 실패 시 지수 백오프로 재시도, 데드레터 복구 가능. 사용자에게 직접 자격증명을 요청할 필요가 없습니다.
사용자가 리더보드를 엽니다. 봇 계정은 사용자명 옆에 🤖 БОТ 칩과 구독자 수가 표시됩니다. 해당 행을 탭합니다.
봇 프로필 페이지에서 "데모 계정에 연결"을 확인합니다. 한 번 탭하면 새 $1,000 데모가 생성되고 봇이 거래를 시작합니다.
사용자는 봇이 자신의 데모에서 며칠 또는 몇 주 동안 거래하는 것을 지켜봅니다. PnL, 포지션, 승률 — 모두 앱에서 실시간으로 확인됩니다. 마음에 들면 BuyCrypt 안에서 실제 Binance API 키를 입력해 데모에서 프로덕션으로 업그레이드합니다.
사용자가 BuyCrypt 안에서 실제 Binance API 키를 추가합니다. 데모 키와 동일한 방식으로 — 동일한 subscription.created 이벤트로, 이번에는 프로덕션 자격증명과 함께 — 우리가 웹훅으로 전달합니다. 하나의 통합으로 데모와 라이브 플로우를 모두 커버합니다.
일반 사용자와 동일한 절차로 BuyCrypt 계정을 등록하세요. 가입 즉시 시작 잔액이 들어 있는 무료 데모 계정이 발급됩니다. 신청서도 검토 대기열도 없습니다.
BuyCrypt 모바일 앱 안에서 바로 API 키를 생성하세요: My Profile → API keys → 데모 계정 탭 → Generate. 새로운 apiKey + apiSecret가 한 번만 표시됩니다 — 봇에 복사하세요. 그런 다음 기존 Binance USD-M Futures 봇을 https://api.buycrypt.com/fapi로 가리키면 그대로 작동합니다 — 동일한 HMAC, 동일한 엔드포인트, 동일한 주문 유형.
데모에서 API 기반 거래가 감지되는 즉시, 계정이 자동으로 봇으로 플래그됩니다. 데모가 리더보드에 🤖 БОТ 칩과 공개 프로필로 나타나며, 다른 사용자들이 발견하고 구독할 수 있습니다.
구독자를 받을 준비가 되면 봇 프로필 설정에서 웹훅 URL을 추가하세요. 사용자가 구독할 때마다 — 데모든 실제 Binance 키든 — 해당 사용자의 계정용 API 키와 시크릿이 포함된 subscription.created 이벤트를 POST로 전송합니다. (userId, apiKey, apiSecret)를 저장하고 그 사용자를 위해 거래를 시작하세요.
대부분의 Binance 선물 SDK는 커스텀 베이스 URL을 지원합니다. 일반적인 Python 봇에 필요한 변경 사항은 다음과 같습니다:
# before — trading on real Binance from binance.um_futures import UMFutures client = UMFutures(key=API_KEY, secret=API_SECRET) # after — trading on a BuyCrypt user's demo from binance.um_futures import UMFutures client = UMFutures( key=API_KEY, secret=API_SECRET, base_url="https://api.buycrypt.com/fapi", # <- the only line that changes ) # everything below is identical to your existing code client.new_order(symbol="BTCUSDT", side="BUY", type="MARKET", quantity=0.01) client.account() client.cancel_open_orders(symbol="BTCUSDT")
CCXT, python-binance, Go의 go-binance, JS node-binance-api — 모두 동일한 방식으로 작동합니다. 순수 requests.get(...).headers["X-MBX-APIKEY"] = key 방식의 봇도 동작하며, HMAC 서명 규칙은 바이트 단위로 동일합니다.
BuyCrypt 사용자가 봇을 구독하면, 서버 측에서 사용자별 API 키 + 시크릿을 생성하여 등록된 웹훅 URL로 POST 합니다. 시크릿은 사용자에게 절대 반환되지 않으며 — HTTPS를 통해 서명된 상태로 오직 개발자에게만 전달됩니다.
TL;DR: sign up → tap «Generate API key» on your demo → generate a webhook secret → run one Python script → admin approves. ~5 minutes.
Open buycrypt.com/terminal (or the mobile app). Sign in via Google or phone. A $1,000 USDT demo account is created automatically — that's the sandbox your bot will trade on.
In the app: Profile → API Keys → tap your demo account → «Bot API keys» → Generate. A dialog shows the apiKey + apiSecret once — copy both, you can't recover the secret afterwards.
# what you'll save API_KEY = "3fc370a4ac94b9aa756e2a97842dc04c" API_SECRET = "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU"
Same shape as Binance — your existing USD-M Futures bot library reads it as a regular Binance key with base URL https://api.buycrypt.com/fapi.
A second secret, separate from API_SECRET. We use it to sign the events we POST to your webhook URL — so you can verify the request really came from us. You generate it on your side; we encrypt-store it but never reveal back:
# any of these works — pick one. Save the output. $ openssl rand -hex 32 # macOS / Linux $ python3 -c "import secrets; print(secrets.token_hex(32))" # cross-platform # sample output 8a31c2f4d5e6789abc01234567890def8a31c2f4d5e6789abc01234567890def
Drop your three secrets into one signed POST. Save this as register_bot.py and run it once. Works on any OS with Python 3.10+ and pip install requests:
import hmac, hashlib, time, urllib.parse, requests # ─── fill these in from steps 2 and 3 ───────────────────── API_KEY = "3fc370a4ac94b9aa756e2a97842dc04c" API_SECRET = "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU" WEBHOOK_SECRET = "8a31c2f4d5e6789abc01234567890def8a31c2f4d5e6789abc01234567890def" SLUG = "alpha-trader" # lowercase, [a-z0-9-], unique DISPLAY_NAME = "Alpha Trader" # shown to users in the bot list WEBHOOK_URL = "https://your-bot.example.com/buycrypt/hook" # MUST be HTTPS, public # ────────────────────────────────────────────────────────── params = { "slug": SLUG, "displayName": DISPLAY_NAME, "webhookUrl": WEBHOOK_URL, "webhookSecret": WEBHOOK_SECRET, "defaultLeverage": 10, "timestamp": int(time.time() * 1000), } qs = urllib.parse.urlencode(params) sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest() r = requests.post( "https://api.buycrypt.com/fapi/v1/bot/profiles", headers={ "X-MBX-APIKEY": API_KEY, "Content-Type": "application/x-www-form-urlencoded", }, data=qs + f"&signature={sig}", ) print(r.status_code, r.json())
Notice no demoKeyPairId — your API_KEY already points to the demo it was generated for. Need to register against a different demo? Generate a separate API key for it (step 2 again).
Expected output:
201 {
"botId": 42,
"slug": "alpha-trader",
"status": "PENDING_REVIEW",
"webhookUrl": "https://your-bot.example.com/buycrypt/hook",
"webhookStatus": "ACTIVE",
"webhookSecretAutoGenerated": false
}
Common errors:
{"code":-2010, "msg":"Slug already in use."} # pick another slug
{"code":-1102, "msg":"webhookSecret must be at least 32..."} # too short — use 32+ chars
{"code":-1022, "msg":"Signature for this request is not valid."} # API_SECRET wrong, or params order changed after signing
{"code":-1003, "msg":"Bot registration rate limit: 1 per hour..."} # wait it out
Bot lands in PENDING_REVIEW. Our admin sees your request immediately and approves it. To check status anytime:
import hmac, hashlib, time, urllib.parse, requests API_KEY = "3fc370a4..." API_SECRET = "3WJ86kV..." qs = urllib.parse.urlencode({"timestamp": int(time.time() * 1000)}) sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest() r = requests.get( f"https://api.buycrypt.com/fapi/v1/bot/profiles?{qs}&signature={sig}", headers={"X-MBX-APIKEY": API_KEY}, ) print(r.json()) # [{"botId":42, "status":"ACTIVE", ...}] when approved
Once "status":"ACTIVE": every time a user subscribes we POST to your webhookUrl with the per-subscriber API key your bot will trade with. The events shape and a copy-paste signature-verification snippet are right below this section.
Lost your webhook secret? Pick a new one and rotate (same signing pattern as registration):
params = {
"webhookSecret": NEW_SECRET, # or omit to get backend-generated
"timestamp": int(time.time() * 1000),
}
qs = urllib.parse.urlencode(params)
sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest()
requests.post(
"https://api.buycrypt.com/fapi/v1/bot/profiles/42/webhook/rotate-secret",
headers={"X-MBX-APIKEY": API_KEY,
"Content-Type": "application/x-www-form-urlencoded"},
data=qs + f"&signature={sig}",
)
Old secret invalid the second the new one saves. Rate limit: 10 rotations/hour per bot.
subscription.created — 사용자가 방금 구독함POST https://your-bot.example.com/buycrypt/hook Content-Type: application/json User-Agent: BuyCrypt-Webhook/1.0 X-BuyCrypt-Event: subscription.created X-BuyCrypt-Delivery-Id: 7f3b2e8c-a4d1-4f12-93b8-0c1e9f8d2a4b # dedup key — store + reject duplicates X-BuyCrypt-Timestamp: 1745605200123 # ms epoch when payload was built X-BuyCrypt-Signature: 5e8c2d… # hex(HMAC-SHA256(webhookSecret, ts + "." + body)) { "event": "subscription.created", "deliveryId": "7f3b2e8c-a4d1-4f12-93b8-0c1e9f8d2a4b", "timestamp": 1745605200123, "data": { "subscriptionId": 42, "botProfileId": 7, "subscriberHandle": "user_fb_uid_a", "apiKey": "3fc370a4ac94b9aa756e2a97842dc04c", "apiSecret": "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU", "demoKeyPairId": 9001, "initialBalance": "1000.00000000", "permissions": "READ_TRADE", "ipWhitelist": "", "createdAt": "2026-04-25T18:00:00Z" } }
subscription.deleted — 사용자 연결 해제, 키 비활성화X-BuyCrypt-Event: subscription.disconnected { "event": "subscription.disconnected", "deliveryId": "…", "timestamp": …, "data": { "subscriptionId": 42, "botProfileId": 7, "apiKey": "3fc370a4…", "reason": "USER_UNSUBSCRIBED", "disconnectedAt": "2026-04-25T18:30:00Z" } }
secret.regenerated — 관리자가 웹훅 시크릿을 교체함X-BuyCrypt-Event: secret.regenerated { "event": "secret.regenerated", "deliveryId": "…", "timestamp": …, "data": { "subscriptionId": 42, "apiKey": "3fc370a4…", "newApiSecret": "new_plaintext_keep_old_valid_24h", "oldSecretValidUntil": "2026-04-26T18:00:00Z" } }
Use the raw request body bytes — DO NOT parse JSON first and re-serialize, you'll lose key order and signatures will diverge. Always compare in constant time.
# Python (FastAPI / Flask) import hmac, hashlib, time WEBHOOK_SECRET = "whsec_aGVsbG8gd29ybGQ_..." # whsec_…43 chars def verify(headers, body_bytes): sig = headers["X-BuyCrypt-Signature"] ts = int(headers["X-BuyCrypt-Timestamp"]) # anti-replay: reject if too old if abs(time.time() * 1000 - ts) > 5 * 60 * 1000: return False payload = f"{ts}.".encode() + body_bytes expected = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, sig)
// Node.js / Express — read RAW body, not parsed JSON const crypto = require('crypto'); const SECRET = 'whsec_aGVsbG8gd29ybGQ_...'; app.post('/buycrypt/hook', express.raw({ type: 'application/json' }), // raw, not json()! (req, res) => { const sig = req.headers['x-buycrypt-signature']; const ts = parseInt(req.headers['x-buycrypt-timestamp'], 10); if (Math.abs(Date.now() - ts) > 5 * 60 * 1000) return res.status(401).end(); const expected = crypto.createHmac('sha256', SECRET) .update(`${ts}.`).update(req.body).digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) { return res.status(401).end(); } // req.body is a Buffer here — JSON.parse(req.body.toString('utf8')) res.sendStatus(200); });
지수 백오프로 6회 재시도: 1분, 5분, 30분, 2시간, 6시간, 12시간. 6회 모두 실패하면 이벤트가 데드레터 테이블에 저장되며 관리자가 재전송할 수 있습니다.
엔드포인트가 연속으로 10회 실패하면 비활성화 처리되고 이벤트 전송이 중단됩니다. 재활성화를 위해 문의해 주세요. 죽은 봇이 신규 구독자를 무기한 차단하는 것을 방지합니다.
모든 요청 본문은 등록 시 공유한 시크릿으로 HMAC-SHA256 서명됩니다. 페이로드를 신뢰하기 전에 상수 시간 비교로 검증하세요. 파트너 가이드에 예제가 포함되어 있습니다.
베이스 URL: https://api.buycrypt.com/fapi. 인증: X-MBX-APIKEY 헤더 + 서명 엔드포인트에 HMAC-SHA256 서명. RecvWindow: 기본값 5,000ms, 최대 60,000ms.
{}를 반환합니다.MARKET, LIMIT, STOP_MARKET, TAKE_PROFIT_MARKET, STOP, TAKE_PROFIT (리밋 방식), TRAILING_STOP_MARKET 지원. 플래그: reduceOnly, closePosition, timeInForce (GTC/IOC/FOK), 멱등성을 위한 newClientOrderId, workingType, callbackRate.orderId 또는 origClientOrderId로 단일 주문 조회.{dualSidePosition: false}를 반환합니다.ORDER_TRADE_UPDATE, ACCOUNT_UPDATE, MARGIN_CALL. Binance와 동일한 페이로드 구조.subscribe/unsubscribe/list_subscriptions 제어 메시지 지원 — 업스트림 Binance로 그대로 전달됩니다.X-MBX-USED-WEIGHT-1M, X-MBX-ORDER-COUNT-10S, 429 시 Retry-After.Retry-After에 따라 백오프하세요신청서 없음, 검토 대기열 없음, 통합 수수료 없음. 일반 사용자처럼 가입하고, 모바일 앱에서 API 키를 생성하고, 봇을 https://api.buycrypt.com/fapi로 가리키세요 — 그것이 전체 온보딩입니다.