CommunityWriting & Editinggithub.com

atomchung/fomo-kernel

把交易 CSV 復盤成一張行為誠實卡的 Claude Code skill:機械層抓大放小 × 大師鏡片問動機 | Turn your broker CSV into one behavioral review card

What is fomo-kernel?

fomo-kernel is a Claude Code agent skill that 把交易 CSV 復盤成一張行為誠實卡的 Claude Code skill:機械層抓大放小 × 大師鏡片問動機 | Turn your broker CSV into one behavioral review card.

Works withClaude Code~Codex CLI~Cursor
npx skills add atomchung/fomo-kernel

Installed? Explore more Writing & Editing skills: steipete/notion, affaan-m/seo, affaan-m/brand-voice · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

FOMO Kernel · 用哲學鏡片復盤你的交易

把一份交易紀錄,變成一張「逼你下次只改一件事」的復盤卡。 機械層(Python)負責抓大放小——只挑最大的行為漏洞;哲學鏡片負責找動機——問出那筆交易背後你不願承認的原因。

何時用

用戶想復盤自己的交易、想知道「我反覆犯的錯是什麼」、丟給你一份券商 CSV / 對帳單、或直接說 /fomo-kernel。沒有資料時,請他提供券商 CSV / 對帳單(截圖也行,Step 0 讀得懂),並同時給「試駕」選項(見下節);只想看靜態長相 → README 的範例卡。

🧪 試駕模式(沒資料也能體驗流程;三個防護缺一不可)

