SPECIMEN

Sample memo and method

Method

How the figures are computed: the scripts and data, the glossary, the export guide for a firm’s own records, and the client script, partner_disposition.py.

Specimen memo, computed on public data

Specimen. Not a client engagement. No firm commissioned or saw it. Every figure comes from the published disposition-effect study.
Question
In this panel, what share of traders realised gains more readily than losses, counting every day a position was held?
Figure
76.1% (3,547 of 4,659 traders; Chinese A-share social-trading portfolios, 2016–18)
95% interval
74.9–77.3%, treating traders as independent; the study explains why they are not quite
Denominator
Traders with at least one winner and at least one loser on some day, so that both halves of the measure are defined: 4,659 of the 4,731 in the panel
Supporting figures
Per trader then averaged: PGR 0.1925, PLR 0.1200, ratio 1.61. Pooled across every observation: PGR 0.0785, PLR 0.0535, ratio 1.47
Period
24 Jun 2016 to 27 Mar 2018
Observations
2,780,879 trader-stock-lot-days across 3,501 stocks; every day a position was held counts
Data
Jin, Li & Zhu, replication files for PLoS ONE (2021), CC0, Harvard Dataverse
Script
reproduce.py in the public repository. Client engagements use partner_disposition.py, which computes pooled PGR and PLR from a five-column extract, counting only days on which an account sold something, as Odean did; since 17 Oct 2026 it also makes the same comparison with holding age held fixed. It does not produce a share of traders or a per-trader ratio. The like-for-like public figure is the pooled ratio, 1.47, and even that is only approximately comparable, because the two count days differently
What this does not show
  • It measures the problem, not a fix. Nothing here says whether anything corrects the disposition effect.
  • Chinese A-shares, mid-2016 to early 2018, one social-trading platform. Whether it holds for North American retail today is an assumption, not a result.
  • Social-trading users are a selected population. Xueqiu cube operators chose to run public portfolios.
  • I did not collect this data. It is someone else’s replication file, released CC0. The contribution is the computation and publishing it.
  • The pooled and per-trader ratios differ, 1.47x against 1.61x, because the denominators differ. Both are above. Quoting one without the other is picking.
  • Significance is not effect size. The t-statistics are enormous because n is enormous. Cohen’s d of 0.49 is the honest magnitude: moderate, not overwhelming.
  • No minimum activity was required. A trader with one winning and one losing held day counts the same as one with thousands; how sensitive the share is to a minimum-activity rule has not been computed.
  • Traders whose two rates are exactly equal are counted as not showing the effect; reproduce.py does not report how many there are.
  • The source data’s gain flag is 1 only when a position was in profit, so a break-even day counts on the loss side. The client script, partner_disposition.py, drops break-even rows instead.
  • Holding age: positions are sold less often the longer they are held. A robustness update to the study (17 Oct 2026) finds the direction survives when holding age is held fixed, but the size depends on how “gain” is timed, and a V-shaped selling pattern is not ruled out.
Reliance
A client memo is for the client’s use only; no other party may rely on it.

Client-format specimen, synthetic file

Synthetic test file, not real traders. Synthetic data with a planted effect. Not a finding about anyone.

This is the complete output of the published client script, partner_disposition.py, run when this page was built on the synthetic file that the published make_fixture.py writes. It is what a client’s staff send back, and the figures a client memo is built from.

$ python3 make_fixture.py --out fixture.csv --seed 7
$ python3 partner_disposition.py fixture.csv --pnl-basis price
pnl basis:            price (funding excluded)
accounts:             30
qualifying sale days: 256

data rows parsed:                   14400
blank records skipped:              0
rows in qualifying pairs:           2048
rows discarded (no close that day): 12352
same-day marks superseded by close: 0
orphan closes (no mark row):        14 of 335 (4.2%)
identifier spelling collisions:     0
unicode data version:               14.0.0

PGR                   0.2178
PLR                   0.1119
ratio (PGR/PLR)       1.9470
t statistic           6.51
n gains  (RG+PG)      1001
n losses (RL+PL)      1046

realised gains   RG   218
paper gains      PG   783
realised losses  RL   117
paper losses     PL   929
zero-pnl rows excluded 1

AGE-CONTROLLED FIGURES (holding age = calendar days since the
position's first row in this file)
positions with a known open date:               329
positions open on the first date (left out):    240
rows used (qualifying pairs, pnl not 0):        975

ratio, ages pooled, same rows:                  2.0325
within-age ratio (Mantel-Haenszel):             1.9273
                                                95% interval 1.3825 to 2.6867, by account

holding age      gain rows  loss rows     PGR     PLR   ratio  within-age
<=1 day                 43         32  0.2093  0.0938   2.233       2.232
2-5 days                73         75  0.2055  0.1333   1.541       1.618
6-20 days              192        224  0.2240  0.0804   2.787       2.595
21-60 days             168        168  0.1667  0.1071   1.556       1.441
61-250 days              0          0       -       -       -           -
251+ days                0          0       -       -       -           -

fixed effects (account + holding day), linear probability model:
  gain coefficient:                             0.103986
  standard error (account and date):            0.021478
  ratio implied for gain rows (ATT):            2.0878
                                                95% interval 1.4495 to 3.7307

gain timing (rows that have a previous row for the same position):
  rows:                                         933
  ratio, ages pooled: same day / previous row:  2.0604 / 1.8627
  within-age ratio: same day / previous row:    1.9488 / 1.7481
  fixed-effects ATT: same day / previous row:   2.0993 / 1.8586
  sign changed since previous row, sold rows:   10 of 138
  sign changed since previous row, unsold rows: 60 of 795

HOW TO READ THE AGE-CONTROLLED FIGURES
- Positions are sold less often the longer they are held. If losing
  positions are on average older than winning ones, the pooled ratio at
  the top can show an effect that is only an age pattern. The within-age
  ratio compares winners and losers of the same holding age; use it, not
  the pooled ratio, for the size of the effect.
- Holding age is counted from each position's first row in this file.
  Positions already open on the file's first date are left out of these
  figures because their age is unknown; the pooled figures still use them.
- The fixed-effects line compares each account with itself, at the same
  holding day. It is a different average from the pooled ratio, so the two
  are not expected to match.
- Gain timing: a position's gain or loss on the day it is sold may partly
  reflect the sale itself (the fill or the closing mark). The lines that
  take the gain or loss from the position's previous row avoid that. If
  they are much smaller, the effect depends on same-day timing.
- Not ruled out: traders may sell large moves in either direction more
  often (a V-shaped pattern, Ben-David and Hirshleifer 2012). Separating
  that from a preference for selling winners needs the size of each gain
  or loss relative to the position, which these five columns do not carry.
- The intervals here are clustered by account (the within-age ratio) and
  by account and date (fixed effects). The t statistic at the top treats
  every row as independent and is not.
- NOT MEASURED means the data cannot support the figure, never zero.

How to read the age-controlled figure

The headline ratio compares the share of winning positions you sold with the share of losing positions you sold, pooled over every position however long it had been held. Positions are usually sold less often the older they get, so if losers tend to be older, part of the headline is an age pattern, not a habit. The within-age ratio makes the same comparison only between positions of the same age (same number of days since opened), then combines the days. That is the figure to use for the size of the effect. The table by holding age shows where the effect sits: often mostly in positions held a day or less. The fixed-effects ratio compares each account with itself at the same holding age; it is a different average and is not expected to equal the pooled one. If the within-age interval includes 1, the data do not show an effect once age is held fixed. NOT MEASURED means the data cannot support that figure; it never means zero.

Scripts: partner_disposition.py · make_fixture.py