Prediction Market 套利策略深度解析:跨交易所套利機制與實戰架構
概述
預測市場(Prediction Market)是一種基於事件結果進行交易的金融市場。與傳統金融市場不同,預測市場的價格直接反映市場對特定事件發生機率的集體判斷。這種特性創造了獨特的套利機會:當同一事件在不同平台上的隱含機率不一致時,套利者可以通過買低賣高獲取無風險利潤。
本文將深入分析預測市場套利的各種策略,從基礎的跨交易所價差套利,到複雜的多腿套利組合,提供完整的技術實現架構。
主要預測市場交易所分析
交易所生態系統全景
graph TB
subgraph "中心化預測市場"
Kalshi[Kalshi<br/>CFTC 監管]
Predictit[PredictIt<br/>學術研究豁免]
Betfair[Betfair Exchange<br/>英國博彩牌照]
end
subgraph "去中心化預測市場 - Polygon"
Polymarket[Polymarket<br/>CTF Exchange]
Limitless[Limitless<br/>CTF Exchange Fork]
end
subgraph "去中心化預測市場 - 其他鏈"
Azuro[Azuro<br/>Gnosis/Polygon]
Hedgehog[Hedgehog Markets<br/>Solana]
end
subgraph "預測聚合平台"
Metaculus[Metaculus<br/>預測聚合]
Manifold[Manifold Markets<br/>Play Money]
end
Polymarket -->|"Fork"| Limitless
Polymarket -->|"使用"| CTF[Gnosis CTF]
Limitless -->|"使用"| CTF
各交易所核心特性比較
| 特性 | Polymarket | Limitless | Kalshi | PredictIt |
|---|---|---|---|---|
| 類型 | 去中心化 | 去中心化 | 中心化 | 中心化 |
| 區塊鏈 | Polygon | Polygon | N/A | N/A |
| 結算代幣 | USDC | USDC | USD | USD |
| 訂單類型 | Limit/Market | Limit/Market | Limit/Market | Limit |
| 手續費 | 0% maker/-2% taker | 可配置 | 7% on profit | 10% on profit |
| 最小單位 | 0.01 USDC | 0.01 USDC | $1 | $1 |
| 槓桿 | 無 | 無 | 無 | 無 |
| API 訪問 | 完整 REST/WS | 完整 REST/WS | 有限 | 有限 |
| 流動性 | 高 | 中 | 高 | 低 |
套利機會類型分析
類型一:跨交易所價差套利
最基本的套利形式,當同一事件在不同交易所的價格存在差異時執行。
sequenceDiagram
participant Arb as 套利機器人
participant PM as Polymarket
participant LM as Limitless
participant Oracle as 結算預言機
Note over PM,LM: 同一事件:美國總統大選
PM->>PM: YES 價格: $0.52
LM->>LM: YES 價格: $0.48
Note over Arb: 發現價差: 4 cents
Arb->>LM: 買入 YES @ $0.48
Arb->>PM: 賣出 YES @ $0.52
Note over Arb: 鎖定利潤: $0.04/share
Oracle->>PM: 事件結算
Oracle->>LM: 事件結算
alt YES 獲勝
PM->>Arb: 支付 $0.48 (1.00 - 0.52)
LM->>Arb: 收取 $0.52 (1.00 - 0.48)
Note over Arb: 淨利潤: $0.04
else NO 獲勝
PM->>Arb: 收取 $0.52
LM->>Arb: 支付 $0.48
Note over Arb: 淨利潤: $0.04
end
價差套利數學模型
設兩個交易所的 YES 價格分別為 和 ():
套利利潤公式:
最小可套利價差:
實際範例計算:
Polymarket YES 價格: $0.52 (賣出)
Limitless YES 價格: $0.48 (買入)
交易規模: 1000 shares
Polymarket taker fee: 2% × $520 = $10.40
Limitless taker fee: 2% × $480 = $9.60
Gas 成本 (Polygon): ~$0.10
毛利潤: ($0.52 - $0.48) × 1000 = $40
淨利潤: $40 - $10.40 - $9.60 - $0.10 = $19.90
ROI: $19.90 / $480 = 4.15%
類型二:同交易所 YES/NO 價差套利
當 YES + NO 的價格不等於 1.00 時存在套利機會。
graph LR
subgraph "正常狀態"
Y1[YES: $0.60] --> |"+"| N1[NO: $0.40]
N1 --> |"="| T1[Total: $1.00]
end
subgraph "套利機會:價格過高"
Y2[YES: $0.62] --> |"+"| N2[NO: $0.42]
N2 --> |"="| T2[Total: $1.04]
T2 --> |"賣出兩邊"| P2[利潤: $0.04]
end
subgraph "套利機會:價格過低"
Y3[YES: $0.58] --> |"+"| N3[NO: $0.38]
N3 --> |"="| T3[Total: $0.96]
T3 --> |"買入兩邊"| P3[利潤: $0.04]
end
CTF MINT/MERGE 套利機制
基於 Polymarket 和 Limitless 的 CTF Exchange 智能合約:
// 當 YES + NO < 1.00 時:MINT 套利
// 用 1 USDC 鑄造 1 YES + 1 NO,然後賣出兩邊
function mintArbitrage(
uint256 conditionId,
uint256 yesPrice, // 例如 0.58
uint256 noPrice // 例如 0.38
) external {
require(yesPrice + noPrice < 1e6, "No arb opportunity");
// 1. 用 1 USDC 鑄造 position tokens
// splitPosition(collateral, parentCollectionId, conditionId, partition, amount)
ctf.splitPosition(
usdc,
bytes32(0),
conditionId,
partition, // [1, 2] for binary
amount
);
// 2. 在 orderbook 賣出 YES 和 NO
// 收到: yesPrice + noPrice = 0.96 USDC
// 成本: 1.00 USDC
// 但如果價格回歸,可獲利
}
// 當 YES + NO > 1.00 時:MERGE 套利
// 買入 YES + NO,然後 merge 回 1 USDC
function mergeArbitrage(
uint256 conditionId,
uint256 yesPrice, // 例如 0.62
uint256 noPrice // 例如 0.42
) external {
require(yesPrice + noPrice > 1e6, "No arb opportunity");
// 1. 買入 YES tokens @ 0.62
// 2. 買入 NO tokens @ 0.42
// 總成本: 1.04 USDC
// 3. Merge 回 collateral
ctf.mergePositions(
usdc,
bytes32(0),
conditionId,
partition,
amount
);
// 收到: 1.00 USDC
// 虧損: 0.04 USDC
// 注意:這裡是虧損的,需要等價格修正
// 真正的套利是在價格 > 1.00 時「做空」
}
類型三:多結果市場套利
對於有多個互斥結果的市場(如選舉有多位候選人),套利條件是所有結果價格之和應等於 1.00。
graph TB
subgraph "美國總統大選 2024"
A[候選人 A: $0.45]
B[候選人 B: $0.42]
C[候選人 C: $0.08]
Other[其他: $0.03]
end
Sum[總和: $0.98]
A --> Sum
B --> Sum
C --> Sum
Other --> Sum
Sum --> |"< $1.00"| Arb[套利機會:買入所有結果]
Arb --> |"保證獲得"| Profit[$0.02 利潤]
多結果套利公式:
當 :買入所有結果,保證獲利
當 :賣出所有結果,保證獲利
跨交易所套利技術架構
系統架構設計
graph TB
subgraph "數據採集層"
WS1[Polymarket WebSocket]
WS2[Limitless WebSocket]
WS3[Kalshi API]
REST[REST API Fallback]
end
subgraph "數據處理層"
Normalizer[事件標準化引擎]
OrderBook[Orderbook 聚合器]
PriceIndex[價格指數計算]
end
subgraph "策略引擎"
ArbDetector[套利檢測器]
RiskManager[風險管理器]
SizeCalculator[倉位計算器]
end
subgraph "執行層"
Router[智能訂單路由]
TxBuilder[交易構建器]
GasOptimizer[Gas 優化器]
end
subgraph "區塊鏈層"
Polygon[Polygon RPC]
Mempool[Mempool 監控]
Flash[Flashbots Protect]
end
WS1 --> Normalizer
WS2 --> Normalizer
WS3 --> Normalizer
REST --> Normalizer
Normalizer --> OrderBook
OrderBook --> PriceIndex
PriceIndex --> ArbDetector
ArbDetector --> RiskManager
RiskManager --> SizeCalculator
SizeCalculator --> Router
Router --> TxBuilder
TxBuilder --> GasOptimizer
GasOptimizer --> Polygon
Mempool --> ArbDetector
事件標準化引擎
不同交易所對同一事件的描述可能不同,需要建立映射關係:
interface NormalizedEvent {
// 標準化事件 ID
canonicalId: string;
// 事件描述
description: string;
// 結果列表
outcomes: {
id: string;
description: string;
// 對應各交易所的 token/market ID
mappings: {
polymarket?: {
conditionId: string;
tokenId: string;
clob_token_id: string;
};
limitless?: {
conditionId: string;
tokenId: string;
};
kalshi?: {
ticker: string;
seriesTicker: string;
};
};
}[];
// 結算時間
resolutionTime: Date;
// 結算來源
resolutionSource: string;
}
class EventNormalizer {
private eventMappings: Map<string, NormalizedEvent> = new Map();
// 通過 NLP 和規則匹配建立事件映射
async matchEvents(
polymarketEvents: PolymarketEvent[],
limitlessEvents: LimitlessEvent[],
kalshiEvents: KalshiEvent[]
): Promise<NormalizedEvent[]> {
const normalized: NormalizedEvent[] = [];
for (const pmEvent of polymarketEvents) {
// 1. 精確匹配:通過 condition ID
let match = this.findExactMatch(pmEvent, limitlessEvents);
// 2. 模糊匹配:通過事件描述相似度
if (!match) {
match = this.findFuzzyMatch(pmEvent, [...limitlessEvents, ...kalshiEvents]);
}
if (match) {
normalized.push(this.createNormalizedEvent(pmEvent, match));
}
}
return normalized;
}
private findFuzzyMatch(event: PolymarketEvent, candidates: Event[]): Event | null {
const threshold = 0.85; // 相似度閾值
for (const candidate of candidates) {
const similarity = this.calculateSimilarity(
event.description,
candidate.description
);
if (similarity >= threshold) {
// 額外驗證:結算時間應該接近
if (Math.abs(event.endDate - candidate.endDate) < 86400000) { // 1 day
return candidate;
}
}
}
return null;
}
private calculateSimilarity(a: string, b: string): number {
// 使用 Levenshtein 距離或更複雜的 NLP 方法
// 這裡簡化為 Jaccard 相似度
const wordsA = new Set(a.toLowerCase().split(/\s+/));
const wordsB = new Set(b.toLowerCase().split(/\s+/));
const intersection = new Set([...wordsA].filter(x => wordsB.has(x)));
const union = new Set([...wordsA, ...wordsB]);
return intersection.size / union.size;
}
}
Orderbook 聚合與價差計算
interface AggregatedOrderbook {
event: NormalizedEvent;
outcome: string;
// 各交易所的最佳買賣價
exchanges: {
name: string;
bestBid: { price: number; size: number; };
bestAsk: { price: number; size: number; };
depth: {
bids: { price: number; size: number; }[];
asks: { price: number; size: number; }[];
};
}[];
// 套利機會
arbitrage?: {
buyExchange: string;
sellExchange: string;
buyPrice: number;
sellPrice: number;
maxSize: number;
expectedProfit: number;
profitPercentage: number;
};
}
class OrderbookAggregator {
async aggregate(event: NormalizedEvent): Promise<AggregatedOrderbook[]> {
const results: AggregatedOrderbook[] = [];
for (const outcome of event.outcomes) {
const orderbooks = await Promise.all([
this.fetchPolymarketOrderbook(outcome.mappings.polymarket),
this.fetchLimitlessOrderbook(outcome.mappings.limitless),
this.fetchKalshiOrderbook(outcome.mappings.kalshi),
]);
const aggregated = this.mergeOrderbooks(orderbooks);
aggregated.arbitrage = this.findArbitrageOpportunity(aggregated);
results.push(aggregated);
}
return results;
}
private findArbitrageOpportunity(
orderbook: AggregatedOrderbook
): AggregatedOrderbook['arbitrage'] | undefined {
let bestBuy = { exchange: '', price: Infinity, size: 0 };
let bestSell = { exchange: '', price: 0, size: 0 };
for (const ex of orderbook.exchanges) {
// 找最低買入價(對方的 ask)
if (ex.bestAsk.price < bestBuy.price) {
bestBuy = {
exchange: ex.name,
price: ex.bestAsk.price,
size: ex.bestAsk.size,
};
}
// 找最高賣出價(對方的 bid)
if (ex.bestBid.price > bestSell.price) {
bestSell = {
exchange: ex.name,
price: ex.bestBid.price,
size: ex.bestBid.size,
};
}
}
// 只有當 賣出價 > 買入價 時才有套利機會
if (bestSell.price > bestBuy.price && bestBuy.exchange !== bestSell.exchange) {
const maxSize = Math.min(bestBuy.size, bestSell.size);
const grossProfit = (bestSell.price - bestBuy.price) * maxSize;
// 扣除手續費
const fees = this.calculateFees(
bestBuy.exchange,
bestSell.exchange,
bestBuy.price * maxSize,
bestSell.price * maxSize
);
const netProfit = grossProfit - fees;
if (netProfit > 0) {
return {
buyExchange: bestBuy.exchange,
sellExchange: bestSell.exchange,
buyPrice: bestBuy.price,
sellPrice: bestSell.price,
maxSize,
expectedProfit: netProfit,
profitPercentage: netProfit / (bestBuy.price * maxSize) * 100,
};
}
}
return undefined;
}
private calculateFees(
buyExchange: string,
sellExchange: string,
buyValue: number,
sellValue: number
): number {
const feeRates: Record<string, { maker: number; taker: number }> = {
polymarket: { maker: 0, taker: 0.02 },
limitless: { maker: 0, taker: 0.02 },
kalshi: { maker: 0, taker: 0.07 }, // 利潤的 7%
};
const buyFee = buyValue * feeRates[buyExchange]?.taker || 0;
const sellFee = sellValue * feeRates[sellExchange]?.taker || 0;
// 加上預估 gas 成本
const gasCost = this.estimateGasCost(buyExchange, sellExchange);
return buyFee + sellFee + gasCost;
}
private estimateGasCost(buyEx: string, sellEx: string): number {
// Polygon 上的 gas 成本通常很低
if (buyEx.includes('poly') || sellEx.includes('poly') ||
buyEx.includes('limit') || sellEx.includes('limit')) {
return 0.10; // 約 $0.10
}
return 0;
}
}
套利執行引擎
sequenceDiagram
participant Detector as 套利檢測器
participant Risk as 風險管理
participant Router as 訂單路由
participant PM as Polymarket
participant LM as Limitless
Detector->>Detector: 發現套利機會<br/>買 LM @0.48, 賣 PM @0.52
Detector->>Risk: 請求風險評估
Risk->>Risk: 檢查倉位限制
Risk->>Risk: 檢查流動性深度
Risk->>Risk: 評估滑點風險
Risk->>Risk: 計算最優倉位
Risk->>Router: 批准交易 (Size: 500)
par 並行執行
Router->>LM: 提交買單 500 @ 0.48
Router->>PM: 提交賣單 500 @ 0.52
end
LM-->>Router: 成交確認: 500 @ 0.48
PM-->>Router: 成交確認: 480 @ 0.52
Note over Router: 部分成交處理
Router->>Risk: 報告持倉不平衡
Risk->>Router: 調整對沖
Router->>LM: 賣出多餘 20 shares
class ArbitrageExecutor {
private riskManager: RiskManager;
private polymarket: PolymarketClient;
private limitless: LimitlessClient;
async executeArbitrage(opportunity: ArbitrageOpportunity): Promise<ExecutionResult> {
// 1. 風險檢查
const riskCheck = await this.riskManager.evaluate(opportunity);
if (!riskCheck.approved) {
return { success: false, reason: riskCheck.reason };
}
const optimalSize = riskCheck.recommendedSize;
// 2. 預簽名交易(減少延遲)
const [buyOrder, sellOrder] = await Promise.all([
this.prepareBuyOrder(opportunity, optimalSize),
this.prepareSellOrder(opportunity, optimalSize),
]);
// 3. 原子性執行(盡可能同時提交)
const startTime = Date.now();
const [buyResult, sellResult] = await Promise.all([
this.executeBuyOrder(buyOrder),
this.executeSellOrder(sellOrder),
]);
const executionTime = Date.now() - startTime;
// 4. 處理執行結果
return this.reconcileResults(buyResult, sellResult, executionTime);
}
private async prepareBuyOrder(
opp: ArbitrageOpportunity,
size: number
): Promise<PreparedOrder> {
const exchange = opp.buyExchange;
if (exchange === 'limitless') {
// Limitless 使用 EIP-712 簽名訂單
const order = {
tokenId: opp.tokenId,
side: 'BUY',
price: opp.buyPrice,
size: size,
expiration: Math.floor(Date.now() / 1000) + 120, // 2 分鐘有效
nonce: await this.limitless.getNonce(),
};
const signature = await this.signOrder(order, 'limitless');
return { exchange, order, signature };
}
// Polymarket 類似處理
return this.preparePolymarketOrder(opp, size, 'BUY');
}
private async reconcileResults(
buyResult: OrderResult,
sellResult: OrderResult,
executionTime: number
): Promise<ExecutionResult> {
const buyFilled = buyResult.filledSize;
const sellFilled = sellResult.filledSize;
// 計算實際利潤
const profit = (sellResult.avgPrice * sellFilled) - (buyResult.avgPrice * buyFilled);
// 處理不平衡持倉
if (buyFilled !== sellFilled) {
const imbalance = buyFilled - sellFilled;
if (Math.abs(imbalance) > 10) { // 閾值
// 需要對沖不平衡部分
await this.hedgeImbalance(imbalance, buyResult.tokenId);
}
}
return {
success: true,
buyFilled,
sellFilled,
profit,
executionTime,
fees: buyResult.fees + sellResult.fees,
netProfit: profit - buyResult.fees - sellResult.fees,
};
}
private async hedgeImbalance(imbalance: number, tokenId: string): Promise<void> {
// 策略 1: 立即市價對沖
// 策略 2: 設置限價單等待成交
// 策略 3: 在另一交易所對沖
if (imbalance > 0) {
// 持有多餘的多頭,需要賣出
await this.limitless.placeOrder({
tokenId,
side: 'SELL',
type: 'MARKET',
size: Math.abs(imbalance),
});
} else {
// 持有多餘的空頭,需要買入
await this.limitless.placeOrder({
tokenId,
side: 'BUY',
type: 'MARKET',
size: Math.abs(imbalance),
});
}
}
}
同交易所內套利策略
策略一:MINT/MERGE 套利
基於 Gnosis Conditional Token Framework 的特性,可以在 YES + NO 價格偏離 1.00 時套利。
graph TB
subgraph "MINT 套利 (當 YES + NO < 1.00)"
M1[1 USDC] --> |splitPosition| M2[1 YES + 1 NO]
M2 --> |賣出 YES| M3[收到 0.55 USDC]
M2 --> |賣出 NO| M4[收到 0.48 USDC]
M3 --> M5[總收入: 1.03 USDC]
M4 --> M5
M5 --> |扣除手續費| M6[淨利潤: ~0.01 USDC]
end
subgraph "MERGE 套利 (當 YES + NO > 1.00)"
R1[買入 YES @ 0.48] --> R3[持有 1 YES + 1 NO]
R2[買入 NO @ 0.48] --> R3
R3 --> |mergePositions| R4[收到 1 USDC]
R4 --> |成本 0.96| R5[淨利潤: ~0.02 USDC]
end
class MintMergeArbitrage {
private ctf: ConditionalTokenFramework;
private exchange: CTFExchange;
async checkOpportunity(conditionId: string): Promise<MintMergeOpportunity | null> {
const [yesOrderbook, noOrderbook] = await Promise.all([
this.exchange.getOrderbook(conditionId, 'YES'),
this.exchange.getOrderbook(conditionId, 'NO'),
]);
const yesBestBid = yesOrderbook.bids[0]?.price || 0;
const yesBestAsk = yesOrderbook.asks[0]?.price || 1;
const noBestBid = noOrderbook.bids[0]?.price || 0;
const noBestAsk = noOrderbook.asks[0]?.price || 1;
// MINT 套利機會:賣出兩邊的總價 > 1.00
const mintProfit = (yesBestBid + noBestBid) - 1.00;
if (mintProfit > this.minProfitThreshold) {
const maxSize = Math.min(
yesOrderbook.bids[0]?.size || 0,
noOrderbook.bids[0]?.size || 0
);
return {
type: 'MINT',
conditionId,
expectedProfit: mintProfit * maxSize,
maxSize,
yesPrice: yesBestBid,
noPrice: noBestBid,
};
}
// MERGE 套利機會:買入兩邊的總價 < 1.00
const mergeProfit = 1.00 - (yesBestAsk + noBestAsk);
if (mergeProfit > this.minProfitThreshold) {
const maxSize = Math.min(
yesOrderbook.asks[0]?.size || 0,
noOrderbook.asks[0]?.size || 0
);
return {
type: 'MERGE',
conditionId,
expectedProfit: mergeProfit * maxSize,
maxSize,
yesPrice: yesBestAsk,
noPrice: noBestAsk,
};
}
return null;
}
async executeMint(opp: MintMergeOpportunity): Promise<ExecutionResult> {
const { conditionId, maxSize, yesPrice, noPrice } = opp;
// 1. 鑄造 position tokens
const mintTx = await this.ctf.splitPosition(
this.collateralToken,
bytes32(0), // parent collection
conditionId,
[1, 2], // partition for binary outcome
maxSize
);
await mintTx.wait();
// 2. 並行賣出 YES 和 NO
const [yesSell, noSell] = await Promise.all([
this.exchange.placeOrder({
conditionId,
outcome: 'YES',
side: 'SELL',
price: yesPrice,
size: maxSize,
type: 'LIMIT',
}),
this.exchange.placeOrder({
conditionId,
outcome: 'NO',
side: 'SELL',
price: noPrice,
size: maxSize,
type: 'LIMIT',
}),
]);
return {
success: true,
profit: (yesPrice + noPrice - 1.00) * maxSize,
transactions: [mintTx.hash, yesSell.orderId, noSell.orderId],
};
}
async executeMerge(opp: MintMergeOpportunity): Promise<ExecutionResult> {
const { conditionId, maxSize, yesPrice, noPrice } = opp;
// 1. 並行買入 YES 和 NO
const [yesBuy, noBuy] = await Promise.all([
this.exchange.placeOrder({
conditionId,
outcome: 'YES',
side: 'BUY',
price: yesPrice,
size: maxSize,
type: 'LIMIT',
}),
this.exchange.placeOrder({
conditionId,
outcome: 'NO',
side: 'BUY',
price: noPrice,
size: maxSize,
type: 'LIMIT',
}),
]);
// 等待訂單成交
await Promise.all([
this.waitForFill(yesBuy.orderId),
this.waitForFill(noBuy.orderId),
]);
// 2. Merge 回 collateral
const mergeTx = await this.ctf.mergePositions(
this.collateralToken,
bytes32(0),
conditionId,
[1, 2],
maxSize
);
await mergeTx.wait();
return {
success: true,
profit: (1.00 - yesPrice - noPrice) * maxSize,
transactions: [yesBuy.orderId, noBuy.orderId, mergeTx.hash],
};
}
}
策略二:Complementary Token 套利
Polymarket CTF Exchange 支援 fillOrderCOMPLEMENTARY 操作,允許用互補代幣成交訂單。
sequenceDiagram
participant Maker as Maker
participant Arb as 套利者
participant Exchange as CTF Exchange
participant CTF as Conditional Tokens
Note over Maker: 掛單賣出 1000 YES @ $0.60
Maker->>Exchange: 簽名賣單
Note over Arb: 發現機會:NO 價格 $0.38
Note over Arb: YES + NO = $0.98 < $1.00
Arb->>CTF: 鑄造 1000 YES + 1000 NO<br/>(成本 $1000)
Arb->>Exchange: fillOrderCOMPLEMENTARY<br/>用 NO 成交 YES 賣單
Exchange->>Exchange: Maker 的 YES 與 Arb 的 NO 匹配
Exchange->>CTF: mergePositions
CTF->>Arb: 釋放 $1000 × (1 - 0.60) = $400
CTF->>Maker: 釋放 $1000 × 0.60 = $600
Note over Arb: 持有 1000 YES
Note over Arb: 賣出 YES @ $0.60 = $600
Note over Arb: 總收入: $400 + $600 = $1000
Note over Arb: 成本: $1000
Note over Arb: 但省去了單獨買 YES 的滑點!
// CTFExchange.sol - COMPLEMENTARY 匹配邏輯
function _matchOrders(
Order memory takerOrder,
Order memory makerOrder,
uint256 takerFillAmount,
uint256 makerFillAmount
) internal {
// ...
if (matchType == MatchType.COMPLEMENTARY) {
// taker 用互補代幣填充 maker 訂單
// 例如:maker 賣 YES,taker 提供 NO
// 計算要 merge 的數量
uint256 mergeAmount = Math.min(takerFillAmount, makerFillAmount);
// Merge YES + NO → Collateral
_mergePositions(order.conditionId, mergeAmount);
// 分配 collateral
uint256 makerProceeds = mergeAmount * makerOrder.price / ONE;
uint256 takerProceeds = mergeAmount - makerProceeds;
collateralToken.transfer(makerOrder.maker, makerProceeds);
collateralToken.transfer(takerOrder.maker, takerProceeds);
}
}
策略三:時間套利(結算前套利)
在事件即將結算時,如果市場價格還未完全反映確定性,可以進行套利。
graph LR
subgraph "結算前狀態"
News[新聞: 候選人 A 確定獲勝]
Price[市場價格: YES A @ $0.95]
Delay[市場反應延遲: 30 秒]
end
subgraph "套利機會"
Buy[買入 YES A @ $0.95]
Wait[等待結算]
Settle[結算: $1.00]
Profit[利潤: $0.05/share]
end
News --> Delay
Delay --> Buy
Buy --> Wait
Wait --> Settle
Settle --> Profit
class SettlementArbitrage {
private newsAggregator: NewsAggregator;
private priceMonitor: PriceMonitor;
async monitorSettlementOpportunities(): Promise<void> {
// 監控即將結算的事件
const upcomingSettlements = await this.getUpcomingSettlements(
60 * 60 * 1000 // 1 小時內
);
for (const event of upcomingSettlements) {
// 訂閱相關新聞
this.newsAggregator.subscribe(event.keywords, async (news) => {
const predictedOutcome = await this.analyzeNews(news, event);
if (predictedOutcome.confidence > 0.95) {
const currentPrice = await this.priceMonitor.getPrice(
event.id,
predictedOutcome.outcome
);
// 如果價格還未反映確定性
if (currentPrice < 0.98) {
const profit = (1.00 - currentPrice) * this.maxPosition;
const risk = currentPrice * this.maxPosition;
// 確保風險收益比合理
if (profit / risk > 0.05) { // 5% 以上收益
await this.executeTrade(event, predictedOutcome.outcome, currentPrice);
}
}
}
});
}
}
private async analyzeNews(news: News, event: Event): Promise<OutcomePrediction> {
// 使用 NLP 分析新聞是否確定了結果
// 這需要高度可靠的新聞來源和分析
const keywords = this.extractKeywords(news.content);
const sentiment = await this.analyzeSentiment(news.content, event.outcomes);
// 尋找確定性語言
const certaintyPatterns = [
/確定|確認|正式|官方宣布|finally|officially|confirmed/i,
];
const isCertain = certaintyPatterns.some(p => p.test(news.content));
if (isCertain && sentiment.confidence > 0.9) {
return {
outcome: sentiment.predictedOutcome,
confidence: 0.98,
};
}
return { outcome: null, confidence: 0 };
}
}
風險管理與資金分配
風險類型分析
graph TB
subgraph "執行風險"
Slippage[滑點風險]
PartialFill[部分成交風險]
Latency[延遲風險]
GasSpike[Gas 價格波動]
end
subgraph "市場風險"
LiquidityRisk[流動性枯竭]
PriceMove[價格快速變動]
Correlation[相關性風險]
end
subgraph "技術風險"
ContractRisk[智能合約風險]
OracleRisk[預言機風險]
APIRisk[API 不可用]
end
subgraph "監管風險"
Regulatory[監管變化]
AccountFreeze[帳戶凍結]
end
Slippage --> |"解決方案"| S1[設置最大滑點限制]
PartialFill --> |"解決方案"| S2[對沖殘餘部位]
Latency --> |"解決方案"| S3[多節點冗餘]
LiquidityRisk --> |"解決方案"| S4[限制單筆交易規模]
PriceMove --> |"解決方案"| S5[實時監控取消訂單]
ContractRisk --> |"解決方案"| S6[審計和限制暴露]
OracleRisk --> |"解決方案"| S7[多預言機交叉驗證]
風險管理系統實現
interface RiskParameters {
// 最大單一事件暴露
maxEventExposure: number;
// 最大單一交易所暴露
maxExchangeExposure: number;
// 最大總持倉
maxTotalPosition: number;
// 最大滑點容忍度
maxSlippageBps: number;
// 最小利潤閾值
minProfitThreshold: number;
// 最大 gas 成本
maxGasCost: number;
// 流動性深度要求
minLiquidityDepth: number;
}
class RiskManager {
private params: RiskParameters;
private positions: Map<string, Position> = new Map();
private exchangeExposure: Map<string, number> = new Map();
evaluate(opportunity: ArbitrageOpportunity): RiskEvaluation {
const checks: RiskCheck[] = [];
// 1. 檢查事件暴露
const eventExposure = this.getEventExposure(opportunity.eventId);
const newExposure = eventExposure + opportunity.buyPrice * opportunity.size;
if (newExposure > this.params.maxEventExposure) {
checks.push({
passed: false,
reason: `Event exposure would exceed limit: ${newExposure} > ${this.params.maxEventExposure}`,
adjustedSize: (this.params.maxEventExposure - eventExposure) / opportunity.buyPrice,
});
}
// 2. 檢查交易所暴露
const buyExExposure = this.exchangeExposure.get(opportunity.buyExchange) || 0;
if (buyExExposure + opportunity.buyPrice * opportunity.size > this.params.maxExchangeExposure) {
const maxSize = (this.params.maxExchangeExposure - buyExExposure) / opportunity.buyPrice;
checks.push({
passed: false,
reason: `Exchange exposure limit`,
adjustedSize: maxSize,
});
}
// 3. 檢查滑點
const expectedSlippage = this.estimateSlippage(opportunity);
if (expectedSlippage > this.params.maxSlippageBps) {
checks.push({
passed: false,
reason: `Slippage too high: ${expectedSlippage}bps`,
adjustedSize: this.findSizeWithAcceptableSlippage(opportunity),
});
}
// 4. 檢查流動性
const minDepth = Math.min(
opportunity.buyDepth,
opportunity.sellDepth
);
if (minDepth < this.params.minLiquidityDepth) {
checks.push({
passed: false,
reason: `Insufficient liquidity: ${minDepth}`,
adjustedSize: minDepth * 0.5, // 只用一半深度
});
}
// 5. 檢查利潤閾值(扣除所有成本後)
const netProfit = this.calculateNetProfit(opportunity);
if (netProfit < this.params.minProfitThreshold) {
checks.push({
passed: false,
reason: `Profit below threshold: ${netProfit}`,
});
}
// 綜合評估
const failedChecks = checks.filter(c => !c.passed);
if (failedChecks.length === 0) {
return {
approved: true,
recommendedSize: opportunity.size,
expectedProfit: netProfit,
};
}
// 嘗試調整倉位大小
const adjustedSizes = failedChecks
.map(c => c.adjustedSize)
.filter(s => s !== undefined && s > 0);
if (adjustedSizes.length > 0) {
const recommendedSize = Math.min(...adjustedSizes);
const adjustedProfit = this.calculateNetProfit({
...opportunity,
size: recommendedSize,
});
if (adjustedProfit >= this.params.minProfitThreshold) {
return {
approved: true,
recommendedSize,
expectedProfit: adjustedProfit,
warnings: failedChecks.map(c => c.reason),
};
}
}
return {
approved: false,
reason: failedChecks.map(c => c.reason).join('; '),
};
}
private estimateSlippage(opp: ArbitrageOpportunity): number {
// 基於 orderbook 深度估算滑點
let buySlippage = 0;
let remainingSize = opp.size;
let totalCost = 0;
for (const level of opp.buyOrderbook.asks) {
const fillSize = Math.min(remainingSize, level.size);
totalCost += fillSize * level.price;
remainingSize -= fillSize;
if (remainingSize <= 0) break;
}
const avgPrice = totalCost / opp.size;
buySlippage = (avgPrice - opp.buyPrice) / opp.buyPrice * 10000; // bps
// 類似計算賣出滑點
// ...
return buySlippage; // + sellSlippage
}
updatePosition(eventId: string, exchange: string, delta: number): void {
const currentPos = this.positions.get(eventId) || { size: 0, avgPrice: 0 };
currentPos.size += delta;
this.positions.set(eventId, currentPos);
const currentExposure = this.exchangeExposure.get(exchange) || 0;
this.exchangeExposure.set(exchange, currentExposure + Math.abs(delta));
}
}
資金分配策略
pie title 套利資金分配建議
"Polymarket 流動資金" : 40
"Limitless 流動資金" : 25
"Kalshi 流動資金" : 15
"Gas 儲備 (ETH/MATIC)" : 5
"緊急儲備" : 15
class CapitalAllocator {
private totalCapital: number;
private allocations: Map<string, number> = new Map();
constructor(totalCapital: number) {
this.totalCapital = totalCapital;
this.initializeAllocations();
}
private initializeAllocations(): void {
// 基於歷史機會頻率和利潤分配資金
const allocationRatios = {
'polymarket': 0.40, // 最大流動性
'limitless': 0.25, // 較新但成長中
'kalshi': 0.15, // 中心化但受監管
'gas_reserve': 0.05, // Gas 儲備
'emergency': 0.15, // 緊急儲備
};
for (const [venue, ratio] of Object.entries(allocationRatios)) {
this.allocations.set(venue, this.totalCapital * ratio);
}
}
rebalance(performanceData: VenuePerformance[]): void {
// 基於最近表現動態調整分配
const totalProfit = performanceData.reduce((sum, v) => sum + v.profit, 0);
for (const venue of performanceData) {
const profitContribution = venue.profit / totalProfit;
const currentAllocation = this.allocations.get(venue.name) || 0;
// 向表現好的場所傾斜,但設置上下限
const targetAllocation = this.totalCapital * Math.max(0.10, Math.min(0.50,
profitContribution * 1.2 // 給表現好的額外 20%
));
// 漸進調整
const newAllocation = currentAllocation * 0.8 + targetAllocation * 0.2;
this.allocations.set(venue.name, newAllocation);
}
// 確保總和不超過資本
this.normalizeAllocations();
}
private normalizeAllocations(): void {
const total = Array.from(this.allocations.values()).reduce((a, b) => a + b, 0);
if (total > this.totalCapital) {
const scale = this.totalCapital / total;
for (const [venue, amount] of this.allocations) {
this.allocations.set(venue, amount * scale);
}
}
}
getAvailableCapital(venue: string): number {
return this.allocations.get(venue) || 0;
}
}
實戰監控與警報系統
監控儀表板架構
graph TB
subgraph "數據源"
PM_WS[Polymarket WebSocket]
LM_WS[Limitless WebSocket]
Chain[區塊鏈事件]
end
subgraph "實時處理"
StreamProcessor[Stream Processor]
ArbScanner[套利掃描器]
PnLCalculator[盈虧計算器]
end
subgraph "存儲"
TimeSeries[(TimescaleDB)]
Redis[(Redis Cache)]
end
subgraph "可視化"
Grafana[Grafana Dashboard]
Alerts[AlertManager]
Mobile[Mobile Notifications]
end
PM_WS --> StreamProcessor
LM_WS --> StreamProcessor
Chain --> StreamProcessor
StreamProcessor --> ArbScanner
StreamProcessor --> PnLCalculator
ArbScanner --> Redis
PnLCalculator --> TimeSeries
Redis --> Grafana
TimeSeries --> Grafana
ArbScanner --> Alerts
PnLCalculator --> Alerts
Alerts --> Mobile
關鍵指標監控
interface DashboardMetrics {
// 實時指標
realtime: {
activeArbitrageOpportunities: number;
averageSpread: number;
totalOpenPositions: number;
unrealizedPnL: number;
availableCapital: {
polymarket: number;
limitless: number;
kalshi: number;
};
};
// 日內指標
intraday: {
tradesExecuted: number;
profitLoss: number;
winRate: number;
averageProfit: number;
largestWin: number;
largestLoss: number;
sharpeRatio: number;
};
// 系統健康
system: {
latencyMs: {
polymarket: number;
limitless: number;
execution: number;
};
errorRate: number;
uptime: number;
};
}
class MetricsCollector {
private metrics: DashboardMetrics;
private trades: Trade[] = [];
recordTrade(trade: Trade): void {
this.trades.push(trade);
this.updateMetrics(trade);
this.checkAlerts(trade);
}
private updateMetrics(trade: Trade): void {
// 更新交易計數
this.metrics.intraday.tradesExecuted++;
// 更新盈虧
this.metrics.intraday.profitLoss += trade.profit;
// 更新勝率
const wins = this.trades.filter(t => t.profit > 0).length;
this.metrics.intraday.winRate = wins / this.trades.length;
// 更新平均利潤
this.metrics.intraday.averageProfit =
this.metrics.intraday.profitLoss / this.trades.length;
// 更新最大盈虧
if (trade.profit > this.metrics.intraday.largestWin) {
this.metrics.intraday.largestWin = trade.profit;
}
if (trade.profit < this.metrics.intraday.largestLoss) {
this.metrics.intraday.largestLoss = trade.profit;
}
}
private checkAlerts(trade: Trade): void {
// 單筆虧損警報
if (trade.profit < -100) { // $100 以上虧損
this.sendAlert({
level: 'warning',
message: `Large loss detected: $${Math.abs(trade.profit).toFixed(2)}`,
trade,
});
}
// 連續虧損警報
const recentTrades = this.trades.slice(-5);
const consecutiveLosses = recentTrades.every(t => t.profit < 0);
if (consecutiveLosses && recentTrades.length >= 5) {
this.sendAlert({
level: 'critical',
message: '5 consecutive losing trades detected',
trades: recentTrades,
});
}
// 系統延遲警報
if (trade.executionLatency > 1000) { // 1 秒以上
this.sendAlert({
level: 'warning',
message: `High execution latency: ${trade.executionLatency}ms`,
});
}
}
private sendAlert(alert: Alert): void {
// 發送到各個通道
// Telegram, Discord, Email, PagerDuty 等
}
}
進階套利策略
策略一:統計套利
基於歷史價格關係進行均值回歸交易。
class StatisticalArbitrage {
private priceHistory: Map<string, number[]> = new Map();
private correlations: Map<string, number> = new Map();
// 計算兩個相關市場之間的價差
calculateSpread(eventA: string, eventB: string): SpreadAnalysis {
const pricesA = this.priceHistory.get(eventA) || [];
const pricesB = this.priceHistory.get(eventB) || [];
if (pricesA.length < 100 || pricesB.length < 100) {
return { valid: false };
}
// 計算價差序列
const spreads = pricesA.map((a, i) => a - pricesB[i]);
// 統計分析
const mean = spreads.reduce((a, b) => a + b) / spreads.length;
const std = Math.sqrt(
spreads.reduce((sum, s) => sum + Math.pow(s - mean, 2), 0) / spreads.length
);
const currentSpread = pricesA[pricesA.length - 1] - pricesB[pricesB.length - 1];
const zScore = (currentSpread - mean) / std;
return {
valid: true,
mean,
std,
currentSpread,
zScore,
signal: this.generateSignal(zScore),
};
}
private generateSignal(zScore: number): TradingSignal {
if (zScore > 2) {
// 價差過高,做空價差(賣 A 買 B)
return { action: 'SHORT_SPREAD', strength: Math.min(zScore / 3, 1) };
} else if (zScore < -2) {
// 價差過低,做多價差(買 A 賣 B)
return { action: 'LONG_SPREAD', strength: Math.min(Math.abs(zScore) / 3, 1) };
}
return { action: 'HOLD', strength: 0 };
}
}
策略二:跨鏈套利
利用不同區塊鏈上的預測市場價差。
sequenceDiagram
participant Arb as 套利者
participant Polygon as Polygon (Polymarket)
participant Solana as Solana (Hedgehog)
participant Bridge as 跨鏈橋
Note over Polygon,Solana: 同一事件價差
Polygon->>Polygon: YES @ $0.50
Solana->>Solana: YES @ $0.55
Arb->>Polygon: 買入 YES @ $0.50
Arb->>Bridge: 傳遞套利意圖
Note over Bridge: 延遲 ~15 分鐘
Bridge->>Solana: 確認資金到位
Arb->>Solana: 賣出 YES @ $0.55
Note over Arb: 利潤: $0.05 - 橋接費用
class CrossChainArbitrage {
private bridges: Map<string, BridgeClient> = new Map();
async findCrossChainOpportunities(): Promise<CrossChainOpportunity[]> {
const opportunities: CrossChainOpportunity[] = [];
// 獲取各鏈上的價格
const [polygonPrices, solanaPrices, gnosisPrices] = await Promise.all([
this.getPolygonPrices(),
this.getSolanaPrices(),
this.getGnosisPrices(),
]);
// 匹配相同事件
const matchedEvents = this.matchEventsAcrossChains(
polygonPrices,
solanaPrices,
gnosisPrices
);
for (const event of matchedEvents) {
const chainPrices = event.prices;
// 找最低買入價和最高賣出價
let minBuy = { chain: '', price: Infinity };
let maxSell = { chain: '', price: 0 };
for (const [chain, price] of Object.entries(chainPrices)) {
if (price.ask < minBuy.price) {
minBuy = { chain, price: price.ask };
}
if (price.bid > maxSell.price) {
maxSell = { chain, price: price.bid };
}
}
if (maxSell.chain !== minBuy.chain && maxSell.price > minBuy.price) {
// 計算橋接成本
const bridgeCost = await this.getBridgeCost(minBuy.chain, maxSell.chain);
const bridgeTime = this.getBridgeTime(minBuy.chain, maxSell.chain);
const netProfit = (maxSell.price - minBuy.price) - bridgeCost;
if (netProfit > 0) {
opportunities.push({
event: event.id,
buyChain: minBuy.chain,
buyPrice: minBuy.price,
sellChain: maxSell.chain,
sellPrice: maxSell.price,
bridgeCost,
bridgeTime,
netProfit,
risk: this.assessCrossChainRisk(bridgeTime, event),
});
}
}
}
return opportunities;
}
private assessCrossChainRisk(bridgeTime: number, event: any): number {
// 風險因素:
// 1. 橋接時間越長,價格變動風險越高
// 2. 事件結算時間越近,風險越高
// 3. 橋接協議的安全性
const timeToSettlement = event.settlementTime - Date.now();
const bridgeTimeRatio = bridgeTime / timeToSettlement;
if (bridgeTimeRatio > 0.1) { // 橋接時間超過剩餘時間的 10%
return 1.0; // 高風險
}
return bridgeTimeRatio * 10; // 0-1 風險評分
}
}
策略三:做市與套利結合
通過提供流動性同時捕捉套利機會。
class MarketMakingArbitrage {
private inventory: Map<string, number> = new Map();
async runMarketMaking(event: NormalizedEvent): Promise<void> {
// 獲取各交易所價格
const prices = await this.getAllPrices(event);
// 計算公允價值(加權平均)
const fairValue = this.calculateFairValue(prices);
// 在價格偏離的交易所提供流動性
for (const [exchange, price] of Object.entries(prices)) {
const deviation = (price.mid - fairValue) / fairValue;
if (Math.abs(deviation) > 0.01) { // 1% 偏離
if (deviation > 0) {
// 價格過高,掛賣單
await this.placeOrder(exchange, event, {
side: 'SELL',
price: price.mid * 0.995, // 略低於市價
size: this.calculateOrderSize(deviation),
});
} else {
// 價格過低,掛買單
await this.placeOrder(exchange, event, {
side: 'BUY',
price: price.mid * 1.005, // 略高於市價
size: this.calculateOrderSize(deviation),
});
}
}
}
// 管理庫存
await this.rebalanceInventory(event);
}
private async rebalanceInventory(event: NormalizedEvent): Promise<void> {
const currentInventory = this.inventory.get(event.canonicalId) || 0;
const maxInventory = 10000; // 最大持倉
if (Math.abs(currentInventory) > maxInventory * 0.8) {
// 庫存過大,需要在其他交易所對沖
const hedgeSize = currentInventory * 0.5;
const hedgeExchange = this.findBestHedgeVenue(event, currentInventory > 0 ? 'SELL' : 'BUY');
await this.placeOrder(hedgeExchange, event, {
side: currentInventory > 0 ? 'SELL' : 'BUY',
type: 'MARKET',
size: Math.abs(hedgeSize),
});
}
}
}
實際部署考量
基礎設施需求
graph TB
subgraph "生產環境"
subgraph "計算層"
Primary[Primary Server<br/>AWS c6i.xlarge]
Backup[Backup Server<br/>Different AZ]
end
subgraph "數據層"
Redis[(Redis Cluster)]
Postgres[(PostgreSQL)]
TimeSeries[(TimescaleDB)]
end
subgraph "網絡層"
LB[Load Balancer]
WSProxy[WebSocket Proxy]
end
end
subgraph "區塊鏈節點"
Polygon1[Polygon RPC 1]
Polygon2[Polygon RPC 2]
Archive[Archive Node]
end
Primary --> Redis
Primary --> Postgres
Backup --> Redis
LB --> Primary
LB --> Backup
WSProxy --> Polygon1
WSProxy --> Polygon2
Primary --> Archive
延遲優化
class LatencyOptimizer {
// 連接池管理
private connectionPools: Map<string, ConnectionPool> = new Map();
// 預熱連接
async warmConnections(): Promise<void> {
const venues = ['polymarket', 'limitless', 'kalshi'];
for (const venue of venues) {
const pool = new ConnectionPool({
maxConnections: 10,
minConnections: 3,
idleTimeout: 60000,
});
await pool.initialize();
this.connectionPools.set(venue, pool);
}
}
// 預簽名交易
async prepareSignedOrders(
opportunities: ArbitrageOpportunity[]
): Promise<PreparedOrder[]> {
return Promise.all(
opportunities.map(async (opp) => {
const order = this.buildOrder(opp);
const signature = await this.signOrder(order);
return {
order,
signature,
expiresAt: Date.now() + 30000, // 30 秒有效
};
})
);
}
// 並行執行
async executeParallel(orders: PreparedOrder[]): Promise<ExecutionResult[]> {
const startTime = process.hrtime.bigint();
const results = await Promise.allSettled(
orders.map((order) => this.submitOrder(order))
);
const endTime = process.hrtime.bigint();
const latencyNs = endTime - startTime;
console.log(`Parallel execution completed in ${Number(latencyNs) / 1e6}ms`);
return results.map((r) =>
r.status === 'fulfilled' ? r.value : { error: r.reason }
);
}
}
成本效益分析
| 項目 | 月成本估算 | 說明 |
|---|---|---|
| AWS 計算 | $200-500 | c6i.xlarge + backup |
| 數據庫 | $100-200 | RDS + Redis |
| RPC 節點 | $100-300 | Alchemy/QuickNode |
| 監控 | $50-100 | Grafana Cloud |
| Gas 費用 | $50-200 | Polygon 交易 |
| API 費用 | $0-100 | 部分交易所 API |
| 總計 | $500-1,400 |
盈虧平衡計算:
假設月固定成本: $1,000
假設平均套利利潤: 0.5% per trade
假設每日套利機會: 10 次
假設成功率: 80%
假設平均交易規模: $500
日均利潤 = 10 × 0.8 × $500 × 0.5% = $20
月利潤 = $20 × 30 = $600
需要將交易規模提高到 ~$850 或增加套利機會才能達到盈虧平衡
總結
預測市場套利是一個技術密集型的領域,成功的套利策略需要結合:
- 深度市場理解:了解各交易所的機制差異、費用結構、結算規則
- 高效技術架構:低延遲數據採集、快速決策引擎、可靠執行系統
- 嚴格風險管理:倉位控制、滑點管理、對沖策略
- 持續優化:監控表現、調整參數、適應市場變化
主要套利策略包括:
- 跨交易所價差套利:最基本但競爭激烈
- MINT/MERGE 套利:利用 CTF 機制的結構性機會
- 多結果市場套利:複雜但利潤穩定
- 統計套利:需要歷史數據和模型
- 跨鏈套利:高利潤但高風險
隨著預測市場生態系統的成熟,套利機會會逐漸減少,但新的市場和機制的出現會創造新的機會。持續的技術創新和市場洞察是長期成功的關鍵。