服务调研关于联系
← Back to Research
2026-10-22AI 实战·

Rules Before CodeA Field Record of Building an After-Hours A-Share Monitoring System, and the Method Behind It

How do you build an A-share stock screener that keeps itself up to date online? Most tutorials stop at "install a market-data library, write a loop, print the tickers that pass." But once the thing has to update itself after every close, stay online for other people to use, and run for years, the problems change. What if the data source hasn't finished loading today's data? Who decides the definitions the requester never spelled out? After a one-line change, which version is actually running in production? If the network drops at midnight, does a half-finished history backfill count?

This essay starts from those questions. Half of the answers are in the code. The other half aren't.

Quick overview · TL;DR · definition · FAQ

TL;DR

  • A friend who is a veteran A-share investor handed over a threshold table: "three market-cap tiers × two signal types × six conditions." Three weeks later it had become an after-hours monitoring system covering every A-share, ten years of daily candles going back to 2015, and eight modules, updated automatically by a single command each trading day.
  • The first six days were unchecked growth. There was no requirements doc, production doubled as the acceptance environment, and one day saw 9 deploys against only 2 commits. The retrospective produced two things: a working protocol of six stages + three tiers, and two gates written into the program (no deploy with uncommitted changes; block publication if the data drops sharply).
  • In the five rounds after the rules were set, roughly 85 hours went into 217 commits and 14 releases. Automated tests grew from 341 to 1,090, and every production version traced back to a specific commit.
  • The transferable method is four pillars on one foundation: process (write before you build), gates (rules enforced in code), evidence (cross-checking beats confidence), and division of labor (humans set direction and press the button, agents do the work). The foundation is architectural restraint: a single write path, read-only modules, zero dependencies, static pages.

Definition

Rules-first AI-assisted development applies to projects where large-model agents write most of the code. Before the work starts, you write down how requirements are recorded, where acceptance happens, when something may ship, and how things stop when they go wrong. Wherever you can, you turn the hard constraints into gates in the program instead of trusting humans or models to comply every time. This is a different problem from "making AI code faster." Rules-first is about whether every release can be explained: which version it is, why it shipped, and on what evidence. The other problem is only about speed.

FAQ

Now that AI writes the code, why bother with the "old-school" stuff like requirements docs, acceptance, and tags?

Because AI has driven the cost of writing code close to zero and done nothing to the cost of rework. The faster code gets written, the faster the old failures show up: starting before the thinking is done, trial-and-error in production, versions that don't match commits. Rules govern direction and boundaries, and those are exactly what an agent can't govern for itself.

What's the difference between "a gate written into the program" and "a rule in the docs that people are asked to follow"?

Rules in docs depend on memory. Gates in code depend on mechanism. In this project the deploy command starts by checking git. If the program, the config, or the user manual has uncommitted changes, it refuses to publish, and there is deliberately no override switch. People forget, and models drift over long conversations. The program doesn't.

Can this system be used to make money in the stock market?

That's the wrong way to read it. The system only screens and records. It runs no return backtests and gives no buy or sell advice. This essay is about how the system was built, not about whether its signals are any good.

Does the method apply outside A-shares?

Yes. It fits any system where the requester can't fully specify the definitions, the data changes every day, and the results have to be archived for the long term. Operational reports, monitoring dashboards, and internal data tools all have the same problem.

References and Further Reading

  • Betsy Beyer et al., Site Reliability Engineering, O'Reilly, 2016, Chapter 8, Release Engineering (traceable, reproducible releases).
  • Michael Feathers, Working Effectively with Legacy Code, 2004 (characterization tests: pin existing behavior down as a baseline before changing anything).
  • Kent Beck, Test-Driven Development: By Example, 2002 (test first).
  • TradingView, Lightweight Charts (open-source financial charting library, Apache-2.0), used for this project's per-stock candlestick charts.

01 · Straight to the Point: Systems Aren't Written, They're Governed

The conclusion first: in a project where AI writes the code, the real bottleneck is never the code. It is the gate that controls how code gets into the real world.

