stock-bot/docs/cache-library-usage.md

13 KiB

Cache Library Usage Guide

The @stock-bot/cache library provides a powerful, flexible caching solution designed specifically for trading bot applications. It supports multiple cache providers including Redis/Dragonfly, in-memory caching, and hybrid caching strategies.

Table of Contents

  1. Installation
  2. Quick Start
  3. Cache Providers
  4. Factory Functions
  5. Cache Decorators
  6. Trading-Specific Usage
  7. Configuration
  8. Monitoring & Metrics
  9. Error Handling
  10. Best Practices

Installation

The cache library is already included in the monorepo. To use it in your service:

{
  "dependencies": {
    "@stock-bot/cache": "workspace:*"
  }
}

Quick Start

Basic Usage

import { createCache } from '@stock-bot/cache';

// Auto-detect best cache type (hybrid if Redis available, otherwise memory)
const cache = createCache('auto');

// Basic operations
await cache.set('user:123', { name: 'John', balance: 1000 }, 3600);
const user = await cache.get<{ name: string; balance: number }>('user:123');
await cache.delete('user:123');

Trading-Optimized Cache

import { createTradingCache } from '@stock-bot/cache';

const cache = createTradingCache({
  keyPrefix: 'trading:',
  ttl: 300, // 5 minutes default
  enableMetrics: true
});

// Cache market data
await cache.set('market:AAPL:price', { price: 150.25, timestamp: Date.now() });

Cache Providers

1. Redis Cache (Dragonfly)

Uses Redis/Dragonfly for distributed caching with persistence and high performance.

import { RedisCache } from '@stock-bot/cache';

const redisCache = new RedisCache({
  keyPrefix: 'app:',
  ttl: 3600,
  enableMetrics: true
});

// Automatic connection to Dragonfly using config
await redisCache.set('key', 'value');

2. Memory Cache

In-memory caching with LRU eviction and TTL support.

import { MemoryCache } from '@stock-bot/cache';

const memoryCache = new MemoryCache({
  maxSize: 1000,        // Maximum 1000 entries
  ttl: 300,            // 5 minutes default TTL
  cleanupInterval: 60   // Cleanup every minute
});

3. Hybrid Cache

Two-tier caching combining fast memory cache (L1) with persistent Redis cache (L2).

import { HybridCache } from '@stock-bot/cache';

const hybridCache = new HybridCache({
  memoryTTL: 60,      // L1 cache TTL: 1 minute
  redisTTL: 3600,     // L2 cache TTL: 1 hour
  memoryMaxSize: 500  // L1 cache max entries
});

// Data flows: Memory -> Redis -> Database
const data = await hybridCache.get('expensive:calculation');

Factory Functions

createCache()

General-purpose cache factory with auto-detection.

import { createCache } from '@stock-bot/cache';

// Auto-detect (recommended)
const cache = createCache('auto');

// Specific provider
const redisCache = createCache('redis', { ttl: 1800 });
const memoryCache = createCache('memory', { maxSize: 2000 });
const hybridCache = createCache('hybrid');

createTradingCache()

Optimized for trading operations with sensible defaults.

import { createTradingCache } from '@stock-bot/cache';

const tradingCache = createTradingCache({
  keyPrefix: 'trading:',
  ttl: 300,              // 5 minutes - good for price data
  enableMetrics: true
});

createMarketDataCache()

Specialized for market data with short TTLs.

import { createMarketDataCache } from '@stock-bot/cache';

const marketCache = createMarketDataCache({
  priceDataTTL: 30,      // 30 seconds for price data
  indicatorDataTTL: 300, // 5 minutes for indicators
  newsDataTTL: 1800      // 30 minutes for news
});

createStrategyCache()

For strategy computations and backtesting results.

import { createStrategyCache } from '@stock-bot/cache';

const strategyCache = createStrategyCache({
  backtestTTL: 86400,    // 24 hours for backtest results
  signalTTL: 300,        // 5 minutes for signals
  optimizationTTL: 3600  // 1 hour for optimization results
});

Cache Decorators

@Cacheable

Automatically cache method results.

import { Cacheable } from '@stock-bot/cache';

class MarketDataService {
  @Cacheable({
    keyGenerator: (symbol: string) => `price:${symbol}`,
    ttl: 60
  })
  async getPrice(symbol: string): Promise<number> {
    // Expensive API call
    return await this.fetchPriceFromAPI(symbol);
  }
}

