|
| 1 | +# Crypto Fear & Greed Index |
| 2 | + |
| 3 | +A [Glance](https://github.com/glanceapp/glance) widget that displays the current **Crypto Fear & Greed Index** — a widely followed market sentiment indicator that measures whether crypto investors are driven by fear or greed at any given moment. The widget shows a live score from 0 to 100, a color-coded gradient bar with a moving indicator, a sentiment label, an emoji, and a trend arrow comparing today's value against yesterday's. |
| 4 | + |
| 5 | +No API key, no sign-up, no configuration beyond pasting the YAML. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Preview |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## What is the Fear & Greed Index? |
| 16 | + |
| 17 | +The Crypto Fear & Greed Index was originally inspired by CNN's Fear & Greed Index for traditional stock markets, adapted for the cryptocurrency ecosystem. It distills complex market signals into a single number between **0** (Extreme Fear) and **100** (Extreme Greed). |
| 18 | + |
| 19 | +The idea is simple: when the market is driven by **fear**, prices tend to be suppressed and may represent buying opportunities. When the market is driven by **greed**, prices may be overextended and due for a correction. |
| 20 | + |
| 21 | + |
| 22 | +### Index scale |
| 23 | + |
| 24 | +| Range | Classification | Color | Emoji | What it means | |
| 25 | +|---|---|---|---|---| |
| 26 | +| 0 – 25 | **Extreme Fear** | 🔴 Red | 😱 | Investors are very worried. Historically a potential buying opportunity. | |
| 27 | +| 26 – 45 | **Fear** | 🟠 Orange | 😨 | Market sentiment is cautious and uncertain. | |
| 28 | +| 46 – 54 | **Neutral** | 🟡 Yellow | 😐 | Market is balanced between buyers and sellers. | |
| 29 | +| 55 – 75 | **Greed** | 🟢 Green | 😏 | Investors are increasingly confident, FOMO may be building. | |
| 30 | +| 76 – 100 | **Extreme Greed** | 💚 Dark Green | 🤑 | Market may be overheated and due for a correction. | |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Features |
| 35 | + |
| 36 | +- 📊 **Live sentiment score** — numeric value from 0 to 100 displayed prominently |
| 37 | +- 🎨 **Dynamic color coding** — the score and label change color automatically based on the current classification |
| 38 | +- 📍 **Gradient progress bar** — a full spectrum bar (red → orange → yellow → green) with a white indicator dot that moves to the exact position of the current score |
| 39 | +- 😱 → 🤑 **Emoji + sentiment label** — human-readable classification shown alongside the score |
| 40 | +- **▲ ▼ → Trend arrow** — compares today's score against yesterday's so you can see at a glance whether sentiment is improving or deteriorating |
| 41 | +- 🗓 **Yesterday's value** — displayed as secondary text beneath the classification |
| 42 | +- 🆓 **Completely free** — uses the public [alternative.me](https://alternative.me/crypto/fear-and-greed-index/) API with no authentication required |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +## API |
| 47 | + |
| 48 | +This widget relies on the **alternative.me Crypto Fear & Greed Index API**, a free and widely used public API that has been running since 2018. |
| 49 | + |
| 50 | +| Property | Value | |
| 51 | +|---|---| |
| 52 | +| **Endpoint** | `https://api.alternative.me/fng/?limit=2` | |
| 53 | +| **Authentication** | None — no API key required | |
| 54 | +| **Rate limit** | None enforced for normal usage | |
| 55 | +| **Update frequency** | Once per day (scores are published daily) | |
| 56 | +| **Response format** | JSON | |
| 57 | + |
| 58 | +The `?limit=2` query parameter tells the API to return the **two most recent daily entries**: today's value (`data.0`) and yesterday's (`data.1`). This is what enables the trend arrow comparison. |
| 59 | + |
| 60 | +### Example API response |
| 61 | + |
| 62 | +```json |
| 63 | +{ |
| 64 | + "name": "Fear and Greed Index", |
| 65 | + "data": [ |
| 66 | + { |
| 67 | + "value": "13", |
| 68 | + "value_classification": "Extreme Fear", |
| 69 | + "timestamp": "1708905600", |
| 70 | + "time_until_update": "74093" |
| 71 | + }, |
| 72 | + { |
| 73 | + "value": "18", |
| 74 | + "value_classification": "Extreme Fear", |
| 75 | + "timestamp": "1708819200", |
| 76 | + "time_until_update": null |
| 77 | + } |
| 78 | + ], |
| 79 | + "metadata": { |
| 80 | + "error": null |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +### Fields used by this widget |
| 86 | + |
| 87 | +| JSON path | Type | Used for | |
| 88 | +|---|---|---| |
| 89 | +| `data.0.value` | `Int` | Current day's score (0–100) | |
| 90 | +| `data.1.value` | `Int` | Yesterday's score for trend comparison | |
| 91 | +| `data.0.value_classification` | `String` | Sentiment label (e.g. `"Extreme Fear"`) | |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## Installation |
| 96 | + |
| 97 | +### 1. Open your Glance config file |
| 98 | + |
| 99 | +Typically located at `glance.yml` or wherever you have your Glance configuration set up (local install or Docker volume mount). |
| 100 | + |
| 101 | +### 2. Add the widget |
| 102 | + |
| 103 | +Paste the following block into the `widgets` list of any column on any page: |
| 104 | + |
| 105 | +```yaml |
| 106 | +- type: custom-api |
| 107 | + title: Crypto Fear & Greed Index |
| 108 | + cache: 1h |
| 109 | + url: https://api.alternative.me/fng/?limit=2 |
| 110 | + template: | |
| 111 | + {{ $value := .JSON.Int "data.0.value" }} |
| 112 | + {{ $prev := .JSON.Int "data.1.value" }} |
| 113 | + {{ $label := .JSON.String "data.0.value_classification" }} |
| 114 | +
|
| 115 | + {{ $color := "#e74c3c" }} |
| 116 | + {{ if ge $value 26 }}{{ $color = "#e67e22" }}{{ end }} |
| 117 | + {{ if ge $value 46 }}{{ $color = "#f1c40f" }}{{ end }} |
| 118 | + {{ if ge $value 55 }}{{ $color = "#2ecc71" }}{{ end }} |
| 119 | + {{ if ge $value 76 }}{{ $color = "#27ae60" }}{{ end }} |
| 120 | +
|
| 121 | + {{ $emoji := "😱" }} |
| 122 | + {{ if ge $value 26 }}{{ $emoji = "😨" }}{{ end }} |
| 123 | + {{ if ge $value 46 }}{{ $emoji = "😏" }}{{ end }} |
| 124 | + {{ if ge $value 76 }}{{ $emoji = "🤑" }}{{ end }} |
| 125 | +
|
| 126 | + {{ $arrow := "→" }} |
| 127 | + {{ if gt $value $prev }}{{ $arrow = "▲" }}{{ end }} |
| 128 | + {{ if lt $value $prev }}{{ $arrow = "▼" }}{{ end }} |
| 129 | +
|
| 130 | + <div style="display: flex; flex-direction: column; gap: 0.75em;"> |
| 131 | +
|
| 132 | + <div style="display: flex; align-items: center; gap: 0.5em;"> |
| 133 | + <span style="font-size: 3em; font-weight: bold; color: {{ $color }}; line-height: 1;"> |
| 134 | + {{ $value }} |
| 135 | + </span> |
| 136 | + <div style="display: flex; flex-direction: column;"> |
| 137 | + <span style="font-size: 1.1em; font-weight: bold; color: {{ $color }};"> |
| 138 | + {{ $emoji }} {{ $label }} |
| 139 | + </span> |
| 140 | + <span style="font-size: 0.78em; color: var(--color-subdue);"> |
| 141 | + Yesterday: {{ $prev }} {{ $arrow }} |
| 142 | + </span> |
| 143 | + </div> |
| 144 | + </div> |
| 145 | +
|
| 146 | + <div style="position: relative; height: 10px; border-radius: 5px; |
| 147 | + background: linear-gradient(to right, #e74c3c 0%, #e67e22 25%, #f1c40f 45%, #2ecc71 75%, #27ae60 100%);"> |
| 148 | + <div style=" |
| 149 | + position: absolute; |
| 150 | + left: calc({{ $value }}% - 6px); |
| 151 | + top: 50%; |
| 152 | + transform: translateY(-50%); |
| 153 | + width: 13px; height: 13px; |
| 154 | + background: white; |
| 155 | + border-radius: 50%; |
| 156 | + border: 2px solid {{ $color }}; |
| 157 | + box-shadow: 0 0 4px rgba(0,0,0,0.5); |
| 158 | + "></div> |
| 159 | + </div> |
| 160 | +
|
| 161 | + <div style="display: flex; justify-content: space-between; font-size: 0.68em; color: var(--color-subdue);"> |
| 162 | + <span>😱 Extreme Fear</span> |
| 163 | + <span>Fear</span> |
| 164 | + <span>Greed</span> |
| 165 | + <span>🤑 Extreme Greed</span> |
| 166 | + </div> |
| 167 | +
|
| 168 | + <div style="font-size: 0.8em; color: var(--color-subdue); border-top: 1px solid var(--color-border); padding-top: 0.4em;"> |
| 169 | + Source: alternative.me · Updates daily |
| 170 | + </div> |
| 171 | +
|
| 172 | + </div> |
| 173 | +``` |
| 174 | +
|
| 175 | +### 3. Restart or reload Glance |
| 176 | +
|
| 177 | +```bash |
| 178 | +# If running locally |
| 179 | +./glance |
| 180 | + |
| 181 | +# If running with Docker |
| 182 | +docker compose restart glance |
| 183 | +``` |
| 184 | + |
| 185 | +The widget will appear immediately on your dashboard. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## Configuration |
| 190 | + |
| 191 | +This widget has no required configuration — it works out of the box. The only optional field you may want to adjust is `cache`. |
| 192 | + |
| 193 | +### Cache |
| 194 | + |
| 195 | +```yaml |
| 196 | +cache: 1h |
| 197 | +``` |
| 198 | +
|
| 199 | +Since the index only updates **once per day**, a cache of `1h` is more than sufficient. Setting it lower (e.g. `5m`) will not give you fresher data and only creates unnecessary HTTP requests to the free API. You could even set it as high as `12h` if you prefer. |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +## How the template works |
| 204 | + |
| 205 | +The widget uses Glance's `custom-api` widget type with a Go template. Here is a breakdown of what each section does: |
| 206 | + |
| 207 | +**Variable extraction** |
| 208 | +``` |
| 209 | +{{ $value := .JSON.Int "data.0.value" }} |
| 210 | +{{ $prev := .JSON.Int "data.1.value" }} |
| 211 | +{{ $label := .JSON.String "data.0.value_classification" }} |
| 212 | +``` |
| 213 | +Reads today's score, yesterday's score, and the text classification from the API JSON response. Note that the API returns `value` as a string in the JSON, but Glance's `.JSON.Int` helper automatically coerces it to an integer. |
| 214 | +
|
| 215 | +**Dynamic color** |
| 216 | +``` |
| 217 | +{{ $color := "#e74c3c" }} |
| 218 | +{{ if ge $value 26 }}{{ $color = "#e67e22" }}{{ end }} |
| 219 | +{{ if ge $value 46 }}{{ $color = "#f1c40f" }}{{ end }} |
| 220 | +{{ if ge $value 55 }}{{ $color = "#2ecc71" }}{{ end }} |
| 221 | +{{ if ge $value 76 }}{{ $color = "#27ae60" }}{{ end }} |
| 222 | +``` |
| 223 | +Uses `ge` (greater than or equal) comparisons to progressively override the color variable as the score crosses each threshold. The comparisons are chained so only the highest matching band wins — a value of `80` will pass all four checks, ending on dark green `#27ae60`. |
| 224 | +
|
| 225 | +**Trend arrow** |
| 226 | +``` |
| 227 | +{{ $arrow := "→" }} |
| 228 | +{{ if gt $value $prev }}{{ $arrow = "▲" }}{{ end }} |
| 229 | +{{ if lt $value $prev }}{{ $arrow = "▼" }}{{ end }} |
| 230 | +``` |
| 231 | +Compares today vs yesterday. Arrow points up if sentiment improved, down if it worsened, and stays sideways if the value is identical. |
| 232 | +
|
| 233 | +**Gradient bar indicator** |
| 234 | +```css |
| 235 | +left: calc({{ $value }}% - 6px); |
| 236 | +``` |
| 237 | +The indicator dot is positioned using the raw score as a CSS percentage. A value of `72` places the dot at `72%` along the bar, which naturally lands in the green "Greed" zone. The `-6px` offset re-centers the 13px dot on that exact position. |
| 238 | + |
| 239 | +--- |
| 240 | + |
| 241 | +## Credits |
| 242 | +- Index data provided by [alternative.me](https://alternative.me/crypto/fear-and-greed-index/) |
| 243 | +- Built for [Glance](https://github.com/glanceapp/glance) using the `custom-api` widget type with Go templates |
0 commit comments