The story is simple. A friend with many years in A-shares had a set of hard-won chart-reading habits, condensed into a single table. It had small, mid, and large market-cap tiers, two kinds of breakout signal (moderate and strong), and six conditions (turnover rate, volume ratio, amplitude, closing pattern, breakout position, and so on), each with tiered thresholds. What he wanted was just as simple: after every close, pick out the stocks that qualify so I don't have to flip through them one by one.

Three weeks later, that table had become this:

DimensionStarting point (10-07)Now (after round 5, 10-21)
CoverageA fixed pool of about 100 stocksAll A-shares, plus 269 indices and sectors and broad-based ETFs
HistoryTwo yearsFrom 2015-01-05: 2,864 trading days, 11.84 million rows of daily candles
ModulesOne: breakout signalsBreakout signals, chip-distribution lines, indices & sectors, sentiment, breadth, leaderboards, fund flows, per-stock candlestick charts
Code5,457 lines12,523 lines, Python standard library only, zero third-party dependencies
Automated tests3411,090, all passing
Daily operations—One command, about 5 minutes, published automatically as a static website

AI did most of the coding. One main session handled planning, dispatch, acceptance, and archiving. Several sub-agents wrote the modules and tests in parallel, and a model from a different provider was brought in as a read-only reviewer. The human did four things: relay the requester's exact words, settle definitions, sign off at acceptance, and press the deploy key personally.

This essay isn't about A-shares. It's about the falls we took over those three weeks and the rules they produced. "Without the compass and the square, one cannot draw circles or squares," as Mencius put it more than two thousand years ago. This time, though, the rules weren't written for people alone. They were written for people and AI to follow together, and the hardest of them were simply built into the program.

02 · Six Days of Unchecked Growth: Diligence Can't Rescue a Process

From October 7 to 12 the project moved fast. Breakout signals, historical lookback, valuation and announcement records, full-market chip-line scans, the website, the daily end-to-end pipeline, and the index and sector status tables all shipped within a week. There were 24 commits, each with tests.

It looked diligent. Frankly, the diligence was the problem.

On the evening of the 12th we held a retrospective, checking those 24 commits one by one against the six stages we would go on to adopt. The verdict hurt:

FailureEvidenceConsequence
No requirements doc written before development11 deliverables, 0 docsWorking out why something was decided meant piecing it together from conversations, comments, and docs
Production used as the acceptance environment3 fixes within an hour of the 10-11 release; the sector page's widths were measured in production, which cost 4 extra deploysThe requester could see half-finished work at any moment
Production version didn't match any commitOn 10-12, 9 deploys and only 2 commits, with no tags at allNobody could say which version was live, and there was no way to roll back if something broke
Several things packed into one commitChip lines, the website, and the daily pipeline in a single commit: 20 files, 2,459 linesNo way to revert just one of them when something went wrong
Docs written after the code4 separate "align the docs" commitsBugs in the code were only discovered while back-filling docs

Figure 1 · Same team, same project, before and after: what was missing wasn't effort, it was gates

It was like a team with decent skills and plenty of running but no formation and no substitution discipline. Everyone was giving their all, yet when the ball was lost, nobody could say whose position had been left open. Diligence is a virtue, but it can't plug the holes in a process. It only makes them leak faster. With AI writing code this is especially true. The model doesn't tire or get bored and turns out work very quickly, and a person who gets excited can't resist "just pushing it live to see how it looks." The proverb says a dike a thousand li long collapses through an ant's nest. Every "let's just see it live" was another ant's nest.

03 · Setting the Rules: Six Stages, Each with Its Own Boundary

That night, the rules went into the project's entry document. At their core is a six-stage table that spells out, for each step, what you do, where the boundary lies, and what you hand over:

StageWhat you doBoundaryDeliverable
1 Requirements docRequester's exact words, our understanding, delivery form, what's out of scope, open questions, acceptance criteriaDocs only, no code or config changesA dated requirements doc, committed as soon as it's written
2 Exploration (optional)Can we get the data, and is the result useful?Read-only access to the main database, scripts and data kept separate, nothing published; "don't build it" is an allowed conclusionResearch log
3 DevelopmentTests first, then implementationWork on a branch, no deploys; main must run cleanly at 17:00 every dayCode and tests
4 AcceptanceBuild the site locally and take screenshots at desktop and mobile widthsDon't touch production; layout gets measured at this stepOwner's sign-off
5 ReleaseMerge to main → deploy → tagWith uncommitted changes, the program refuses to deployA tag
6 ArchiveUpdate the user manual and entry doc; at the end of the requirements doc, record what was actually builtThe manual never runs ahead of the featureDocs commit