@CacheEvict

Invalidate cache entries when data changes.

import { CacheEvict } from '@stock-bot/cache';

class PortfolioService {
  @CacheEvict({
    keyPattern: 'portfolio:*'
  })
  async updatePosition(symbol: string, quantity: number): Promise<void> {
    // Update database
    await this.savePosition(symbol, quantity);
    // Cache automatically invalidated
  }
}

@CachePut

Always execute method and update cache.

import { CachePut } from '@stock-bot/cache';

class StrategyService {
  @CachePut({
    keyGenerator: (strategyId: string) => `strategy:${strategyId}:result`
  })
  async runStrategy(strategyId: string): Promise<StrategyResult> {
    const result = await this.executeStrategy(strategyId);
    // Result always cached after execution
    return result;
  }
}

Trading-Specific Usage

Market Data Caching

import { createMarketDataCache, CacheKeyGenerator } from '@stock-bot/cache';

const marketCache = createMarketDataCache();
const keyGen = new CacheKeyGenerator();

// Cache price data
const priceKey = keyGen.priceKey('AAPL');
await marketCache.set(priceKey, { price: 150.25, volume: 1000000 }, 30);

// Cache technical indicators
const smaKey = keyGen.indicatorKey('AAPL', 'SMA', { period: 20 });
await marketCache.set(smaKey, 148.50, 300);

// Cache order book
const orderBookKey = keyGen.orderBookKey('AAPL');
await marketCache.set(orderBookKey, orderBookData, 5);

Strategy Result Caching

import { createStrategyCache, CacheKeyGenerator } from '@stock-bot/cache';

const strategyCache = createStrategyCache();
const keyGen = new CacheKeyGenerator();

// Cache backtest results
const backtestKey = keyGen.backtestKey('momentum-strategy', {
  startDate: '2024-01-01',
  endDate: '2024-12-31',
  symbol: 'AAPL'
});
await strategyCache.set(backtestKey, backtestResults, 86400);

// Cache trading signals
const signalKey = keyGen.signalKey('AAPL', 'momentum-strategy');
await strategyCache.set(signalKey, { action: 'BUY', confidence: 0.85 }, 300);

Portfolio Data Caching

import { createTradingCache, CacheKeyGenerator } from '@stock-bot/cache';

const portfolioCache = createTradingCache();
const keyGen = new CacheKeyGenerator();

// Cache portfolio positions
const positionsKey = keyGen.portfolioKey('user123', 'positions');
await portfolioCache.set(positionsKey, positions, 300);

// Cache risk metrics
const riskKey = keyGen.riskKey('user123', 'VaR');
await portfolioCache.set(riskKey, { var95: 1250.50 }, 3600);

Configuration

Cache configuration is handled through the @stock-bot/config package. Key settings:

// Dragonfly/Redis configuration
DRAGONFLY_HOST=localhost
DRAGONFLY_PORT=6379
DRAGONFLY_PASSWORD=your_password
DRAGONFLY_DATABASE=0
DRAGONFLY_MAX_RETRIES=3
DRAGONFLY_RETRY_DELAY=100
DRAGONFLY_CONNECT_TIMEOUT=10000
DRAGONFLY_COMMAND_TIMEOUT=5000

// TLS settings (optional)
DRAGONFLY_TLS=true
DRAGONFLY_TLS_CERT_FILE=/path/to/cert.pem
DRAGONFLY_TLS_KEY_FILE=/path/to/key.pem
DRAGONFLY_TLS_CA_FILE=/path/to/ca.pem

Monitoring & Metrics

Cache Statistics

const cache = createTradingCache({ enableMetrics: true });

// Get cache statistics
const stats = await cache.getStats();
console.log(`Hit rate: ${stats.hitRate}%`);
console.log(`Total operations: ${stats.total}`);
console.log(`Uptime: ${stats.uptime} seconds`);

Health Checks

const cache = createCache('hybrid');

// Check cache health
const isHealthy = await cache.isHealthy();
if (!isHealthy) {
  console.error('Cache is not healthy');
}

// Monitor connection status
cache.on('connect', () => console.log('Cache connected'));
cache.on('disconnect', () => console.error('Cache disconnected'));
cache.on('error', (error) => console.error('Cache error:', error));

Metrics Integration

// Export metrics to Prometheus/Grafana
const metrics = await cache.getStats();

// Custom metrics tracking
await cache.set('key', 'value', 300, {
  tags: { service: 'trading-bot', operation: 'price-update' }
});

Error Handling

The cache library implements graceful error handling:

Automatic Failover

// Hybrid cache automatically falls back to memory if Redis fails
const hybridCache = createCache('hybrid');

// If Redis is down, data is served from memory cache
const data = await hybridCache.get('key'); // Never throws, returns null if not found

Circuit Breaker Pattern

const cache = createTradingCache({
  maxConsecutiveFailures: 5,  // Open circuit after 5 failures
  circuitBreakerTimeout: 30000 // Try again after 30 seconds
});

try {
  await cache.set('key', 'value');
} catch (error) {
  // Handle cache unavailability
  console.warn('Cache unavailable, falling back to direct data access');
}

Error Events

cache.on('error', (error) => {
  if (error.code === 'CONNECTION_LOST') {
    // Handle connection loss
    await cache.reconnect();
  }
});

Best Practices

1. Choose the Right Cache Type

  • Memory Cache: Fast access, limited by RAM, good for frequently accessed small data
  • Redis Cache: Persistent, distributed, good for shared data across services
  • Hybrid Cache: Best of both worlds, use for hot data with fallback

2. Set Appropriate TTLs

// Trading data TTL guidelines
const TTL = {
  PRICE_DATA: 30,        // 30 seconds - very volatile
  INDICATORS: 300,       // 5 minutes - calculated values
  NEWS: 1800,           // 30 minutes - slower changing
  BACKTEST_RESULTS: 86400, // 24 hours - expensive calculations
  USER_PREFERENCES: 3600   // 1 hour - rarely change during session
};

3. Use Proper Key Naming

// Good key naming convention
const keyGen = new CacheKeyGenerator();
const key = keyGen.priceKey('AAPL'); // trading:price:AAPL:2024-01-01

// Avoid generic keys
// Bad: "data", "result", "temp"
// Good: "trading:price:AAPL", "strategy:momentum:signals"

4. Implement Cache Warming

// Pre-populate cache with frequently accessed data
async function warmupCache() {
  const symbols = ['AAPL', 'GOOGL', 'MSFT'];
  const cache = createMarketDataCache();
  
  for (const symbol of symbols) {
    const price = await fetchPrice(symbol);
    await cache.set(keyGen.priceKey(symbol), price, 300);
  }
}

5. Monitor Cache Performance

// Regular performance monitoring
setInterval(async () => {
  const stats = await cache.getStats();
  if (stats.hitRate < 80) {
    console.warn('Low cache hit rate:', stats.hitRate);
  }
}, 60000); // Check every minute

6. Handle Cache Invalidation

// Invalidate related cache entries when data changes
class PositionService {
  async updatePosition(symbol: string, quantity: number) {
    await this.saveToDatabase(symbol, quantity);
    
    // Invalidate related cache entries
    await cache.delete(`portfolio:positions`);
    await cache.delete(`portfolio:risk:*`);
    await cache.delete(`strategy:signals:${symbol}`);
  }
}

Advanced Examples

Custom Cache Provider

class DatabaseCache implements CacheProvider {
  async get<T>(key: string): Promise<T | null> {
    // Implement database-backed cache
  }
  
  async set<T>(key: string, value: T, ttl?: number): Promise<boolean> {
    // Store in database with expiration
  }
  
  // ... implement other methods
}

// Use with factory
const dbCache = new DatabaseCache();

Batch Operations

// Efficient batch operations
const keys = ['price:AAPL', 'price:GOOGL', 'price:MSFT'];
const values = await cache.mget<number>(keys);

const updates = new Map([
  ['price:AAPL', 150.25],
  ['price:GOOGL', 2800.50],
  ['price:MSFT', 350.75]
]);
await cache.mset(updates, 300);

Conditional Caching

class SmartCache {
  async getOrCompute<T>(
    key: string,
    computeFn: () => Promise<T>,
    shouldCache: (value: T) => boolean = () => true
  ): Promise<T> {
    let value = await this.cache.get<T>(key);
    
    if (value === null) {
      value = await computeFn();
      if (shouldCache(value)) {
        await this.cache.set(key, value, this.defaultTTL);
      }
    }
    
    return value;
  }
}

This cache library provides enterprise-grade caching capabilities specifically designed for trading bot applications, with built-in monitoring, error handling, and performance optimization.