用戶沒資料或想先體驗 → 用 AskUserQuestion 給兩選項:「提供我的 CSV / 先用內建假資料試駕一遍」。選試駕 → 拿 mock/mock_trades.csv 走完整四步流程,但:

  1. 狀態一律不落盤:TR_STATE_OUT 指到臨時目錄(如 mktemp -d 下);~/.trade-coach/ 的 log.jsonl / theses.jsonl / profile.md 一個字都不寫——假資料的承諾進了教練記憶,下次真復盤的對帳基準就是髒的。收尾改成一句講解:「真實使用時,這條規矩會存進你本機的教練記憶,下週回來先對帳」。
  2. Step 2 照問,但標明是演練:動機問題照走 AskUserQuestion——試駕就是要讓他體驗「我的答案會改變卡」這個差異化;但問句裡標明「示範資料,隨便選一個,看卡怎麼跟著變」,不逼他為不是他的交易編動機(#53 的尷尬就消了)。
  3. 卡標示範:卡頭標「示範 · 假資料,非真實成績」;α/β 附一句「示範資料失真,別當真」——失真警告是呈現層(你)的責任,引擎對任何輸入一致、沒有 demo 分支(#89)。

卡尾必收一句引導:「想復盤自己的交易 → /fomo-kernel your.csv」。

🔒 隱私第一(每次都要遵守)

  • 用戶的交易 CSV 全程留在他本機。你只在他的環境裡跑 engine/trade_recap.py,不上傳、不複製到別處、不寫進任何雲端。
  • 不要把用戶的交易內容寫進記憶、不要外傳給任何人(包括 skill 作者)。
  • 誠實邊界(隱私話術別過度承諾):資料不上傳後端、不落地儲存到別處、作者永遠拿不到;但你(Claude)為了復盤必須讀 CSV/JSON,交易內容自然進你的 context —— 這跟用戶平常用 Claude 一樣,不是「完全不經過任何伺服器」。README / 卡上的隱私話術照這個精度寫,別講成「絕對不離開你的電腦」。
  • 要回給作者的只有一件事:「這張卡有沒有用」的文字反饋(用戶自願)——不含任何交易明細。
  • 用戶沒給資料時,請他提供或走試駕模式(內建假資料、不落盤);絕不要主動去翻他機器上的真實對帳單。

🌐 Output language (apply every time)

Everything the user sees — your dialogue, the AskUserQuestion options, and the final card — must be in one resolved output language. Do not hardcode a language. Resolve it per session, first match wins:

  1. Explicit request this session — the user says "give it to me in English" / "用中文" / passes lang=en.
  2. Saved preferenceoutput_lang: in ~/.trade-coach/profile.md, if present.
  3. Conversation language — the language the user is speaking to you in right now. This is the default; follow it, don't impose a language.
  4. Fallback — Traditional Chinese (zh-TW).

Once resolved:

  • Run the whole flow (dialogue, questions, card) in that language. Lens files (rubric/*.lens.json) currently carry Traditional-Chinese quotes/prompts — translate them faithfully on the fly into the resolved language when you write the card.
  • Pass it to the engine each run as TR_LANG=<code> (e.g. TR_LANG=en) for forward-compatibility. The engine does not consume it yet — its own printed CLI card and lens strings stay Chinese for now; full engine/lens localization keyed on TR_LANG (a strings table) is tracked separately as internationalization work. This still governs what the user sees today, because the card is one you write from build_card_data()'s structured JSON, not the engine's printed card.
  • Persist it: on first run, or whenever the user switches, write output_lang: <code> into ~/.trade-coach/profile.md (alongside the profile principles) so the next session resolves to their preference at step 2.

The Traditional-Chinese phrasings, question templates, and card examples throughout the rest of this SKILL are illustrative of intent, not literal strings to copy — express their meaning in the resolved language.

工作流程(四步)

分工原則:engine 做純算(確定性),Claude 做世界知識(格式 / 分類 / 動機)。 需要認得世界的事都交給 Claude,engine 不 hardcode。

開場 · 讀本機狀態 + 偵測這次要處理什麼(weekly loop 入口)

投資不是復盤一次就結束。 這個 skill 是一條每週迴圈:匯入 CSV → 偵測新交易 / 新倉 → 只問缺的動機 → 寫本週 review → 出卡。目標是取代你每週的交易紀錄,而不是每次重算同一個洞。動 CSV 前先讀本機狀態(都在 ~/.trade-coach/,純本機、不外傳):

mkdir -p ~/.trade-coach
cat ~/.trade-coach/log.jsonl    2>/dev/null   # 每行一次 review session(薄 metric + 承諾);空 = 第一次
cat ~/.trade-coach/theses.jsonl 2>/dev/null   # 每行一筆 thesis event(append-only,不覆蓋);持股動機庫
cat ~/.trade-coach/profile.md   2>/dev/null   # 你的交易目標 + 3 條個人原則(復盤對照基準);空 = 第一次幫你建

路由(讀完上面兩檔 + 跑完 Step 1 engine 後判定):

  • log 空 → 初診:跑完整 Step 0→4,收尾寫第一筆 session + 為值得問的持倉建 thesis。
  • log 非空 → 對帳(每週迴圈),依序:
    1. 偵測新交易 = engine state date_end 與 log 最後一筆 date_end 之間的交易(本週新動作,復盤重點;不再從頭講舊帳)。
    2. 偵測缺 thesis 的持倉 = engine state holdings.positions 每個 cycle_id 比對 theses.jsonl新建倉(新 cycle_id)或從沒寫過 thesis 的持倉 = 缺
    3. 先對帳(Step 2.5):上次 commitment 的 metric 新舊值 + 上次每筆 active thesis 的 exit_trigger 有沒有觸發。
    4. 補缺的 thesis(Step 2):缺 thesis 的持倉由 AI (標 inferred、零提問),只對「行為矛盾、金額最大的 1 檔」問一句;已有 thesis 的不碰(除非 trigger 觸發)。

兩個狀態檔都是用戶自己的本機教練記憶,永不外傳、不回作者(隱私第一)。log.jsonl 存聚合 metric + 承諾(max_pos_pct=0.48、「虧損不加碼」);theses.jsonl 存 per-position「為什麼持有 + 什麼條件算錯」。append-only:修正 thesis = 補一筆新 event(帶 revises 指回舊的),不蓋舊的 —— 才能跨期看你當初怎麼想、後來怎麼變(蓋掉 = 跨期對帳失效 + 鼓勵事後合理化)。

Step 0 · 把任意券商格式變成引擎吃得下的(用讀檔者自己的 Claude)

用戶的 CSV 可能來自任何券商、欄位名各異,甚至是一張對帳單截圖。不要寫死 parser——你(Claude)直接讀它,轉成標準欄位存暫存 CSV:Symbol,Action(BUY|SELL),Quantity,Price,TradeDate(YYYY-MM-DD),RecordType(填 Trade)。這步用的是用戶自己的 Claude 額度,零後端成本,且天生吃得下所有券商——不必為每家券商寫轉換器。

Step 0.5 · 生成 driver map(讓冷門股不失準)

引擎 sector 表只認常見股,冷門股會變「未分類」→ 分散維失真。你(Claude)對用戶實際持倉用世界知識分類:每檔 → [sector, thematic],thematic=1 表示跟別檔同屬一個跨產業主題(AI capex / 減重藥 / 太空…)。寫成 JSON {"PLTR":["軟體雲",1],"CEG":["核電",1],"XOM":["能源",0]},用環境變數餵進去:TR_DRIVER_MAP=/path/driver_map.json python3 engine/trade_recap.py <csv>

Step 1 · 跑引擎,抓大放小

SKILL 走 JSON 模式拿結構化資料,Step 3 你自己寫卡 —— 不要照搬 engine 預設輸出(那是 README quickstart 用的乾淨人話卡 / fallback,不是 SKILL 規格那張定論卡):

mkdir -p ~/.trade-coach
TR_JSON=1 TR_STATE_OUT=~/.trade-coach/last_state.json python3 engine/trade_recap.py <標準化後的CSV>
# TR_JSON=1   → stdout 純 JSON(build_card_data,給你在 Step 3 寫敘事卡用);meta 走 stderr
# TR_STATE_OUT → 寫一份薄 state(對帳用),跟 TR_JSON 平行,可同時設
# 都不設 → 印預設人話卡(README quickstart 用)
# TR_DEBUG=1 → 在預設輸出補回 5 維 severity raw 表(開發/驗證用,絕不上卡)

🔧 引擎報 ModuleNotFoundError(如 pandas / yfinance):依賴多半裝在 venv / pyenv 的另一個 python 裡。找到裝了依賴的直譯器路徑重跑一次即可,常見是 repo 根的 .venv/bin/python3(README 安裝節的 venv 三行裝出來的)——把上面指令的 python3 換成那個路徑;別急著全域 pip(新 macOS 會被 PEP 668 擋)。 引擎吃標準欄位(Symbol / Action(BUY|SELL) / Quantity / Price / TradeDate),TR_JSON=1 吐的結構含:

  • top_holes:已選好的 top 1–2 機械洞 + 對應鏡片 quote(融入敘事,別當結語)。
  • candidate_rules:2–3 條候選規矩(卡上列候選,Step 3.5 讓用戶挑/改一條,別只給第一條;引擎只給一條時就用那條)。
  • thesis_questions:per-ticker 持股假設問句 — 這是給 Step 2 對話用的,絕不准印在卡上(SKILL 鐵律:確認在出卡之前)。
  • alpha_beta_breakdown / payoff_attribution / ticker_diagnosis:完整數字,你拿去組敘事。
  • dims_raw:5 維行為診斷(每維 severity 0–1)— 別整張攤出來,用「一句人話」帶過非 headline 的維度(SKILL 鐵律:不放 5 維小數表)。
  • alpha/beta:贏大盤多少、其中多少只是「膽子大(高 beta)」、真本事(Jensen's α)剩多少。excess_split 把「贏大盤」機械拆成 押對賽道(allocation)+ 板塊內選股(selection),兩項相加恆等於贏大盤 pp——這兩個數是會計恆等式、不需統計顯著,永遠可講;alpha_stat 給 α 的 95% 區間 / t 值 / 分級(顯著與否),語氣照它走。
  • 結構化 state(TR_STATE_OUT):給對帳用的薄 JSON,讀這幾個欄位 ——
    • headline_dim / headline_metric:這次最大的洞 +(key, value)。
    • commitment:{rule, metric_key, metric_value, goal} = 引擎的機械預設承諾(下次只改這一件 + 追蹤哪個 metric)。Step 2 動機問完可能推翻它(實例:engine 給「別加碼」,用戶答「計畫內定投」→ 改盯 ai_pct)→ 收尾要存卡上最終那條,不是這個預設。對帳比 metric_key,別比 headline(規矩維 ≠ headline 維才不對錯帳)。
    • metrics:全 metric 快照(max_pos_pct / avgdown_count / ai_pct / max_sector_pct / top3_pct / payoff / beta / alpha_ann …),對帳時拿承諾的 metric_key 反查新值(集中度承諾就追 ai_pct)。
    • alpha_ann / alpha_t / alpha_credible永遠有數,語氣看統計alpha_credible=true(樣本 ≥1 年且 |t|≥1.96)才可用「真本事」語氣(顯著的負 α 也是可講的定論);false → 數字照講但必帶不確定性:「α 年化 +X%,但 95% 區間 −Y%~+Z%——統計上還分不出是本事還是運氣」。卡在哪要講清楚,引 alpha_beta_breakdown.alpha_stat.gate.reason:sample_short=不到 1 年 → 才是樣本不足;not_significant=區間太寬 → 常見原因是持倉集中、個股雜訊大(這條跟『最大的洞=集中度』是同一件事,要串起來講——但這是工具的侷限,不是他沒本事)。贏大盤幾 pp 必配拆帳:押對賽道 vs 板塊內選股(excess_split),coverage<1 時補一句「X 檔無板塊對照、按大盤計」。
    • insufficient_data:true(round-trip<3 或交易跨度<~84 日曆日≈60 交易日)→ 只做體檢、不硬出 commitment(見開場/收尾)。

抓大放小鐵律:只看引擎排在最前面的 1–2 個洞,其餘忽略。不要把 5 維全攤給用戶——那就變成另一份報表了。引擎已經幫你收斂,你不要再展開。

Step 2 · 出卡前的對話確認(持股假設 + 動機)——這層才是鏡片,不可省

流程鐵律:確認在出卡之前,不在卡上。 機械算得出「你做了什麼」(what),算不出「你為什麼這樣做」(why)。所以先在對話裡問完所有需要你定性的問題、拿到答案,Step 3 才出最終卡——卡是確認後的定論,不是帶問號的待辦。別把問題做成卡上的按鈕(那是把 Step 2/3 混在一起)。

問法鐵律(#55):動機/定性問題一律用 AskUserQuestion 工具問,不要寫成文字段落等用戶打字。 每題二選一(選項裡把兩個動機都寫成人話)+ 用戶可跳過,一次最多 2–3 題,5 秒可點完。自由打字 = 摩擦:用戶會直接略過 Step 2,卡就只能標「待確認」半成品,教練迴圈斷在第一環。只有執行環境沒有 AskUserQuestion 工具(非 Claude Code 的 agent)才退回對話問。

消重鐵律:答過的不重問(每週被問同一題 = 教練失憶,用戶會走)。 engine 只讀 CSV,不讀記憶——thesis_questions 每次都會對同一批標的重新生成,消重是你(Claude)的責任:問任何一題之前,先比對 theses.jsonl(Step 2.5 重建出的 active thesis)與 log.jsonl 最近一筆的動機定性。同一 cycle_id 用戶已答過(thesis maturity=testable、或上次卡已標凹單/逢低定論)→ 這題不再問,直接引用舊答案入卡(「上次你說 MSTR 是凹單」);要更新認知走 Step 2.5 對帳的「順手改」,不是重新問卷。只有三種情況同一標的可以再問:① 新 cycle(清倉後重建倉)② 行為顯著變了(上次答逢低、這次又深虧加碼 N 次 → 對帳語氣問「還是當初那個理由嗎」,引用他上次的答案)③ 用戶上次跳過(inferred 不算答過,但也只在它仍是「金額最大 + 行為矛盾」時才重問一次)。

(a) 持股假設:逢低加碼 vs 凹單(標的層挑出來的) —— 引擎 ticker_diagnosis 對「金額大 + 虧損中狂加碼」的標的生成 thesis_q。機械分不出逢低/凹單,因為差別在加碼當下 thesis 還在不在(= why,算不出),所以挑出來問你:

  • 還在虧的(如 MSTR 加 26 次還虧):「你還相信當初的理由,還是不想認賠在凹單?」
  • 賺回來的(如 GOOG 加 9 次現賺):「計劃內核心倉,還是套牢後才合理化、剛好漲回?」 → 答「凹單/合理化」→ 卡標凹單;答「逢低/計劃內」→ 移除警告、標逢低。機械挑誰問,你的答案定性。

(b) 動機(鏡片) —— 從引擎的洞,對應最該問的交易,用下面的鏡片動機單元問。讀 rubric/vincent-yu.md 拿原話,讓問題真的是「這套哲學會問的」,不是泛泛而問。

鏡片動機單元 → 交易訊號 → 問句模板:

引擎抓到的洞鏡片單元(去 rubric 看原話)問用戶的二選一(舉例,要換成他的真實 ticker/數字)
虧損中加碼攤平A2 試探≠加碼、G 不想認賠「PLTR 你從 24 一路加到 15,是因為你知道了一個進場時不知道的新利多,還是不想認賠、想攤低成本等回本?」
winner 賣太早D1 時間軸、G1 焦慮驅動「你賣掉賺錢的有 71% 後來繼續漲。那些賣出是thesis 到價了,還是賺了怕回吐、落袋為安?」
部位梭哈B1 賠率、A1 信念是光譜上的sizing「PLTR 佔你 48%。這個 size 是算過最壞情況能承受,還是就是很看好、直接重壓?」
集中在同一 driverB2 driver 不同才算分散「你 X 檔 Y% 是 AI。你當初覺得這樣算分散,還是刻意押這個賽道?」→ 答案決定標題,見下規則
把 beta 當 alphaE2 拆解你承擔什麼風險「你贏大盤 +80pp,但 β=1.8。這些報酬你算自己選股的本事,還是敢押高波動 AI換來的?」
連勝後加大 sizingG2 連勝是該檢查的警報「這筆加大,是有獨立的新理由,還是最近都對、覺得手感正順?」

規則:

  • 一次最多問 2–3 個(抓大放小,別審問)。每個都是二選一,5 秒可答。
  • 用戶選哪個都不要說教——這是鏡子,不是審判。他選「不想認賠」就接「好,那這就是下面那條規矩要擋的事」。
  • 答案要改標題,不是只補在『看動機』那行——這是 Step 2 的全部意義。最常踩雷的是集中度:用戶答「刻意押賽道 / 知道集中」→ 那個洞絕不准叫「假分散」(他沒在騙自己,你問了他還罵他=自相矛盾)。改框成「你選的集中押注」,打的點變兩個:① 它讓你的選股本事測不出來(就是 α 判不出的原因,串起來講)② 集中回檔風險——有沒有減碼/停損線。答「以為分散」→ 才用「假分散」。凹單/逢低、梭哈同理:答案怎麼說,標題就怎麼標。
  • 用戶若略過不答,就只用機械洞出卡,不強逼。

(c) 建立 / 更新 thesis(AI 猜為主、問為輔 —— 取代週記錄的核心)

鐵律:降摩擦 + 克制。 這產品的命是「不變成你想逃的重系統」。thesis 絕不逼用戶坐下來填 —— 由你(Claude)從交易行為 + ticker 世界知識,預設落盤標 inferred,用戶不爽再改。讓用戶冷啟動就有完整 thesis 庫、零填寫成本

主路徑:AI 推測,不問用戶。 對每個缺 thesis 的持倉,用 engine 行為訊號(ticker_diagnosis 的 定投/凹單/押太重/紀律持有 + 加碼次數 + cur_ret + 持有天數)+ ticker 是什麼公司 / 賽道,一筆 thesis:

  • why:從行為猜(規律加碼 + 長抱 + 獲利 → 「定投型核心倉」;疑似凹單 → 「攤平等回本(待確認)」)。
  • 三 trigger:從 ticker 類別猜常見的 —— 成長股 → 營收 / 用戶增速失速;週期股 → 週期反轉;AI 概念 → capex 轉弱。reduce 從當前 sizing 猜(已超標 → 該檔減碼線)。
  • 每條標來源 (推測自:規律加碼+長抱),讓用戶一眼看出是猜的、好校正。
  • maturity = inferred,全部直接落盤、零提問

只在一種情況問用戶一句(抓大放小,別審問):

  • 行為矛盾、金額最大的那 1 檔(疑似凹單 / 深虧還加碼)—— 機械分不出「逢低 vs 凹單」,差別只在 why(算不出)。問一句(同樣走 AskUserQuestion,三個選項直接給他點):「{ticker} 加碼 N 次還虧 X%,我猜是不想認賠(凹單)—— 對 / 有新理由(逢低)/ 跳過」。
  • 一次最多問 1 檔;其他全用猜的,不打擾。用戶跳過 → 留 inferred,不追問。

校正走「對帳時順手改」,不是「坐下來填」:對帳(Step 2.5)呈現猜的 thesis + trigger 觸發,用戶看到猜歪的順手改一條 → 該 thesis 升 testable(用戶確認過)。明說「投機跟風沒 thesis」→ 標 draft。thesis 越用越準,但從不逼填。

鐵律不變:exit_trigger(看錯了,事實)≠ stop(跌多少賣,價格)。 猜的時候 exit 也猜「thesis 失效的事實」,不是猜停損價。寧可 inferred 也不要假的 testable

Step 2.5 · 對帳上次的 thesis 與承諾(只在對帳模式 / log 非空)

先重建「目前有效的 thesis」(append-only 讀取必做,否則 active 名單會爆掉):theses.jsonl 是 append-only,同一 thesis 有多筆 revision。讀取時按 thesis_id 建 event log,每個 cycle 只取 latest 未被 supersede 的:

  • 後出現的 revises: <舊 id> → 把舊 id 標 superseded、排除。
  • cycle 已清倉(該 cycle_id 不在 engine holdings.positions)→ 該 thesis 標 closed、不進對帳(歷史保留)。
  • 結果 = 每個 active cycle 恰一筆有效 thesis。

出新卡先回看上次:

  1. 承諾 metric:上次 commitment.metric_key 舊值 → 這次 engine state 新值(「上次說壓到 20%,當時 51% → 現在 48%:在降、沒達標」)。
  2. trigger 檢查 —— 只查三類,別逐檔掃(逐檔掃 = 把復盤變研究任務 = 回到高級拖延):
    • 只查:本週有交易的 ticker + 上次承諾關聯的 ticker + 最大風險 1 檔。其餘 active thesis 標「本週未檢查」。外部新聞 / 基本面查是 opt-in(用戶說要才查,不每週必跑)。
    • 對這幾檔看 trigger 觸發,措辭依 maturity 分(最關鍵 —— 別把 AI 猜的當你的承諾):
      • testable(你確認過的) → 才用定論:exit_trigger 觸發 = 🔴「你定的『{exit}』發生了 —— thesis broken,該走」。
      • inferred(AI 猜的)只能用問句,絕不說「該走」:🟡「我的失效條件『{exit}』似乎發生了 —— 這符合你當初買的邏輯嗎?符合 → 考慮出場;不符 → 順手改成你真正的 exit」。inferred 一律帶 [⚠️ AI 猜測待校正] 標。
    • review_trigger 觸發 → 提示重看,不催賣。
  3. 對帳完才講本週新洞(headline)。只收斂一個洞 + 一條規矩,別把每筆 thesis 攤成報表。

Step 3 · 出一張卡(收斂鐵律)——拿到 Step 2 答案後才出

🚦 出卡前 self-check(沒過一律不准出卡):

  1. engine 用 TR_JSON=1 跑過了嗎? 拿到的是 build_card_data() 結構化 JSON,不是預設那張人話卡。
  2. Step 2 對話完成了嗎?thesis_questions 至少對「金額最大 + 行為矛盾」的 1 檔問過 + 拿到答案;主要動機鏡片(對應 headline_dim 的)問過 1 句。沒問完就出卡 = 退化成「engine + 套版」,失去 SKILL 的價值。
  3. 你打算自己用敘事寫卡,不是照搬 JSON 欄位? 把 JSON 當資料源,自己組句子,不要列 〔X〕內容 的 dashboard 拼接。

三項都過了,才讀 card-spec.md,照裡面的規格出卡——卡的結構、禁止清單、private/public 兩種卡與 redact 規則、敘事鐵律、處方層全在那份檔裡,這裡不重複。 Step 2 還沒問完,不要提前打開它:在那之前,你唯一的目標是把動機問完、拿到答案。

Step 3.5 · 規矩收斂:讓用戶挑一條,存進記憶(不可省——這是下次對帳的入口)

卡上列的 2–3 條候選規矩不是結局。出完卡立刻用 AskUserQuestion(選項 = 各候選 + 「這週不承諾」,Other 可改寫)問一句:「選一條當下週對帳的承諾?會存進本機 log,下次開場第一句就對它:說到有沒有做到。」(#56:你不准代選,他點了哪條才存哪條。)

  • 選項標籤 = 規矩短語,description 寫「下週看哪個數 + 現在的值」——一律人話,內部 metric key 不准出現在任何用戶看得到的文字裡(真人反饋:「追蹤 max_pos_pct,本週基線 42%」= 拗口)。✅「下週就看:最大單注佔比,現在 42%」/ ❌「追蹤 max_pos_pct,基線 42%」。用戶要能 5 秒選完。
  • metric_key 對映(log 存內部名,顯示用人話):單一標的佔比 → max_pos_pct(人話「最大單注佔比」);虧損加碼 → avgdown_count(「虧損加碼次數」);賽道集中 → ai_pct(「同賽道佔比」);板塊 → max_sector_pct(「最大板塊佔比」);盈虧比 → payoff。對帳比 metric,不比 headline(規矩維 ≠ headline 維才不對錯帳)。
  • 用戶挑完 → 立刻走下面收尾腳本落盤,FINAL_RULE / METRIC_KEY 填他選(或改寫)的那條。
  • insufficient_data=true 時的分工:機械預設 commitment 照舊不出(引擎已設 null,別把缺資料的猜測當承諾);但用戶自己選的規矩照存——行為承諾是他的意志,跟樣本夠不夠無關;樣本不足影響的只是 metric 基線的解讀。落盤時標 source: "user_chosen" + baseline_note: "short-sample baseline",下次對帳措辭看方向(在降/沒動/變糟),不判達標。
  • 用戶選「這週不承諾」/ 跳過 → 收尾 FINAL_RULESKIP:log 照存本週 metrics(供趨勢對帳),commitment=null,下週不拿規矩對他。

Step 4 · 收一句反饋(驗證用)

出完卡,問一句:「這張卡,有戳中你嗎?還是哪裡不對?」 把這句反饋(純文字、不含交易明細)記下來給作者——這是這個 skill 唯一要回收的東西,用來驗證「這面鏡片產出的卡對別人有沒有用」。

鏡片的定位:普世機械 + 一套可換的哲學

  • 判分的 5 維算法是普世行為金融(Odean 的處置效應、beta 歸因)——這層誰來都一樣,跟用哪套哲學無關。
  • 鏡片不可替代的地方在 Step 2 找動機:用什麼框架解讀「你為什麼攤平、為什麼賣太早」,以及 Step 3 那句原話。換一套哲學,問法與原話就不同——這才是鏡片的價值,不是貼個名字。
  • 預設鏡片是「存活紀律派」:來自一位投資人公開文章的原則蒸餾(rubric/vincent-yu.md 逐條標出處),屬引用非轉載、非經本人背書。
  • 鏡片是可換層:換一套哲學 = 換 rubric/*.lens.json,engine 程式碼一律不動;同一架構可掛多套哲學。
  • 對外定位:research / coaching support,不構成投資建議。

狀態迴圈(記憶 + 持續):對帳 + 收尾

「投資不是復盤一次就結束。」第二張卡的價值在進度——上次那條規矩守了沒,不是再照出同一個「分散」(機械洞會收斂、會重複)。這靠開場讀、收尾寫的本機狀態 ~/.trade-coach/log.jsonl 撐起來。

對帳(log 非空時,卡開頭先做):

  1. 讀 log 最後一行commitment = {rule, metric_key, metric_value}
  2. 這次引擎 state 的 metrics[commitment.metric_key] = 新值。
  3. 第一句就對帳:上次說要{rule 白話},當時{metric 人話}={舊值} → 現在 {新值}:{在降/沒動/變糟}{達標沒}(例:「上次說逢低加碼要有頂,當時最大單注 42% → 現在 31%:在降」)。用戶的數字、白話、metric key 內部名不上卡(人話對映見 Step 3.5)。commitment 帶 source:"user_chosen" → 措辭用「你上次自己選的規矩」(這是他的承諾,不是系統派的);帶 baseline_note:"short-sample baseline" → 只講方向(在降/沒動/變糟),不判達標。
  4. 講新一輪的洞(headline_dim)——若跟上次同維,直說「這條還沒過關,先別開新戰場」;若是新維,才開新洞。永遠只收斂一個洞 + 一條規矩。

規矩承諾:用戶主動選,你不准代選(#56)。 挑規矩的互動走 Step 3.5(AskUserQuestion:候選各一 + 「這週不承諾」,Other 可改寫)。用戶沒點選之前,任何規矩都不准寫進 log —— 承諾是下週對帳的錨點,錨不是他自己下的,對帳時他只會一頭霧水、迴圈失效。選「這週不承諾」→ 下面 FINAL_RULESKIP(照存本週 metrics 供趨勢對帳,但 commitment 為空、下週不拿規矩對他)。

收尾(出完卡 + Step 3.5 用戶挑完規矩 + Step 4 收完反饋,append 一行):

# 把這次的薄狀態接到 log(只存聚合 metric + 規矩,不存任何交易)。
# ⚠️ commitment 要存【用戶在 Step 3.5 親選的那條】(#56),不是引擎機械預設、更不是你代選的 ——
#    Step 2 動機問完常推翻引擎預設。實例:engine 預設「虧損別加碼」,但用戶答「NVDA 是計畫內定投」
#    → 用戶改挑「盯集中度 ai_pct」那條。
#    兩格填用戶挑的規矩 + 對應 state.metrics 鍵;用戶選「這週不承諾」→ FINAL_RULE 填 SKIP。
#    gate 規則:SKIP 一律不存 commitment;insufficient_data 只擋「engine 預設 fallback」,不擋用戶明選
#    (行為承諾跟樣本無關,只是基線標 short-sample、下次對帳看方向不判達標)。
FINAL_RULE="AI 暴險封頂 70%:要加 AI 新倉先問新賽道還是同一注往上疊"
METRIC_KEY="ai_pct"
python3 - "$FINAL_RULE" "$METRIC_KEY" <<'PY'
import json, os, sys, pathlib
st = json.load(open(os.path.expanduser("~/.trade-coach/last_state.json")))
dflt = st.get("commitment") or {}                         # engine 機械預設(fallback)
arg1 = sys.argv[1] if len(sys.argv) > 1 else ""
skip = (arg1 == "SKIP")                                   # 用戶明確「這週不承諾」(#56):不硬塞錨點
user_chose = (not skip) and bool(arg1.strip())            # 填了非 SKIP = Step 3.5 用戶親選
rule = ("" if skip else arg1) or dflt.get("rule")
mk   = (sys.argv[2] if len(sys.argv) > 2 else "") or dflt.get("metric_key")
commitment = None
if not skip and rule and mk and (user_chose or not st["insufficient_data"]):  # SKIP 一律不存;樣本不足只擋機械預設
    commitment = {"rule": rule, "metric_key": mk,
                  "metric_value": st["metrics"].get(mk), "goal": "down",
                  "source": "user_chosen" if user_chose else "engine_default"}
    if user_chose and st["insufficient_data"]:
        commitment["baseline_note"] = "short-sample baseline"   # 下次對帳看方向,不判達標
entry = {"date_end": st["date_end"], "headline_dim": st["headline_dim"],
         "commitment": commitment,                        # 對帳對的是這條(用戶選定版,非機械版)
         "metrics_snapshot": {k: st["metrics"].get(k)
                              for k in ("ai_pct","max_pos_pct","avgdown_count","avgdown_breach")}}
p = pathlib.Path(os.path.expanduser("~/.trade-coach/log.jsonl"))
with p.open("a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n")
print("appended commitment:", json.dumps(commitment, ensure_ascii=False))
PY

收尾 part 2 · 把本週建立 / 更新的 thesis append 到 theses.jsonl(append-only):

python3 - <<'PY'
import json, os, pathlib
# Claude 把本週「新建倉 / 缺 thesis / trigger 觸發後更新」的 thesis 填 theses[]。
# thesis 是對話 articulate 出來的(engine 不碰);append-only:更新用 revises 指回舊 thesis_id,不蓋舊的。
session_date = "YYYY-MM-DD"          # 本次 review 日(= engine state 的 date_end)
theses = [
  # ⚠️ cycle_id 必須【照抄】engine state holdings.positions[ticker].cycle_id(3 段格式 ticker#開倉日#序號,
  #    如 "NVDA#2024-01-12#1"),別自己拼 2 段 —— 格式不符 → 對帳(開場 §偵測缺 thesis)永遠匹配不上 →
  #    每週把已寫過 thesis 的持倉當「缺 thesis」重問,記憶迴圈失效。
  # {"ticker":"NVDA","cycle_id":"NVDA#2024-01-12#1",
  #  "why":"一句:為什麼持有",
  #  "triggers":{"review":"什麼消息/數字該重看","reduce":"什麼情況減碼","exit":"什麼代表看錯(非股價跌)"},
  #  "maturity":"inferred",          # inferred(AI 猜,預設)| testable(用戶確認過)| draft(投機跟風沒 thesis)
  #  "stop":"", "target_size":"20%",
  #  "revises": None},               # 更新既有 thesis 才填舊 thesis_id
]
import time; _sid = int(time.time())                       # session 戳:防同日多次 review 撞 id
p = pathlib.Path(os.path.expanduser("~/.trade-coach/theses.jsonl"))
with p.open("a", encoding="utf-8") as f:
    for i, t in enumerate(theses):
        t.setdefault("status", "active"); t["session_date"] = session_date
        t["thesis_id"] = f"{t['ticker']}-{session_date}-{_sid}-{i}"
        f.write(json.dumps(t, ensure_ascii=False) + "\n")
print(f"appended {len(theses)} thesis events")
PY

theses.jsonl 是 append-only 動機庫:只追加、不改不刪。清倉不刪 thesis(留著當歷史);下次同 ticker 重建倉 = 新 cycle_id = 新 thesis。Step 2.5 對帳讀每筆 active thesis 的 trigger 檢查觸發。隱私同 log:純本機、不外傳、不回作者。

收尾 part 3 · 個人 profile(只第一次建,當復盤對照基準):~/.trade-coach/profile.md 不存在 → 第一次從交易行為 3 條個人原則寫進去(同 inference-first:不逼填,用戶可改):持有風格(長抱 / 短打)、集中度傾向、紀律缺口(出場 / 加碼)。例:1. 長期持有型(中位 X 天) 2. 易重押單一賽道(AI X%) 3. 弱點在出場擇時(賣完常續漲)。之後每週對帳順帶一句「這批交易符合你定的原則嗎」,用戶要改直接改檔。

第一次樣本不足(insufficient_data=true):round-trip<3 或交易跨度<~84 日曆日(≈60 交易日),引擎已把 commitment 設成 null機械層只做體檢、不硬出規矩(否則下次把缺資料的猜測當成已確認的承諾來對帳)。但 Step 3.5 照走:用戶自己挑的規矩照存(source:"user_chosen" + baseline_note,見收尾腳本 gate)——體檢卡也要留下記憶入口,否則第二週還是初診。卡收尾講一句「資料還太短,基線先存個底,累積多幾筆 round-trip 後對帳才看達標」;用戶跳過不選 → log append(commitment=null),下次來就接得上。

驗收這套有沒有真的「記憶」:engine/test_state_loop.py 把一份 CSV 按時間切兩段,累積跑「初診→對帳」,驗第二張卡有沒有真的對帳第一張承諾的那一維(而非重新初診)。改完 engine 或這段流程都先跑它。

Related Skills

steipete/notion

Notion CLI/API for pages, Markdown content, data sources, files, comments, search, Workers, and raw API calls.

community

affaan-m/seo

Audit, plan, and implement SEO improvements across technical SEO, on-page optimization, structured data, Core Web Vitals, and content strategy. Use when the user wants better search visibility, SEO remediation, schema markup, sitemap/robots work, or keyword mapping.

community

affaan-m/brand-voice

Build a source-derived writing style profile from real posts, essays, launch notes, docs, or site copy, then reuse that profile across content, outreach, and social workflows. Use when the user wants voice consistency without generic AI writing tropes.

community

affaan-m/crosspost

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

community

affaan-m/x-api

X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.

community

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

community