Crypto trading volume screener development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Crypto trading volume screener development
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Crypto Volume Screener Development

A volume screener is a tool for finding instruments with anomalous trading activity. Sharp volume spikes often precede major price movements. Professional traders use volume screeners to discover trading opportunities before moves become obvious.

Volume Metrics

Volume Ratio: current volume / average volume over N periods. Ratio > 3 indicates anomalous volume.

Relative Volume (RVOL): current candle volume relative to the average volume for that time of day. RVOL > 2 at 14:00 means: today at 14:00 the volume is 2x higher than usual for that time.

Volume Spike: a one-off volume surge in a single candle — often signals a large player.

OBV (On Balance Volume): cumulative indicator. Accumulation = smart money entering. Distribution = smart money exiting.

Screener Architecture

interface VolumeScreenerItem {
  symbol: string;
  exchange: string;
  currentVolume: number;
  avgVolume20: number;       // average over 20 periods
  volumeRatio: number;       // current / avg20
  rvol: number;              // relative to same-hour average
  volumeDelta: number;       // buy volume - sell volume (if available)
  volumeTrend: 'increasing' | 'decreasing' | 'spike';
  priceChange: number;       // % over period
  volumePrice: 'confirming' | 'diverging';  // does volume confirm price move?
}

Volume Metrics Calculation

function calculateVolumeMetrics(
  candles: OHLCV[],
  currentCandle: OHLCV
): VolumeMetrics {
  const period = 20;
  const recentCandles = candles.slice(-period);
  
  // Average volume
  const avgVolume = recentCandles.reduce((sum, c) => sum + c.volume, 0) / period;
  
  // Volume Ratio
  const volumeRatio = currentCandle.volume / avgVolume;
  
  // Volume trend: linear regression over last 5 candles
  const recentVolumes = candles.slice(-5).map(c => c.volume);
  const volumeTrendSlope = linearRegressionSlope(recentVolumes);
  
  // Confirmation: price up + volume up = bullish confirmation
  const priceChange = (currentCandle.close - candles.slice(-2)[0].close) / candles.slice(-2)[0].close;
  const volumeChange = currentCandle.volume / candles.slice(-2)[0].volume - 1;
  
  const confirming = (priceChange > 0 && volumeChange > 0) || (priceChange < 0 && volumeChange > 0);
  
  return {
    avgVolume,
    volumeRatio,
    volumeTrend: volumeTrendSlope > 0.1 ? 'increasing' : volumeTrendSlope < -0.1 ? 'decreasing' : 
                 (volumeRatio > 3 ? 'spike' : 'normal'),
    volumePrice: confirming ? 'confirming' : 'diverging',
  };
}

function linearRegressionSlope(values: number[]): number {
  const n = values.length;
  const xMean = (n - 1) / 2;
  const yMean = values.reduce((a, b) => a + b) / n;
  
  let numerator = 0;
  let denominator = 0;
  
  for (let i = 0; i < n; i++) {
    numerator += (i - xMean) * (values[i] - yMean);
    denominator += (i - xMean) ** 2;
  }
  
  return denominator ? numerator / denominator : 0;
}

Data Collection for Multiple Pairs

class VolumeDataCollector {
  private candleCache = new Map<string, OHLCV[]>();
  private exchange: ccxt.Exchange;
  
  async fetchAllCandles(symbols: string[], timeframe: string): Promise<void> {
    // Parallel requests respecting rate limits
    const chunks = chunkArray(symbols, 10);  // 10 requests in parallel
    
    for (const chunk of chunks) {
      await Promise.all(
        chunk.map(async (symbol) => {
          const candles = await this.exchange.fetchOHLCV(symbol, timeframe, undefined, 100);
          this.candleCache.set(`${symbol}:${timeframe}`, candles.map(formatCandle));
        })
      );
      await sleep(100);  // small pause between chunks
    }
  }
  
