Skip to content

Second opinion from free models, maker limit orders in the simulated portfolio - #22

Merged
botta0oss merged 1 commit into
mainfrom
claude/lucid-lovelace-h1c8tm
Sep 26, 2026
Merged

botta0oss merged 1 commit into
mainfrom
claude/lucid-lovelace-h1c8tm

Conversation

@botta0oss

Copy link
Copy Markdown
Owner

Two more changes aimed at the losses of the first simulated portfolio.

Second opinion (backend/ai/second_opinion.py)

  • When it is asked: only when a buy is possible, meaning evidence ≥ MIN_EVIDENCE and the calibrated Jev estimate is at least MIN_EDGE from the price.
  • What it sees: the free model gets the same state as Jev (question, rules, dates, news, no price). It is asked for the base rate first, then P(YES). Gemini is tried first, then Groq (SECOND_OPINION_PROVIDERS).
  • When it blocks: a buy needs the second opinion on the same side of the price as the forecast, optionally beyond it by SECOND_OPINION_MARGIN. Otherwise the economic assessment gets the blocking reason second_opinion, and the plan says Avoid with the reason.
  • When no provider answers: the forecast goes on with Jev alone, unless SECOND_OPINION_REQUIRED=true.
  • Where it shows up: stored on the prediction (second_opinion, second_opinion_provider), shown in the explanation card, included in the export.
  • Scope: multi-outcome events and the backtest do not use it.

Maker limit orders (backend/betting/orders.py, new paper_orders table)

  • Placing: with PAPER_ORDER_MODE=maker (the default), automatic buys become a limit order instead of taking the ask. The order goes one tick above the side's best bid, stays below the ask, and never goes above the assessment's maximum price. No fee.
  • Filling: a market sync that shows the side's best ask at or below the limit fills the order at its price. The bet is saved with entry = maker.
  • Ending without a fill:
    • it expires after MAKER_ORDER_TTL_HOURS (6);
    • a new forecast replaces it;
    • the closing-line guard's pause or an exclusion cancels it;
    • a market that closes against the side fills it (adverse selection).
  • Reserved money: the order's money is reserved. It is left out of available cash and counts toward exposure limits, but it is included in total value.
  • Manual bets: they still take the book and cancel a pending order on that market.
  • Endpoints: GET /portfolio/orders and POST /portfolio/orders/{id}/cancel (admin). The market economics response now includes pending_orders and maker_price.
  • Where it shows up:
    • portfolio card with pending orders, filled/expired/cancelled counts, fill rate, and savings against the book price;
    • a note on the market card;
    • an Orders sheet in the Excel export and the bets' entry column;
    • the settings page.

Tests

  • tests/test_orders.py, 12 new tests. Older tests run with PAPER_ORDER_MODE=taker, and without AI keys the second opinion is not asked. 226 passed.
  • Playwright smoke test on the portfolio, market detail and settings pages, in IT and EN: no JS errors, no missing translations.
  • Docs updated in EN and IT: method, strategy, configuration, API, development, costs, .env.example.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JLWZrfy12imc6dQRsEtFjj


Generated by Claude Code

…portfolio

- Second opinion: when a buy is possible, Gemini (then Groq) gets the same
  state as Jev (no price) and gives P(YES); buys need it on the same side of
  the price (blocking reason second_opinion). Stored with the forecast, shown
  in the explanation; SECOND_OPINION_* settings
- Maker orders: automatic buys become limit orders one tick above the bid
  (no fee), filled when a sync shows the ask at the limit, expired after
  MAKER_ORDER_TTL_HOURS, replaced by new forecasts, cancelled by the guard's
  pause or exclusions. Their money is reserved (cash, exposure). Manual bets
  still take the book. PAPER_ORDER_MODE=taker restores the old behaviour
- Orders API, portfolio card with fill rate and savings, export sheet,
  settings, docs (EN/IT), .env.example and tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLWZrfy12imc6dQRsEtFjj
@botta0oss
botta0oss merged commit 157ae80 into main Sep 26, 2026
2 checks passed
botta0oss pushed a commit that referenced this pull request Sep 26, 2026
…by text

The assertion searched the text "0.3" in the state sent to Jev, which also
matched timestamps such as 07:47:40.392993 depending on the clock: main
failed after the merge of #22. It now checks that no number in the state
equals the price and that no field is named after a price.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLWZrfy12imc6dQRsEtFjj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants