Розробка теплової карти крипторинку

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка теплової карти крипторинку
Середній
~3-5 днів
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1288
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Розроблення теплової карти крипторинку

Теплова карта (heatmap) криптовалютного ринку — це візуалізація зміни цін усього ринку одночасно. Кожен актив представлено прямокутником, розмір якого пропорціональний капіталізації, колір відображає відсоток змін за період. Незамінний інструмент для швидкого розуміння структури ринку.

Алгоритм Treemap Layout

Основа heatmap — алгоритм Treemap, який оптимально упаковує прямокутники різних розмірів:

interface HeatmapCell {
  symbol: string;
  marketCap: number;
  changePercent: number;
  price: number;
  volume24h: number;
  x: number;      // обчислюється алгоритмом
  y: number;
  width: number;
  height: number;
}

class SquarifiedTreemap {
  layout(
    data: HeatmapCell[],
    bounds: {x: number; y: number; width: number; height: number}
  ): HeatmapCell[] {
    // Сортуємо по спаданню розміру (капіталізація)
    const sorted = [...data].sort((a, b) => b.marketCap - a.marketCap);
    const totalMarketCap = sorted.reduce((sum, d) => sum + d.marketCap, 0);

    return this.squarify(sorted, bounds, totalMarketCap);
  }

  private squarify(items: HeatmapCell[], bounds: Bounds, total: number): HeatmapCell[] {
    if (items.length === 0) return [];

    const isHorizontal = bounds.width >= bounds.height;
    const result: HeatmapCell[] = [];
    let row: HeatmapCell[] = [];
    let rowArea = 0;
    let offset = 0;

    for (const item of items) {
      const itemArea = (item.marketCap / total) * (bounds.width * bounds.height);
      const testRow = [...row, item];
      const testArea = rowArea + itemArea;

      if (this.wouldImproveAspectRatio(testRow, testArea, bounds, isHorizontal)) {
        row.push(item);
        rowArea = testArea;
      } else {
        // Завершуємо поточний рядок
        result.push(...this.layoutRow(row, rowArea, bounds, isHorizontal, offset, total));
        const rowThickness = rowArea / (isHorizontal ? bounds.width : bounds.height);
        offset += rowThickness;

        row = [item];
        rowArea = itemArea;
      }
    }

    if (row.length > 0) {
      result.push(...this.layoutRow(row, rowArea, bounds, isHorizontal, offset, total));
    }

    return result;
  }
}

Отримання даних

import httpx
import asyncio

class MarketDataProvider:
    COINGECKO_URL = "https://api.coingecko.com/api/v3"

    async def get_heatmap_data(
        self,
        vs_currency: str = 'usd',
        top_n: int = 100
    ) -> list[dict]:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.COINGECKO_URL}/coins/markets",
                params={
                    "vs_currency": vs_currency,
                    "order": "market_cap_desc",
                    "per_page": top_n,
                    "page": 1,
                    "sparkline": False,
                    "price_change_percentage": "1h,24h,7d"
                }
            )

        coins = response.json()
        return [
            {
                "symbol": c["symbol"].upper(),
                "name": c["name"],
                "market_cap": c["market_cap"] or 0,
                "change_1h": c.get("price_change_percentage_1h_in_currency", 0) or 0,
                "change_24h": c.get("price_change_percentage_24h", 0) or 0,
                "change_7d": c.get("price_change_percentage_7d_in_currency", 0) or 0,
                "volume_24h": c.get("total_volume", 0) or 0,
                "price": c["current_price"],
                "image": c["image"]
            }
            for c in coins if c["market_cap"]
        ]

Кешуємо дані: CoinGecko безплатний API — 10-30 запитів/хв. Оновлюємо кожні 60 секунд:

async def get_cached_data(self) -> list[dict]:
    cache_key = "heatmap_data"
    cached = await self.redis.get(cache_key)

    if cached:
        return json.loads(cached)

    data = await self.get_heatmap_data()
    await self.redis.setex(cache_key, 60, json.dumps(data))
    return data

React візуалізація

import React, { useMemo } from 'react';

const getColor = (changePercent: number): string => {
  const intensity = Math.min(Math.abs(changePercent) / 10, 1);
  if (changePercent > 0) {
    const green = Math.floor(180 * intensity + 60);
    return `rgb(0, ${green}, 0)`;
  } else {
    const red = Math.floor(180 * intensity + 60);
    return `rgb(${red}, 0, 0)`;
  }
};

const HeatmapCell: React.FC<{cell: HeatmapCell; period: '1h'|'24h'|'7d'}> = ({cell, period}) => {
  const change = period === '1h' ? cell.change1h : period === '24h' ? cell.change24h : cell.change7d;
  const bgColor = getColor(change);

  return (
    <div
      style={{
        position: 'absolute',
        left: cell.x,
        top: cell.y,
        width: cell.width - 2,
        height: cell.height - 2,
        backgroundColor: bgColor,
        border: '1px solid rgba(0,0,0,0.3)',
        overflow: 'hidden',
        cursor: 'pointer'
      }}
      title={`${cell.symbol}: ${change > 0 ? '+' : ''}${change.toFixed(2)}%`}
    >
      {cell.width > 40 && (
        <div className="p-1 text-white">
          <div className="font-bold text-xs">{cell.symbol}</div>
          {cell.height > 30 && (
            <div className={`text-xs ${change > 0 ? 'text-green-200' : 'text-red-200'}`}>
              {change > 0 ? '+' : ''}{change.toFixed(2)}%
            </div>
          )}
        </div>
      )}
    </div>
  );
};

const CryptoHeatmap: React.FC = () => {
  const [data, setData] = useState<HeatmapCell[]>([]);
  const [period, setPeriod] = useState<'1h'|'24h'|'7d'>('24h');
  const containerRef = useRef<HTMLDivElement>(null);
  const [dimensions, setDimensions] = useState({width: 1200, height: 700});

  const layoutData = useMemo(() => {
    const treemap = new SquarifiedTreemap();
    return treemap.layout(data, {x: 0, y: 0, ...dimensions});
  }, [data, dimensions]);

  return (
    <div>
      <div className="flex gap-2 mb-4">
        {(['1h', '24h', '7d'] as const).map(p => (
          <button key={p} onClick={() => setPeriod(p)}
            className={`px-3 py-1 rounded ${period === p ? 'bg-blue-600' : 'bg-gray-700'}`}>
            {p}
          </button>
        ))}
      </div>
      <div ref={containerRef} className="relative bg-gray-900"
           style={{width: dimensions.width, height: dimensions.height}}>
        {layoutData.map(cell => (
          <HeatmapCell key={cell.symbol} cell={cell} period={period} />
        ))}
      </div>
    </div>
  );
};

Теплова карта — це killer feature для крипто-платформи. Користувачі повертаються до неї щодня для моніторингу ринку. Хорошо реалізована система включає: фільтрацію по секторах (DeFi, Layer1, GameFi), zoom на окремі сектори та clickthrough до торгівельної пари.