ranranrunforit commited on
Commit
a94fbea
·
verified ·
1 Parent(s): 66e1c15

Delete BLOG_DRAFT.md

Browse files
Files changed (1) hide show
  1. BLOG_DRAFT.md +0 -107
BLOG_DRAFT.md DELETED
@@ -1,107 +0,0 @@
1
- # Field Notes: Putting a 20-Year-Old Chinese Trading Theory on a Fine-Tuned 1.7B
2
-
3
- *Built for the Build Small hackathon — a custom-frontend Gradio app (`gradio.Server`), a local llama.cpp sub-agent pool, a model I fine-tuned myself, and zero cloud APIs.*
4
-
5
- ## The problem (a real one)
6
-
7
- My family trades US stocks using 缠论 (Chan theory) — a Chinese technical-analysis
8
- framework built on fractals, strokes, segments and "pivots" (中枢), famous for its
9
- rigor and infamous for its complexity. We had a battle-tested Python engine that
10
- decomposes monthly → weekly → daily → 60m → 30m → 15m → 5m charts and emits the
11
- classic three buy / three sell points. Two problems: the engine's reasoning logs
12
- are dense Chinese jargon, and someone had to run it by hand every evening.
13
-
14
- **Chan Compass** fixes both: a Gradio Space that auto-updates after the US close
15
- (18:10 ET), answers exactly one question per ticker — *do I buy or sell tomorrow,
16
- at what buy point, in which zone, and where am I wrong?* — and uses a pool of small
17
- local models as the translators and analysts the rule engine can't be. The results
18
- are persisted to disk, so when the family opens the app the next morning, the
19
- overnight run is already there waiting — no spinner, no recompute.
20
-
21
- ## Why small models are the honest fit
22
-
23
- The LLMs are **not** doing the math. Fractal merging, divergence grading and
24
- nested-interval confirmation are deterministic code — a 32B (or 320B) model would
25
- add nothing but hallucination risk and latency. What the rule engine can't do is
26
- *language*: turn a Chinese decision chain into a plain-English summary, brief
27
- today's headlines on my holdings, narrate sector rotation, write a structured
28
- research note. That's bounded, evidence-grounded text work — exactly what 1.7B–4B
29
- models handle on 8 CPU cores. The constraint drew the architecture: **rules
30
- compute, small models speak.**
31
-
32
- ## A pool of sub-agents, not one model
33
-
34
- Early versions shared a single model behind one lock, so the moment two features
35
- ran at once you'd get "model busy." The fix was a **sub-agent pool** — four small
36
- models, each with its own lock, running through llama.cpp:
37
-
38
- * **Interpreter** (Signals → AI Interpret) — my **fine-tuned Chan-Tuned Qwen3-1.7B**
39
- * **Narrator** (sector rotation) — Qwen3-1.7B
40
- * **Reporter** (watchlist news + research support) — Qwen3-1.7B
41
- * **Analyst** (Auto Research) — Qwen3-4B
42
-
43
- They genuinely run in parallel: you can stream a research report while the Signals
44
- summary writes, with no collision.
45
-
46
- ## The fine-tune (the part I'm proudest of)
47
-
48
- The Interpreter sub-agent runs a model I trained myself. The app captures every
49
- (raw read → English summary) pair it produces into a dataset on the `/data`
50
- bucket; a couple hundred focused pairs later, I exported the JSONL straight from
51
- the app's Model tab, LoRA-tuned **Qwen3-1.7B** on a free Colab T4 with Unsloth,
52
- converted to GGUF, and published it to the Hub
53
- (`ranranrunforit/chan-compass-qwen3-1.7b-gguf`). One line in `MODEL_ZOO` wires it
54
- back in. The loop closes: the app generated its own training data, and now runs on
55
- the model that data produced — a 1.7B doing one job well instead of a giant model
56
- doing everything adequately.
57
-
58
- ## What's in the box
59
-
60
- * **Tomorrow's plan** — one row per ticker: BUY/SELL/HOLD/WAIT, explicit **buy
61
- point**, entry zone, invalidation price. Long-hold mode: ride the weekly pivot
62
- uplift, exit only on third-sell / structural stop / an armed nested-interval line.
63
- * **Sector rotation** — flow proxy (Δ% × dollar volume) across the 11 SPDR sector
64
- ETFs vs SPY, 1/5/20-day windows, instant tables + on-demand AI narrative.
65
- * **Watchlist news** — today-only headlines per holding, streamed as they arrive,
66
- each with an AI brief.
67
- * **Multi-agent Auto Research** — PLAN → six evidence tools in parallel
68
- (fundamentals, financials, price, *the Chan engine itself*, money-flow proxy,
69
- news) → Analyst and Reporter writing different sections **simultaneously**:
70
- valuation · tech moat · supply-chain map with tickers · bull/bear · money flow ·
71
- Chan timing · risks. Every run writes a full JSON trace — the plan, each tool
72
- call and its result, each sub-agent's request and response — **published as a Hub
73
- dataset** so anyone can see how the agent actually reasoned.
74
- * **Email any result** — each tab can email its output; on HF (where SMTP is
75
- blocked) delivery goes over the Resend HTTPS API, markdown rendered to clean HTML.
76
- * **A real custom frontend** — instead of the default Gradio component render, `server.py` builds a `gradio.Server` that serves a hand-built React + Adobe Spectrum 2 UI at `/` and exposes the Python backend as `/api/*` JSON + streaming endpoints. It's still a Gradio app — just wearing its own clothes.
77
-
78
- ## Five things I learned the hard way
79
-
80
- 1. **HF build containers are tiny.** `llama-cpp-python` got OOM-killed compiling at
81
- *build* time on a Space with 32 GB of *runtime* RAM. Fix: install the runtime at
82
- first launch (prebuilt CPU wheel, capped-compile fallback), persisted to `/data`
83
- so it happens once. Pin `python_version: 3.11` — prebuilt wheels stop at 3.12.
84
- 2. **Don't put the model on the hot path.** My first "Run analysis" generated an AI
85
- narrative inline — 10 tickers took 186 s. Moving all LLM calls off the rule path
86
- and parallelizing downloads brought it to a few seconds. CPU inference is fine;
87
- it just can't be a synchronous dependency of everything.
88
- 3. **One lock per model.** A shared lock turns concurrent features into "model
89
- busy." A sub-agent pool — one instance and lock per job — fixed it and made the
90
- fine-tune swappable for just the one agent that needed it.
91
- 4. **Platforms block SMTP; ship HTTPS email.** Outbound mail ports are closed on
92
- Spaces, so email failed with "network unreachable." The Resend REST API over
93
- 443 works — and it needs a `User-Agent` header or Cloudflare 403s you.
94
- 5. **A custom frontend on a Gradio Space wants `gradio.Server`, not `.launch()`.**
95
- Mounting a React UI and calling `app.launch()` made Gradio's SSR startup check
96
- hit `gradio_api/startup-events` and 404. The fix: treat `gradio.Server` as the
97
- ASGI app it is and serve it with uvicorn (Docker SDK) — custom look, still a
98
- Gradio app.
99
-
100
- ## What's next
101
-
102
- The agent traces are already public as a Hub dataset; next I want to grow the
103
- fine-tune set so the Interpreter model keeps sharpening on exactly the phrasing this
104
- task needs, and feed real family usage back into that loop.
105
-
106
- *Not investment advice. Engine logic unchanged from the original; the app around
107
- it — and the model that explains it — is what this hackathon built.*