remotion-trading

📁 josebarnetche/remotion-trading 📅 Today
2
总安装量
2
周安装量
#63050
全站排名
安装命令
npx skills add https://github.com/josebarnetche/remotion-trading --skill remotion-trading

Agent 安装分布

claude-code 2
mcpjam 1
kilo 1
junie 1
windsurf 1
zencoder 1

Skill 文档

Remotion Trading — Bloomberg-Style Crypto Reels

You create polished crypto price journey videos as Instagram Reels (1080×1920 vertical). The aesthetic is Bloomberg terminal — clean, data-dense, professional, minimal animation. Every video loops seamlessly and works without sound.

Core Principles

  1. Real data only — Every number comes from Binance or CoinGecko. Never fabricate prices.
  2. Bloomberg terminal aesthetic — Data-dense, monospace numbers, thin grid lines, structured panels. No flashy glows or glass morphism. Precision is the style.
  3. Silent-first — Text overlays carry the entire story. No audio dependency. A viewer scrolling on mute must understand everything.
  4. Always loop — Every composition seamlessly loops. The last frame blends into the first.
  5. Price journey — The chart draws from past to present. The viewer watches history unfold, then the current price locks in with a counter/scramble effect.
  6. Weekly premium — 2-3 polished videos per week. Quality over speed. Every frame matters.

Before Building

Ask only what’s needed (many defaults are set):

1. Asset & Timeframe

  • Which coin? (BTC, ETH, SOL, etc.)
  • What timeframe? (1D, 7D, 30D, 90D, YTD)
  • Default: 30D daily candles from Binance

2. Story Focus

  • Any key moment to annotate? (ATH, crash, breakout, support bounce)
  • Any specific stat to highlight beyond price? (volume spike, dominance shift)
  • Default: clean price journey with current price reveal

3. Variation

  • Candle chart or line chart?
  • Include a light indicator? (one MA, S/R line, or none)
  • Default: line chart, no indicators

Output Format — Vertical Reel

All compositions are 1080×1920 at 30fps. Duration is 15-30 seconds (450-900 frames).

export const reelConfig = {
  width: 1080,
  height: 1920,
  fps: 30,
} as const;

Vertical Layout Zones

┌──────────────────┐
│                  │ ← Zone 1: Header (asset + timeframe)
│   BTC / USDT     │    y: 80-200
│   30D · Binance  │
│                  │
│                  │
│  ┌────────────┐  │ ← Zone 2: Chart area
│  │            │  │    y: 280-1100 (820px tall)
│  │  ▁▂▃▅▆█▇▅ │  │    padded 60px sides
│  │            │  │
│  └────────────┘  │
│                  │
│   $97,432.18     │ ← Zone 3: Hero price (counter reveal)
│   ▲ +3.60%       │    y: 1200-1500
│                  │
│  Vol  $28.4B     │ ← Zone 4: Stats row
│  MCap $1.91T     │    y: 1550-1750
│  24h H $98,100   │
│                  │
│                  │ ← Zone 5: Bottom margin (safe area)
└──────────────────┘    y: 1750-1920

Safe areas: 60px padding on all sides. Bottom 170px is safe zone for Instagram UI overlay.


Design System

Color Palette — Bloomberg Terminal

export const colors = {
  // Backgrounds
  bg: '#000000',              // Pure black
  bgPanel: '#0d1117',         // Slightly lifted panels
  bgRow: '#161b22',           // Alternating row highlight

  // Price action
  up: '#3fb950',              // Green (Bloomberg green)
  down: '#f85149',            // Red
  neutral: '#8b949e',         // Unchanged/muted

  // Text
  primary: '#e6edf3',         // Primary text (near-white)
  secondary: '#8b949e',       // Labels, timestamps
  muted: '#484f58',           // Subtle grid, borders

  // Chart
  priceLine: '#58a6ff',       // Blue price line
  grid: 'rgba(139,148,158,0.1)', // Very subtle grid
  axis: 'rgba(139,148,158,0.25)',

  // Accent (used sparingly)
  accent: '#58a6ff',          // Blue for emphasis
} as const;

Typography

export const fonts = {
  mono: '"JetBrains Mono", "SF Mono", "Fira Code", "Consolas", monospace',
  label: '"Inter", "SF Pro", system-ui, sans-serif',
} as const;

Rules:

  • ALL numbers use fonts.mono — prices, percentages, volumes, dates, everything
  • Labels use fonts.label at weight 500, uppercase, letter-spacing 1-2px
  • Hero price: fonts.mono, weight 300, size 88-110px
  • Decimals: same font, 70% of the whole-number size, opacity 0.7
  • No bold on prices — thin weight (300) reads more terminal-like

Spring Configs

// Data elements (chart lines, bars) — smooth and precise
const dataSpring = { damping: 18, stiffness: 80, mass: 0.7 };

// UI elements (labels, badges) — snappy
const uiSpring = { damping: 14, stiffness: 120, mass: 0.4 };

// Counter settle — quick lock
const counterSpring = { damping: 10, stiffness: 180, mass: 0.3 };

Minimal animation. No bouncy overshoots. Things appear with purpose and settle fast.


The Price Journey — Core Composition

Every video follows this arc:

Timeline (15s / 450 frames)

Phase 1: Header (0-45, 1.5s)
  ├── Asset pair fades in: "BTC / USDT" (mono, 36px, primary color)
  ├── Timeframe + source below: "30D · Binance" (label, 18px, secondary)
  └── Thin horizontal divider line draws left→right

Phase 2: Chart Draw (45-270, 7.5s)
  ├── Y-axis price labels appear (right side, mono, secondary)
  ├── X-axis date labels appear (bottom, mono, muted)
  ├── Grid lines: horizontal only, very subtle
  ├── Price line draws left→right (strokeDasharray animation)
  ├── Small tracking dot at the drawing tip (4px, accent color)
  └── Optional: one MA line draws behind price (dashed, muted)

Phase 3: Price Lock (270-360, 3s)
  ├── Chart holds (animation complete)
  ├── Digits scramble: each character cycles rapidly then locks left→right
  ├── Final price locked: "$ 97,432.18" (mono, 96px, primary)
  ├── Percent badge appears below: "▲ +3.60%" (up=green, down=red)
  └── Subtle crosshair or horizontal line connects chart endpoint to price

Phase 4: Stats (360-420, 2s)
  ├── Stats rows cascade in (stagger 4 frames each):
  │   ├── "24h Vol    $28.4B"
  │   ├── "MCap       $1.91T"
  │   └── "24h Range  $94,200 — $98,100"
  └── All mono, aligned with tabular spacing

Phase 5: Loop Crossfade (420-450, 1s)
  ├── Everything fades out (opacity → 0)
  └── Overlaps with Phase 1 of next loop (crossfade blend)

For 30s version: expand Phase 2 to 15s, add a “Key Moment” pause mid-chart where an annotation callout appears at a notable point (ATH, dip, breakout), hold 2s, then continue drawing.


Data Integration — Crypto Only

Binance (Primary — No API Key)

// Klines (OHLCV)
const fetchKlines = async (symbol: string, interval: string, limit: number): Promise<OHLCV[]> => {
  const res = await fetch(
    `https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=${interval}&limit=${limit}`
  );
  const raw = await res.json();
  return raw.map((k: any[]) => ({
    time: k[0],
    open: parseFloat(k[1]),
    high: parseFloat(k[2]),
    low: parseFloat(k[3]),
    close: parseFloat(k[4]),
    volume: parseFloat(k[5]),
  }));
};

// 24h Ticker
const fetchTicker = async (symbol: string) => {
  const res = await fetch(`https://api.binance.com/api/v3/ticker/24hr?symbol=${symbol}`);
  return res.json();
};

CoinGecko (Supplementary — Market Cap, Coin Details)

const fetchCoinDetails = async (coinId: string) => {
  const res = await fetch(`https://api.coingecko.com/api/v3/coins/${coinId}`);
  return res.json();
  // market_data.market_cap.usd, total_volume, ath, circulating_supply
};

Common Symbols

Coin Binance Symbol CoinGecko ID
BTC BTCUSDT bitcoin
ETH ETHUSDT ethereum
SOL SOLUSDT solana
BNB BNBUSDT binancecoin
XRP XRPUSDT ripple
DOGE DOGEUSDT dogecoin
ADA ADAUSDT cardano
AVAX AVAXUSDT avalanche-2

Data Loading Pattern

Always use calculateMetadata — fetch before render, never during:

export const calculatePriceJourneyMetadata: CalculateMetadataFunction<Props> = async ({ props }) => {
  const { symbol = 'BTCUSDT', interval = '1d', limit = 30 } = props;
  const [klines, ticker] = await Promise.all([
    fetchKlines(symbol, interval, limit),
    fetchTicker(symbol),
  ]);
  validateOHLCV(klines);
  return {
    props: {
      ...props,
      data: klines,
      currentPrice: parseFloat(ticker.lastPrice),
      change24h: parseFloat(ticker.priceChangePercent),
      volume24h: parseFloat(ticker.quoteVolume),
      high24h: parseFloat(ticker.highPrice),
      low24h: parseFloat(ticker.lowPrice),
    },
    durationInFrames: 450,
    fps: 30,
    width: 1080,
    height: 1920,
  };
};

Counter / Scramble Effect — The Price Reveal

This is the hero moment. Digits cycle rapidly then lock one by one, left to right.

function scramblePrice(
  targetFormatted: string,  // "$97,432.18"
  frame: number,
  lockStartFrame: number,
  framesPerLock: number,    // 3 frames between each character locking
): string {
  const chars = '0123456789';
  return targetFormatted.split('').map((char, i) => {
    // Non-digit characters ($, comma, dot) appear immediately
    if (!'0123456789'.includes(char)) return char;

    const charLockFrame = lockStartFrame + i * framesPerLock;
    if (frame >= charLockFrame) return char; // Locked

    // Cycling: change digit every 2 frames
    const cycleIndex = Math.floor(frame / 2 + i * 3) % 10;
    return chars[cycleIndex];
  }).join('');
}

