lesson-book 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lesson_book-0.1.0/LICENSE +21 -0
- lesson_book-0.1.0/PKG-INFO +180 -0
- lesson_book-0.1.0/README.md +161 -0
- lesson_book-0.1.0/pyproject.toml +32 -0
- lesson_book-0.1.0/setup.cfg +4 -0
- lesson_book-0.1.0/src/lesson_book/__init__.py +29 -0
- lesson_book-0.1.0/src/lesson_book/__main__.py +4 -0
- lesson_book-0.1.0/src/lesson_book/cli.py +136 -0
- lesson_book-0.1.0/src/lesson_book/lessons.py +282 -0
- lesson_book-0.1.0/src/lesson_book/mistakes.py +234 -0
- lesson_book-0.1.0/src/lesson_book.egg-info/PKG-INFO +180 -0
- lesson_book-0.1.0/src/lesson_book.egg-info/SOURCES.txt +16 -0
- lesson_book-0.1.0/src/lesson_book.egg-info/dependency_links.txt +1 -0
- lesson_book-0.1.0/src/lesson_book.egg-info/entry_points.txt +3 -0
- lesson_book-0.1.0/src/lesson_book.egg-info/top_level.txt +1 -0
- lesson_book-0.1.0/tests/test_cli.py +66 -0
- lesson_book-0.1.0/tests/test_lessons.py +113 -0
- lesson_book-0.1.0/tests/test_mistakes.py +95 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lesson Book contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lesson-book
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tuition memory for traders: a local-first, deterministic mistake ledger with situation matching — before you act, it reminds you where you paid before.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: trading,journal,mistakes,lessons,discipline,cli,local-first
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# lesson-book
|
|
21
|
+
|
|
22
|
+
**Tuition memory for traders.** A local-first, deterministic mistake ledger:
|
|
23
|
+
record what each mistake cost you, tag it, and `lb match` reminds you of it
|
|
24
|
+
the next time the same situation shows up — **before** you act. Python
|
|
25
|
+
3.11+, **zero dependencies**, Windows / Linux / macOS. No LLM, no cloud, no
|
|
26
|
+
statistics: the reminder is reproducible and auditable.
|
|
27
|
+
|
|
28
|
+
**Status:** v0.1 — alpha. The matching logic is distilled from a production
|
|
29
|
+
trading system's lesson-matching module; this standalone package is new.
|
|
30
|
+
|
|
31
|
+
## Why this exists
|
|
32
|
+
|
|
33
|
+
Your trading system has a memory problem: it forgets. The mistake you paid
|
|
34
|
+
1,200 for last month looks like a fresh opportunity today, because nothing
|
|
35
|
+
stood between the idea and the order. Trading journals solve the *recording*
|
|
36
|
+
half — they are ledgers of what happened. `lesson-book` solves the
|
|
37
|
+
*retrieval* half: it keeps the tuition in a form that can speak up when the
|
|
38
|
+
same situation appears again.
|
|
39
|
+
|
|
40
|
+
Two design commitments make it different from journaling apps and LLM
|
|
41
|
+
"memory" systems:
|
|
42
|
+
|
|
43
|
+
1. **Deterministic, not statistical.** Scoring is a fixed weighted formula
|
|
44
|
+
(code +3, industry +2, market cap +1, volatility proximity bonus, tag
|
|
45
|
+
overlap bonus). Same situation, same reminder, every time — the opposite
|
|
46
|
+
of an LLM memory that improvises.
|
|
47
|
+
2. **Local-first and verifiable.** The book is a plain append-only JSONL
|
|
48
|
+
file in your repo. `git log` on it *is* your audit trail; nothing ever
|
|
49
|
+
leaves your machine.
|
|
50
|
+
|
|
51
|
+
## Philosophy
|
|
52
|
+
|
|
53
|
+
**Tuition is capital — the system does not forget what you paid for, and it
|
|
54
|
+
reminds you before you pay again.**
|
|
55
|
+
|
|
56
|
+
This is the checklist culture of aviation and medicine, applied to trading:
|
|
57
|
+
Gawande's [*The Checklist Manifesto*](https://en.wikipedia.org/wiki/The_Checklist_Manifesto)
|
|
58
|
+
is the canonical argument that simple checklists reduce catastrophic error
|
|
59
|
+
rates by an order of magnitude. And it is the **premortem** in reverse:
|
|
60
|
+
[Klein (2007), "Performing a Project Premortem"](https://hbr.org/2007/09/performing-a-project-premortem)
|
|
61
|
+
asks you to imagine, before acting, that you already failed and explain why.
|
|
62
|
+
`lb match` is the automated premortem: it surfaces the historical answers to
|
|
63
|
+
"why will this fail?" without you having to ask.
|
|
64
|
+
|
|
65
|
+
The [IOM (1999), *To Err Is Human*](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system)
|
|
66
|
+
framing applies directly: errors are a system problem, not a character
|
|
67
|
+
flaw. The book exists to improve the system — classification, review and
|
|
68
|
+
retrieval — never to punish the person. That is why records carry `cost`
|
|
69
|
+
(a number, not a shame) and why the tool classifies but never enforces:
|
|
70
|
+
rules, positions and limits stay with you.
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# install from PyPI (once published)
|
|
76
|
+
pip install lesson-book
|
|
77
|
+
|
|
78
|
+
# or run without installing anything:
|
|
79
|
+
# PYTHONPATH=src python -m lesson_book --help
|
|
80
|
+
|
|
81
|
+
python examples/demo.py # record, match, review on a scratch book
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Your own book:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# record a mistake — classification happens via the rule table
|
|
88
|
+
lb add --book book.jsonl \
|
|
89
|
+
--title "bought into the open gap" \
|
|
90
|
+
--issue execution_failed --error-category price_limit \
|
|
91
|
+
--date 2026-08-01 --code 600000 --industry banking \
|
|
92
|
+
--volatility 0.03 --tags gap,limit-up --cost 1200 \
|
|
93
|
+
--lesson "never chase the open gap; wait for the retest"
|
|
94
|
+
|
|
95
|
+
# before acting tomorrow: ask the book
|
|
96
|
+
lb match --book book.jsonl --industry banking --volatility 0.03 \
|
|
97
|
+
--code 600000 --tags gap
|
|
98
|
+
# -> the 2026-08-01 record, score 8.0, lesson: "never chase the open gap..."
|
|
99
|
+
|
|
100
|
+
# import an existing ##-style markdown knowledge base
|
|
101
|
+
lb import-lessons --from LESSONS.md --book book.jsonl
|
|
102
|
+
|
|
103
|
+
# daily review of what the day cost
|
|
104
|
+
lb review --book book.jsonl --day 2026-08-01 --out reviews/
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Commands
|
|
108
|
+
|
|
109
|
+
| Command | What it does |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `add` | Record a mistake: `--title`, `--issue` (required), `--error-category`, context fields (`--code`, `--industry`, `--volatility`, `--market-cap`, `--tags`), `--cost`, `--lesson`, `--situation`. Classified via the rule table (P1/P2, fail-closed to P2) |
|
|
112
|
+
| `import-lessons` | Parse a `##`-headed markdown knowledge base with `**Field:**` metadata (Code/Industry/Volatility/Market Cap/Tags/Cost/Lesson/...) into the book. Idempotent by title |
|
|
113
|
+
| `match` | Rank lessons relevant to the current situation; exit 1 when nothing matches (a pre-action hook can fail-closed on it) |
|
|
114
|
+
| `review` | Daily review: cards grouped by category and priority, total cost, markdown output |
|
|
115
|
+
| `version` | Print version |
|
|
116
|
+
|
|
117
|
+
## The book
|
|
118
|
+
|
|
119
|
+
`book.jsonl` — append-only, one JSON record per line:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{"schema_version": "lesson_book.lesson.v1", "record_id": "...",
|
|
123
|
+
"title": "bought into the open gap", "date": "2026-08-01",
|
|
124
|
+
"code": "600000", "industry": "banking", "volatility": 0.03,
|
|
125
|
+
"market_cap": "large", "tags": ["gap", "limit-up"],
|
|
126
|
+
"category": "price_limit_rejected", "priority": "P1",
|
|
127
|
+
"cost": 1200.0, "lesson": "never chase the open gap",
|
|
128
|
+
"situation": "", "recorded_at": "..."}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Matching score (deterministic):
|
|
132
|
+
|
|
133
|
+
| Signal | Weight |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| same code | +3.0 |
|
|
136
|
+
| same industry | +2.0 |
|
|
137
|
+
| same market cap | +1.0 |
|
|
138
|
+
| volatility proximity | up to +1.5 (decays 5× the gap) |
|
|
139
|
+
| tag overlap | +0.5 per tag, capped at +1.5 |
|
|
140
|
+
|
|
141
|
+
A primary match (code / industry / market cap) is required for a non-zero
|
|
142
|
+
score — the book never speaks up about situations it has no grounds to
|
|
143
|
+
compare.
|
|
144
|
+
|
|
145
|
+
## Classification rules
|
|
146
|
+
|
|
147
|
+
`add` classifies `issue` + `error_category` through a plain rule table
|
|
148
|
+
(`category`, `priority`, `action`), fully overridable in code. Defaults:
|
|
149
|
+
|
|
150
|
+
| issue | error_category | category | priority |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `execution_without_action_plan` | — | `planning_gap` | P1 |
|
|
153
|
+
| `execution_failed` | `price_limit` | `price_limit_rejected` | P1 |
|
|
154
|
+
| `execution_failed` | `trading_time_closed` | `trading_time_closed` | P1 |
|
|
155
|
+
| `execution_failed` | `receipt_reader_error` | `receipt_reader_error` | P1 |
|
|
156
|
+
| `execution_failed` | — | `execution_failure` | P1 |
|
|
157
|
+
| anything else | — | `unclassified` | P2 |
|
|
158
|
+
|
|
159
|
+
Unclassified records are P2 with "review manually and extend the rule table"
|
|
160
|
+
— the taxonomy grows with you, never silently.
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
python -m pip install -e . pytest
|
|
166
|
+
python -m pytest
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
CI runs the full test suite on Ubuntu, Windows and macOS with Python 3.11
|
|
170
|
+
and 3.12. Issues are handled on weekends; pull requests are welcome.
|
|
171
|
+
|
|
172
|
+
## Related work
|
|
173
|
+
|
|
174
|
+
- [Klein (2007), Performing a Project Premortem (HBR)](https://hbr.org/2007/09/performing-a-project-premortem) — imagine the failure before it happens
|
|
175
|
+
- [Gawande (2009), The Checklist Manifesto](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) — checklists as error-rate reduction
|
|
176
|
+
- [IOM (1999), To Err Is Human](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system) — errors as system problems
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# lesson-book
|
|
2
|
+
|
|
3
|
+
**Tuition memory for traders.** A local-first, deterministic mistake ledger:
|
|
4
|
+
record what each mistake cost you, tag it, and `lb match` reminds you of it
|
|
5
|
+
the next time the same situation shows up — **before** you act. Python
|
|
6
|
+
3.11+, **zero dependencies**, Windows / Linux / macOS. No LLM, no cloud, no
|
|
7
|
+
statistics: the reminder is reproducible and auditable.
|
|
8
|
+
|
|
9
|
+
**Status:** v0.1 — alpha. The matching logic is distilled from a production
|
|
10
|
+
trading system's lesson-matching module; this standalone package is new.
|
|
11
|
+
|
|
12
|
+
## Why this exists
|
|
13
|
+
|
|
14
|
+
Your trading system has a memory problem: it forgets. The mistake you paid
|
|
15
|
+
1,200 for last month looks like a fresh opportunity today, because nothing
|
|
16
|
+
stood between the idea and the order. Trading journals solve the *recording*
|
|
17
|
+
half — they are ledgers of what happened. `lesson-book` solves the
|
|
18
|
+
*retrieval* half: it keeps the tuition in a form that can speak up when the
|
|
19
|
+
same situation appears again.
|
|
20
|
+
|
|
21
|
+
Two design commitments make it different from journaling apps and LLM
|
|
22
|
+
"memory" systems:
|
|
23
|
+
|
|
24
|
+
1. **Deterministic, not statistical.** Scoring is a fixed weighted formula
|
|
25
|
+
(code +3, industry +2, market cap +1, volatility proximity bonus, tag
|
|
26
|
+
overlap bonus). Same situation, same reminder, every time — the opposite
|
|
27
|
+
of an LLM memory that improvises.
|
|
28
|
+
2. **Local-first and verifiable.** The book is a plain append-only JSONL
|
|
29
|
+
file in your repo. `git log` on it *is* your audit trail; nothing ever
|
|
30
|
+
leaves your machine.
|
|
31
|
+
|
|
32
|
+
## Philosophy
|
|
33
|
+
|
|
34
|
+
**Tuition is capital — the system does not forget what you paid for, and it
|
|
35
|
+
reminds you before you pay again.**
|
|
36
|
+
|
|
37
|
+
This is the checklist culture of aviation and medicine, applied to trading:
|
|
38
|
+
Gawande's [*The Checklist Manifesto*](https://en.wikipedia.org/wiki/The_Checklist_Manifesto)
|
|
39
|
+
is the canonical argument that simple checklists reduce catastrophic error
|
|
40
|
+
rates by an order of magnitude. And it is the **premortem** in reverse:
|
|
41
|
+
[Klein (2007), "Performing a Project Premortem"](https://hbr.org/2007/09/performing-a-project-premortem)
|
|
42
|
+
asks you to imagine, before acting, that you already failed and explain why.
|
|
43
|
+
`lb match` is the automated premortem: it surfaces the historical answers to
|
|
44
|
+
"why will this fail?" without you having to ask.
|
|
45
|
+
|
|
46
|
+
The [IOM (1999), *To Err Is Human*](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system)
|
|
47
|
+
framing applies directly: errors are a system problem, not a character
|
|
48
|
+
flaw. The book exists to improve the system — classification, review and
|
|
49
|
+
retrieval — never to punish the person. That is why records carry `cost`
|
|
50
|
+
(a number, not a shame) and why the tool classifies but never enforces:
|
|
51
|
+
rules, positions and limits stay with you.
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# install from PyPI (once published)
|
|
57
|
+
pip install lesson-book
|
|
58
|
+
|
|
59
|
+
# or run without installing anything:
|
|
60
|
+
# PYTHONPATH=src python -m lesson_book --help
|
|
61
|
+
|
|
62
|
+
python examples/demo.py # record, match, review on a scratch book
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Your own book:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# record a mistake — classification happens via the rule table
|
|
69
|
+
lb add --book book.jsonl \
|
|
70
|
+
--title "bought into the open gap" \
|
|
71
|
+
--issue execution_failed --error-category price_limit \
|
|
72
|
+
--date 2026-08-01 --code 600000 --industry banking \
|
|
73
|
+
--volatility 0.03 --tags gap,limit-up --cost 1200 \
|
|
74
|
+
--lesson "never chase the open gap; wait for the retest"
|
|
75
|
+
|
|
76
|
+
# before acting tomorrow: ask the book
|
|
77
|
+
lb match --book book.jsonl --industry banking --volatility 0.03 \
|
|
78
|
+
--code 600000 --tags gap
|
|
79
|
+
# -> the 2026-08-01 record, score 8.0, lesson: "never chase the open gap..."
|
|
80
|
+
|
|
81
|
+
# import an existing ##-style markdown knowledge base
|
|
82
|
+
lb import-lessons --from LESSONS.md --book book.jsonl
|
|
83
|
+
|
|
84
|
+
# daily review of what the day cost
|
|
85
|
+
lb review --book book.jsonl --day 2026-08-01 --out reviews/
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Commands
|
|
89
|
+
|
|
90
|
+
| Command | What it does |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `add` | Record a mistake: `--title`, `--issue` (required), `--error-category`, context fields (`--code`, `--industry`, `--volatility`, `--market-cap`, `--tags`), `--cost`, `--lesson`, `--situation`. Classified via the rule table (P1/P2, fail-closed to P2) |
|
|
93
|
+
| `import-lessons` | Parse a `##`-headed markdown knowledge base with `**Field:**` metadata (Code/Industry/Volatility/Market Cap/Tags/Cost/Lesson/...) into the book. Idempotent by title |
|
|
94
|
+
| `match` | Rank lessons relevant to the current situation; exit 1 when nothing matches (a pre-action hook can fail-closed on it) |
|
|
95
|
+
| `review` | Daily review: cards grouped by category and priority, total cost, markdown output |
|
|
96
|
+
| `version` | Print version |
|
|
97
|
+
|
|
98
|
+
## The book
|
|
99
|
+
|
|
100
|
+
`book.jsonl` — append-only, one JSON record per line:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{"schema_version": "lesson_book.lesson.v1", "record_id": "...",
|
|
104
|
+
"title": "bought into the open gap", "date": "2026-08-01",
|
|
105
|
+
"code": "600000", "industry": "banking", "volatility": 0.03,
|
|
106
|
+
"market_cap": "large", "tags": ["gap", "limit-up"],
|
|
107
|
+
"category": "price_limit_rejected", "priority": "P1",
|
|
108
|
+
"cost": 1200.0, "lesson": "never chase the open gap",
|
|
109
|
+
"situation": "", "recorded_at": "..."}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Matching score (deterministic):
|
|
113
|
+
|
|
114
|
+
| Signal | Weight |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| same code | +3.0 |
|
|
117
|
+
| same industry | +2.0 |
|
|
118
|
+
| same market cap | +1.0 |
|
|
119
|
+
| volatility proximity | up to +1.5 (decays 5× the gap) |
|
|
120
|
+
| tag overlap | +0.5 per tag, capped at +1.5 |
|
|
121
|
+
|
|
122
|
+
A primary match (code / industry / market cap) is required for a non-zero
|
|
123
|
+
score — the book never speaks up about situations it has no grounds to
|
|
124
|
+
compare.
|
|
125
|
+
|
|
126
|
+
## Classification rules
|
|
127
|
+
|
|
128
|
+
`add` classifies `issue` + `error_category` through a plain rule table
|
|
129
|
+
(`category`, `priority`, `action`), fully overridable in code. Defaults:
|
|
130
|
+
|
|
131
|
+
| issue | error_category | category | priority |
|
|
132
|
+
| --- | --- | --- | --- |
|
|
133
|
+
| `execution_without_action_plan` | — | `planning_gap` | P1 |
|
|
134
|
+
| `execution_failed` | `price_limit` | `price_limit_rejected` | P1 |
|
|
135
|
+
| `execution_failed` | `trading_time_closed` | `trading_time_closed` | P1 |
|
|
136
|
+
| `execution_failed` | `receipt_reader_error` | `receipt_reader_error` | P1 |
|
|
137
|
+
| `execution_failed` | — | `execution_failure` | P1 |
|
|
138
|
+
| anything else | — | `unclassified` | P2 |
|
|
139
|
+
|
|
140
|
+
Unclassified records are P2 with "review manually and extend the rule table"
|
|
141
|
+
— the taxonomy grows with you, never silently.
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
python -m pip install -e . pytest
|
|
147
|
+
python -m pytest
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
CI runs the full test suite on Ubuntu, Windows and macOS with Python 3.11
|
|
151
|
+
and 3.12. Issues are handled on weekends; pull requests are welcome.
|
|
152
|
+
|
|
153
|
+
## Related work
|
|
154
|
+
|
|
155
|
+
- [Klein (2007), Performing a Project Premortem (HBR)](https://hbr.org/2007/09/performing-a-project-premortem) — imagine the failure before it happens
|
|
156
|
+
- [Gawande (2009), The Checklist Manifesto](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) — checklists as error-rate reduction
|
|
157
|
+
- [IOM (1999), To Err Is Human](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system) — errors as system problems
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lesson-book"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Tuition memory for traders: a local-first, deterministic mistake ledger with situation matching — before you act, it reminds you where you paid before."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
keywords = ["trading", "journal", "mistakes", "lessons", "discipline", "cli", "local-first"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
lb = "lesson_book.cli:main"
|
|
26
|
+
lesson-book = "lesson_book.cli:main"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
|
30
|
+
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Tuition memory: a local-first, deterministic mistake ledger.
|
|
2
|
+
|
|
3
|
+
Record what each mistake cost you, tag it, and let ``match`` remind you of
|
|
4
|
+
it the next time the same situation shows up — before you act. No LLM, no
|
|
5
|
+
cloud, no statistics: just an append-only JSONL book and deterministic
|
|
6
|
+
scoring, so the reminder is reproducible and auditable.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .lessons import import_lessons_markdown, match_lessons, score_lesson
|
|
10
|
+
from .mistakes import (
|
|
11
|
+
add_mistake,
|
|
12
|
+
classify,
|
|
13
|
+
default_rules,
|
|
14
|
+
load_book,
|
|
15
|
+
review_day,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"add_mistake",
|
|
22
|
+
"classify",
|
|
23
|
+
"default_rules",
|
|
24
|
+
"import_lessons_markdown",
|
|
25
|
+
"load_book",
|
|
26
|
+
"match_lessons",
|
|
27
|
+
"review_day",
|
|
28
|
+
"score_lesson",
|
|
29
|
+
]
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Command-line interface for lesson-book.
|
|
2
|
+
|
|
3
|
+
Subcommands:
|
|
4
|
+
|
|
5
|
+
- ``add`` record a mistake (classified via the rule table)
|
|
6
|
+
- ``import-lessons`` parse a ``##``-style markdown knowledge base into the book
|
|
7
|
+
- ``match`` rank lessons relevant to a current situation (pre-action)
|
|
8
|
+
- ``review`` daily review markdown of one day's mistakes
|
|
9
|
+
- ``version`` print version
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from . import __version__
|
|
20
|
+
from .lessons import import_lessons_markdown, match_lessons
|
|
21
|
+
from .mistakes import add_mistake, review_day
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _print_json(body: Any) -> None:
|
|
25
|
+
print(json.dumps(body, ensure_ascii=False, indent=2))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
prog="lb",
|
|
31
|
+
description="Tuition memory: a local-first, deterministic mistake ledger.",
|
|
32
|
+
)
|
|
33
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
34
|
+
|
|
35
|
+
add = sub.add_parser("add", help="record a mistake")
|
|
36
|
+
add.add_argument("--book", required=True, help="JSONL book path")
|
|
37
|
+
add.add_argument("--title", required=True)
|
|
38
|
+
add.add_argument("--issue", required=True)
|
|
39
|
+
add.add_argument("--error-category", default=None)
|
|
40
|
+
add.add_argument("--date", default=None)
|
|
41
|
+
add.add_argument("--code", default="")
|
|
42
|
+
add.add_argument("--industry", default="")
|
|
43
|
+
add.add_argument("--volatility", type=float, default=None)
|
|
44
|
+
add.add_argument("--market-cap", default="")
|
|
45
|
+
add.add_argument("--tags", default="", help="comma-separated tags")
|
|
46
|
+
add.add_argument("--cost", type=float, default=None)
|
|
47
|
+
add.add_argument("--lesson", default="")
|
|
48
|
+
add.add_argument("--situation", default="")
|
|
49
|
+
|
|
50
|
+
imp = sub.add_parser("import-lessons", help="import a markdown knowledge base")
|
|
51
|
+
imp.add_argument("--from", dest="from_path", required=True, help="markdown lessons file")
|
|
52
|
+
imp.add_argument("--book", required=True, help="JSONL book path")
|
|
53
|
+
|
|
54
|
+
match = sub.add_parser("match", help="match the current situation against the book")
|
|
55
|
+
match.add_argument("--book", required=True)
|
|
56
|
+
match.add_argument("--code", default="")
|
|
57
|
+
match.add_argument("--industry", required=True)
|
|
58
|
+
match.add_argument("--volatility", type=float, required=True)
|
|
59
|
+
match.add_argument("--market-cap", default="")
|
|
60
|
+
match.add_argument("--tags", default="", help="comma-separated tags")
|
|
61
|
+
match.add_argument("--top-n", type=int, default=3)
|
|
62
|
+
match.add_argument("--days-window", type=int, default=None)
|
|
63
|
+
|
|
64
|
+
review = sub.add_parser("review", help="daily review of one day's mistakes")
|
|
65
|
+
review.add_argument("--book", required=True)
|
|
66
|
+
review.add_argument("--day", required=True, help="YYYY-MM-DD")
|
|
67
|
+
review.add_argument("--out", default=None, help="output directory for the markdown")
|
|
68
|
+
|
|
69
|
+
sub.add_parser("version", help="print version")
|
|
70
|
+
return parser
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _split_tags(value: str) -> tuple[str, ...]:
|
|
74
|
+
return tuple(tag.strip() for tag in value.split(",") if tag.strip())
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main(argv: list[str] | None = None) -> int:
|
|
78
|
+
parser = build_parser()
|
|
79
|
+
args = parser.parse_args(argv)
|
|
80
|
+
|
|
81
|
+
if args.command == "version":
|
|
82
|
+
print(__version__)
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
if args.command == "add":
|
|
86
|
+
record = add_mistake(
|
|
87
|
+
args.book,
|
|
88
|
+
title=args.title,
|
|
89
|
+
issue=args.issue,
|
|
90
|
+
error_category=args.error_category,
|
|
91
|
+
date=args.date,
|
|
92
|
+
code=args.code,
|
|
93
|
+
industry=args.industry,
|
|
94
|
+
volatility=args.volatility,
|
|
95
|
+
market_cap=args.market_cap,
|
|
96
|
+
tags=_split_tags(args.tags),
|
|
97
|
+
cost=args.cost,
|
|
98
|
+
lesson=args.lesson,
|
|
99
|
+
situation=args.situation,
|
|
100
|
+
)
|
|
101
|
+
_print_json(record)
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
if args.command == "import-lessons":
|
|
105
|
+
result = import_lessons_markdown(args.from_path, args.book)
|
|
106
|
+
print(f"import: {result['imported']} imported, {result['skipped']} skipped")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
if args.command == "match":
|
|
110
|
+
matches = match_lessons(
|
|
111
|
+
args.code,
|
|
112
|
+
args.industry,
|
|
113
|
+
args.volatility,
|
|
114
|
+
market_cap=args.market_cap,
|
|
115
|
+
tags=_split_tags(args.tags),
|
|
116
|
+
top_n=args.top_n,
|
|
117
|
+
path=args.book,
|
|
118
|
+
days_window=args.days_window,
|
|
119
|
+
)
|
|
120
|
+
if not matches:
|
|
121
|
+
print("match: no relevant lessons (this situation is new tuition)")
|
|
122
|
+
return 1
|
|
123
|
+
_print_json({"matches": matches})
|
|
124
|
+
return 0
|
|
125
|
+
|
|
126
|
+
if args.command == "review":
|
|
127
|
+
payload = review_day(args.book, args.day, out_dir=args.out)
|
|
128
|
+
_print_json(payload)
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
parser.error(f"unknown command: {args.command}")
|
|
132
|
+
return 2
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
sys.exit(main())
|