Cash to ash?
Slow down, research, decide, learn.
Weekly research for a small stock portfolio, with the trade left in your hands.
One week, on one sheet.
CashAsh runs on your own machine. Once a week it researches each holding against the thesis you wrote, proposes a few constrained actions, and sends them to your phone. You approve or reject there. The trade itself is still yours to place in your broker's app, and then to record.
The run, in order.
-
1Sync
Prices, FX, earnings dates, consensus. A failed fetch keeps last week's prices, marked stale. By default the run fires on Sunday at 18:00.
-
2Triage
One call over the whole portfolio, ranking holdings by what changed in each thesis. The ones passed over are recorded too.
-
3Research
Questions planned from your thesis, answered from evidence. A weakened thesis gets a proposed revision beside it, never a rewrite.
-
4Recommend
BUY, ADD, HOLD, TRIM, EXIT or REVIEW. Then the rules run, in code. Every refusal is kept.
-
5Report
To Telegram, with each finding, citation and refusal. A stage that failed is shown as failed.
-
6You
The next morning. Approve, reject or later. Approval reserves the cash. The trade is yours to place, then to record.
The rules are code, not a request.
A prompt asking a model to respect a limit is a suggestion. The limits are applied afterwards, in code, to whatever it proposed. These are the shipped defaults. Each one is a setting.
- No position above 20% of the portfolio.The batch is checked as a whole, so five small buys cannot do what one large one may not.
- One trade up to €100. One week up to €100.An approved buy reserves its cash until it is executed or rejected.
- Only cash that is actually there funds a buy.Planned contributions are noted, never spent.
- EXIT needs a condition you wrote down to have happened.A bad week is not one. Anything less becomes TRIM.
- A price older than 4 days, or a missing FX rate, blocks any trade.A review still goes through. Money does not.
- Advice expires after 7 days.No decision can be recorded against advice a later run withdrew.
Every default lives in src/config.py beside a note on how it was chosen. main.py process prints the ones in force.
Every finding carries a label.
- sourcedCites a supplied evidence item by ID and quotes it exactly. URL and date come from the item, not the model. An unknown ID or an invented quote demotes it to unanswered.
- backgroundWhat the model brought with it. Never enough on its own to move money.
- unansweredStays unanswered, in storage and on your phone. An open question is a finding.
A quote check proves the excerpt exists, not that the conclusion follows. That part is still reading.
Your thesis is yours. Research that finds it weakened writes a proposed version beside it and stops. The baseline the system is judged against stays the one you wrote.
It will probably cost more than it earns.
The portfolio this was built for is small enough that a weekly pipeline over several models can cost more in a year than any excess return it could earn. That is the accepted trade: a better research habit and an interesting system, not a return.
So models route cheap by default, and the expensive ones are opt-in and metered. Nothing a model says carries investment authority. Its stated confidence is not a probability and is never stored as one.
The evals measure the process, because that is what can be measured here:
- the output has the shape it was asked for;
- every claim that says it has a source actually has one;
- the rules were obeyed, and each refusal was recorded;
- no figure was invented;
- a rerun gives the same answer.
Whether the advice was good is not measurable at this scale. Outcomes are recorded beside the same money left in a FTSE All-World (accumulating) tracker. Recorded, never claimed.
Start from a screenshot of your broker.
Type in what your broker shows: ticker, quantity, value, blended return. Cost basis is derived from those and the total is reconciled against the broker's figure, so a typo cannot become your cost basis. The file stays in your home directory, never in the repository.
# one profile, one snapshot, one database
uv run python main.py profile add adam --telegram-id 123456789 --operator
cp seed_snapshot.example.toml ~/Documents/cash-ash/profiles/adam/seed_snapshot.toml
uv run python main.py init --dry-run # does it reconcile?
uv run python main.py init
# then, each week
uv run python main.py weekly --telegram
Every command, the weekly cycle and the evidence format are in the docs. What is not built yet is on the roadmap.