Timing: Start scramble at Phase 3 frame 0. Lock first digit after 15 frames. Each subsequent digit locks 3 frames later. Full price locked in ~40 frames (~1.3s). Then settle with counterSpring scale 1.02→1.0.


Seamless Looping

Every composition MUST loop. Use the crossfade overlap technique:

const OVERLAP = 30; // 1 second crossfade
const totalFrames = composition.durationInFrames;

// In the main component:
const outOpacity = frame >= totalFrames - OVERLAP
  ? interpolate(frame, [totalFrames - OVERLAP, totalFrames], [1, 0], { extrapolateRight: 'clamp' })
  : 1;

// Render a <Sequence from={totalFrames - OVERLAP}> with the intro content
// at opacity: 1 - outOpacity

Design for loops:

  • Phase 1 (header) must be simple enough to crossfade cleanly
  • No elements that would look broken mid-transition
  • Background grid is continuous (modular frame: frame % gridCycleLength)

Chart Drawing

Line Chart (Default)

SVG <path> with strokeDasharray draw animation. See references/chart-components.md for full implementation.

Key specs for vertical Reel:

  • Chart area: 960px wide (60px padding each side), 820px tall
  • Price line: strokeWidth: 2.5, color: colors.priceLine
  • Grid: horizontal lines only, spaced every ~160px, color: colors.grid
  • Y-axis: right-aligned price labels in fonts.mono, 14px, colors.secondary
  • X-axis: bottom date labels, 12px, colors.muted
  • Tracking dot: 4px radius, colors.accent, subtle 8px glow ring

Candlestick Chart (Variation)

For when user requests candles. Same chart area, staggered build animation. See references/chart-components.md.

  • Body width: (chartWidth / dataLength) * 0.6
  • Wick: 1.5px centered on body
  • Colors: colors.up / colors.down
  • Last candle: slightly brighter (opacity 1.0 vs 0.85)

Optional Light Indicator

One of:

  • SMA line: 20-period, dashed (strokeDasharray: "4 6"), colors.muted, drawn behind price line
  • S/R line: horizontal, user-specified price level, dashed, colors.secondary

Never more than one indicator. Clean chart = better content.


Text Overlay System (Silent-First)

Since there’s no audio, text overlays tell the story:

Annotation Callout

For key moments mid-chart:

         ┌─────────────┐
         │ ATH $73,750  │  ← mono, 16px, panel background
         └──────┬───────┘
                │  (thin leader line)
                ● (4px dot on chart)
  • Panel: colors.bgPanel background, 1px colors.muted border, 8px radius
  • Leader line: 1px, colors.muted
  • Appears with fade (10 frames), holds 60 frames, fades out (10 frames)

Stat Labels

Tabular layout — label left-aligned, value right-aligned:

24h Vol      $28.4B
MCap        $1.91T
24h Range   $94.2K — $98.1K
  • Label: fonts.label, 16px, colors.secondary, uppercase
  • Value: fonts.mono, 16px, colors.primary
  • Row height: 44px
  • Cascade in: stagger 4 frames per row

Formatting

function formatPrice(price: number): string {
  if (price >= 1000) return price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  if (price >= 1) return price.toFixed(2);
  if (price >= 0.01) return price.toFixed(4);
  return price.toFixed(8); // sub-cent altcoins
}

function formatCompact(n: number): string {
  if (n >= 1e12) return `$${(n / 1e12).toFixed(2)}T`;
  if (n >= 1e9) return `$${(n / 1e9).toFixed(2)}B`;
  if (n >= 1e6) return `$${(n / 1e6).toFixed(1)}M`;
  if (n >= 1e3) return `$${(n / 1e3).toFixed(1)}K`;
  return `$${n.toFixed(2)}`;
}

function formatPercent(v: number): string {
  return `${v >= 0 ? '▲' : '▼'} ${v >= 0 ? '+' : ''}${v.toFixed(2)}%`;
}

Critical Rules

  1. NEVER fabricate data — If API fails, abort. No placeholder prices.
  2. Correct decimals — BTC/ETH: 2 for USD price. Sub-$1 alts: 4-8 decimals.
  3. Monospace everything numeric — Prices, percents, volumes, dates. No exceptions.
  4. Always 1080×1920 — Vertical Reel format. No landscape variants unless explicitly asked.
  5. Always loop — Crossfade overlap on every composition.
  6. No branding — No logos, watermarks, handles, or CTAs. Pure data.
  7. No audio components — No <Audio> tags. Silent by design.
  8. Pre-fetch only — All data in calculateMetadata. Zero network calls during render.
  9. Minimal animation — Things appear, settle, hold. No bouncy overshoots. Terminal precision.
  10. Hold still for reading — After every reveal, minimum 2s of static frame.

For component blueprints: See references/chart-components.md For animation recipes: See references/animation-patterns.md For API details: See references/data-integration.md For scene templates: See references/scene-templates.md