# Sod

> Run the Start-of-Day analysis and verdict session for the trading journal (CLAUDE.md Section 11, at /Users/latuconsinafr/Personal/Trading/Journals/CLAUDE.md). Global, MCP-only — writes exclusively to the trading-journal MCP server, no markdown output. Auto-detects today's date, asks for the pilot's thesis, then runs the full daily protocol. Invoke every trading morning.

- Skill: `latuconsinafr/sod` (Agent Skill)
- Install (CLI): `npx skillmds@latest add latuconsinafr/sod`
- Raw SKILL.md: https://api.skillmd.com/api/skills/latuconsinafr/sod/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: latuconsinafr (https://skillmd.com/u/latuconsinafr)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/latuconsinafr/sod

---


# Daily Analysis — Start of Day (global, MCP-only)

**This skill replaces the old project-local `/sod`** (formerly at
`Journals/.claude/skills/sod/`, deferring to `Journals/PROMPTS.md`). That file and its
markdown-writing sibling files are left in place, unused — this skill does not read or write
any of `journal/`, `metrics/`, `account/`, or `PROMPTS.md`. The session protocol below is now
owned by this file directly, since nothing else reads `PROMPTS.md` to keep it in sync anymore.

**What still has one source of truth:** `CLAUDE.md` at
`/Users/latuconsinafr/Personal/Trading/Journals/CLAUDE.md` remains the actual rulebook —
Section numbers referenced below (§3, §5, §6, §7, §8, §9, §15, §16, §19) point there. Read it in
full if it hasn't been read yet this session. `trading_rules` (the DB table meant to eventually
hold this content structurally) is empty as of 2026-08-18 — porting CLAUDE.md's 21 sections into
structured rows correctly is its own high-stakes task, not something to do casually here. Until
that exists, this file keeps reading CLAUDE.md directly rather than trusting an empty table.

**If anything this session suggests a rule should change**, per CLAUDE.md §20.14: add it to
CLAUDE.md §21.3 (a short row in the parked-observations table) — **do not act on it.** Rule
changes belong at quarter boundaries (§13). This is the one legitimate edit this skill makes to
CLAUDE.md itself; nothing else in this file writes to it.

**Instruments — resolved live via `get_active_instruments`, never hardcoded.** Call it in Step 2
below and use its result for the rest of this session — do not assume EU/GU/UJ/XAU is still the
active set (CLAUDE.md §2; toggled via `pairs.isActive`). It returns each active pair's full code
(`EURUSD`, not the shorthand this file still uses for readability) plus its base/quote currencies
— every other MCP tool's `instrument` parameter needs the full code, confirmed by an actual
rejected call during this skill's own smoke test (2026-08-18, back when this was a fixed table).

## Step 0 — confirm the MCP server is actually reachable

**Do this before anything else.** There is no markdown fallback anymore — if the MCP server is
down, nothing from this session can be recorded at all. Call `get_account_state` (cheap, no
side effects) as a connectivity check.

- **Succeeds** → proceed.
- **Fails** → stop. Tell the pilot plainly: the `trading-journal` MCP server isn't reachable,
  likely because the production stack isn't running (`just run` in `webapp/`, which builds the
  app and brings up the Cloudflare tunnel together). Ask whether to wait while they start it and
  retry, or proceed analysis-only with an explicit, repeated warning that nothing said in this
  session will be saved anywhere. Never silently proceed as if recording will happen when it
  won't — that's the exact hindsight/reconstruction failure mode CLAUDE.md §18 exists to prevent,
  just in a new shape.

## Step 1 — resolve the date

Use today's system date, unless the pilot gives one explicitly.

**Hard guard: refuse any date before today.** This skill writes a directional thesis, and a
thesis for a day that already happened is being written with knowledge of what the market
actually did — worse than no record, because it looks genuine. If asked for a past date, say
plainly that SOD cannot run for it, and point to `/eod [DATE]` instead — reviewing what happened
is legitimate; inventing what would have been thought is not. A future date is fine (pre-planning
ahead of a known gap) but flag that it needs re-confirming against same-day charts before
anything is actually traded on it.

## Step 2 — load context via MCP, not files

No `account/account_spec.md` or `account/trading_conventions.md` to read — that content now
lives in the DB and is the whole reason those files are being left behind. Call:

- **`get_active_instruments`** — the active pair set, this account's specs for them, and the
  currencies involved. Resolve every instrument/currency reference for the rest of this session
  from this result, not from memory of a prior session or from this file's own shorthand table.
- **`get_account_state`** — balance, sizing phase, cumulative deposited, hard-stop threshold.
- **`get_trading_conventions`** — the declared breakeven/partial/trailing convention in force.
  Required before approving any entry (CLAUDE.md §20.10) — never assume which one is active.
- **`get_trading_rules`** — the live rulebook values (§3.7, §5, §6, §8, §9, §10, §17). State which
  value was used and why when it matters to a verdict.
