Skip to content

Repository files navigation

CryptoGuard

A crypto risk dashboard that shows real numbers — or tells you it couldn't get them.

CryptoGuard scores any listed crypto asset from 0–100 on volatility, liquidity and market capitalisation, shows which factors drove that score, tracks it over time, and alerts you when the risk or the price crosses a line you set.

Every figure on screen comes from a live upstream feed. Where a feed is unreachable, the app says so rather than falling back to a plausible-looking number.

CryptoGuard dashboard: live Fear and Greed index, global market stats, a 7-day bitcoin chart and a risk heatmap


Contents


What it does

Feature What it actually does
Risk Scanner Scores a searched asset 0–100 and shows the per-factor breakdown. Google Gemini reads those specific numbers back to you in plain language.
Dashboard Live Fear & Greed index, total market cap, 24h change, BTC dominance, a real 7-day BTC chart, and a risk heatmap of the top 10 assets.
Watchlist Assets you star, persisted in your browser. Star them from the dashboard or the scanner.
Risk alerts risk above, price above and price below thresholds, evaluated against live market data every 60 seconds, with a 30-minute re-notify window. These raise the only notifications the app produces.
Compare Two assets side by side — scores, per-factor metrics, and 30-day performance indexed to 100 so both fit on one axis honestly.
Portfolio Holdings priced live, with allocation, average risk, a real 30-day value history, and an optional AI rebalancing audit. Holdings persist locally.
Reports Your watchlist as an equal-weight basket: indexed performance, realised daily volatility, live risk scores. Exports to PDF and CSV.
AI Assistant A chat interface backed by Gemini, primed with current market data.
Learning Hub A built-in education section: curated video embeds, a concept glossary, and a structured learning path.
Settings Your profile, a summary of what is stored locally, and a one-click wipe of all local data.

Adding a portfolio holding requires picking a coin that CoinGecko's search actually returned. You cannot type a name and have the app invent an asset around it.


Screenshots

Risk Scanner

A deterministic 0–100 score, the factor breakdown behind it, and a live AI reading of those numbers.

Risk Scanner

Portfolio

Live pricing, allocation and per-asset risk. Holdings survive a reload.

Portfolio

Reports

The watchlist as an equal-weight basket indexed to 100, with its realised daily volatility.

Reports

Compare

Two assets, one axis. Performance is indexed to a common base rather than plotted on two scales.

Compare

Watchlist

Tracked assets with live scores and per-coin alert thresholds.

Watchlist

AI Assistant

Backed by Gemini, and explicit about what it is not given: no on-chain, whale or sentiment data. When you ask about your portfolio it is handed your real saved holdings, or told you have none.

AI Assistant

Mobile

Every screen is responsive. The sidebar becomes an off-canvas drawer below lg.

CryptoGuard dashboard on a 390px phone viewport

Landing and sign-in

Landing Sign-in
CryptoGuard landing page CryptoGuard demo sign-in screen

These are captured from the running app, not mocked up. To regenerate: npm run build && npm start, then node scripts/warm-cache.mjs && node scripts/screenshots.mjs.


How the risk score works

The score starts at a baseline of 50 and is adjusted by three factors, all of them measured. It is deterministic: the same asset with the same market data always produces the same score, on every page and on every machine.

Factor Source Effect on the score
Volatility 24h price change > 10% → +20 · > 5% → +10 · < 2% → −5
Market cap Live market cap < $10M → +25 · < $100M → +15 · > $10B → −15
Liquidity Volume-to-cap ratio Thin volume relative to cap raises the score

An unknown input contributes nothing rather than being guessed at. A missing market cap is not treated as a small market cap, and an unknown 24h change is not treated as a calm one — a coin we know nothing about scores the neutral 50, not a flattering 45.

The score used to include two further factors, sentiment and whaleActivity, which had no data source behind them: each was an FNV hash of the coin's name, and together they moved the published score by up to 24 of its 100 points. Two coins with identical market cap, volume and 24h change could land a risk badge apart purely because their names hashed differently, and a risk above 70 alert could fire on the strength of a string hash. They have been removed.

Scores band as 0–29 LOW, 30–59 MEDIUM, 60–84 HIGH, 85+ CRITICAL.


Architecture

Browser  (React 19 + Vite)
   │
   │  /api/*   — same origin; no API keys ever reach the client bundle
   ▼
Express  (server.ts)
   │  · 5-minute response cache, bounded at 500 entries
   │  · serves stale cache when an upstream fails, instead of blanking the UI
   │  · rate limits — 120/min on data routes, 10/min on the AI route
   │  · validates coin ids against /^[a-z0-9][a-z0-9-]{0,63}$/ before proxying
   ▼
CoinGecko  ·  alternative.me (Fear & Greed)  ·  Google Gemini

Why a server at all? The Gemini key must never reach the browser — it is read server-side and sent upstream as a header. And CoinGecko's free tier rate-limits hard, so responses are cached centrally and served stale on failure rather than each visitor being throttled independently.

On the client, a read-through cache with in-flight deduplication means three components that each want market data produce one request, and navigating back to a page you just left doesn't refetch what it already has.

Built with: React 19 · Vite 6 · TypeScript 5.8 (strict) · Tailwind CSS v4 · Recharts 3 · framer-motion · Express 4 · axios.


Getting started

Prerequisites: Node.js 20+ and npm.

git clone https://github.com/murthyroshan/CryptoGuard.git
cd CryptoGuard
npm install

Create a .env in the project root:

GEMINI_API_KEY=your_google_gemini_api_key

Run it:

npm run dev          # http://localhost:3001

The dev server runs Express with Vite in middleware mode, so the API and the frontend share one origin and one port — no CORS setup, no proxy config.

Production build:

npm run build
npm start            # serves dist/ with gzip and immutable asset caching

Without a Gemini key the app still runs. Every market feature works — scores, charts, alerts, exports — and the AI panels state plainly that analysis is unavailable. They do not invent one. Keys come from Google AI Studio.


Environment variables

Variable Required Default Purpose
GEMINI_API_KEY for AI features Google Gemini key. Server-side only.
GEMINI_MODEL no gemini-2.5-flash Overridable, so a model retirement doesn't need a code change.
PORT no 3001 Server port.
NODE_ENV no development production serves the built dist/ instead of Vite middleware.

.env is gitignored — never commit it. This repository leaked a key that way once, and undoing it meant rewriting git history and force-pushing.


Scripts

Script What it does
npm run dev Express + Vite middleware on :3001, with HMR.
npm run build Builds to dist/.
npm start Production server: gzip, immutable asset caching, SPA fallback.
npm run type-check tsc --noEmit, strict mode.
npm run clean Removes dist/.
node scripts/warm-cache.mjs Primes the server cache, honouring CoinGecko's Retry-After.
node scripts/screenshots.mjs Regenerates docs/screenshots/ by driving your installed Chrome.

Both scripts target http://localhost:$PORT and accept BASE_URL to override it. If Chrome isn't in a standard location (a per-user install, Chromium, a snap), point CHROME_PATH at the executable. Run warm-cache first — without it the capture goes to CoinGecko live, gets throttled, and shoots the empty states.


API reference

All routes are same-origin under /api, cached for 5 minutes, and rate-limited.

Route Returns
GET /api/health { status: "ok" }
GET /api/market-data Top coins by market cap. ?ids=bitcoin,aave prices an explicit set instead.
GET /api/coin/:id Full detail for one coin. Ticker aliases (btcbitcoin) are resolved.
GET /api/search?q= Coin search. The only way an id enters the app — the client cannot mint one.
GET /api/global Total market cap, 24h change, BTC dominance, active coins.
GET /api/fear-greed The live Fear & Greed index and its label.
GET /api/history/:id?days= Daily price points.
GET /api/trending What is trending right now.
POST /api/analyze Gemini completion. Limited to 10/min; prompts capped at 8,000 characters.

A response served from stale cache carries X-Cache: stale. A 429 carries Retry-After.


Performance

The app shipped as a single 1.4 MB JavaScript chunk, served uncompressed, containing every page and library — including a PDF generator most visitors never invoke.

Before After
Initial payload (landing page) 1,464 KB 557 KB raw · 168 KB gzipped
Compression none gzip
Chunks 1 route-split; react / charts / motion / vendor cached separately

What changed:

  • Route-level code splitting. Every page behind the login is React.lazy-loaded, so someone landing on the marketing page no longer downloads and parses the entire dashboard.
  • jsPDF loaded on click. It drags in html2canvas and dompurify — roughly 225 KB that every visitor used to pay for whether or not they ever exported a report.
  • The chart library made genuinely lazy. Subtler than it sounds: naming a package in manualChunks pulls its whole dependency tree in with it, and clsx — which the always-loaded shell also uses — was being swept into the chart chunk, forcing the entry to preload all 380 KB of Recharts just to reach it.
  • The canvas backdrop rewritten. It allocated 90 radial gradients per frame and re-randomised every connection curve on every tick, which is why the lines shimmered. It now blits a pre-rendered glow sprite, compares squared distances instead of taking ~4,000 square roots a frame, halves its particle count on small screens, caps the device-pixel ratio at 2, pauses on a hidden tab, and holds still under prefers-reduced-motion.
  • Request deduplication and caching on the client, so navigation stops refetching what it has.
  • Cache-Control: immutable on content-hashed assets; no-cache on index.html.

Honest limitations

This section exists because the app used to fake most of what follows. The fixes are worth stating plainly rather than quietly omitting.

  • Sign-in is a demo. There is no user database and no password check — any valid-looking email signs you in. The screen says exactly that, and asks you not to enter a real password. Your profile, holdings, watchlist and alerts live in localStorage and never leave your browser.
  • There is no on-chain analysis. A "Rug Pull Detector" that reported liquidity locks, mint functions and owner privileges was removed, because every one of its verdicts was computed from market_cap_rank < 200 rather than from a contract. Real checks need an on-chain source such as Etherscan or GoPlus. A fabricated contract audit is more dangerous than no audit at all.
  • The AI output is a language model's reading of the numbers. It is not financial advice. It is given the top coins by market cap and, if you ask about your portfolio, your saved holdings — it has no on-chain, whale-movement or social-sentiment data, and is instructed not to pretend otherwise. When the model is unreachable, the app says so rather than substituting a canned analysis.
  • CoinGecko's free tier is heavily rate-limited. The server caches and serves stale data on failure, but under sustained throttling some panels will report that data is unavailable. That is the intended behaviour, not a bug.
  • No automated tests and no ESLint config. npm run type-check is currently the only gate.

None of this is investment advice. CryptoGuard is a data-visualisation project.


Project layout

cryptoguard/
├── server.ts                 Express: API proxy, cache, rate limits, static serving
├── vite.config.ts            Build config and manual chunking
├── scripts/
│   ├── warm-cache.mjs        Primes the upstream cache, with backoff
│   └── screenshots.mjs       Regenerates docs/screenshots via local Chrome
├── docs/screenshots/         Captured from the running app
└── src/
    ├── App.tsx               Routes, providers, lazy boundaries
    ├── components/
    │   ├── ui/               Card, Button, RiskBadge, Table, Modal, ScoreBar…
    │   ├── RiskDial.tsx      Semicircular risk gauge
    │   └── Sidebar.tsx       Off-canvas below lg
    ├── context/              Auth, Notifications, Watchlist, Alerts
    ├── hooks/useLocalStorage.ts
    ├── layouts/DashboardLayout.tsx
    ├── pages/                Dashboard · CoinScanner · Watchlist · Compare · Portfolio
    │                         Reports · AIAssistant · LearningHub · Settings
    ├── services/api.ts       Typed API client with a read-through cache
    └── utils/
        ├── risk.ts           The scoring engine
        ├── chartTheme.ts     Single source of truth for chart colour
        └── csv.ts            CSV export

Design tokens live in src/index.css, under Tailwind v4's @theme block. The chart palette was validated for colour-vision deficiency — an amber/orange pair separated by a ΔE of only 2.1 under deuteranopia was caught by computation and replaced.


Licence

MIT — see LICENSE.

About

CryptoGuard — AI-powered crypto risk intelligence platform that analyzes tokens, tracks whale activity, and delivers actionable risk scores for safer trading decisions.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages