บทเรียนสร้างบอทโพลีมาร์เก็ต · บทที่ 24 จาก 32

โพลีมาร์เก็ต Perps มี API สาธารณะครบชุด พร้อม SDK ทางการสำหรับ TypeScript และ Python บทนี้สร้างบอทบนอินเทอร์เฟซจริง ครอบคลุมการยืนยันตัวตนด้วยคีย์เทรดแบบพร็อกซี การดึงข้อมูลตลาดผ่าน REST และ WebSocket การส่งคำสั่งพร้อมทำกำไร (take-profit) และตัดขาดทุน (stop-loss) รวมถึงระบบควบคุมความเสี่ยงที่ป้องกันไม่ให้บอทที่ใช้เลเวอเรจล้างบัญชีจนหมด

Build your own theme park, free in your browserโฆษณา ลงโฆษณาที่นี่
ก่อนเริ่มสร้าง ไม่อนุญาตให้ส่งคำสั่ง Perps จากสหรัฐอเมริกา แคนาดา คิวบา อิหร่าน เกาหลีเหนือ ซีเรีย ไครเมีย โดเนตสก์ หรือลูฮันสก์ และ polymarket.com ยังบล็อกประเทศอื่นเพิ่มเติมอีก การใช้บอทหรือเซิร์ฟเวอร์ในต่างประเทศไม่ได้เปลี่ยนข้อกำหนดนี้ เมื่อใช้เลเวอเรจ บั๊กเพียงจุดเดียวทำให้เสียเงินได้อย่างรวดเร็ว จึงควรทดสอบด้วยขนาดขั้นต่ำ หากเพิ่งเริ่มใช้ Perps ให้อ่านคู่มือโพลีมาร์เก็ต Perpsก่อน