Not every small change needs all six steps, though, or the rules turn into shackles. So changes are sorted into three tiers by size. When in doubt, go up a tier:

Figure 2 · Six stages, three tiers: a config tweak takes three steps, a new module takes the full set

Several items in this table look minor but proved to be key:

The manual is not written first. The system's user manual is a page on the website, published every day along with the data. If a new feature goes into the manual first, the requester reads about a feature that doesn't exist yet that same evening. The requirements doc is what comes first. The manual can only change after acceptance and the merge to main, in the same release as the feature.

The delivery form is decided in the requirements doc. The site is purely static, and scripts are allowed in only five places: the reading-progress indicator, table sorting, click-to-zoom images in the manual, jump-to-date on the home page, and the per-stock candlestick charts. Adding a script or animation anywhere else counts as changing the rules. It's handled at the "large" tier, and a local prototype goes to the owner first. Restraint is a design choice: the plainer the page, the less there is to break.

Sign-off is authorization. When the owner signs off at step 4, that authorizes the merge and deploy in step 5, and nobody has to ask again. The rules exist to prevent loss of control, not to make people say yes over and over.

If the requirements change, go back to step 1. Requesters change their minds and dig deeper as they go, which is perfectly normal. But once definitions start shifting mid-development, the requirements doc becomes waste paper. Updating it takes a few minutes, and afterward every later step knows what standard it is working to.

04 · Rules Need Teeth: Writing the Gates into the Program

Rules written in documents depend on self-discipline. People forget, models drift over long conversations, and at two in the morning everyone has a "just this once" moment. So the hardest rules went straight into the program.

Gate one: no deploy with uncommitted changes. The deploy command starts by checking git. If the program, the config, or the user manual has any uncommitted change, whether staged or newly untracked, it stops, the log lists the offending files, and the website doesn't update. Why only those three? Because they determine what production looks like. Writing a line of dev notes or editing a test shouldn't stop that day's site update. The most important part is that there is deliberately no override switch. A gate with a back door will sooner or later be used as the front door.

Gate two: block publication if the data drops sharply. Upstream data is the only thing that changes day to day, so there is a single data self-check before publishing. If today's full-market stock count is more than 5% below the previous trading day's, publication is blocked and a notification goes out for a human to check. The check guards against a source that hasn't finished loading the day's data and returns only some of the stocks. Without it, the program would mark the day "synced" and the list would be permanently short, with no warning at all. The threshold isn't arbitrary. Across 500 trading days, the largest single-day drop was only 0.48%, so normal fluctuation never comes close to 5%. The days in July 2015 when more than a thousand stocks were suspended would be blocked, then released manually after a check. That is expected behavior.

Beyond the two gates, one iron rule runs through the daily pipeline: if any step fails, stop. Better no update today than half a result published.

Figure 3 · One command a day: failure in a core step stops the run; errors in auxiliary pages only raise warnings

There's a sense of proportion here. The auxiliary pages (sentiment, breadth, leaderboards, fund flows) only raise warnings when they fail and don't block the main flow. One broken chart page shouldn't stop the main list from going out. Be hard where it matters and soft where it doesn't. Railway signals work the same way: a red light on the main line means stop, while a fault on a siding gets a warning sign and a detour.

On the night the rules were set, the rules themselves were released under the new rules. Tests came first, then the gate. During acceptance, 3 uncommitted files were deliberately left in the working tree and the deploy was run anyway. It was blocked: the log listed the 3 files, the exit code was 1, and the site didn't update. The first time the gate bit, it bit the person who made the rules. It was a good bite.

05 · One Well, Many Pipes: Architectural Restraint

The biggest surgery came in the October 18 round: pulling all market data out into a shared market-data layer.

Before that, the chip lines, the indices and sectors, and the sentiment module each fetched their own copy of market data from the source and stored it separately. Across the three index tables, the same index's close on the same day might have come from three different requests. It was like a village where every household digs its own well: whether the water matches is pure luck.