  async getScreenerData(timeframe: string, minVolumeRatio: number = 2): Promise<VolumeScreenerItem[]> {
    const results: VolumeScreenerItem[] = [];
    
    for (const [key, candles] of this.candleCache) {
      if (!key.endsWith(`:${timeframe}`)) continue;
      const symbol = key.split(':')[0];
      
      if (candles.length < 21) continue;
      
      const metrics = calculateVolumeMetrics(candles.slice(0, -1), candles[candles.length - 1]);
      
      if (metrics.volumeRatio >= minVolumeRatio) {
        results.push({
          symbol,
          currentVolume: candles[candles.length - 1].volume,
          ...metrics,
        });
      }
    }
    
    return results.sort((a, b) => b.volumeRatio - a.volumeRatio);
  }
}

Screener UI

function VolumeScreener() {
  const [timeframe, setTimeframe] = useState('1h');
  const [minRatio, setMinRatio] = useState(2);
  const [sortBy, setSortBy] = useState<'volumeRatio' | 'currentVolume'>('volumeRatio');
  const [data, setData] = useState<VolumeScreenerItem[]>([]);
  
  // Update every 5 minutes
  useEffect(() => {
    const interval = setInterval(() => refreshScreener(), 5 * 60 * 1000);
    return () => clearInterval(interval);
  }, [timeframe, minRatio]);
  
  return (
    <div>
      <Controls
        timeframe={timeframe} onTimeframeChange={setTimeframe}
        minRatio={minRatio} onMinRatioChange={setMinRatio}
      />
      
      <table>
        <thead>
          <tr>
            <th>Symbol</th>
            <th onClick={() => setSortBy('volumeRatio')}>Vol Ratio ↕</th>
            <th>RVOL</th>
            <th>Volume ($)</th>
            <th>Price Change</th>
            <th>Trend</th>
            <th>Confirmation</th>
          </tr>
        </thead>
        <tbody>
          {data.map(item => (
            <VolumeRow key={item.symbol} item={item} />
          ))}
        </tbody>
      </table>
    </div>
  );
}

const VolumeRow: React.FC<{ item: VolumeScreenerItem }> = ({ item }) => (
  <tr className={item.volumeRatio > 5 ? 'highlight-spike' : ''}>
    <td><a href={`/trade/${item.symbol}`}>{item.symbol}</a></td>
    <td>
      <VolumeRatioBar ratio={item.volumeRatio} />
      <span>{item.volumeRatio.toFixed(1)}x</span>
    </td>
    <td>{item.rvol.toFixed(1)}x</td>
    <td>{formatVolume(item.currentVolume)}</td>
    <td className={item.priceChange > 0 ? 'green' : 'red'}>
      {item.priceChange > 0 ? '+' : ''}{item.priceChange.toFixed(2)}%
    </td>
    <td><TrendIcon trend={item.volumeTrend} /></td>
    <td>
      <span className={item.volumePrice === 'confirming' ? 'green' : 'yellow'}>
        {item.volumePrice === 'confirming' ? '✓ Confirm' : '⚡ Diverge'}
      </span>
    </td>
  </tr>
);

Alerts

interface VolumeAlert {
  symbol: string;
  minVolumeRatio: number;  // trigger when reached
  notifyVia: 'telegram' | 'webhook' | 'email';
}

async function checkVolumeAlerts(screenerData: VolumeScreenerItem[], alerts: VolumeAlert[]) {
  for (const alert of alerts) {
    const item = screenerData.find(d => d.symbol === alert.symbol);
    if (!item) continue;
    
    if (item.volumeRatio >= alert.minVolumeRatio) {
      await sendAlert(alert.notifyVia, {
        message: `Volume spike on ${item.symbol}! Ratio: ${item.volumeRatio.toFixed(1)}x avg | Price: ${item.priceChange > 0 ? '+' : ''}${item.priceChange.toFixed(2)}%`,
      });
    }
  }
}

Development of a volume screener with multi-timeframe support, alerts, and multi-exchange coverage: 4–6 weeks.