Skip to content

Commit

Permalink
refactor: update hummingbot plugin implementation
Browse files Browse the repository at this point in the history
- Update HummingbotConfig to use top-level apiUrl and apiKey
- Add proper type definitions for market making components
- Improve simple market making strategy implementation
- Fix TypeScript errors and improve type safety
  • Loading branch information
Sumeet Chougule authored and Sumeet Chougule committed Dec 31, 2024
1 parent 48df0af commit 83d2ad3
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 98 deletions.
23 changes: 15 additions & 8 deletions packages/plugin-hummingbot/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ export class HummingbotPlugin extends Plugin {
public async init(context: IAgentRuntime): Promise<void> {
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);
Expand Down Expand Up @@ -82,7 +82,14 @@ export class HummingbotPlugin extends Plugin {
* @returns Market data
*/
async getMarketData(exchange: string, symbol: string): Promise<MarketData> {
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
};
}

/**
Expand Down Expand Up @@ -162,9 +169,9 @@ export class HummingbotPlugin extends Plugin {
* @returns Promise resolving to array of portfolio balances
*/
async getBalances(): Promise<PortfolioBalance[]> {
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;
Expand Down
109 changes: 45 additions & 64 deletions packages/plugin-hummingbot/src/strategies/simple-market-making.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number | null> {
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<void> {
Expand Down
76 changes: 50 additions & 26 deletions packages/plugin-hummingbot/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export interface HummingbotConfig {
apiUrl: string;
wsUrl: string;
apiKey: string;
strategy: StrategyConfig;
strategy?: StrategyConfig;
}

export interface MarketData {
Expand All @@ -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;
}

0 comments on commit 83d2ad3

Please sign in to comment.