บทนี้ครอบคลุมอะไรบ้าง

  • ตัวเลขสำคัญสำหรับผู้สร้างบอท
  • โครงสร้างของ Perps API
  • ขั้นตอนที่ 1: ติดตั้ง SDK และเปิดเซสชัน
  • ขั้นตอนที่ 2: อ่านข้อมูลตลาดผ่าน REST และ WebSocket
  • ขั้นตอนที่ 3: ส่งคำสั่งพร้อมทำกำไรและตัดขาดทุน
  • ขั้นตอนที่ 4: โครงบอทพร้อมระบบควบคุมความเสี่ยงในโค้ด
  • ข้อผิดพลาด สวิตช์ป้องกันกรณีบอทหยุดทำงาน (dead man's switch) กฎสำหรับบอท และการโฮสต์

ทุกเมธอดของ SDK และทุก endpoint ในบทนี้มาจากเอกสาร Perps ของโพลีมาร์เก็ต (docs.polymarket.com/perps อ่านเมื่อ 6 พฤศจิกายน 2026) ตรรกะที่เราเขียนเองมีระบุไว้ในคอมเมนต์ และฟังก์ชันกลยุทธ์เป็นเพียงตัวยึดตำแหน่งที่ติดป้ายไว้ชัดเจน

ตัวเลขสำคัญสำหรับผู้สร้างบอท (ข้อมูล ณ พฤศจิกายน 2026)

รายการค่า
REST base URLhttps://api.perpetuals.polymarket.com
WebSocketwss://ws.perpetuals.polymarket.com/v1/ws
TypeScript SDK@polymarket/client (ใช้ร่วมกับ viem สำหรับการลงนาม)
Python SDKpolymarket-client (async)
ข้อมูลสาธารณะ/v1/info/* ไม่ต้องยืนยันตัวตน
การอ่านข้อมูลส่วนตัว/v1/account/* พร้อมเฮดเดอร์ polymarket-proxy และ polymarket-secret
การเขียนที่ต้องลงนาม/v1/trade/* ลงนามแบบ EIP-712 ด้วยคีย์พร็อกซี มีอายุ 5 นาที
ข้อมูลรับรองของเซสชันหมดอายุหลังหนึ่งสัปดาห์โดยค่าเริ่มต้น
คำสั่งขั้นต่ำมูลค่าสถานะ $10 ฝากขั้นต่ำ 10 pUSD
คำสั่งตลาดสูงสุด$1M ในตลาด 50x และ 20x, $500K ในตลาดส่วนใหญ่, $50K ในเหรียญ 3x และ 5x
รูปแบบราคาและขนาดสตริงทศนิยม ไม่เกินจำนวนทศนิยมของตลาดและไม่เกิน 5 เลขนัยสำคัญ
เลเวอเรจสูงสุด50x (BTC, ETH, USA500, US100) ตลาดอื่นเป็น 20x, 10x, 5x หรือ 3x แล้วแต่ตลาด และต่ำลงสำหรับสถานะขนาดใหญ่
ค่าธรรมเนียม (ระดับพื้นฐาน)Taker 0.040%, maker 0.0125% ส่วน ANT คิดครึ่งราคา
ค่าฟันดิงทุกชั่วโมง จำกัดไว้ที่ +/-4% ต่อชั่วโมง
ทริกเกอร์ TP/SLใช้ราคามาร์ก ไม่ใช่ราคาซื้อขายล่าสุด
WebSocket keepaliveส่ง ping ประมาณทุก 25 วินาที ถือว่าการเชื่อมต่อค้างหากเงียบไปราว 65 วินาที
ความสดของ tickerล่าช้าได้สูงสุด 10 วินาที

โครงสร้างของ Perps API

Perps เป็นกระดานเทรดแบบไฮบริด การจับคู่คำสั่งเกิดขึ้นนอกเชน ส่วนการดูแลสินทรัพย์และการชำระราคาอยู่บน Polygon มีเพียงการฝากและถอนที่เกิดขึ้นบนเชน จึงไม่มีค่าแก๊สต่อคำสั่ง API แบ่งเป็นสามชั้น:

  • ข้อมูลสาธารณะ (/v1/info/): รายการสินทรัพย์ ticker สมุดคำสั่ง แท่งเทียน รายการซื้อขาย ค่าฟันดิง ค่าธรรมเนียม ไม่ต้องใช้คีย์และไม่มีการบล็อกตามพื้นที่
  • การอ่านข้อมูลส่วนตัว (/v1/account/): พอร์ต ยอดคงเหลือ คำสั่ง รายการที่จับคู่แล้ว การตั้งค่า
  • การเขียนที่ต้องลงนาม (/v1/trade/): คำสั่ง การยกเลิก เลเวอเรจ มาร์จิ้นแบบแยก (isolated) การยกเลิกอัตโนมัติ ทุกการเขียนถูกเข้ารหัสแบบ MessagePack แฮชด้วย keccak256 และลงนามเป็นข้อมูลแบบ EIP-712 typed data ด้วยคีย์พร็อกซี

SDK จัดการการลงนามให้คุณ นอกจากนี้ยังมีสเปก OpenAPI (/api-spec/perps-openapi.json บนเว็บไซต์เอกสาร) และสเปก AsyncAPI สำหรับ WebSocket

สัญลักษณ์ต่างจากตัวอย่างในเอกสาร เอกสารแสดง BTC-PERP แต่ API จริงส่งกลับ BTC-USD, SP500-USD (USA500), NAS100-USD (US100), ANT-USD เป็นต้น ให้ดึงรายการสินทรัพย์ จับคู่กับ symbol จริง แล้วเก็บ id ที่เป็นตัวเลขไว้ อย่าคัดลอก ID จากตัวอย่าง: ในระบบจริง BTC คือ 6 และ SP500 คือ 1

ขั้นตอนที่ 1: ติดตั้ง SDK และเปิดเซสชัน

ติดตั้งด้วย pip install polymarket-client หรือ npm install @polymarket/client@latest viem บอทต้องใช้ที่อยู่วอลเล็ตโพลีมาร์เก็ตของคุณและคีย์ส่วนตัวของผู้ลงนามที่เป็นเจ้าของบัญชี Perps (บัญชีผูกกับ EOA ของผู้ลงนาม ไม่ใช่ Safe) การเปิดเซสชันจะลงนามครั้งเดียวเพื่ออนุมัติคีย์เทรดแบบพร็อกซีชุดใหม่ ซึ่งจะลงนามทุกคำสั่งหลังจากนั้น คีย์ที่ใช้ฝากเงินของคุณจึงไม่เคยถูกใช้เทรด เก็บ session.credentials แบบเข้ารหัสไว้เพื่อกลับมาใช้ต่อภายหลัง

import asyncio
import os
from datetime import timedelta

from polymarket import AsyncSecureClient


async def main():
    client = await AsyncSecureClient.create(
        private_key=os.environ["PRIVATE_KEY"],
        wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
    )
    # One signature creates a proxy trading key. Orders are signed by the proxy,
    # not by your wallet key. Credentials expire after one week by default.
    session = await client.open_perps_session(
        expires_in=timedelta(days=1),
        label="perps-bot",
    )
    stored = session.credentials.model_dump(mode="json")  # keep it encrypted

    instruments = await client.fetch_perps_instruments()
    btc = next(i for i in instruments if i.symbol == "BTC-USD")
    print(btc.id, btc.max_leverage, btc.min_notional, btc.price_decimals)


asyncio.run(main())
import { createSecureClient } from "@polymarket/client";
import { privateKey } from "@polymarket/client/viem";

const client = await createSecureClient({
  wallet: process.env.POLYMARKET_WALLET_ADDRESS!,
  signer: privateKey(process.env.PRIVATE_KEY!),
});

// One signature creates a proxy trading key. Orders are signed by the proxy,
// not by your wallet key. Credentials expire after one week by default.
const session = await client.openPerpsSession({
  expiresIn: 24 * 60 * 60 * 1000,
  label: "perps-bot",
});
const stored = session.credentials; // keep it encrypted

const instruments = await client.fetchPerpsInstruments();
const btc = instruments.find((i) => i.symbol === "BTC-USD");
if (!btc) throw new Error("BTC-USD not listed");
console.log(btc.id, btc.maxLeverage, btc.minNotional, btc.priceDecimals);

การฝากเงินจากโค้ด (deposit_to_perps / depositToPerps) ต้องใช้ Relayer API key จากเมนู Settings, API Keys ของ polymarket.com ด้วย วิธีที่ง่ายและปลอดภัยกว่าคือเติมเงินเข้าบัญชีผ่านเว็บแอป และไม่ให้บอทยุ่งกับการโอนเงิน ตั้งแต่ 5 พฤศจิกายน 2026 บัญชีที่ใช้สมุดที่อยู่สำหรับถอนเงินจะถอนได้เฉพาะไปยังที่อยู่ที่บันทึกไว้เท่านั้น สำหรับแดชบอร์ด ให้ใช้คีย์แบบอ่านอย่างเดียว (พร็อกซีที่ติดป้าย read-only: ซึ่งจะปฏิเสธการเขียนที่ต้องลงนาม) และอย่าใช้คีย์เทรดเด็ดขาด การจัดเก็บคีย์อธิบายไว้ในบทวอลเล็ตและการยืนยันตัวตน

ขั้นตอนที่ 2: อ่านข้อมูลตลาดผ่าน REST และ WebSocket

ticker มีราคาสามแบบ: index (มูลค่ายุติธรรมจากแหล่งข้อมูลอย่าง Pyth, Chainlink Data Streams และ Hyperliquid), mark (ใช้คำนวณ PnL มาร์จิ้น การถูกบังคับปิดสถานะ และ TP/SL) และ last (ใช้เฉพาะการจับคู่คำสั่ง) พร้อมอัตราฟันดิงและเวลาฟันดิงรอบถัดไป ให้สร้างสัญญาณจากราคามาร์กหรือราคา index

# Inside your async main(), after Step 1 (btc is the BTC-USD instrument)
from polymarket import AsyncPublicClient
from polymarket.streams import PerpsBboSpec

client = AsyncPublicClient()

ticker = await client.fetch_perps_ticker(instrument_id=btc.id)
print(ticker.mark_price, ticker.index_price, ticker.funding_rate)

stream = await client.subscribe(PerpsBboSpec(instrument_id=btc.id))
async for event in stream:
    print(event.payload.bid_price, event.payload.ask_price)
// After Step 1 (btc is the BTC-USD instrument)
import { createPublicClient } from "@polymarket/client";

const client = createPublicClient();

const ticker = await client.fetchPerpsTicker({ instrumentId: btc.id });
console.log(ticker.markPrice, ticker.indexPrice, ticker.fundingRate);

const stream = await client.subscribe([{ topic: "perps.bbo", instrumentId: btc.id }]);
for await (const event of stream) {
  console.log(event.payload.bidPrice, event.payload.askPrice);
}

หากไม่ใช้ SDK ให้สมัครรับข้อมูลบน WebSocket โดยตรงด้วย {"id": 1, "req": "sub", "chs": ["bbo::6"]} ช่องข้อมูลมีทั้ง bbo::<id>, book::<id>, trades::<id>, tickers::<id> และ klines::<id>::1m ทุกอัปเดตมีหมายเลขลำดับ sq: หากลำดับขาดหายหรือมีการเชื่อมต่อใหม่ ให้อ่านสถานะผ่าน REST อีกครั้ง สำหรับการทดสอบย้อนหลัง list_perps_candles / listPerpsCandles จะดึงข้อมูลย้อนหลังแบบแบ่งหน้า (endpoint REST ส่งกลับได้สูงสุด 1,000 แท่งต่อการเรียกหนึ่งครั้ง)

ขั้นตอนที่ 3: ส่งคำสั่งพร้อมทำกำไรและตัดขาดทุน

คำสั่งหนึ่งรายการมี side (BUY หรือ SELL), quantity ในหน่วยของสินทรัพย์หลัก, price เป็น pUSD (ไม่บังคับ) และระยะเวลาของคำสั่ง (gtc, ioc หรือ fok) คำสั่งแบบ post-only ต้องใช้ GTC ส่วน reduce-only ทำได้เพียงลดขนาดสถานะ ไม่มีแฟล็กระบุ long หรือ short: เมื่อยังไม่มีสถานะ BUY จะเปิด long และ SELL จะเปิด short

คำสั่งแบบ bracket จะผูกคำสั่งทำกำไรหนึ่งรายการและตัดขาดทุนหนึ่งรายการเข้ากับคำสั่งเข้า ทั้งสองจะรอไว้เฉยๆ จนกว่าคำสั่งเข้าจะจับคู่ครบทั้งหมด และจะถูกยกเลิกหากคำสั่งเข้าถูกยกเลิกหรือจับคู่เพียงบางส่วน ราคาด้านล่างเป็นตัวอย่างสมมติ

# Inside your async main(), after Step 1 (btc is the BTC-USD instrument)
from polymarket import PerpsTpSlTrigger

result = await session.place_order(
    instrument_id=btc.id,
    side="BUY",
    price="100000",
    quantity="0.01",
    time_in_force="gtc",
    take_profit=PerpsTpSlTrigger(trigger_price="104000"),
    stop_loss=PerpsTpSlTrigger(trigger_price="98000"),
)
print(result.order.id, result.order.status)
// After Step 1 (btc is the BTC-USD instrument)
import { OrderSide, PerpsTimeInForce } from "@polymarket/client";

const result = await session.placeOrder({
  instrumentId: btc.id,
  side: OrderSide.BUY,
  price: "100000",
  quantity: "0.01",
  timeInForce: PerpsTimeInForce.GTC,
  takeProfit: { triggerPrice: "104000" },
  stopLoss: { triggerPrice: "98000" },
});
console.log(result.order.id, result.tpSl.stopLoss?.orderId);

สำหรับสถานะที่เปิดอยู่ place_position_tp_sl / placePositionTpSl จะเพิ่มทริกเกอร์ที่ปิดสถานะทั้งหมด กฎมีดังนี้:

  • ทริกเกอร์ติดตามราคามาร์กและส่งคำสั่งแบบ reduce-only TP/SL ของสถานะจะปิดด้วยราคาตลาด จึงควรคาดว่าจะเกิดสลิปเพจในสมุดคำสั่งที่บาง
  • ทำกำไรและตัดขาดทุนของสถานะได้อย่างละหนึ่งรายการต่อตลาด การเลื่อนจุดตัดขาดทุนต้องยกเลิกแล้วสร้างใหม่ ซึ่งไม่ได้เกิดขึ้นพร้อมกันในขั้นตอนเดียว
  • ทริกเกอร์ที่ราคามาร์กข้ามไปแล้วจะถูกปฏิเสธ และราคาลิมิตที่ห่างจากราคามาร์กมากเกินไปก็จะถูกปฏิเสธเช่นกัน

หากต้องการลองขั้นตอนการส่งคำสั่งด้วยตัวเองก่อนทำเป็นระบบอัตโนมัติ ให้เปิดตลาด BTC บนโพลีมาร์เก็ต Perpsแล้วส่งคำสั่งขนาดเล็กที่สุดที่อนุญาต การได้เห็นการจับคู่ ค่าธรรมเนียม และแผงสถานะจริงจะช่วยให้อ่านผลตอบกลับของ API ได้ง่ายขึ้น

ขั้นตอนที่ 4: โครงบอทพร้อมระบบควบคุมความเสี่ยงในโค้ด

โครงบอท Python ด้านล่างใช้เฉพาะการเรียก SDK ที่มีในเอกสาร ส่วนตรรกะความเสี่ยงเป็นของเราเอง มีการป้องกันสามชั้น:

  1. เพดานเลเวอเรจ ตั้งเลเวอเรจไว้ที่ 3x หรือค่าสูงสุดของตลาด แล้วแต่ว่าค่าใดต่ำกว่า ใช้มาร์จิ้นแบบแยก และปฏิเสธขนาดที่ตกอยู่ในระดับความเสี่ยงที่มีเพดานต่ำกว่า
  2. สวิตช์หยุดฉุกเฉิน (kill switch) ได้แก่ ไฟล์ HALT ขีดจำกัดขาดทุนรายวัน 5% และการออกเมื่อบัญชีถูกบังคับปิดสถานะ สวิตช์จะปิดสถานะด้วยคำสั่ง IOC แบบ reduce-only ยกเลิกคำสั่งที่เหลือ แล้วตรวจสอบว่าทั้งสองอย่างหมดไปแล้ว
  3. ตัดขาดทุนทุกสถานะ คำสั่งเข้าแบบ fill-or-kill ทำให้ไม่มีการจับคู่บางส่วนที่ไม่มีการป้องกัน ทุกครั้งที่จับคู่จะตามด้วยการตั้งตัดขาดทุนและทำกำไรของสถานะ หากถูกปฏิเสธ สถานะจะถูกปิดทันที
import asyncio
import logging
import os
from datetime import timedelta
from decimal import Decimal, ROUND_DOWN

from polymarket import AsyncSecureClient, PerpsPositionTpSlTrigger

SYMBOL = "BTC-USD"                   # live API symbol; docs examples say "BTC-PERP"
LEVERAGE_CAP = 3                     # our cap, far below the 50x BTC allows
RISK_PER_TRADE = Decimal("20")       # pUSD we accept losing if the stop fills
STOP_PCT = Decimal("0.02")           # stop 2% from entry
TAKE_PCT = Decimal("0.04")           # take profit 4% from entry
MAX_NOTIONAL = Decimal("2000")       # hard cap per position, in pUSD
MAX_SLIPPAGE = Decimal("0.002")      # worst entry price: ask + 0.2%
MAX_FUNDING = Decimal("0.0005")      # skip longs if funding > 0.05% per hour
DAILY_LOSS_LIMIT = Decimal("0.05")   # kill switch at -5% of start equity
HALT_FILE = "/opt/perps-bot/HALT"    # touch this file to stop the bot


def fit(value: Decimal, decimals: int, sig: int = 5) -> str:
    """Round down to the market's decimals and to 5 significant figures."""
    v = value.quantize(Decimal(1).scaleb(-decimals), rounding=ROUND_DOWN)
    if v != 0:
        step = Decimal(1).scaleb(v.adjusted() - sig + 1)
        if step > Decimal(1).scaleb(-decimals):
            v = v.quantize(step, rounding=ROUND_DOWN)
    return format(v, "f")


def tier_cap(inst, notional: Decimal) -> int:
    """Max leverage allowed for this notional by the market's risk tiers."""
    cap = inst.max_leverage
    for tier in inst.risk_tiers:
        if notional >= Decimal(tier.lower_bound):
            cap = tier.max_leverage
    return cap


async def close_all(session):
    """Close every position with reduce-only IOC orders, then cancel the rest."""
    portfolio = await session.fetch_portfolio()
    for pos in portfolio.positions:
        if pos.size != 0:
            await session.place_order(
                instrument_id=pos.instrument_id,
                side="SELL" if pos.size > 0 else "BUY",
                quantity=abs(pos.size),
                time_in_force="ioc",
                reduce_only=True,
            )
    await session.cancel_all_orders()


async def kill_switch(session, reason: str):
    logging.error("KILL SWITCH: %s", reason)
    for attempt in range(3):
        await close_all(session)
        portfolio = await session.fetch_portfolio()
        open_orders = await session.fetch_open_orders()
        if not open_orders and all(p.size == 0 for p in portfolio.positions):
            return
        await asyncio.sleep(2)
    logging.critical("Positions still open after kill switch: act manually")


async def open_long(client, session, inst):
    ticker = await client.fetch_perps_ticker(instrument_id=inst.id)
    if Decimal(ticker.funding_rate) > MAX_FUNDING:
        return                                     # longs would pay too much
    book = await client.fetch_perps_book(instrument_id=inst.id, depth=10)
    if not book.asks:
        return
    ask = Decimal(book.asks[0].price)
    stop = ask * (1 - STOP_PCT)
    qty = RISK_PER_TRADE / (ask - stop)            # size from the stop, not leverage
    qty = min(qty, MAX_NOTIONAL / ask)
    notional = qty * ask
    if notional < Decimal(inst.min_notional):
        return
    if tier_cap(inst, notional) < LEVERAGE_CAP:
        return                                     # size would break our cap

    entry = await session.place_order(
        instrument_id=inst.id,
        side="BUY",
        price=fit(ask * (1 + MAX_SLIPPAGE), inst.price_decimals),
        quantity=fit(qty, inst.quantity_decimals),
        time_in_force="fok",                       # all or nothing
    )
    if entry.order.status != "filled":
        return

    try:                                           # stop on the whole position
        await session.place_position_tp_sl(
            instrument_id=inst.id,
            take_profit=PerpsPositionTpSlTrigger(
                trigger_price=fit(ask * (1 + TAKE_PCT), inst.price_decimals)),
            stop_loss=PerpsPositionTpSlTrigger(
                trigger_price=fit(stop, inst.price_decimals)),
        )
    except Exception:
        logging.exception("TP/SL rejected: closing the unprotected position")
        await close_all(session)


async def my_signal(client, inst) -> bool:
    """PLACEHOLDER: your strategy goes here. Not part of the SDK."""
    return False


async def main():
    logging.basicConfig(level=logging.INFO)
    client = await AsyncSecureClient.create(
        private_key=os.environ["PRIVATE_KEY"],
        wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
    )
    session = await client.open_perps_session(
        expires_in=timedelta(days=1), label="perps-bot")
    inst = next(i for i in await client.fetch_perps_instruments()
                if i.symbol == SYMBOL)

    # Control 1: leverage cap and isolated margin, set before any order
    await session.update_leverage(
        instrument_id=inst.id,
        leverage=min(LEVERAGE_CAP, inst.max_leverage),
        cross_margin=False,
    )

    # Field names below follow the documented portfolio JSON
    # (margin.total_account_value, in_liquidation). Check them in your SDK.
    start_equity = Decimal((await session.fetch_portfolio()).margin.total_account_value)

    while True:
        portfolio = await session.fetch_portfolio()
        equity = Decimal(portfolio.margin.total_account_value)

        # Control 2: kill switches
        if os.path.exists(HALT_FILE):
            await kill_switch(session, "HALT file found")
            break
        if equity < start_equity * (1 - DAILY_LOSS_LIMIT):
            await kill_switch(session, f"daily loss limit hit: {equity}")
            break
        if portfolio.in_liquidation:
            await kill_switch(session, "account in liquidation")
            break

        in_position = any(p.instrument_id == inst.id and p.size != 0
                          for p in portfolio.positions)
        if not in_position and await my_signal(client, inst):
            await open_long(client, session, inst)   # Control 3 lives here

        await asyncio.sleep(5)


asyncio.run(main())

การคำนวณขนาดสถานะ ขนาดสถานะคำนวณจากระยะห่างของจุดตัดขาดทุน ไม่ใช่จากเลเวอเรจ สมมติราคาเสนอขาย BTC อยู่ที่ 100,000 pUSD และตั้งตัดขาดทุน 2% จุดตัดขาดทุนจะอยู่ที่ 98,000 ความเสี่ยงต่อหน่วยคือ 2,000 ดังนั้นปริมาณคือ 20 / 2,000 = 0.01 BTC คิดเป็นมูลค่าสถานะ 1,000 pUSD ที่ 3x ต้องใช้มาร์จิ้นประมาณ 333 pUSD อัตรามาร์จิ้นรักษาสถานะของ BTC คือ 1% (0.5 / 50x) ราคาบังคับปิดสถานะของ long จึงเป็น 100,000 x (1 - 1/3) / (1 - 0.01) = ประมาณ 67,340 หรือราคาร่วงลง 32.7% จุดตัดขาดทุนที่ -2% จะทำงานก่อนหน้านั้นนานมาก หากตัดขาดทุนจับคู่ที่ราคาทริกเกอร์พอดี จะขาดทุน 20 pUSD บวกค่าธรรมเนียม: 0.40 ตอนเปิด และประมาณ 0.39 ตอนปิดที่อัตรา taker 0.040% รวมประมาณ 20.79 pUSD ก่อนคิดสลิปเพจ เลเวอเรจเป็นตัวกำหนดเพียงว่ามาร์จิ้นถูกกันไว้เท่าใด ไม่ได้เปลี่ยนยอดขาดทุนที่จุดตัดขาดทุน

ก่อนใช้งานจริง ให้เพิ่มขีดจำกัดจำนวนเทรดต่อวัน การแจ้งเตือนเมื่อมีการจับคู่และเมื่อสวิตช์หยุดฉุกเฉินทำงาน และการกระทบยอดตอนเริ่มระบบ (fetch_portfolio, fetch_open_orders, fetch_orders, list_fills) ดูบทโค้ดจัดการความเสี่ยงและการเทรดด้วยเงินเสมือน รวมถึงคู่มือเลเวอเรจและการถูกบังคับปิดสถานะของ Perpsและคู่มืออัตราฟันดิงของ Perps

ข้อผิดพลาด การลองใหม่ และสวิตช์ป้องกันกรณีบอทหยุดทำงาน

  • 503 service_unavailable ไม่ได้พิสูจน์ว่าคำขอล้มเหลว ให้ลองอ่านข้อมูลและเปลี่ยนเลเวอเรจใหม่แบบเว้นระยะ (backoff) และปฏิบัติตาม Retry-After สำหรับการเปลี่ยนยอดเงินที่ต้องลงนาม (มาร์จิ้น การถอนเงิน) ให้ส่งซ้ำเฉพาะคำขอที่ลงนามเดิมทุกประการเท่านั้น เพราะการลงนามใหม่จะนับเป็นการปรับยอดครั้งที่สอง
  • signature expired: ลายเซ็นเก่าเกิน 5 นาที ให้ซิงก์นาฬิกาอยู่เสมอ
  • insufficient_margin / invalid_leverage: การตรวจสอบนับรวมสถานะของคุณและคำสั่งที่รออยู่ทั้งหมด
  • message_rate_limited: ใช้โควตาข้อความของ WebSocket หมดแล้ว คีย์แบบอ่านอย่างเดียวได้ 1,000 ข้อความต่อนาทีต่อ IP (burst 50) และสี่การเชื่อมต่อต่อบัญชีต่อเกตเวย์ ส่วนที่เหลือดูได้ในหน้า Rate Limits ของเอกสาร
  • ข้อผิดพลาดด้านรูปแบบ: ทศนิยมมากเกินไปหรือเกิน 5 เลขนัยสำคัญ นี่คือเหตุผลที่มี fit()

การยกเลิกอัตโนมัติ (auto-cancel) จะยกเลิกคำสั่งที่เปิดอยู่ทั้งหมดหากบอทหยุดตั้งค่าใหม่ (re-arm) เส้นตายต้องอยู่ล่วงหน้าอย่างน้อย 5 วินาที และจำนวนครั้งที่ทำงานได้ต่อวันตามเวลา UTC มีจำกัด ดังนั้นให้ปิดการทำงาน (disarm) เมื่อปิดระบบตามปกติ

// For quoting bots: cancel all resting orders if this process dies
await session.armAutoCancel({ cancelAt: Date.now() + 60_000 });
const rearm = setInterval(() => {
  // A missed re-arm is fail-safe: the armed switch still fires.
  session.armAutoCancel({ cancelAt: Date.now() + 60_000 }).catch(console.error);
}, 20_000);

// On a clean shutdown
clearInterval(rearm);
await session.disarmAutoCancel();
การยกเลิกอัตโนมัติกับจุดตัดขาดทุน เอกสารระบุว่าการยกเลิกอัตโนมัติเป็นหนึ่งในกรณีที่คำสั่ง TP/SL ถูกยกเลิก การที่บอทล่มจึงอาจลบจุดตัดขาดทุนของสถานะที่เปิดอยู่ได้ การยกเลิกอัตโนมัติเหมาะกับบอทวางราคาเสนอ เช่น market maker โครงบอทของเราไม่มีคำสั่งเข้าที่รออยู่ จึงปิดการยกเลิกอัตโนมัติไว้เพื่อรักษาจุดตัดขาดทุน ไม่ว่าแบบใด เลเวอเรจต่ำคือสิ่งที่ป้องกันไม่ให้บอทที่ล่มถูกบังคับปิดสถานะ

การแข่งขัน builder code และกฎสำหรับบอท

เอกสารระบุว่า "บอทเทรดที่ตอบสนองต่อสัญญาณตลาด" และ market maker เป็นการใช้งาน API ที่รองรับ มีกฎสองชุดที่ต้องปฏิบัติตาม

  • การแข่งขัน ห้าม wash trading การเทรดกับตัวเอง การใช้หลายบัญชี การเทรดโดยมุ่งเพิ่มปริมาณการซื้อขายเป็นหลัก และ "บอท สคริปต์ หรือวิธีการอัตโนมัติที่มีเจตนาบิดเบือนโปรแกรม" บอทที่เทรดจริงใช้ได้ แต่บอทที่ปั่นปริมาณการซื้อขายอาจถูกตัดสิทธิ์
  • Builder code แอปที่เทรดแทนผู้ใช้รายอื่นสามารถเพิ่มค่าธรรมเนียมได้สูงสุด 10 basis points โดยได้รับความยินยอมจากเทรดเดอร์แต่ละคน แอปต้องตรวจสอบตำแหน่งที่ตั้งของผู้ใช้แต่ละคนและบล็อกคำสั่งจากเขตอำนาจที่ถูกจำกัด ไม่ใช่แค่แสดงคำเตือน

รางวัลสภาพคล่องสำหรับ maker และการแข่งขันอธิบายไว้ในคู่มือโพลีมาร์เก็ต Perps

การโฮสต์บอท

บอทที่ใช้เลเวอเรจไม่ควรรันบนแล็ปท็อป บอทต้องรักษาการเชื่อมต่อ WebSocket ตอบสนองต่อการจับคู่ภายในไม่กี่วินาที และทนต่ออินเทอร์เน็ตบ้านที่หลุดได้ ให้รันบน VPS ภายใต้ systemd พร้อมรีสตาร์ตอัตโนมัติ เก็บคีย์ไว้ในไฟล์ environment ที่มีเพียงผู้ใช้ของบอทเท่านั้นที่อ่านได้ และซิงก์นาฬิกา ไม่อนุญาตให้ส่งคำสั่งจากพื้นที่ที่ถูกจำกัด จึงห้ามโฮสต์ในสหรัฐอเมริกาหรือแคนาดา และห้ามใช้ VPS เพื่อหลบเลี่ยงการบล็อกที่มีผลกับคุณ เรารันบอทของเราเองบน TradingVPS (ลิงก์พันธมิตร: เราได้รับค่าคอมมิชชันเล็กน้อย โดยคุณจ่ายราคาเท่าเดิม) บทโฮสต์ VPSของเราเปรียบเทียบผู้ให้บริการนี้กับรายอื่นในด้านความหน่วง ราคา และภูมิภาค

หลังจากเทรดด้วยเงินเสมือนแล้ว ให้เติมเงินจำนวนเล็กน้อยเข้าบัญชีจากหน้าโพลีมาร์เก็ต Perps และรันด้วยขนาดขั้นต่ำเป็นเวลาหนึ่งสัปดาห์ก่อนขยายขนาด

ประเด็นสำคัญ

  • ใช้ SDK ทางการ @polymarket/client หรือ polymarket-client วอลเล็ตของคุณลงนามเพียงครั้งเดียว จากนั้นคีย์พร็อกซีจะลงนามคำสั่ง
  • จับคู่ตลาดด้วยสัญลักษณ์จริง (BTC-USD ไม่ใช่ BTC-PERP) และเก็บ ID ที่เป็นตัวเลขไว้
  • คำนวณขนาดจากระยะจุดตัดขาดทุน จำกัดเลเวอเรจในโค้ด ใช้มาร์จิ้นแบบแยก และตั้งตัดขาดทุนให้ทุกสถานะทันทีหลังจับคู่
  • TP/SL ทำงานตามราคามาร์กและปิดด้วยราคาตลาด การยกเลิกอัตโนมัติอาจลบจุดตัดขาดทุนได้
  • อนุญาตให้ใช้บอท แต่ห้ามบิดเบือนการแข่งขัน และห้ามเทรดจากประเทศที่ถูกจำกัดเด็ดขาด

คำถามที่พบบ่อย

โพลีมาร์เก็ต Perps มี API สำหรับบอทเทรดหรือไม่
มี โดยมี REST API ที่ api.perpetuals.polymarket.com, WebSocket ที่ ws.perpetuals.polymarket.com และ SDK ทางการ ได้แก่ @polymarket/client สำหรับ TypeScript และ polymarket-client สำหรับ Python เอกสารของโพลีมาร์เก็ตระบุว่าบอทเทรดและระบบ market making เป็นการใช้งานที่รองรับ
ควรใช้ SDK ของ TypeScript หรือ Python
ใช้ได้ทั้งสองแบบ ทั้งคู่ครอบคลุมขั้นตอนเดียวกัน: สร้าง secure client เปิดเซสชัน Perps ดึงรายการสินทรัพย์ ส่งคำสั่งพร้อม TP/SL และสตรีมเหตุการณ์ SDK ของ TypeScript ใช้ viem ในการลงนาม ส่วน SDK ของ Python เป็นแบบ async ให้เลือกภาษาที่บอทของคุณเขียนไว้อยู่แล้ว
บอทของฉันต้องใช้คีย์ส่วนตัวของวอลเล็ตหลักหรือไม่
ใช้เฉพาะตอนเปิดเซสชัน ลายเซ็นครั้งเดียวนั้นจะอนุมัติคีย์เทรดแบบพร็อกซี และหลังจากนั้นพร็อกซีจะเป็นผู้ลงนามคำสั่ง ข้อมูลรับรองของเซสชันหมดอายุหลังหนึ่งสัปดาห์โดยค่าเริ่มต้น ให้เก็บแบบเข้ารหัสและใช้คีย์แบบอ่านอย่างเดียวสำหรับแดชบอร์ด
จะตั้งตัดขาดทุนผ่าน Perps API ได้อย่างไร
ผูกทริกเกอร์ takeProfit และ stopLoss เข้ากับคำสั่งเข้าแบบ bracket หรือเรียก placePositionTpSl (ใน Python คือ place_position_tp_sl) กับสถานะที่เปิดอยู่ ทริกเกอร์จะติดตามราคามาร์กและปิดด้วยคำสั่งแบบ reduce-only TP/SL ของสถานะจะปิดสถานะทั้งหมดด้วยราคาตลาด
ขนาดคำสั่งขั้นต่ำบนโพลีมาร์เก็ต Perps คือเท่าไร
มูลค่าสถานะ $10 ต่อคำสั่ง และฝากขั้นต่ำ 10 pUSD (ข้อมูล ณ พฤศจิกายน 2026) ราคาและขนาดต้องเป็นไปตามจำนวนทศนิยมของแต่ละตลาดและไม่เกิน 5 เลขนัยสำคัญ มิฉะนั้นคำสั่งจะถูกปฏิเสธ
มีสวิตช์หยุดฉุกเฉินหรือสวิตช์ป้องกันกรณีบอทหยุดทำงานหรือไม่
มี การยกเลิกอัตโนมัติจะยกเลิกคำสั่งที่เปิดอยู่ทั้งหมดหากบอทหยุดตั้งค่าใหม่ และ cancelAllOrders ล้างคำสั่งได้ทุกเมื่อที่ต้องการ เอกสารระบุว่าการยกเลิกอัตโนมัติเป็นหนึ่งในกรณีที่คำสั่ง TP/SL อาจถูกยกเลิก การที่บอทล่มจึงอาจลบจุดตัดขาดทุนของคุณด้วย ควรเขียนสวิตช์หยุดฉุกเฉินของคุณเองด้วย
รันบอทโพลีมาร์เก็ต Perps จากสหรัฐอเมริกาหรือแคนาดาได้หรือไม่
ไม่ได้ ไม่อนุญาตให้ส่งคำสั่ง Perps จากสหรัฐอเมริกา แคนาดา คิวบา อิหร่าน เกาหลีเหนือ ซีเรีย ไครเมีย โดเนตสก์ หรือลูฮันสก์ ห้ามโฮสต์บอทในพื้นที่เหล่านั้น และห้ามใช้ VPS หรือ VPN เพื่อหลบเลี่ยงการบล็อกที่มีผลกับคุณ
อนุญาตให้ใช้บอทในการแข่งขันเทรดของโพลีมาร์เก็ต Perps หรือไม่
อนุญาตให้เทรดด้วยบอทได้ กฎการแข่งขันห้าม wash trading การเทรดกับตัวเอง การใช้หลายบัญชี การเทรดโดยมุ่งสร้างปริมาณการซื้อขายเป็นหลัก และบอทหรือสคริปต์ที่มีเจตนาบิดเบือนโปรแกรม การฝ่าฝืนอาจนำไปสู่การถูกตัดสิทธิ์