Spaces:
Runtime error
Runtime error
Upload 22 files
Browse files- BLOG_DRAFT.md +79 -48
- Dockerfile +21 -0
BLOG_DRAFT.md
CHANGED
|
@@ -1,69 +1,100 @@
|
|
| 1 |
-
# Field Notes: Putting a 20-Year-Old Chinese Trading Theory on a
|
| 2 |
|
| 3 |
-
*Built for the Build Small hackathon — a Gradio Space, a llama.cpp
|
| 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 charts and emits the
|
| 11 |
-
three buy / three sell points. Two problems: the engine's reasoning logs
|
| 12 |
-
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 |
-
in which
|
| 17 |
-
|
| 18 |
|
| 19 |
-
## Why
|
| 20 |
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
## What's in the box
|
| 31 |
|
| 32 |
-
* **Tomorrow's plan** — one row per ticker: BUY/SELL/HOLD/WAIT,
|
| 33 |
-
invalidation price. Long-hold mode: ride the weekly pivot
|
| 34 |
-
third-sell / structural stop / an armed nested-interval line.
|
| 35 |
-
* **Sector rotation** — flow proxy (Δ% × dollar volume) across the 11 SPDR
|
| 36 |
-
|
| 37 |
-
* **
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
| 57 |
it just can't be a synchronous dependency of everything.
|
| 58 |
-
3. **
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
## What's next
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
|
| 68 |
*Not investment advice. Engine logic unchanged from the original; the app around
|
| 69 |
-
it is what this hackathon built.*
|
|
|
|
| 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 Gradio Space, 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.
|
| 18 |
|
| 19 |
+
## Why small models are the honest fit
|
| 20 |
|
| 21 |
+
The LLMs are **not** doing the math. Fractal merging, divergence grading and
|
| 22 |
+
nested-interval confirmation are deterministic code — a 32B (or 320B) model would
|
| 23 |
+
add nothing but hallucination risk and latency. What the rule engine can't do is
|
| 24 |
+
*language*: turn a Chinese decision chain into a plain-English summary, brief
|
| 25 |
+
today's headlines on my holdings, narrate sector rotation, write a structured
|
| 26 |
+
research note. That's bounded, evidence-grounded text work — exactly what 1.7B–4B
|
| 27 |
+
models handle on 8 CPU cores. The constraint drew the architecture: **rules
|
| 28 |
+
compute, small models speak.**
|
| 29 |
+
|
| 30 |
+
## A pool of sub-agents, not one model
|
| 31 |
+
|
| 32 |
+
Early versions shared a single model behind one lock, so the moment two features
|
| 33 |
+
ran at once you'd get "model busy." The fix was a **sub-agent pool** — four small
|
| 34 |
+
models, each with its own lock, running through llama.cpp:
|
| 35 |
+
|
| 36 |
+
* **Summary** (Signals → AI Summary) — my **fine-tuned Chan-Tuned Qwen3-1.7B**
|
| 37 |
+
* **Narrator** (sector rotation) — Qwen3-1.7B
|
| 38 |
+
* **Reporter** (watchlist news + research support) — Qwen3-1.7B
|
| 39 |
+
* **Analyst** (Auto Research) — Qwen3-4B
|
| 40 |
+
|
| 41 |
+
They genuinely run in parallel: you can stream a research report while the Signals
|
| 42 |
+
summary writes, with no collision.
|
| 43 |
+
|
| 44 |
+
## The fine-tune (the part I'm proudest of)
|
| 45 |
+
|
| 46 |
+
The Summary sub-agent runs a model I trained myself. The app captures every
|
| 47 |
+
(raw read → English summary) pair it produces into a dataset on the `/data`
|
| 48 |
+
bucket; a couple hundred focused pairs later, I exported the JSONL straight from
|
| 49 |
+
the app's Model tab, LoRA-tuned **Qwen3-1.7B** on a free Colab T4 with Unsloth,
|
| 50 |
+
converted to GGUF, and published it to the Hub
|
| 51 |
+
(`ranranrunforit/chan-compass-qwen3-1.7b-gguf`). One line in `MODEL_ZOO` wires it
|
| 52 |
+
back in. The loop closes: the app generated its own training data, and now runs on
|
| 53 |
+
the model that data produced — a 1.7B doing one job well instead of a giant model
|
| 54 |
+
doing everything adequately.
|
| 55 |
|
| 56 |
## What's in the box
|
| 57 |
|
| 58 |
+
* **Tomorrow's plan** — one row per ticker: BUY/SELL/HOLD/WAIT, explicit **buy
|
| 59 |
+
point**, entry zone, invalidation price. Long-hold mode: ride the weekly pivot
|
| 60 |
+
uplift, exit only on third-sell / structural stop / an armed nested-interval line.
|
| 61 |
+
* **Sector rotation** — flow proxy (Δ% × dollar volume) across the 11 SPDR sector
|
| 62 |
+
ETFs vs SPY, 1/5/20-day windows, instant tables + on-demand AI narrative.
|
| 63 |
+
* **Watchlist news** — today-only headlines per holding, streamed as they arrive,
|
| 64 |
+
each with an AI brief.
|
| 65 |
+
* **Multi-agent Auto Research** — PLAN → six evidence tools in parallel
|
| 66 |
+
(fundamentals, financials, price, *the Chan engine itself*, money-flow proxy,
|
| 67 |
+
news) → Analyst and Reporter writing different sections **simultaneously**:
|
| 68 |
+
valuation · tech moat · supply-chain map with tickers · bull/bear · money flow ·
|
| 69 |
+
Chan timing · risks. Every run writes a full JSON trace — the plan, each tool
|
| 70 |
+
call and its result, each sub-agent's request and response — **published as a Hub
|
| 71 |
+
dataset** so anyone can see how the agent actually reasoned.
|
| 72 |
+
* **Email any result** — each tab can email its output; on HF (where SMTP is
|
| 73 |
+
blocked) delivery goes over the Resend HTTPS API, markdown rendered to clean HTML.
|
| 74 |
+
* **Spectrum 2 UI** — Adobe's design language in Gradio.
|
| 75 |
+
|
| 76 |
+
## Four things I learned the hard way
|
| 77 |
+
|
| 78 |
+
1. **HF build containers are tiny.** `llama-cpp-python` got OOM-killed compiling at
|
| 79 |
+
*build* time on a Space with 32 GB of *runtime* RAM. Fix: install the runtime at
|
| 80 |
+
first launch (prebuilt CPU wheel, capped-compile fallback), persisted to `/data`
|
| 81 |
+
so it happens once. Pin `python_version: 3.11` — prebuilt wheels stop at 3.12.
|
| 82 |
+
2. **Don't put the model on the hot path.** My first "Run analysis" generated an AI
|
| 83 |
+
narrative inline — 10 tickers took 186 s. Moving all LLM calls off the rule path
|
| 84 |
+
and parallelizing downloads brought it to a few seconds. CPU inference is fine;
|
| 85 |
it just can't be a synchronous dependency of everything.
|
| 86 |
+
3. **One lock per model.** A shared lock turns concurrent features into "model
|
| 87 |
+
busy." A sub-agent pool — one instance and lock per job — fixed it and made the
|
| 88 |
+
fine-tune swappable for just the one agent that needed it.
|
| 89 |
+
4. **Platforms block SMTP; ship HTTPS email.** Outbound mail ports are closed on
|
| 90 |
+
Spaces, so email failed with "network unreachable." The Resend REST API over
|
| 91 |
+
443 works — and it needs a `User-Agent` header or Cloudflare 403s you.
|
| 92 |
|
| 93 |
## What's next
|
| 94 |
|
| 95 |
+
The agent traces are already public as a Hub dataset; next I want to grow the
|
| 96 |
+
fine-tune set so the Summary model keeps sharpening on exactly the phrasing this
|
| 97 |
+
task needs, and feed real family usage back into that loop.
|
| 98 |
|
| 99 |
*Not investment advice. Engine logic unchanged from the original; the app around
|
| 100 |
+
it — and the model that explains it — is what this hackathon built.*
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
RUN useradd -m -u 1000 user
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
|
| 6 |
+
# system deps for building wheels if needed
|
| 7 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
+
build-essential git curl && rm -rf /var/lib/apt/lists/*
|
| 9 |
+
|
| 10 |
+
COPY --chown=user requirements.txt requirements.txt
|
| 11 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 12 |
+
|
| 13 |
+
COPY --chown=user . /app
|
| 14 |
+
USER user
|
| 15 |
+
|
| 16 |
+
ENV HOME=/home/user \
|
| 17 |
+
PATH=/home/user/.local/bin:$PATH \
|
| 18 |
+
AUTO_LOAD_MODEL=1
|
| 19 |
+
|
| 20 |
+
EXPOSE 7860
|
| 21 |
+
CMD ["python", "app.py"]
|