Skip to main content

price-alert

Fire when the tracked token does something - new ATH, sharp 1h move, or operator-set target crossed. Silent on normal days.

Aller à l'installation

Informations de source

Dépôt
aeonfun/aeon
Dernière activité de la source
4 août 2026 à 21:01
Langue détectée de SKILL.md
anglais
Étoiles
738
Forks
265

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
price-alert
description
Fire when the tracked token does something - new ATH, sharp 1h move, or operator-set target crossed. Silent on normal days.
metadata
{"title":"Price Alert","category":"basics","var":"","tags":["crypto"]}
> **${var}** — Optional. Pass one or more `target_price` levels (comma-separated USD numbers, scientific notation allowed) to fire a one-time alert when the price crosses any of them. Empty = only ATH and sharp-move gates run. Pass `dry-run` to skip notify (state still updates). Pass `set-target:<price>` — the shape the Telegram force-reply sends (step 7) — to register a target and get a one-line confirmation. Today is ${today}. `repo-pulse` reports star/fork deltas once a day, and the other scheduled digests run at fixed hours. None of them tell the operator "the price just hit a new high" or "the token moved 28% in the last hour" — both are events that warrant attention the moment they happen, not 14 hours later in a daily digest. This skill closes that window. ## Why this exists Scheduled daily digests are calm summaries. Real moves need real-time signal. Three classes of move are worth a same-day ping: - **New all-time high.** The single most narrative-shaping price event a token can have. Worth marking on the timeline regardless of size. - **Sharp 1h moves (±20%).** Either a buyer wave or a liquidation cascade — both change what the operator does next (post about it, watch for follow-through, check the chart). - **Operator-set target crossings.** The operator may want to know when price clears $X (often a personal stretch goal or a level tied to a tweet/launch). One alert per target per direction. Everything else is noise not worth a same-day ping. ## Config Reads: - `memory/MEMORY.md` "Tracked Token" section — the tracked token's contract/chain. If absent, exit silently. - `memory/topics/price-alert-state.json` — last-known ATH, last-alert timestamps per event type, target-crossing history. Created with defaults on first run. Writes: - `memory/topics/price-alert-state.json` — updated state every run. - `memory/logs/${today}.md` — one log block per run, even on `OK`. - Notification via `./notify` — only when a gate fires. No new secrets. Uses keyless DexScreener; falls back to WebFetch when a public `curl` GET is flaky (there is no network sandbox). ## State schema ```json { "contract": "0xbf8e8f0e8866a7052f948c16508644347c57aba3", "chain": "base", "ath": { "price_usd": 0.000003757, "observed_at": "2026-05-10T19:00:00Z", "announced_at": "2026-05-10T19:30:00Z" }, "last_alerts": { "ath": "2026-05-10T19:30:00Z", "sharp_move": null, "target_hit": null }, "targets": { "0.000005": { "side": "above", "first_seen_below_at": "2026-05-11T08:00:00Z", "hit_at": null, "announced_at": null } } } ``` Key invariants: - `ath.price_usd` only ever monotonically increases. If a run sees a lower price than the stored ATH, leave the ATH alone. - `last_alerts.*` powers the 4h dedup window. Each gate has its own clock. - `targets.${price}.side` is `above` if `current_price < target` when the target was first observed (operator is waiting for the price to climb to it), and `below` otherwise. Set once, never flipped. - `targets.${price}.hit_at` is set the run the cross happens. `announced_at` is set the run the notification fires. They differ only if the run lands inside a dedup window — but target alerts never re-fire, so `hit_at == announced_at` in practice. ## Steps ### 1. Parse var - If `${var}` starts with `set-target:` → set `FROM_REPLY=1`, `MODE=execute`. Strip the `set-target:` prefix (`${var#set-target:}`); the remainder is the target(s) list, parsed exactly like the comma-split below. This is the shape `scripts/telegram-route.sh` sends when the operator replies to the step-7 force-reply prompt. The run proceeds normally (register the target, don't fire on first observation) and closes the loop with a confirmation in step 7. - Else if `${var}` matches `^dry-run` → `MODE=dry-run`. Strip the prefix; remainder (if any) is treated as targets. - Otherwise `MODE=execute`. - Split the remainder on `,` (commas) and strip whitespace. - For each token: if it parses as a positive float (scientific notation OK, e.g. `5e-6`), include it. Reject zero / negative / non-numeric tokens and log `PRICE_ALERT_BAD_TARGET: ${token}` — continue with the surviving targets. - If after filtering the remainder was non-empty but yielded zero valid targets → log `PRICE_ALERT_BAD_VAR: ${var}` and exit. Normally no notify — **but if `FROM_REPLY=1`**, first close the loop with `./notify "Couldn't read \"${var#set-target:}\" as a price. Reply with a number like 0.000005."` (a force-reply always deserves an acknowledgement). - If the remainder was empty, `TARGETS=()` is fine — ATH and sharp-move gates still run. ### 2. Resolve tracked token ```bash mkdir -p memory/topics [ -f memory/topics/price-alert-state.json ] || cat > memory/topics/price-alert-state.json <<'EOF' {"contract":null,"chain":null,"ath":null,"last_alerts":{"ath":null,"sharp_move":null,"target_hit":null},"targets":{}} EOF ``` Parse the "Tracked Token" table in `memory/MEMORY.md`. Pull `CONTRACT` (column 2 of the first data row) and `CHAIN` (column 3, lowercased). If the section is missing, the table is empty, or the contract field doesn't match `^0x[0-9a-fA-F]{40}$` → log `PRICE_ALERT_NO_TOKEN` and exit (no notify, no state write). If the state file's `contract` is set and differs from the resolved contract → log `PRICE_ALERT_TOKEN_CHANGED` and reset the state file to defaults with the new contract/chain. ATH starts over with the new token. ### 3. Fetch current price (DexScreener primary) ```bash RESP=$(curl -fsS "https://api.dexscreener.com/latest/dex/tokens/${CONTRACT}" 2>/dev/null || echo "") ``` If the response is empty, falsy, or `jq` can't parse it → fall back to **WebFetch** on the same URL with prompt `"Return the raw JSON body verbatim."` and try again. If both paths fail → log `PRICE_ALERT_FETCH_FAIL` and exit with status `ERROR` (no notify, no state mutation beyond touching `last_run_at`). From the parsed JSON: - Filter `.pairs[]` to entries where `.chainId == "${CHAIN}"`. - Of those, pick the entry with the **highest `.liquidity.usd`** — call this the deepest pool. If no pair matches the chain, fall back to the highest-liquidity pair across all chains and mark `chain=fallback` in the log. - Extract: `CURRENT_PRICE=.priceUsd` (float), `H1_CHANGE_PCT=.priceChange.h1` (float, may be missing → treat as 0), `H24_CHANGE_PCT=.priceChange.h24` (float, may be missing → treat as 0), `POOL_URL=.url`. If `CURRENT_PRICE` is missing, zero, or non-numeric → log `PRICE_ALERT_BAD_PRICE` and exit `ERROR`. ### 4. Evaluate ATH gate ``` prior_ath = state.ath.price_usd (null on first run) new_ath = (prior_ath is null) OR (CURRENT_PRICE > prior_ath) ``` If `new_ath`: - Always update `state.ath.price_usd = CURRENT_PRICE`, `state.ath.observed_at = NOW`. - Notify only if the last `ath` alert was >4h ago (or never). On the first run after token initialisation, suppress the notification (we don't know if this is genuinely an ATH or just the starting baseline) — set `state.ath.announced_at = NOW` so subsequent strict-higher prices alert correctly. - If suppressed because `prior_ath is null` (first run), record verdict `ATH_BASELINE` for the log; no notify. - Otherwise verdict for this gate is `ATH`. ### 5. Evaluate sharp-move gate ``` sharp = abs(H1_CHANGE_PCT) >= 20.0 ``` If `sharp`: - If `state.last_alerts.sharp_move` is within the last 4h → verdict `SHARP_MOVE_DEDUPED`, no notify. - Otherwise verdict `SHARP_MOVE`, notify, set `state.last_alerts.sharp_move = NOW`. Sign of `H1_CHANGE_PCT` decides the direction word in the message (`up` for ≥0, `down` for <0). ### 6. Evaluate target-crossing gate For each target in `TARGETS`: - If the target isn't yet in `state.targets`: this is the first time the operator has set it. Record `side` (`above` if `CURRENT_PRICE < target`, else `below`), `first_seen_below_at=NOW`, `hit_at=null`, `announced_at=null`. **Do not notify on first observation.** This avoids "the price was already there when you set the target" noise. - If the target is in `state.targets` and `hit_at` is already set → skip; target-hit alerts are one-shot per side. - Otherwise check the cross: - `side=above` & `CURRENT_PRICE >= target` → crossed. - `side=below` & `CURRENT_PRICE <= target` → crossed. - On cross: set `hit_at=NOW`, `announced_at=NOW`, verdict `TARGET_HIT` for this target, notify (subject to 4h dedup on `last_alerts.target_hit`). Targets that crossed in prior runs stay in the state file with `hit_at` set so they don't refire if the price wobbles back. ### 7. Combine verdicts and notify Run-level verdict precedence (used for the log status line and the notification ordering): 1. `ATH` — strict new high after baseline. 2. `TARGET_HIT` — operator-set level crossed. 3. `SHARP_MOVE` — ±20% in 1h. 4. `OK` — no gate fired. If multiple gates fired in the same run, send one notification per gate. Each gate independently respects its own dedup clock — an ATH and a TARGET_HIT in the same run produce two messages; an ATH and a SHARP_MOVE_DEDUPED produce one. #### Notification templates **ATH:** ``` *$TOKEN — New ATH — ${today}* $TOKEN just printed a new all-time high at $X.XXXXe-N. Previous ATH: $Y.YYYYe-N (set ${prior_ath_age} ago). 24h move: ±Z.Z% · 1h move: ±W.W% Chart: ${POOL_URL} ``` **SHARP_MOVE:** ``` *$TOKEN — Sharp 1h Move — ${today}* $TOKEN ${up|down} ${abs(h1):.1f}% in the last hour — now $X.XXXXe-N. 24h: ±Z.Z% ${one of: "Buyer wave — watch for follow-through." | "Selling pressure — watch the next hour for stabilisation." } Chart: ${POOL_URL} ``` (Direction phrase is hard-coded by sign: positive = buyer wave; negative = selling pressure. No freelance commentary.) **TARGET_HIT:** ``` *$TOKEN — Target Hit — ${today}* $TOKEN just crossed $${target} (now $X.XXXXe-N). Direction: ${above|below} 24h: ±Z.Z% · 1h: ±W.W% Chart: ${POOL_URL} ``` If `MODE == dry-run`: build the messages, log the planned notifications, but skip `./notify`. State still updates so dedup clocks advance correctly. Cap each message at ~2500 chars; price-alert messages are short by nature and shouldn't approach this. #### Send the alert Build each firing gate's message to a file and send it with `./notify -f alert.md` (one send per gate). State still advances even on a deduped run, so the dedup clocks stay correct. #### Set-a-target follow-up (force-reply) Buttons and force_reply can't share one message, so this is a **separate** send. After a genuine **ATH** alert (post-baseline, not a deduped repeat), and only when no un-hit operator target currently sits above `CURRENT_PRICE` (don't nag operators who already queued one), offer to capture the next level: ```bash ./notify "New high — want an alert when $SYMBOL clears a level above this? Reply with a price." \ --force-reply --placeholder "e.g. 0.000005" \ --context "price-alert::set-target" ``` The reply routes back as `var=set-target:<price>` (handled in step 1). On that run, once the target is registered (step 6, first-observation — no cross alert), close the loop with a one-line confirmation: `./notify "Target set: \$<price> for $SYMBOL — I'll alert you when it crosses."` (Send it only when a *new* target was actually registered this run.) ### 8. Persist state Rewrite `memory/topics/price-alert-state.json` atomically: ```bash TMP=$(mktemp) jq --arg ts "$(date -u +%FT%TZ)" ' .last_run_at = $ts | .contract = $contract | .chain = $chain | .ath = $ath_obj | .last_alerts = $last_alerts_obj | .targets = $targets_obj ' memory/topics/price-alert-state.json > "$TMP" mv "$TMP" memory/topics/price-alert-state.json ``` Validate with `jq empty memory/topics/price-alert-state.json` after writing; if it fails, restore from a `.bak` copy and log `PRICE_ALERT_STATE_CORRUPT`. Keep one `.bak` rolling. Cap `state.targets` to 20 most-recent entries (LRU by `first_seen_below_at`) so a long-running fork doesn't accumulate stale operator targets. ### 9. Log Append to `memory/logs/${today}.md`: ``` ## Price Threshold Alert - **Skill**: price-alert - **Token**: ${SYMBOL} (${CONTRACT}) - **Current**: $X.XXXXe-N | 1h: ±W.W% | 24h: ±Z.Z% - **ATH**: $Y.YYYYe-N (set ${YYYY-MM-DD HH:MM} UTC) [${UNCHANGED|NEW}] - **Sharp-move gate**: ${FIRED|QUIET|DEDUPED} - **Targets evaluated**: ${comma-list of target prices} → ${comma-list of per-target verdicts} - **Verdicts fired**: ${comma-list, or NONE} - **Notifications sent**: ${N} - **Status**: ${PRICE_ALERT_OK | PRICE_ALERT_ATH | PRICE_ALERT_SHARP_MOVE | PRICE_ALERT_TARGET_HIT | PRICE_ALERT_DRY_RUN | PRICE_ALERT_NO_TOKEN | PRICE_ALERT_TOKEN_CHANGED | PRICE_ALERT_FETCH_FAIL | PRICE_ALERT_BAD_PRICE | PRICE_ALERT_BAD_VAR | PRICE_ALERT_STATE_CORRUPT} ``` The status field carries the *highest-priority* gate fired this run, or the most relevant error. `OK` means the run completed cleanly and no gate fired. ## Exit taxonomy | Status | Meaning | Notify? | |--------|---------|---------|
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub