暗号資産取引システムにおいて、Binanceの現物市場(スポット)と証拠金取引市場(先物・フューチャーズ)のAPIは、表面上は似ているように見えても、実際にはNumerousなデータ構造と処理ロジックの差異が存在します。この差異を正確に處理しないと、裁定取引Botやポートフォリオ管理システムで致命的な計算誤差が発生します。
本稿では、10年以上の金融API統合経験を基に、両APIの差異を体系的に整理し、パフォーマンスと正確性を両立させた本番レベルの处理アーキテクチャを提案します。
Binance Spot API と Futures API の根本的差異
両APIの設計思想は大きく異なります。スポット市場が「現在时刻の取引」にフォーカスしているのに対し、先物市場は「将来の決済」を前提とした設計になっています。この思想の違いが、データ構造全体に波及します。
| 項目 | Binance Spot API | Binance Futures API | 実務上の影響 |
|---|---|---|---|
| ベースエンドポイント | api.binance.com | fapi.binance.com | 接続先切り替え必須 |
| symbol命名規則 | BTCUSDT | BTCUSDT | 同一だがエンドポイント別 |
| tickSize精度 | ティッカー依存 | ティッカー依存(8小数対応) | 价格計算の丸め処理要注意 |
| maker/taker手数料 | 0.1%/0.1% | 0.02%/0.04% | 裁定取引利益計算に影響 |
| 残高取得 | /api/v3/account | /fapi/v2/account | レスポンス構造完全不同 |
| 注文時のpassId | 必須(重複チェック) | 必須(同一) | Idempotency 制御重要 |
| フィル明細 | リアルタイムWebSocket | ユーザーデータStream | イベント処理アーキテクチャ不同 |
| 約定価格の種類 | 約定時price | 約定時price+気配値価格 | 損益計算ロジック差異 |
アーキテクチャ設計:統合抽象化レイヤー
私が以前担当したヘッジファンドのプロジェクトでは、スポットと先物のAPI差異を 개별的なクライアントに分离した 结果、コードが2倍に肥大化し保守性が著しく低下しました。その後、リ팩タリングする際には、共通抽象化レイヤー(Unified Abstraction Layer)を設計することで这些问题を解決しました。
統一データモデルの定義
// TypeScript実装:統合データモデル
interface UnifiedSymbol {
symbol: string; // BTCUSDT
exchange: 'binance_spot' | 'binance_futures';
baseAsset: string; // BTC
quoteAsset: string; // USDT
tickSize: number; // 0.01
lotSize: number; // 0.00001
minNotional: number; // 最小注文額
status: 'TRADING' | 'HALT' | 'BREAK';
}
interface UnifiedTicker {
symbol: string;
exchange: string;
price: number; // 最終取引価格
bidPrice: number; // 最良買値
askPrice: number; // 最良売値
bidQty: number;
askQty: number;
volume24h: number; // 24時間出来高
quoteVolume24h: number; // 24時間取引額
timestamp: number; // Unix ms
priceChangePercent24h: number;
}
interface UnifiedBalance {
asset: string;
free: number; // 利用可能残高
locked: number; // ロック中(注文拘束)
crossWalletBalance?: number; // 先物のみ:分離証拠金
marginBalance?: number; // 先物のみ:통합証拠金
unrealizedProfit: number; // 先物のみ:評価損益
}
interface UnifiedOrder {
orderId: number;
clientOrderId: string;
symbol: string;
exchange: string;
side: 'BUY' | 'SELL';
type: 'LIMIT' | 'MARKET' | 'STOP_LOSS' | 'TAKE_PROFIT';
status: 'NEW' | 'PARTIALLY_FILLED' | 'FILLED' | 'CANCELED' | 'REJECTED';
price: number;
origQty: number;
executedQty: number;
avgPrice: number;
commission: number; // 手数料
commissionAsset: string; // 手数料通貨
time: number; // 発注時刻
updateTime: number; // 最終更新時刻
isIsolated: boolean; // 先物のみ
}
// 統一RESTクライアント基底クラス
abstract class UnifiedBinanceClient {
protected baseUrl: string;
protected apiKey: string;
protected apiSecret: string;
protected recvWindow: number = 5000; // デフォルト5秒
constructor(baseUrl: string, apiKey: string, apiSecret: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
this.apiSecret = apiSecret;
}
protected async signedRequest(
method: 'GET' | 'POST' | 'DELETE',
endpoint: string,
params: Record = {}
): Promise<any> {
const timestamp = Date.now();
const queryParams = { ...params, timestamp, recvWindow: this.recvWindow };
const queryString = Object.entries(queryParams)
.filter(([_, v]) => v !== undefined)
.map(([k, v]) => ${k}=${encodeURIComponent(v)})
.join('&');
const signature = await this.generateSignature(queryString);
const url = ${this.baseUrl}${endpoint}?${queryString}&signature=${signature};
const response = await fetch(url, {
method,
headers: {
'X-MBX-APIKEY': this.apiKey,
'Content-Type': 'application/x-www-form-urlencoded'
}
});
if (!response.ok) {
const error = await response.json();
throw new BinanceAPIError(error.code, error.msg, endpoint);
}
return response.json();
}
protected async generateSignature(queryString: string): Promise<string> {
const encoder = new TextEncoder();
const key = encoder.encode(this.apiSecret);
const data = encoder.encode(queryString);
// HMAC-SHA256
const cryptoKey = await crypto.subtle.importKey(
'raw', key,
{ name: 'HMAC', hash: 'SHA-256' },
false, ['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, data);
return Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
abstract fetchTicker(symbol: string): Promise<UnifiedTicker>;
abstract fetchBalance(): Promise<UnifiedBalance[]>;
abstract fetchOrder(symbol: string, orderId: number): Promise<UnifiedOrder>;
abstract placeOrder(order: Partial<UnifiedOrder>): Promise<UnifiedOrder>;
}
スポット・先物クライアントの実装
// Binance Spot クライアント実装
class BinanceSpotClient extends UnifiedBinanceClient {
constructor(apiKey: string, apiSecret: string) {
super('https://api.binance.com', apiKey, apiSecret);
}
async fetchTicker(symbol: string): Promise<UnifiedTicker> {
const data = await this.publicRequest('/api/v3/ticker/24hr', { symbol });
return {
symbol: data.symbol,
exchange: 'binance_spot',
price: parseFloat(data.lastPrice),
bidPrice: parseFloat(data.bidPrice),
askPrice: parseFloat(data.askPrice),
bidQty: parseFloat(data.bidQty),
askQty: parseFloat(data.askQty),
volume24h: parseFloat(data.volume),
quoteVolume24h: parseFloat(data.quoteVolume),
timestamp: data.closeTime,
priceChangePercent24h: parseFloat(data.priceChangePercent)
};
}
async fetchBalance(): Promise<UnifiedBalance[]> {
const data = await this.signedRequest('GET', '/api/v3/account');
return data.balances
.filter((b: any) => parseFloat(b.free) > 0 || parseFloat(b.locked) > 0)
.map((b: any) => ({
asset: b.asset,
free: parseFloat(b.free),
locked: parseFloat(b.locked),
unrealizedProfit: 0 // スポットには存在しない
}));
}
async fetchOrder(symbol: string, orderId: number): Promise<UnifiedOrder> {
const data = await this.signedRequest('GET', '/api/v3/order', { symbol, orderId });
return this.parseOrderResponse(data, 'binance_spot');
}
async placeOrder(order: Partial<UnifiedOrder>): Promise<UnifiedOrder> {
const params: Record<string, any> = {
symbol: order.symbol,
side: order.side,
type: order.type,
quantity: order.origQty
};
if (order.type === 'LIMIT') {
params.timeInForce = 'GTC';
params.price = order.price;
}
if (order.clientOrderId) {
params.newClientOrderId = order.clientOrderId;
}
const data = await this.signedRequest('POST', '/api/v3/order', params);
return this.parseOrderResponse(data, 'binance_spot');
}
private parseOrderResponse(data: any, exchange: string): UnifiedOrder {
return {
orderId: data.orderId,
clientOrderId: data.clientOrderId,
symbol: data.symbol,
exchange,
side: data.side,
type: data.type,
status: data.status,
price: parseFloat(data.price),
origQty: parseFloat(data.origQty),
executedQty: parseFloat(data.executedQty),
avgPrice: parseFloat(data.avgPrice || data.price),
commission: parseFloat(data.commission || '0'),
commissionAsset: data.commissionAsset || data.symbol.slice(-4),
time: data.transactTime || data.time,
updateTime: data.updateTime || data.transactTime,
isIsolated: false
};
}
private async publicRequest(endpoint: string, params: Record<string, any> = {}): Promise<any> {
const queryString = Object.entries(params)
.map(([k, v]) => ${k}=${v})
.join('&');
const response = await fetch(${this.baseUrl}${endpoint}?${queryString});
return response.json();
}
}
// Binance Futures クライアント実装
class BinanceFuturesClient extends UnifiedBinanceClient {
constructor(apiKey: string, apiSecret: string) {
super('https://fapi.binance.com', apiKey, apiSecret);
}
async fetchTicker(symbol: string): Promise<UnifiedTicker> {
// 先物は24hrティッカーではなくpremiumIndexを使用
const data = await this.publicRequest('/fapi/v1/ticker/24hr', { symbol });
return {
symbol: data.symbol,
exchange: 'binance_futures',
price: parseFloat(data.lastPrice),
bidPrice: parseFloat(data.bidPrice),
askPrice: parseFloat(data.askPrice),
bidQty: parseFloat(data.bidQty),
askQty: parseFloat(data.askQty),
volume24h: parseFloat(data.volume),
quoteVolume24h: parseFloat(data.quoteVolume),
timestamp: data.closeTime,
priceChangePercent24h: parseFloat(data.priceChangePercent)
};
}
async fetchBalance(): Promise<UnifiedBalance[]> {
// 先物の残高取得はfapi/v2を使用
const data = await this.signedRequest('GET', '/fapi/v2/account');
const assets = data.assets || [];
return assets
.filter((a: any) => parseFloat(a.walletBalance) > 0)
.map((a: any) => ({
asset: a.asset,
free: parseFloat(a.walletBalance),
locked: 0,
crossWalletBalance: parseFloat(a.walletBalance),
marginBalance: parseFloat(a.marginBalance),
unrealizedProfit: parseFloat(a.unrealizedProfit)
}));
}
async fetchOrder(symbol: string, orderId: number): Promise<UnifiedOrder> {
const data = await this.signedRequest('GET', '/fapi/v1/order', { symbol, orderId });
return this.parseOrderResponse(data, 'binance_futures');
}
async placeOrder(order: Partial<UnifiedOrder>): Promise<UnifiedOrder> {
const params: Record<string, any> = {
symbol: order.symbol,
side: order.side,
type: order.type,
quantity: this.normalizeQuantity(order.symbol!, order.origQty!)
};
if (order.type === 'LIMIT') {
params.timeInForce = 'GTX'; // 先物はGTX(ポストのみ)
params.price = this.normalizePrice(order.symbol!, order.price!);
}
if (order.clientOrderId) {
params.newClientOrderId = order.clientOrderId;
}
// 先物特有のショート注文
if ((order as any).reduceOnly !== undefined) {
params.reduceOnly = (order as any).reduceOnly;
}
const data = await this.signedRequest('POST', '/fapi/v1/order', params);
return this.parseOrderResponse(data, 'binance_futures');
}
// 先物用の数量正規化(lotSizeに従う)
private normalizeQuantity(symbol: string, qty: number): string {
// 実際にはexchangeInfoからlotSizeを取得
const lotSize = 0.00001; // BTC先物の例
const precision = Math.round(-Math.log10(lotSize));
return qty.toFixed(precision);
}
// 先物用の価格正規化(tickSizeに従う)
private normalizePrice(symbol: string, price: number): string {
const tickSize = 0.01;
const precision = Math.round(-Math.log10(tickSize));
return price.toFixed(precision);
}
private parseOrderResponse(data: any, exchange: string): UnifiedOrder {
return {
orderId: data.orderId,
clientOrderId: data.clientOrderId,
symbol: data.symbol,
exchange,
side: data.side,
type: data.type,
status: this.mapOrderStatus(data.status),
price: parseFloat(data.price),
origQty: parseFloat(data.origQty),
executedQty: parseFloat(data.executedQty),
avgPrice: parseFloat(data.avgPrice || data.price),
commission: parseFloat(data.commission || '0'),
commissionAsset: data.commissionAsset || 'USDT',
time: data.time,
updateTime: data.updateTime,
isIsolated: data.isIsolated || false
};
}
private mapOrderStatus(status: string): UnifiedOrder['status'] {
const statusMap: Record<string, UnifiedOrder['status']> = {
'NEW': 'NEW',
'PARTIALLY_FILLED': 'PARTIALLY_FILLED',
'FILLED': 'FILLED',
'CANCELED': 'CANCELED',
'REJECTED': 'REJECTED',
'EXPIRED': 'CANCELED'
};
return statusMap[status] || 'REJECTED';
}
private async publicRequest(endpoint: string, params: Record<string, any> = {}): Promise<any> {
const queryString = Object.entries(params)
.map(([k, v]) => ${k}=${v})
.join('&');
const response = await fetch(${this.baseUrl}${endpoint}?${queryString});
return response.json();
}
}
// 統一ファクトリー
class BinanceClientFactory {
static createSpotClient(apiKey: string, apiSecret: string): BinanceSpotClient {
return new BinanceSpotClient(apiKey, apiSecret);
}
static createFuturesClient(apiKey: string, apiSecret: string): BinanceFuturesClient {
return new BinanceFuturesClient(apiKey, apiSecret);
}
// 両市場への统一的アクセス
static createUnifiedClient(apiKey: string, apiSecret: string): Map<string, UnifiedBinanceClient> {
return new Map([
['spot', new BinanceSpotClient(apiKey, apiSecret)],
['futures', new BinanceFuturesClient(apiKey, apiSecret)]
]);
}
}
タイムスタンプ処理の陷阱と解決策
私が過去に経験した最も头痛な 문제는、タイムスタンプの不整合でした。スポットと先物では、时刻情報の粒度と基准が异なるため、跨市場 분석時に致命的な误差が発生します。
// タイムスタンプ正規化ユーティリティ
class TimestampNormalizer {
// Binance API は Unix timestamp (ミリ秒) を使用
// しかし、网络遅延やサーバ负载により slight な误差が発生
static normalize(timestamp: number | string, exchange: string): number {
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
// 先物は sometimes 秒単位を返す
if (ts < 1e12) {
console.warn([${exchange}] Timestamp ${ts} appears to be in seconds, converting);
return ts * 1000;
}
return ts;
}
static toLocalTime(timestamp: number): Date {
return new Date(timestamp);
}
static diffMs(ts1: number, ts2: number): number {
return Math.abs(ts1 - ts2);
}
// ⚠️ Timestamp discrepancy detected: ${tsDiff}ms );
}
// -spread 计算(先物-スポット)
const spread = (futuresTicker.bidPrice - spotTicker.askPrice);
// 年率换算(先物期間约为3个月=90日)
const daysToExpiry = 90;
const annualizedSpread = (spread / spotTicker.price) * (365 / daysToExpiry) * 100;
return {
spotBid: spotTicker.bidPrice,
spotAsk: spotTicker.askPrice,
futuresBid: futuresTicker.bidPrice,
futuresAsk: futuresTicker.askPrice,
spread,
annualizedSpread,
timestamp: Math.max(spotTs, futuresTs),
latencyMs
};
}
}
同時実行制御とレートリミット
Binance APIには詳細なレートリミットがあり、これを無視するとIP或いはAPI Key単位での制限されます。私が運用するシステムでは、spotsとfutures、それそれに个別の Rate Limiter を実装し、合計リクエスト数を制御しています。
// リーキーコンバケツアルゴリズムによるレート制御
class RateLimiter {
private capacity: number;
private refillRate: number; // tokens per second
private tokens: number;
private lastRefill: number;
private queue: Array<() => void> = [];
private processing: boolean = false;
constructor(capacity: number, refillPerSecond: number) {
this.capacity = capacity;
this.refillRate = refillPerSecond;
this.tokens = capacity;
this.lastRefill = Date.now();
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
const newTokens = elapsed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + newTokens);
this.lastRefill = now;
}
async acquire(tokens: number = 1): Promise<void> {
this.refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return;
}
// トークンが回復するまでの待機
const waitTime = ((tokens - this.tokens) / this.refillRate) * 1000;
return new Promise((resolve) => {
this.queue.push(resolve);
setTimeout(() => {
this.refill();
this.tokens -= tokens;
resolve();
this.processQueue();
}, waitTime);
});
}
private processQueue(): void {
if (this.queue.length === 0) return;
this.refill();
while (this.queue.length > 0 && this.tokens > 0) {
this.tokens -= 1;
const resolve = this.queue.shift()!;
resolve();
}
}
getAvailableTokens(): number {
this.refill();
return this.tokens;
}
}
// API クライアントラッパー(レート制限付き)
class RateLimitedClient {
private spotLimiter: RateLimiter;
private futuresLimiter: RateLimiter;
private spotClient: BinanceSpotClient;
private futuresClient: BinanceFuturesClient;
constructor(apiKey: string, apiSecret: string) {
// スポット:1200 requests/min = 20/sec
this.spotLimiter = new RateLimiter(100, 20);
// 先物:2400 requests/min = 40/sec
this.futuresLimiter = new RateLimiter(100, 40);
this.spotClient = BinanceClientFactory.createSpotClient(apiKey, apiSecret);
this.futuresClient = BinanceClientFactory.createFuturesClient(apiKey, apiSecret);
}
async spotRequest<T>(request: () => Promise<T>): Promise<T> {
await this.spotLimiter.acquire();
return request();
}
async futuresRequest<T>(request: () => Promise<T>): Promise<T> {
await this.futuresLimiter.acquire();
return request();
}
// 統合リクエスト(両市場へのアクセスが必要な場合)
async unifiedRequest<T>(
spotRequest: () => Promise<T>,
futuresRequest: () => Promise<T>
): Promise<{ spot: T; futures: T; latencyMs: number }> {
const startTime = Date.now();
const [spot, futures] = await Promise.all([
this.spotRequest(spotRequest),
this.futuresRequest(futuresRequest)
]);
return {
spot,
futures,
latencyMs: Date.now() - startTime
};
}
}
// WebSocket接続管理器( тоже レート制限考虑)
class WebSocketManager {
private spotStreams: Map<string, WebSocket> = new Map();
private futuresStreams: Map<string, WebSocket> = new Map();
private reconnectAttempts: Map<string, number> = new Map();
private maxReconnectAttempts: number = 5;
private baseUrl = {
spot: 'wss://stream.binance.com:9443/ws',
futures: 'wss://fstream.binance.com/ws'
};
subscribeSpot(
symbols: string[],
callback: (data: any) => void
): void {
const streams = symbols.map(s => ${s.toLowerCase()}@ticker);
const ws = new WebSocket(${this.baseUrl.spot}/${streams.join('/')});
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
callback(data);
};
ws.onerror = (error) => {
console.error('Spot WebSocket error:', error);
this.handleReconnect('spot', symbols, callback);
};
symbols.forEach(s => this.spotStreams.set(s, ws));
}
subscribeFutures(
symbols: string[],
callback: (data: any) => void
): void {
const streams = symbols.map(s => ${s.toLowerCase()}@ticker);
const ws = new WebSocket(${this.baseUrl.futures}/${streams.join('/')});
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
callback(data);
};
ws.onerror = (error) => {
console.error('Futures WebSocket error:', error);
this.handleReconnect('futures', symbols, callback);
};
symbols.forEach(s => this.futuresStreams.set(s, ws));
}
private handleReconnect(
type: 'spot' | 'futures',
symbols: string[],
callback: (data: any) => void
): void {
const key = symbols.join(',');
const attempts = this.reconnectAttempts.get(key) || 0;
if (attempts >= this.maxReconnectAttempts) {
console.error(Max reconnect attempts reached for ${key});
return;
}
const delay = Math.min(1000 * Math.pow(2, attempts), 30000);
console.log(Reconnecting to ${type} in ${delay}ms (attempt ${attempts + 1}));
setTimeout(() => {
this.reconnectAttempts.set(key, attempts + 1);
if (type === 'spot') {
this.subscribeSpot(symbols, callback);
} else {
this.subscribeFutures(symbols, callback);
}
}, delay);
}
disconnectAll(): void {
this.spotStreams.forEach(ws => ws.close());
this.futuresStreams.forEach(ws => ws.close());
this.spotStreams.clear();
this.futuresStreams.clear();
}
}
コスト最適化:Binance API vs HolySheep AI
Binance API本身的 costs は低いですが、アルゴリズミック取引には 市场分析、ニュース处理、センチメント分析などのために LLM API が不可欠です。ここでHolySheep AIを活用することで、大幅なコスト削减が可能になります。
| Provider / Model | 価格 ($/1M Tokens) | 相対コスト | 推奨ユースケース |
|---|---|---|---|
| GPT-4.1 | $8.00 | 基准 | 高精度分析·コード生成 |
| Claude Sonnet 4.5 | $15.00 | 1.88x | 長い文脈分析·推論 |
| Gemini 2.5 Flash | $2.50 | 0.31x | 高速处理·批量分析 |
| DeepSeek V3.2 | $0.42 | 0.05x | コスト重視の批量处理 |
私がHolySheep AIを今すぐ登録して最も気に入っている点は、公式為替レートの¥1=$1という破格の料金体系です。現在の日本の銀行為替レート(约¥150/$)と比較して、約85%の节约になります。
// HolySheep AI との統合例:取引シグナル分析
class TradingSignalAnalyzer {
private holySheepApiKey: string;
private baseUrl = 'https://api.holysheep.ai/v1';
constructor(apiKey: string) {
this.holySheepApiKey = apiKey;
}
// 市場センチメント分析
async analyzeMarketSentiment(
spotPrice: number,
futuresPrice: number,
volume24h: number,
fundingRate: number
): Promise<{
signal: 'BULLISH' | 'BEARISH' | 'NEUTRAL';
confidence: number;
reasoning: string;
}> {
const prompt = this.buildSentimentPrompt(spotPrice, futuresPrice, volume24h, fundingRate);
const response = await fetch(${this.baseUrl}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${this.holysheepApiKey},
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-v3.2',
messages: [
{
role: 'system',
content: '你是一个专业的加密货币交易分析师。请基于提供的数据给出简洁的交易信号。'
},
{
role: 'user',
content: prompt
}
],
temperature: 0.3,
max_tokens: 500
})
});
if (!response.ok) {
throw new Error(HolySheep API error: ${response.status});
}
const data = await response.json();
return this.parseSignalResponse(data.choices[0].message.content);
}
// 裁定取引機会の検出
async detectArbitrageOpportunity(
spotBid: number,
spotAsk: number,
futuresBid: number,
futuresAsk: number,
fundingRate: number,
expectedHoldingDays: number
): Promise<{
opportunity: boolean;
grossSpread: number;
netSpread: number;
annualizedReturn: number;
riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
recommendation: string;
}> {
const prompt = `
Spot Market:
- Best Bid: ${spotBid}
- Best Ask: ${spotAsk}
- Spread: ${((spotAsk - spotBid) / spotBid * 100).toFixed(4)}%
Futures Market:
- Best Bid: ${futuresBid}
- Best Ask: ${futuresAsk}
- Spread: ${((futuresAsk - futuresBid) / futuresBid * 100).toFixed(4)}%
Current Funding Rate: ${(fundingRate * 100).toFixed(4)}%
Expected Holding Period: ${expectedHoldingDays} days
Calculate:
1. Gross spread (futures bid - spot ask)
2. Net spread after funding costs
3. Annualized return percentage
4. Risk assessment
Provide your analysis in JSON format.`;
const response = await fetch(${this.baseUrl}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${this.holysheepApiKey},
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gemini-2.5-flash',
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0.1,
response_format: { type: 'json_object' }
})
});
const data = await response.json();
return JSON.parse(data.choices[0].message.content);
}
// プロンプト構築
private buildSentimentPrompt(
spotPrice: number,
futuresPrice: number,
volume24h: number,
fundingRate: number
): string {
const basis = ((futuresPrice - spotPrice) / spotPrice * 100).toFixed(4);
return `
Current Market Data:
- BTC Spot Price: $${spotPrice}
- BTC Futures Price: $${futuresPrice}
- Basis: ${basis}%
- 24h Volume: ${volume24h.toFixed(2)} BTC
- Current Funding Rate: ${(fundingRate * 100).toFixed(4)}%
Analyze the market sentiment and provide:
1. Overall signal (BULLISH/BEARISH/NEUTRAL)
2. Confidence level (0-100%)
3. Brief reasoning
Respond in the following JSON format only:
{
"signal": "BULLISH/BEARISH/NEUTRAL",