Edge Autopsy邊際解剖

8 lines · 0 passed · $3,300 capital · 1 stale · 2026-08-27which one?

Practice Judgement · performance disclosure English

Eight checks for a trading track record. I ran them on my own first.

2,763 words · about 13 min

A track record is a claim about what was left out. Eight questions that surface the omissions, scored against my own record first. It fails two of them.

Someone sends you an equity curve. A screenshot from a bot vendor’s Discord, a funded-account dashboard, a README on GitHub with a 13× backtest at the top. The number on it is probably arithmetically correct. That is rarely the problem.

The problem is that a track record is a claim about what was left out, and omissions do not show up on a chart. Every check below is a question about an omission, and each one has an answer that a real record can produce in under a minute.

You will rarely get all eight answered, and that is fine. The count is the signal, and a refusal usually tells you more than an answer would.

Scoring is at the bottom. Two of the eight are failures here, and one of those is permanent.

1. Simulated or funded — the regulator drew this line for you

US commodity futures rules make the distinction mandatory rather than optional. 17 CFR 4.41(b)(1)(i) requires that any presentation of simulated or hypothetical performance carry prescribed language, and two clauses in it are the test itself: the results “do not represent actual trading”, and hypothetical programs “are designed with the benefit of hindsight” (17 CFR 4.41, retrieved 2026-08-27).

What a real answer looks like: every point on the curve is labelled as one or the other, and the two are never plotted on the same line. A vendor who cannot tell you which points came from a broker statement is not withholding a detail. They are withholding the whole claim.

This site: the performance page splits left and right. Funded money on the left: $3,300 of capital, $3,499.44 of current value as of 2026-08-27T05:10:29Z. An evaluation account on the right shows $50,620.24 and is excluded from the left-hand total.

Dividing one by the other gives 14.5× — my arithmetic on two published figures, not a number the site records. That is the size of the flattery available from a single act of addition, and it is why the data layer refuses to write a file where an account flagged as an evaluation contributes to the total.

2. All the accounts, or the one that worked

The GIPS standards answer this without ambiguity. Rule 3.A.2 requires that all actual, fee-paying, discretionary segregated accounts “be included in at least one composite”, and compliance is claimed firm-wide rather than for a flattering slice of it (GIPS Standards for Firms, 2020, retrieved 2026-08-27; the host blocks plain command-line clients with a 403, so a browser User-Agent is needed to fetch it).

The SEC took the same position for advisers in 2020. Its marketing rule prohibits “including or excluding performance results, or presenting performance time periods, in a manner that is not fair and balanced”, and requires an adviser to be able to substantiate factual claims on demand (SEC, Investment Adviser Marketing, 2020-12-22).

What a real answer looks like: a count of accounts, including the closed ones. Selection is the cheapest fabrication, because every surviving number stays true.

This site: eight lines on the front page, four of them killed. The list keeps a meme-coin ladder that took $0 of capital and had a six-day expectancy between −13% and −18%, a sports line that has never traded real money (15 paper entries), and a software business that produced $0 of revenue. Removing them would improve every remaining figure without a single trade changing.

3. Net or gross

NFA Compliance Rule 2-29(b)(5) sets two conditions at once. Performance must be presented “net of all commissions, fees and expenses”, and the promoter must be able to demonstrate that the figures represent the actual performance of “all reasonably comparable accounts” for the same period (NFA Compliance Rule 2-29, retrieved 2026-08-27).

Both conditions live in the same rule for a reason: account selection and cost exclusion are two brushes on the same retouching job, and a promoter who has reached for one has usually reached for the other. Costs are also the more forgivable of the two in conversation, which is why they are the explanation offered first. A gross figure gets described as an oversight. A curated account list gets described as focus. Neither description changes what the reader is being shown, and only one of them is cheap to check from the outside.

What a real answer looks like: cost per round trip, times trade count, as a share of the return.

This site: the three funded figures are account balances and broker net liquidation values, so costs are already inside them by construction. The counter-example is on this site too. A six-year backtest of mine printed 13.25×; auditing it myself brought it to 2.26×, and none of the three overstatements was cost (the audit). Costs are the explanation people reach for because they are the polite one.

4. Is the sample large enough to carry the claim

Checks 1 through 3 ask what the record is made of. This one asks what it weighs. A return without an n has no size, and a Sharpe ratio computed on 20 trades is a description of those 20 trades.

