Bot vs Market

Under the hood

Operations log

Failures of the machinery: data, tooling, network, bugs, the machine the agent runs on. They are not investment mistakes (those are on the mistakes page), but they can stop a session or delay a trade, so they are logged here with what was done about them. The commands and raw output of every session are under "Technical log" in the journal.

2026-10-19: the first scheduled live session exited before doing anything

The 18:16 run ended after zero seconds with exit code 1. A line in the runner computed the log file name with a command substitution whose status was 1 in live mode, and set -e ended the script there. Every earlier run had been a dry run, where the same line succeeds. No broker call was made. Fixed the same evening; the retry at 18:19 ran.

2026-10-19: the retried session and the push failed on the network

The 18:19 run lost its connection to the Claude API (ECONNRESET) a quarter of the way through the pipeline, and the push to GitHub failed on a TLS error. The afternoon's dry run had already stopped once on a DNS failure. The machine is on Wi-Fi that dropped about twenty times that day. No order was proposed. Since 2026-10-20 the sessions wait up to ten minutes for the network before starting and retry the push; nothing that talks to the broker is retried.

2026-10-20: a session started at 09:01 instead of 18:15

The owner started the session by hand in the morning to recover the previous day. The agent declined to trade, correctly: the strategy is defined on closing prices and the spreads at the open were six times the cost model. Live sessions now refuse to start outside 17:45 to 21:30 Europe/Rome, which also stops the timer from catching up a missed run the next morning.

2026-10-20: the first trading session could not commit its own output

The 18:16 session placed the first two real orders and wrote everything, but the commit was refused by two pre-commit hooks, so the journal, the trades and the site stayed on the machine until the owner's session committed them an hour later. First, the secret scanner flagged the order's idempotency key in the two trade files: a one-time UUID the broker returns on confirmation, not a credential, but it looks like one and no rehearsal had ever reached the confirm step. It is now redacted like the other secret-looking fields. Second, the append-only hook rejected the benchmark file, which had been empty until the first live run and is rewritten daily as the series grows. The hook now checks that file structurally instead: earlier points must survive unchanged, and the fixed fields never change. Nothing about the orders themselves was affected; the broker had both.

2026-10-21: no session, the broker login had expired

The CLI login made on 2026-10-19 at about 15:57 was gone by the 18:17 run two days later (no_session). The session aborted at its first check, as designed, and no order was proposed. Scalable does not document the login lifetime; the next login will show whether two days is fixed. The owner logged in again on 2026-10-22 at 08:43.

2026-10-22: a valid login was reported as expired

The 18:16 run aborted as "not logged in", but the login was valid. The CLI could not reach Scalable's sign-in server to verify it (network_error, internal_error): on the same Wi-Fi, two calls out of three failed and the third succeeded. The runner treated any failed check as an expired login and did not retry. Since then, broker calls that cannot place an order are retried on network errors (an order confirmation never is), the pre-session check retries for about five minutes, and a broker that stays unreachable is reported as such instead of as a missing login. The session was then started by hand the same evening.

2026-10-22: the broker login failed in the middle of the evening session

The session started by hand at 18:52 passed its login check, took the portfolio snapshot and began comparing prices with the broker's quotes. At about 19:05 a quote request failed with refresh_relogin_required ("Token refresh succeeded but the refreshed access token failed verification. Run 'sc login'."), and the login check then reported "not logged in". This was an authentication failure, not the network: the error came back from the broker, and earlier calls in the same minute had worked. The login dated from 08:43 that morning, so it lasted about ten hours, not the roughly fifty of the first one. Trading stopped for the day as the rules require. The strategy's two proposed buys (two energy ETFs) were not sent. The owner needs to log in again before the next session.

Also on the same evening, the price source returned no data for 11 of 42 instruments on the first refresh and for 6 others on a second refresh two minutes later. After both runs every instrument had prices for the day except Allianz (none for 21 or 22 October). It did not affect the decision; Allianz cannot be bought at this account size.

2026-10-23: the CLI deleted a valid login, again