After the surgery, the whole system became a star:

Figure 4 · Star architecture: only the shared market-data layer can go online and write market data; every other module is read-only

PrinciplePracticeBenefit
Single write pathOnly two files in the shared layer can go online and write the market-data tablesData has one source, so pages can't contradict each other
Read-only modules that don't know each otherChip lines, breadth, leaderboards, fund flows… each reads only the shared layer and produces its own reportsAdding a module means adding one more read-only pipe that touches neither the well nor anyone else's pipe
Boundaries guarded by testsModule-boundary tests check automatically: if anyone writes to a table they shouldn't, the tests go redArchitectural constraints don't depend on how sharp the code reviewer is
Zero third-party dependenciesPython standard library only; single-file SQLite; static HTML + inline SVGNo packages to upgrade, no supply-chain risk; if Python is installed, it runs

The last row deserves a comment. Running pip install on a library costs almost nothing today. But for a system that has to run every day for years, every dependency is a long-term loan at an unknown interest rate. The one exception is the charting library behind the per-stock candlestick charts. It ships with the site and pulls nothing from external addresses. Its npm package's sha512 is verified when it enters the repo, and its sha256 is checked again every time the site is assembled, aborting on any mismatch. Dependencies aren't forbidden, but any you take on must be identifiable and locked down.

Once the shared layer was in place, history was backfilled all the way to 2015 in one push: about eight thousand API calls, running straight through the night. Along the way the network dropped twice, the subscription quota hit its five-hour cap once, and the terminal crashed once. Each time, progress was read from the logs and the run resumed from the breakpoint, without losing a single row. Long tasks must be resumable, retry automatically, and write logs. That night carved this lesson in, one cut at a time.

06 · Evidence Over Confidence: Five Cross-Checks

The biggest trap with AI-written code isn't that it's wrong. It's that it looks right. The tests are green, the page looks good, the numbers seem plausible, and yet some corner is slightly off in a way the eye can't catch.

So the core of this project's verification isn't "the tests pass." It's cross-checking: take an independent answer and compare digit by digit.

"Figure 5 · Five cross-checks: "looks right" doesn't count; only "identical digit for digit" does"

A few stories make the point better than numbers.

Byte-for-byte baseline comparison. Before refactoring into the shared layer, the old code generated the complete chip-line output, which was saved as the "reference answer." After the refactor and the history backfill, a byte-for-byte comparison showed 501 of 505 days identical. Each of the 4 differing days had exactly 1 extra stock, and every case had the same cause. Under the old starting date, suspended stocks had only 93 to 100 candles, short of the 101 the formula needs. With the start pushed back to 2015, they had enough. Differences aren't frightening. What's frightening is not being able to explain where a difference comes from.

Cross-checking against the requester's screenshots. In the moving-average channel round, the requester sent only a Tongdaxin formula and 7 screenshots late at night, without a word of explanation. Instead of guessing what he wanted, we first recomputed the three lines for those 7 stocks from the database, and every reading matched. Then we compared the red arrows in the screenshots, stock by stock, against the computed event windows for "low touches the lower band," and those matched too. Intent went from guesswork to evidence. The more important step came next. Checking only whether the price touched the lower band that day would have missed exactly the three examples he gave, because in each case the stock had touched the band the day before and had already left it on a big up candle. That is how the "look back 5 days" definition was derived from the requester's own examples.

Same-source, digit-by-digit comparison. The three lines on the per-stock candlestick charts and the numbers in the moving-average channel table come from the same algorithm. In principle they can't differ. But the tests checked "approximately equal," which let rounding edge cases like .xxx5 slip through. Switching to a string-by-string comparison across 1,423 stocks turned up 5 stocks off by 0.001. The root cause was floating point: one side used prefix sums and the other summed directly, so the final digit rounded in different directions. After switching to exact summation, the published lists stayed byte-for-byte identical and the new charts matched the table with zero differences.

Bringing in a different model to find faults. The model that writes the code doesn't review its own work. A model from a different provider reviews read-only, before acceptance. Over several rounds it raised more than 40 issues in total. Every one reproduced on the real database, with no false positives. The weightiest was caught in the very first round: the breadth module's cache would publish stale readings after a re-fetch or a change in constituents. Problems like that are invisible on the page, and acceptance would never have found them. Another was more blunt: the "32 suspended" shown on the moving-average channel page was wrong that very day. The real figure was 3.