Harvey, Liu and Zhu argued in 2016 that after accounting for how many factors the literature has already tried, a new factor should clear a t-statistic of 3.0 rather than the customary 2.0 (Harvey, Liu & Zhu, 2016, Review of Financial Studies).

What a real answer looks like: n, plus the number of variants tried before this one was kept. The second figure is the denominator of the multiple-testing problem and almost nobody volunteers it.

This site fails here, and the failure is the product. The threshold was fixed in advance at t*=2.0. The strongest of eight lines reaches t=1.88. Nothing clears it, and by the 3.0 standard above my own bar was the lenient one.

The direction is worse than the level. On the futures fade line, t was 1.52 at n=30; five trading days later, at n=53, it had fallen to 0.79. More evidence, weaker result. On the sports line, separating a 60% hit rate from 50% needs roughly 152 entries and there are 15 (the workings).

5. Has the data ever stopped, and how is the gap marked

This one comes before the tamper question deliberately: you have to know a record has holes before it matters whether holes can be filled in afterwards.

Where this site failed: collection on one line stopped on 2026-08-01 and nothing new arrived for 26 days. Nobody noticed. I found it a month later because I happened to think about that line (root cause, including the first analysis I got wrong).

The fix was a liveness panel rather than an apology: for each of the eight lines, when it last produced data, with dead feeds and deliberately closed feeds distinguished. A closed line going quiet is correct behaviour, and rendering it as an alert is a false alarm. Freshness is a constant in the collector: anything older than STALE_H = 36 hours is stale.

As I write this, one line is showing red. The US fade equity is dated 2026-08-19 against a snapshot time of 08-27, so stale is true and the page prints a dated “data ends” warning instead of quietly reusing the old figure. That number also does not animate on load. Motion implies a live feed.

What a real answer looks like: the timestamp of the last write. A dead feed and a live one draw the same chart.

6. Can the history be rewritten

Tamper-evidence, in code, is two invariants: time never moves backwards, and the count of closed trades never decreases. A violation raises, and nothing is written.

# prev defaults to a sentinel; only a production run reads the previous file from disk
if prev:
    if payload['as_of'] < prev['as_of']:
        errs.append('as_of 倒退:%s < %s' % (payload['as_of'], prev['as_of']))
    for name, ln in payload['lines'].items():
        pn = len(prev['lines'].get(name, {}).get('closed_trades', []))
        if len(ln['closed_trades']) < pn:
            errs.append('%s: 已平倉筆數從 %d 掉到 %d' % (name, pn, len(ln['closed_trades'])))

A missing trade has two possible causes: the source broke, or somebody disliked that trade. The check does not need to tell them apart. It refuses to write and escalates to a human, which is the correct division of labour between a program and a person.

The sentinel has a history. This function used to read the previous file unconditionally, so whether the self-test passed depended on how many real trades happened to be sitting on disk: green at first, guaranteed to fail once three closed trades accumulated (fixed 2026-08-27). A test that passes because of ambient state is the exact bug class this site catalogues (five gates, five silent failures).

This site scores half here. Those invariants stop accidents: a fat finger, a schema change upstream, a collector that missed a row. They do not stop intent. I hold write access to the repository and there is no third-party timestamp anywhere in the chain.

What a real answer looks like: append-only storage, or better, an artefact somebody else signed — a broker statement, an exchange API dump. The strength comes from the signature not being yours.

7. Withheld or hidden

Every record omits something. The test is not whether anything is missing. It is whether you can see the shape of the gap.

This site never publishes entry and exit parameters, thresholds, holding times, stop placement or capacity limits, and the method page says why: three of four traded lines are mean-reverting, so anyone front-running them raises my own entry cost. Per-trade disclosure runs off an allow-list rather than a deny-list. Four entry-feature columns exist in the source file and are not on this line, because publishing them per trade would publish the entry condition itself.

# allow-list, not deny-list: a new column is withheld by default
TRADE_FIELDS = ['sym', 'opened', 'closed', 'entry', 'exit', 'ret', 'days', 'why', 'withheld']

Writing this section tripped my own gate. The comment above that line in the source is more specific than the version above, specific enough to match two rules in the leak checker, and I had to rewrite it before it could be published. The code that decides what may be disclosed is subject to the same gate as everything else. That it was blocked is publishable; what was blocked is not, including by way of quoting it to explain the block.