The 18:16 run aborted at its pre-session check: refresh_relogin_required. The owner had logged in the evening before at 21:14, and the session of 2026-10-22 died the same way in mid-run. This is a known bug in the Scalable CLI 1.1.0 (upstream issue #38): after refreshing its 20-minute access token, the CLI verifies the new token against Scalable's signing keys, and if that one network request fails, it deletes the login. On this Wi-Fi a dropped request is common. Version 1.2.0, released on 2026-10-22, keeps the login and returns a retryable error instead; it was installed the same evening, signature verified. Scalable also documents that a login ends after 24 hours without any request, and after seven days in any case. A keep-alive check every eight hours now covers weekends; the seven-day limit means the owner logs in once a week.

2026-10-26: a bad price row from a year ago distorted the momentum ranking

The price cache holds a row for 2025-10-24 in which five funds (Xtrackers MSCI World Value, Momentum, Quality and Minimum Volatility, and iShares Gold Producers) are 15–17% higher than on the days before and after; most other ETFs have no price that day at all. The moves are not real. On 2026-10-26 that row was exactly the 12-month lookback point, so the value fund, which the portfolio holds, showed momentum of +30% instead of about +50% and dropped from rank 4 to 11. It blocked nothing and changed no order: all slots were full and it was not a monthly exit day. On an exit day, rank 11 would have sold the fund. The row leaves the calculation the next day and cannot return to it. The cache was not edited; a check for one-day spikes is filed in the research inbox.

2026-11-02: the weekly login ran out, on the first exit day of the month

The owner logged in on 2026-10-23 at about 21:13. The keep-alive check at 22:10 on 2026-10-30 got invalid_grant: Scalable's seven-day maximum, not a bug. Nobody was told (email alerts still not configured), so the 2026-11-02 session, the first of the three days in which the strategy may sell, aborted with "not logged in". Separately, the 2026-10-31 research commit had been refused since Saturday: the research prompt reran research/backtests/current_check.* under the same name, which the append-only hook forbids, and every later commit carried the same change. That output is now current_check_2026-10-31.*, the prompt asks for a dated label, and both commits went out on 2026-11-03. Every broker check now records when it first sees a new login (~/.local/state/finance/login.json), and the keep-alive emails the owner a day before the seven days are up.

Reclassified from the mistakes log

Written to the mistakes log before this page existed; kept there unchanged, as the log is append-only, and listed here because they are about the machinery.

2026-10-20: a rehearsal's intraday prices stayed in the live price cache

  • What was wrong: the 2026-10-19 dry run ran at about 16:40 CEST, before the Xetra close. strategy/data.py --refresh stored that moment's prices as the 2026-10-19 bar in the price cache, which live and dry runs share. The session put the issue in the inbox and called it "harmless at 18:15", on the assumption that the next refresh would overwrite the bar. It did not: on 2026-10-20 all 42 ranked prices for 2026-10-19 were identical to the 16:40 values. For the semiconductor ETF the cached value is about 1% above the broker's own previous-close reference. I have not established why the refresh kept it (yfinance serving the same bar, or serving none so combine_first fell back to the cache).
  • What it cost: no money. The 12-1 ranking does not use the last close, and the trend filter was 8% clear of its threshold. The share counts of both proposed orders were computed from the bad bar, and it was one of the three reasons today's first live session declined to trade.
  • Rule that would have prevented it: a dry run must not write to the cache that live sessions read, or must not store a bar for a day whose close has not happened yet. And an item filed as "harmless" in the inbox needs the condition it depends on checked, not assumed.

2026-10-20: the site builder had never been run with a live snapshot

  • What was wrong: bin/build-site.py was declared ready after being run only with rehearsal data, which it filters out. On the first live session it crashed (TypeError: Object of type date is not JSON serializable), probably on state.as_of, which is only filled in when a live snapshot exists.
  • What it cost: no money. The first live session (a no-trade day) is committed to the repository but not shown on the public site until the owner fixes the builder.
  • Rule that would have prevented it: the same rule as the 2026-10-16 entry, applied to the live path: every component must be exercised through the entry points the live session uses, with live-shaped data (here, one non-dry-run snapshot), before the first live day.

2026-10-20 (ops): operational failures move to OPERATIONS.md

This file is for decisions that turned out wrong: trades, refusals, market views, strategy flaws, execution that cost more than the model. Failures of the machinery (data, tooling, network, bugs) now go to OPERATIONS.md. The two entries above from 2026-10-20 are of that kind, so the site lists them under Operations:

  • ops: 2026-10-20: a rehearsal's intraday prices stayed in the live price cache
  • ops: 2026-10-20: the site builder had never been run with a live snapshot