diff --git a/packages/plugin-hummingbot/src/index.ts b/packages/plugin-hummingbot/src/index.ts index 31104c47d0..538e07b7b5 100644 --- a/packages/plugin-hummingbot/src/index.ts +++ b/packages/plugin-hummingbot/src/index.ts @@ -36,12 +36,12 @@ export class HummingbotPlugin extends Plugin { public async init(context: IAgentRuntime): Promise { this.context = context; - if (!this.config.instance.url || !this.config.instance.apiKey) { - throw new Error('Missing required configuration: url and apiKey must be provided'); + if (!this.config.apiUrl || !this.config.apiKey) { + throw new Error('Missing required configuration: apiUrl and apiKey must be provided'); } - const baseUrl = this.config.instance.url; - const apiKey = this.config.instance.apiKey; + const baseUrl = this.config.apiUrl; + const apiKey = this.config.apiKey; this.marketDataProvider = new MarketDataProvider(baseUrl, apiKey); this._orderService = new OrderService(baseUrl, apiKey); @@ -82,7 +82,14 @@ export class HummingbotPlugin extends Plugin { * @returns Market data */ async getMarketData(exchange: string, symbol: string): Promise { - return this.marketDataService.getMarketData(exchange, symbol); + const data = await this.marketDataService.getMarketData(exchange, symbol); + return { + symbol: data.symbol, + lastPrice: data.lastPrice, + volume24h: data.volume24h, + price: data.price, + timestamp: data.timestamp + }; } /** @@ -162,9 +169,9 @@ export class HummingbotPlugin extends Plugin { * @returns Promise resolving to array of portfolio balances */ async getBalances(): Promise { - const response = await axios.get(`${this.config.instance.url}/balances`, { - headers: this.config.instance.apiKey ? { - 'X-API-Key': this.config.instance.apiKey + const response = await axios.get(`${this.config.apiUrl}/balances`, { + headers: this.config.apiKey ? { + 'X-API-Key': this.config.apiKey } : undefined }); return response.data; diff --git a/packages/plugin-hummingbot/src/strategies/simple-market-making.ts b/packages/plugin-hummingbot/src/strategies/simple-market-making.ts index b0a67e9a30..34adfda520 100644 --- a/packages/plugin-hummingbot/src/strategies/simple-market-making.ts +++ b/packages/plugin-hummingbot/src/strategies/simple-market-making.ts @@ -101,102 +101,83 @@ export class SimpleMarketMaking { throw new Error('Unable to determine reference price'); } - // Create pricing proposal + // Create pricing and sizing proposals const pricingProposal = this.createPricingProposal(refPrice); - - // Create sizing proposal const sizingProposal = this.createSizingProposal(); - // Combine into final proposal + // Combine into order proposal return { actions: ORDER_PROPOSAL_ACTION_CREATE_ORDERS | ORDER_PROPOSAL_ACTION_CANCEL_ORDERS, - buyOrderType: 'limit' as OrderType, - buyOrderPrices: pricingProposal.buyOrderPrices, - buyOrderSizes: sizingProposal.buyOrderSizes, - sellOrderType: 'limit' as OrderType, - sellOrderPrices: pricingProposal.sellOrderPrices, - sellOrderSizes: sizingProposal.sellOrderSizes, - cancelOrderIds: Array.from(this.activeOrders.keys()) + cancelOrderIds: Array.from(this.activeOrders.keys()), + buyOrderPrices: pricingProposal.buyPrices, + sellOrderPrices: pricingProposal.sellPrices, + buyOrderSizes: sizingProposal.buySizes, + sellOrderSizes: sizingProposal.sellSizes, + buyOrderType: OrderType.LIMIT, + sellOrderType: OrderType.LIMIT }; } private async getRefPrice(marketData: MarketData): Promise { - switch (this.config.params.priceSource) { - case 'current_market': - return marketData.price; - - case 'external_market': - if (!this.config.params.externalExchange || !this.config.params.externalMarket) { - throw new Error('External market price source configured but missing exchange/market'); - } - const externalData = await this.plugin.getMarketData( - this.config.params.externalExchange, - this.config.params.externalMarket - ); - return externalData?.price || null; - - case 'custom_api': - // Implement custom API price source if needed - throw new Error('Custom API price source not implemented'); - - default: - return null; - } + return marketData.price || marketData.lastPrice || null; } private createPricingProposal(refPrice: number): PricingProposal { - const { spreadBasis, priceOffset, orderLevels = 1, minSpread = 0, maxSpread = 100 } = this.config.params; + const { bidSpread, askSpread, orderLevels } = this.config.params; const buyPrices: number[] = []; const sellPrices: number[] = []; for (let i = 0; i < orderLevels; i++) { - const levelMultiplier = i + 1; + const levelSpreadMultiplier = 1 + (i * 0.5); // Increase spread by 50% for each level + const buyPrice = refPrice * (1 - (bidSpread * levelSpreadMultiplier)); + const sellPrice = refPrice * (1 + (askSpread * levelSpreadMultiplier)); - // Calculate spreads with bounds - const effectiveBidSpread = Math.min(Math.max((spreadBasis + priceOffset) * levelMultiplier, minSpread), maxSpread) / 10000; - const effectiveAskSpread = Math.min(Math.max((spreadBasis - priceOffset) * levelMultiplier, minSpread), maxSpread) / 10000; - - // Calculate prices - const buyPrice = refPrice * (1 - effectiveBidSpread); - const sellPrice = refPrice * (1 + effectiveAskSpread); - buyPrices.push(buyPrice); sellPrices.push(sellPrice); } - return { buyOrderPrices: buyPrices, sellOrderPrices: sellPrices }; + return { buyPrices, sellPrices }; } private createSizingProposal(): SizingProposal { - const { orderAmount, orderLevels = 1, inventorySkew = false, targetBaseRatio = 50 } = this.config.params; + const { orderAmount, orderLevels } = this.config.params; + const targetBaseRatio = this.config.params.inventoryTarget || 0.5; + const skewRatios = this.calculateInventorySkew(targetBaseRatio); - let skewRatios: InventorySkewRatios = { bidRatio: 1, askRatio: 1 }; - - if (inventorySkew) { - skewRatios = this.calculateInventorySkew(targetBaseRatio); - } + const buySizes: number[] = []; + const sellSizes: number[] = []; - const buyOrderSizes: number[] = Array(orderLevels).fill(orderAmount * skewRatios.bidRatio); - const sellOrderSizes: number[] = Array(orderLevels).fill(orderAmount * skewRatios.askRatio); + for (let i = 0; i < orderLevels; i++) { + const levelSizeMultiplier = 1 - (i * 0.2); // Decrease size by 20% for each level + const baseSize = orderAmount * levelSizeMultiplier; + + buySizes.push(baseSize * skewRatios.buyRatio); + sellSizes.push(baseSize * skewRatios.sellRatio); + } - return { buyOrderSizes, sellOrderSizes }; + return { buySizes, sellSizes }; } private calculateInventorySkew(targetBaseRatio: number): InventorySkewRatios { - const totalValue = this.baseBalance + this.quoteBalance; - if (totalValue === 0) return { bidRatio: 1, askRatio: 1 }; - + const totalValue = this.baseBalance + (this.quoteBalance / this.config.params.refPrice); const currentBaseRatio = this.baseBalance / totalValue; - const targetRatio = targetBaseRatio / 100; // Convert from percentage - const deviation = currentBaseRatio - targetRatio; - const inventoryRangeMultiplier = this.config.params.inventoryRangeMultiplier ?? 1; - const skewFactor = Math.min(Math.max(-deviation * inventoryRangeMultiplier, -1), 1); - - return { - bidRatio: 1 - skewFactor, - askRatio: 1 + skewFactor - }; + // Calculate skew based on difference from target ratio + const skewMultiplier = Math.min(Math.abs(currentBaseRatio - targetBaseRatio) * 2, 1); + + if (currentBaseRatio < targetBaseRatio) { + // Need more base asset, increase buy orders + return { + buyRatio: 1 + skewMultiplier, + sellRatio: 1 - skewMultiplier + }; + } else { + // Need more quote asset, increase sell orders + return { + buyRatio: 1 - skewMultiplier, + sellRatio: 1 + skewMultiplier + }; + } } private async createOrders(proposal: OrderProposal): Promise { diff --git a/packages/plugin-hummingbot/src/types/index.ts b/packages/plugin-hummingbot/src/types/index.ts index 4ad509237f..355df90f6a 100644 --- a/packages/plugin-hummingbot/src/types/index.ts +++ b/packages/plugin-hummingbot/src/types/index.ts @@ -1,8 +1,7 @@ export interface HummingbotConfig { apiUrl: string; - wsUrl: string; apiKey: string; - strategy: StrategyConfig; + strategy?: StrategyConfig; } export interface MarketData { @@ -18,43 +17,68 @@ export interface OrderParams { side: 'buy' | 'sell'; amount: number; price: number; + exchange: string; + type: OrderType; + timestamp: number; } -export interface StrategyConfig { - symbol: string; - spread: number; - orderSize: number; - minProfitMargin: number; +export type OrderType = 'limit' | 'market'; + +export const ORDER_PROPOSAL_ACTION_CREATE_ORDERS = 1; +export const ORDER_PROPOSAL_ACTION_CANCEL_ORDERS = 2; + +export interface OrderProposal { + actions: number; + cancelOrderIds: string[]; + buyOrderType: OrderType; + buyOrderPrices: number[]; + buyOrderSizes: number[]; + sellOrderType: OrderType; + sellOrderPrices: number[]; + sellOrderSizes: number[]; } -export interface PortfolioBalance { - asset: string; - free: number; - total: number; +export interface PricingProposal { + buyPrices: number[]; + sellPrices: number[]; } -export interface HummingbotInstance { - id: string; - status: 'running' | 'stopped'; - strategy: string; - config: HummingbotConfig; +export interface SizingProposal { + buySizes: number[]; + sellSizes: number[]; } -export interface MarketMakingConfig { +export interface InventorySkewRatios { + buyRatio: number; + sellRatio: number; +} + +export interface StrategyConfig { + name: string; exchange: string; tradingPair: string; + params: MarketMakingParams; +} + +export interface MarketMakingParams { orderAmount: number; orderLevels: number; maxOrderAge: number; - inventorySkewEnabled: boolean; - inventoryTargetBase: number; - inventoryRangeMultiplier: number; + cooldownPeriod: number; bidSpread: number; askSpread: number; - minSpread: number; - maxSpread: number; - priceSource: string; - minimumSpreadEnabled: boolean; - pingPongEnabled: boolean; - orderRefreshTime: number; + inventoryTarget: number; + refPrice: number; +} + +export interface PortfolioBalance { + asset: string; + free: number; + total: number; +} + +export interface MarketMakingConfig { + exchange: string; + tradingPair: string; + params: MarketMakingParams; }