The last column, withheld, is the mechanism. One instrument can be both a closed trade and an open position — close it, re-enter it, and the historical row now names something currently held. The name is masked and the row stays:

if t['withheld'] and t['sym'] is not None:
    errs.append('%s: 標記 withheld 卻仍帶著標的名稱 %s' % (name, t['sym']))
if not t['withheld'] and t['sym'] is None:
    errs.append('%s: sym 為 None 卻沒標記 withheld' % name)

Deleting the row instead would trip check 6 from the inside: three closed trades become two, then three again. Masking keeps the count stable, keeps dates and returns auditable, and the name returns by itself once the position closes.

What a real answer looks like: the omission has a column, a count and a label. A record where you can see the empty seat beats one where you cannot.

8. Who verified it

GIPS again: a firm claiming compliance needs at least five years of compliant history (rule 1.A.3), and verification is performed by an outside party on the firm as a whole, not asserted by the firm about itself.

This site: no third-party audit, no published broker statements, no external verification of any kind. Uptime is measured in months, not years. What exists is seven build gates that I wrote, and a separate review process that still sits inside the same project.

Those gates have a track record of their own, and it argues against trusting them: five of the seven had defects on the day they were written, and every one of them reported success while defective. Self-written checks break, and they break quietly.

This box is not going to get ticked. Ticking it means paying an outside firm to verify an account holding $3,300, which is out of proportion by an order of magnitude. It stays red, and anything above it should be discounted accordingly.

What a real answer looks like: a name, a relationship, a scope.

The scorecard

#CheckThis siteEvidence
1Simulated or fundedPassSplit panels, evaluation excluded
2All accountsPassEight lines, four killed
3Net or grossPassBacktest audit is the counter-example
4Sample sizeFailBest t=1.88 against a 2.0 bar
5Data continuityPass26-day gap, stale flag live
6Tamper evidenceHalfStops accidents, not intent
7Withheld or hiddenPassMethod fixes the line; allow-list plus masked rows
8External verificationFailNone

Four of the eight above are not mine. Three rulebooks — 17 CFR 4.41, NFA 2-29 and the GIPS standards — cover checks 1, 2, 3 and 8, because their authors had to litigate what “performance” means against people with an incentive to blur it. Check 4 is statistics. The remaining three are engineering that runs here daily.

Five passes, one half, two failures. That is a mediocre score, and publishing it is the only thing that makes the list worth anything to you: a ruler that cannot cut the hand holding it is an advertisement. Run it against the next equity curve somebody shows you, count how many of the eight they can answer, and calibrate from there.

That line read “four passes, two halves” until 2026-08-27, while the table above it read five and one. Both add to eight, which is why none of the seven build gates noticed. A reviewer reading the table row by row did.

The line was corrected, not the table. Every row of the table carries a link to its evidence; the sentence is only a restatement of it, and a restatement should be generated from the thing it restates.

Worth naming plainly: the correction moves the self-assessment up a grade, from four passes to five. Nobody adjusted a score after the fact. The table has said five since the first draft and the sentence was wrong the whole time.

A gate now compares the two, checking pass, half and fail counts separately rather than the total.

FAQ

Why not just look at the return? The return is the output of these eight checks rather than an input to them. A 12% that clears seven of them and a 300% that clears none are different kinds of object.

Does a third-party “verified” badge settle check 8? Halfway. Those platforms typically verify that the trades occurred in that account, which leaves check 2 untouched. One person can hold five verified accounts and show you the best one.

Can I use this on an open-source backtest? Yes, and check 1 removes most of them immediately, since a backtest is simulated performance by definition. Check 4 usually finishes the job: the variant count is almost never recorded.

Why publish your own failures? Because a list that only cuts other people convinces nobody. This site puts “nothing clears the threshold” at the top of its front page; recording two failures here is the same decision.

All four external sources were retrieved and read in full on 2026-08-27 rather than summarised at second hand. A fifth was dropped: a hedge-fund study quantifying survivorship and backfill bias returned HTTP 200, but the payload was not the paper and I never read the original. That is an omission caused by retrieval failure, not by judging it unimportant, and the gap sits next to check 2 — beyond selecting accounts, databases select them too.

Real balances and the liveness panel are on the performance page; the disclosure boundary and its reasoning are on the method page; an English account of what happened when this bot ran on a prop-firm evaluation is in this note.