- **`get_open_positions`** and **`get_session_completeness`** (recent) — continuity context, so
  today's read isn't blind to what's already open or how recent sessions went.
- **`get_active_levels`** — every currently-`watching` level, so Step 5.2 can check today's price
  action against them rather than never looking at what's already on file (added 2026-09-05).

## Step 3 — locate today's charts

Charts are filed by **when they were captured** — today, always — not by which entry they
inform. Check, in order:

1. **`charts/[DATE]/`** (flat) — the normal case, one capture serving both SOD and EOD if this
   runs inside `/evening`.
2. **`charts/[DATE]/sod/`** — if SOD and EOD are being captured separately this session.

- **All required found** (D1/H4/M15 × every instrument from Step 2's `get_active_instruments`) →
  read them directly, don't ask the pilot to re-attach anything.
- **Some found, some missing** → name exactly which are missing, ask only for those.
- **None found, and none pasted this turn** → ask the pilot to paste the clean charts for D1/H4/M15
  × every active instrument.
  Pasted images resolve to a real local path when Claude Code and the trading.db/webapp are on
  the same machine (confirmed empirically 2026-08-18) — `attach_chart` in Step 6 uses that path
  directly. **If Claude Code is running on a different machine than the one hosting the MCP
  server**, a pasted image's local path won't be readable by the server — if `attach_chart` fails
  for this reason, say so plainly rather than silently skipping the attachment.
- **Annotated versions also present** → set them aside. Read the clean charts first and
  independently, before looking at annotated ones or the pilot's stated thesis — this ordering
  is deliberate (see Step 5.1); skipping it means anchoring to the pilot's markup instead of
  forming an independent read.

**1H capture stays optional, separate from the required 12 — not a §2 change.** CLAUDE.md §2's
daily-reviewed timeframes (D1/H4/M15) are unaffected and stay exactly as written. If a 1H clean
chart per instrument happens to be available (`charts/[DATE]/{SYMBOL}H1.png`, matching the
existing `{SYMBOL}Daily/H4/M15.png` convention), read it and call `write_technical_score` for it
too (Step 6). **Do not ask the pilot to capture 1H specifically** — D1/H4/M15 are the only charts
this session actually requires; 1H is a bonus if it's already there, not a new ask.

## Step 4 — get the pilot's thesis

Ask once, plainly: *"What's your thesis for each instrument today?"* — and wait. **Do not
proceed without it and do not offer one first.** CLAUDE.md §1 is absolute: Claude never
originates the first directional call, for any instrument, ever.

## Step 5 — run the analysis, in this exact order

1. Using ONLY the clean charts, state an independent structural read per instrument (D1
   structure → H4 confirmation → M15 timing) — **before** reacting to the pilot's thesis or any
   annotated chart. For every level named, state WHERE PRICE PREVIOUSLY TURNED there (date +
   wick) — CLAUDE.md §3.3's validation test. **The date is exact, counted from the nearest labeled
   gridline — never "approx," and a sparsely-labeled chart is never a reason to skip naming one.
   Apply the same counting *effort* to every instrument this session — do not verify one more
   carefully than the others. But decide *per instrument, not for the session as a whole*: if
   one instrument's turn can't be dated, that one is unvalidated — it does not mean skipping
   every other instrument that *can* be dated. Partial coverage across the session is the normal,
   correct outcome, not a fallback. A stated tolerance ("±1-2 days") is not exact and does not
   pass; a level that can't clear the same rigor used elsewhere this session doesn't get written
   with a hedge, it doesn't get written — for that instrument only. Do this by reading the image
   and counting candles — never by
   writing or running a script, pixel-measurement tool, or any automated axis calibration; that's
   a fragile detour, not extra rigor, and a bug in one is never a reason to write zero levels
   instead.** If it can't be named, call it a price, not a level.
   Note volume where it confirms or contradicts a break, rejection candle, or trend (§3.10,
   observational, not pass/fail).
2. **Check today's read against `get_active_levels`'s existing `watching` levels for this
   instrument (added 2026-09-05).** If price has actually traded through a level, or the
   structural thesis it was based on has broken (a close beyond it, a confirmed break of the
   swing structure it came from), note it for `resolve_level` in Step 6 — `triggered` for the
   former, `invalidated` for the latter. A level that's simply gone stale (superseded by a
   tighter/more recent zone, or just old enough it's no longer worth watching) also gets
   `resolve_level`'d with `stale`, not left to accumulate forever. Do not resolve a level just
   because price is *near* it — only an actual trade-through or a confirmed structural break
   counts, same rigor as never inventing a level in the first place.
3. Compare against the pilot's thesis (and annotated charts, if attached). Apply the full §3
   checklist — support, challenge, or flag gaps. Explicitly check the pilot's levels against the
   same standard: prior turn named, zone width within the §3.6 max, and whether the target is the
   FIRST obstacle or a later one (§3.7).
4. Run today's fundamental digest (§4): calendar events, currency strength 0–10 for each non-metal
   currency in Step 2's active-instrument set, risk-sentiment read for each metal in it. §19
   sources only — release times/dates from the issuing institution, never a summary article. State
   times in WIB / server / ET.
5. Apply correlation/clustering (§5), event-risk timing (§6), and the conflict-resolution table
   (§7).
6. Give a verdict per instrument: enter now / pending limit-stop order / wait for [condition] /
   stand aside / skipped — with reasoning. `SKIPPED` needs a stated reason; there's no
   `NOT_ANALYZED` case here since every active instrument always gets *some* verdict in normal
   operation.
7. If wait/stand-aside: the hypothetical gets logged live in Step 6, not reconstructed later.
   Where a setup fails §3.7, it gets logged **twice** — once at the wanted target, once at the
   actual obstacle (§16).
8. If entering now, in-session: confirm the DB-configured minimum R:R (`get_trading_rules`,
   §3.7), check drawdown circuit breakers (§9), size per §8, state the management convention up
   front. If entering **later**, outside this session (the normal case), point to the Pre-Trade
   Check per Step 7 below — the record must exist before the click, not after.

## Step 6 — write the session through MCP (the only write path)

**No markdown, no "additive" fallback — this is the record.** If any call in this list fails
partway through, stop and report exactly which calls succeeded and which didn't, rather than
silently treating the session as fully recorded. Never invent values to fill an argument — every
call here uses values that actually came out of Step 5.

1. **`write_daily_analysis`** — once per instrument, every instrument in the active set, every
   day, including a `SKIPPED` verdict with a `skipReason` if one was deliberately not reviewed.
2. **`write_fundamental_digest`** — once per non-metal currency in the active set.
3. **`write_risk_sentiment`** — once per metal currency in the active set (different scale from
   currency strength — don't conflate the two calls).
4. **`write_calendar_event`** — for each relevant event surfaced this session. Leave `timeUtc`
   unset and say so if it isn't verified against a primary source.
5. **`write_hypothetical`** — for any wait/stand-aside verdict, live. Double-log per §16 where a
   setup failed §3.7.
6. **`write_level`** — for any level named today that clears §3.3's validation bar, at whichever
   timeframe (D1/H4/M15) it was actually read on. As of 2026-08-30 this upserts by matching
   pair+timeframe+type+overlapping price range against an existing `watching` level — a
   re-confirmation merges its prior turns into the existing row instead of duplicating it, so there's
   no need to manually check `get_active_levels` first just to avoid a duplicate. A materially
   different zone (non-overlapping range) still inserts as its own row, which is correct — multiple
   simultaneous support/resistance levels at one timeframe are expected, not an error.
7. **`resolve_level`** — for any level flagged in Step 5.2 as triggered, invalidated, or stale
   (added 2026-09-05). Requires the level's real `id` — use the one `get_active_levels` (Step 2)
   returned, never guess it. A level not flagged in Step 5.2 gets no call here; resolving levels
   speculatively (price merely near one, not actually through it) is the same error class as
   inventing a level that was never validated.
8. **`attach_chart`** — once per screenshot actually read this session (D1/H4/M15 × every active
   instrument), by the path it was read from in Step 3. Never re-paste an image just to attach it.
   If the path isn't readable by the MCP server (cross-machine case, see Step 3), say so rather
   than silently skipping.
9. **`write_technical_score`** — once per instrument per timeframe actually read in Step 5.1 (D1,
   H4, M15 — every active instrument; plus 1H if it happened to be captured). This is not new
   analysis — it's the same structural read from Step 5.1, translated into a -100..100 number
   (fundamentals are computed server-side, never passed in). Rough band alignment with the
   heatmap's own labels: ≥50 Strong Bullish, 20-49 Bullish, -19..19 Neutral, -50..-21 Bearish,
   ≤-50 Strong Bearish — direction and conviction from the actual read, not a fixed formula.
   **Never call this for a pair not actually charted this session** — the whole point is
   replacing fabricated placeholder scores with real ones, not adding new fabricated ones for
   pairs outside the active set (`get_active_instruments` — the wider `pairs` catalogue is not
   in scope just because it exists).
10. **`mark_session`** — `sodDone: true` for today's date.

## Step 7 — the Pre-Trade Check

If any verdict is "enter now" or produces a level to watch for later entry, point the pilot to
the webapp's **`/trades/new`** (`https://trading.latuconsinafr.com/trades/new` if the tunnel is
up, otherwise whatever local URL is reachable this session) — it runs the same §15/§9 gate,
computes R:R and lot size live, and writes a real `OPEN` trade plus the compliance checklist
snapshot when submitted.

**`open_trade` (the MCP tool) is import/backfill only — never call it to represent a live entry.**
If asked to "record" or "log" a trade the pilot says they just placed, decline and point to
`/trades/new` instead. This is a settled decision (2026-08-18), not a judgment call to re-litigate
per session.