Reviewers can fail too. In the candlestick round, the first reviewing model read the code, hit its usage cap, and never delivered its list. The same brief was then handed to another provider, which completed the review in read-only mode, and a check of the working tree afterward confirmed it hadn't modified a single file. Reviewers need a backup. We learned that one by running into it, too.

Tests catch what you thought of. Cross-checks catch what you didn't. You need both.

07 · Neither Deciding for Them Nor Asking Endlessly: The Definitions Ledger

What the requester provided was a table of experience, and such tables naturally have gaps. "What does a big up candle with moderate-tier volume count as?" "Are market-cap tiers based on total or free-float market cap?" "Does the breadth denominator include suspended stocks?" The requester never said, and the program still has to run.

The two usual responses are both wrong. Deciding on the requester's behalf quietly slips your interpretation into his system. Asking about every gap as it comes up wears him out, since he just wants a list, not a seat in your requirements meetings.

This project took a third path: every unclear definition becomes a config item with a default value, recorded in a ledger.

Ledger fieldExample
ID and moduleK7 · High dividend
What the gap isRank by trailing-12-month dividend yield or by static yield?
Current defaultTrailing 12 months
Who decidedThe requester
What to rerun if it changesConfig change only; regenerate the high-dividend page

In the October 18 round, the ledger held 34 items: 13 decided by the requester, 13 by the owner, and the rest settled by our own checks. That afternoon the requester answered 12 definitions in one go, and all of them were implemented within two hours, almost entirely through config changes. The biggest open case in the breakout signals, what a big up candle with moderate volume counts as, is still undecided. The program honestly labels it "pending" and lists it separately for human judgment, and never files it on its own authority.

The ledger's value isn't completeness. It's that who decided, what the default is, and what has to rerun if it changes are visible at a glance. It turns "the requirements weren't clear" from a source of anxiety into an account you can settle at your own pace.

08 · Division of Labor: Agents Do the Work, Humans Press the Button

In this method, the division of labor between human and AI looks like this:

Figure 6 · Four swim lanes: the owner sets direction, signs off, and deploys; the main session wraps things up; sub-agents do the work; an outside model reviews read-only

The most interesting rule here is that a human performs the deploy personally. The agents merge to main, build the site, and check the output. The final deploy command is left for the owner to type into the terminal. This isn't distrust of AI. It keeps a human finger on "going live," the one irreversible action. The process is semi-automatic not because full automation is impossible, but because some friction is left in on purpose.

Here's how the numbers came out:

ItemFigure (cumulative over five rounds)
Model rolesMain session Claude Opus 5.5 · sub-agents Claude Sonnet 5.5 · read-only review by Codex and grok
DurationAbout 85 hours (10-17 23:14 → 10-21 11:55)
Commits and releases217 commits, 14 releases, each tagged
Model calls2,325 across the main session and sub-agents
Total tokensAbout 621 million, over 90% of them cache reads
At API list pricesAbout $187 to $209
Actually paidSubscription, no separate per-token charge

Two points deserve their own comment.

Caching is the bulk of it. More than 90% of the tokens were cache reads. Without caching, billing everything at the base input price, the same work would have cost roughly ten times as much. On a long project, keeping the context stable and cacheable is cost-saving engineering in its own right.

How much efficiency improved, we don't know. During development we ran one conversion: at model prices from six months earlier, the same token volume would have cost about twice as much on the Claude side. But that's a price difference, not an efficiency difference. An older model might have needed more rounds and more rework, the development tools themselves keep changing, and the rules saved a great deal of rework. These effects are tangled together and can't be separated. A credible number would require having the old and new models each build from the same requirements doc. Until that's done, any claim of "X times better" is a guess.

09 · Write Down Every Fall

The method wasn't written on a smooth ride. Every fall over those three weeks bought a rule or a habit:

The fallThe rule it bought
The network dropped twice during the overnight history backfillLong tasks are resumable, retry automatically, and write logs
The subscription quota hit its five-hour cap and an agent was cut off mid-taskCommit often so that after any interruption, work can resume from the latest commit
The terminal crashed and the main session disconnectedProgress lives in logs and the dashboard; a new session reads the logs and carries on
A full-table count on the dev dashboard locked the database mid-write, and the history backfill failedDon't read the database in parallel while writing to it
In one day, 11 commits mistakenly carried an attribution line they shouldn't haveCheck the author and attribution before committing, especially on merge commits
A review was first launched with write permissionsReviews are always read-only, and conclusions must be written out in full in the conversation
The candlestick data volume was estimated at 28 MB in research; the release came in at 50.6 MBCorrect estimates honestly once measured, and offer optional optimizations
The default sort put stocks that crashed through the lower band day after day at the very topIn local preview, really click and really sort; tests can't stand in for human eyes

The last one deserves a comment. Before the moving-average channel shipped, every test was green. But when clicked in a browser, the default sort put stocks in free fall, far below the lower band, at the very top, and the date column wouldn't sort because the parsing function only recognized the year. Tests won't find problems like these. Tests check whether the program does what it was designed to do, not whether the design itself makes sense to a person. Acceptance needs a human, with eyes and hands.

One more lesson moved me more than any other in the whole process: admitting what you don't know. The cost report says "how much efficiency improved, we don't know." The ledger says "the requester didn't specify this; we're using the default for now." The audit report labels every derived figure "estimated." The most valuable quality in a system isn't being right all the time. It's that you always know where it might be wrong.

10 · Generalizing: Four Pillars on One Foundation

Pulled together, three weeks of experience make a house like this:

Figure 7 · One house, four pillars: process, gates, evidence, division of labor, resting on architectural restraint

PillarIn one lineTo carry it into your project, do this one thing first
ProcessWrite before you buildBefore starting the next requirement, write one page: exact words, your understanding, what's out of scope, acceptance criteria
GatesRules written into the programAdd one line at the top of your deploy script: exit if the working tree isn't clean
EvidenceCross-checking beats confidenceBefore the next refactor, save the full current output and compare byte for byte afterward
Division of laborHumans decide, machines doKeep a definitions ledger; leave the deploy step to a human
FoundationArchitectural restraintKeep a single write path for data; before adding any dependency, ask whether you can do without it

None of these five things is new on its own. Requirements docs, release gates, characterization tests, and configuration have all been in the software engineering textbooks for a long time. What's new is the context. When AI pushes the cost of writing code close to zero, these old rules become more valuable, not less. When code was slow to write, the slowness was itself a gate: people naturally paused to think between keystrokes. Now that code arrives in minutes, that natural gate is gone, and only man-made gates can replace it.

The Analects says a craftsman who wants to do good work must first sharpen his tools. The tools are now about as sharp as they can get. What needs work next is the rules for using them.

11 · Epilogue: Rules Are a Road Built for Freedom

Look back at that original threshold table. It still sits at the deepest layer of the system: six conditions, three market-cap tiers, not a word changed. The system has grown tenfold, but the requester's original experience is kept there intact, and everything he left unclear is recorded in the ledger, waiting for him to decide.

Perhaps that is what rules are for. They aren't ropes that bind your hands and feet. They're a road that lets you dare to run fast. Because of the gates, we could let the agents write freely. Because of the cross-checks, we could rebuild the entire data foundation in a single night. Because of the ledger, we could tell the requester: whatever you haven't settled yet, we'll keep a record of for you, and we won't decide it for you.

When code was slow to write, rules were a luxury. Now that code comes fast, rules are a necessity. Perhaps in the next stage AI will propose the rules themselves and humans will make the final call. But that's another essay.


The system described here only screens and records. It runs no return backtests and does not constitute investment advice of any kind. Figures are as of 2026-10-21 and come from the project's internal audit records.

Share
← Back to Research

Related · TryWay Labs

長為試之印(盖印版·自然崩口)
AI 实战2026-10-02
From Canvas to Evidence · A Running Lean Tool Atlas
AI 实战2026-08-14
An Inbox, Not a Feed · A Self-Diagnosing Private Intelligence System
AI 实战2026-07-11
Moving Startup Judgment From People to Rules · From Lean Startup to a Verifiable Analysis Protocol