บทเรียนสร้างบอทโพลีมาร์เก็ต · บทที่ 24 จาก 32
โพลีมาร์เก็ต Perps มี API สาธารณะครบชุด พร้อม SDK ทางการสำหรับ TypeScript และ Python บทนี้สร้างบอทบนอินเทอร์เฟซจริง ครอบคลุมการยืนยันตัวตนด้วยคีย์เทรดแบบพร็อกซี การดึงข้อมูลตลาดผ่าน REST และ WebSocket การส่งคำสั่งพร้อมทำกำไร (take-profit) และตัดขาดทุน (stop-loss) รวมถึงระบบควบคุมความเสี่ยงที่ป้องกันไม่ให้บอทที่ใช้เลเวอเรจล้างบัญชีจนหมด
บทนี้ครอบคลุมอะไรบ้าง
- ตัวเลขสำคัญสำหรับผู้สร้างบอท
- โครงสร้างของ 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 URL | https://api.perpetuals.polymarket.com |
| WebSocket | wss://ws.perpetuals.polymarket.com/v1/ws |
| TypeScript SDK | @polymarket/client (ใช้ร่วมกับ viem สำหรับการลงนาม) |
| Python SDK | polymarket-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 ที่มีในเอกสาร ส่วนตรรกะความเสี่ยงเป็นของเราเอง มีการป้องกันสามชั้น:
- เพดานเลเวอเรจ ตั้งเลเวอเรจไว้ที่ 3x หรือค่าสูงสุดของตลาด แล้วแต่ว่าค่าใดต่ำกว่า ใช้มาร์จิ้นแบบแยก และปฏิเสธขนาดที่ตกอยู่ในระดับความเสี่ยงที่มีเพดานต่ำกว่า
- สวิตช์หยุดฉุกเฉิน (kill switch) ได้แก่ ไฟล์ HALT ขีดจำกัดขาดทุนรายวัน 5% และการออกเมื่อบัญชีถูกบังคับปิดสถานะ สวิตช์จะปิดสถานะด้วยคำสั่ง IOC แบบ reduce-only ยกเลิกคำสั่งที่เหลือ แล้วตรวจสอบว่าทั้งสองอย่างหมดไปแล้ว
- ตัดขาดทุนทุกสถานะ คำสั่งเข้าแบบ 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();
การแข่งขัน 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 ทำงานตามราคามาร์กและปิดด้วยราคาตลาด การยกเลิกอัตโนมัติอาจลบจุดตัดขาดทุนได้
- อนุญาตให้ใช้บอท แต่ห้ามบิดเบือนการแข่งขัน และห้ามเทรดจากประเทศที่ถูกจำกัดเด็ดขาด





