Skip to content

Full Agent Example

Complete autonomous funding rate farming agent.

The Code

typescript
import { ZirodeltaClient, StrategyEngine } from 'zirodelta-agent-toolkit';

interface AgentConfig {
  token: string;
  balances: Record<string, number>;
  dailyTarget: number;
  riskProfile: 'conservative' | 'moderate' | 'aggressive';
  checkIntervalMs: number;
  stopLossPercent: number;
}

class FundingRateAgent {
  private client: ZirodeltaClient;
  private engine: StrategyEngine;
  private config: AgentConfig;
  private running = false;
  private dailyPnL = 0;

  constructor(config: AgentConfig) {
    this.config = config;
    this.client = new ZirodeltaClient({ token: config.token });
    this.engine = new StrategyEngine(this.client);
    
    this.engine.setProfile({
      balances: config.balances,
      riskProfile: config.riskProfile,
      dailyTarget: config.dailyTarget,
      maxPositionSize: 30,
      maxOpenPositions: 5,
      minSpread: 0.03
    });
  }

  async start() {
    this.running = true;
    this.log('🚀 Agent starting...');
    
    // Reset daily PnL at midnight
    this.scheduleDailyReset();
    
    while (this.running) {
      try {
        await this.runCycle();
      } catch (error) {
        this.log(`❌ Error: ${error.message}`);
      }
      
      await this.sleep(this.config.checkIntervalMs);
    }
  }

  stop() {
    this.running = false;
    this.log('🛑 Agent stopped');
  }

  private async runCycle() {
    // 1. Get current portfolio
    const portfolio = await this.client.getPortfolio();
    const currentROI = portfolio.summary.weighted_roi * 100;
    
    this.log(`📊 Portfolio: $${portfolio.summary.total_unrealized_pnl.toFixed(2)} (${currentROI.toFixed(2)}%)`);

    // 2. Check daily target
    if (this.dailyPnL >= this.config.dailyTarget) {
      this.log('🎯 Daily target reached!');
      await this.closeAllPositions(portfolio);
      return;
    }

    // 3. Manage existing positions
    await this.managePositions(portfolio);

    // 4. Open new positions if room
    if (portfolio.executions.length < 5) {
      await this.openNewPositions();
    }
  }

  private async managePositions(portfolio: any) {
    for (const exec of portfolio.executions) {
      // Stop loss
      if (exec.roi < -this.config.stopLossPercent) {
        this.log(`🛑 Stop loss: ${exec.symbol} @ ${exec.roi.toFixed(2)}%`);
        await this.client.closeExecution({ execution_id: exec.execution.id });
        continue;
      }

      // Take profit at 5%
      if (exec.roi > 5) {
        this.log(`💰 Take profit: ${exec.symbol} @ ${exec.roi.toFixed(2)}%`);
        await this.client.closeExecution({ execution_id: exec.execution.id });
        this.dailyPnL += exec.pnl;
      }
    }
  }

  private async openNewPositions() {
    const recs = await this.engine.getRecommendations();
    
    if (recs.opportunities.length === 0) {
      this.log('😴 No opportunities above threshold');
      return;
    }

    const best = recs.opportunities[0];
    this.log(`💡 Opening: ${best.symbol} @ ${best.spread}% spread, $${best.recommendedAmount}`);

    await this.client.executeOpportunity({
      opportunity_id: best.id,
      amount: best.recommendedAmount,
      mode: 'grid'
    });
  }

  private async closeAllPositions(portfolio: any) {
    for (const exec of portfolio.executions) {
      await this.client.closeExecution({ execution_id: exec.execution.id });
      this.dailyPnL += exec.pnl;
    }
    this.log(`📈 Day closed with $${this.dailyPnL.toFixed(2)} PnL`);
  }

  private scheduleDailyReset() {
    const now = new Date();
    const midnight = new Date(now);
    midnight.setHours(24, 0, 0, 0);
    const msUntilMidnight = midnight.getTime() - now.getTime();

    setTimeout(() => {
      this.dailyPnL = 0;
      this.log('🌅 Daily PnL reset');
      this.scheduleDailyReset();
    }, msUntilMidnight);
  }

  private log(message: string) {
    const timestamp = new Date().toISOString().slice(11, 19);
    console.log(`[${timestamp}] ${message}`);
  }

  private sleep(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// ============ USAGE ============

const agent = new FundingRateAgent({
  token: process.env.ZIRODELTA_TOKEN!,
  balances: { bybit: 1000, kucoin: 1000 },
  dailyTarget: 1.0,  // 1% daily
  riskProfile: 'moderate',
  checkIntervalMs: 60000,  // Check every minute
  stopLossPercent: 2  // 2% stop loss
});

// Start
agent.start();

// Graceful shutdown
process.on('SIGINT', () => {
  console.log('\nShutting down...');
  agent.stop();
  process.exit(0);
});

Running the Agent

bash
# Set token
export ZIRODELTA_TOKEN="your-token"

# Run
npx ts-node agent.ts

# Or with pm2 for production
pm2 start agent.ts --name "funding-agent"

Monitoring

The agent logs to stdout. For production, pipe to a file or use pm2 logs:

bash
pm2 logs funding-agent

Built for agents, by agents.