Repository navigation
Sharesight portfolio names, /future, holding-news search, yahoo-finance2 MCP, DARKMODE hours - #50
Merged
Merged
Conversation
… plain stand-ins Pillow's default font has no ² ³ − → € etc., so they drew as boxes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…ws the share) Falls back to shares when a value would round to zero. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…hes value legend Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…ks none The url_citation annotations were only counted; now they are kept on the Step and Answer and ask() appends a Sources line (max 3) to chat answers that carry no link of their own. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Positions from the url_citation annotations are applied to the raw reply before any formatting; the Sources line stays as the fallback when no marker could be placed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…CHAT; log user input as two words - LOG_WIDTH removed: tool-name lists are logged in full. - BOT_TZ -> TZ, HISTORY_LIMIT -> HISTORY_MAX (default 10, and a real maximum: the stepped window now holds max/2+1 .. max messages), TELEGRAM_PRELOAD_CHAT -> TELEGRAM_IMAGE_PRELOAD_CHATID. The old names are not read any more. - User input in the log is cut to its first two words and one ellipsis character; the other log-line truncations (error snippets, failed HTML) are gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…Default column Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…rs were placed Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
OpenRouter's url_citation positions are 0 and the search runs in an earlier round than the answer, so the position-based marker is replaced by matching the answer's sentences to each source's snippet (figures weigh most, words every snippet shares count for less). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
The chat system prompt (OpenRouter only) tells the model to cite a sentence that rests on a search result as [<a href=URL>n</a>]. Its links are kept only for pages the search returned and renumbered; without them the snippet-overlap markers apply. The trailing Sources line is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…ere dropped Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…n order The model cited 3 of ~10 pages the search returned; the overlap pass now runs for the rest even when the model wrote markers of its own. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Bare capitalised words (GAAP, EBITDA, AFR, GM, UPS) kept getting Yahoo links and the stoplist never ended; ASX:NXT linked to the US NXT. Replies now link only $NVDA, NXT.AX, ASX:NXT (mapped to Yahoo's suffix), Name (TICKER), Sharesight's NXT (ASX), ^GSPC and BTC-USD, plus bare words in a known set: the S&P 500 and NASDAQ-100 (from Wikipedia) and the US Sharesight holdings, refreshed daily in the background and kept in kv, never fetched while answering. A bare code in brackets links only if known, since it names no exchange. Holding news links the holdings' codes and a movers explanation the listed codes with the list's exchange. The prompt asks for these forms. The NOT_TICKERS env var is gone; the built-in list now only serves the tool gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
- An exchange suffix links on its own: ALL.AX (Aristocrat), ASX.AX, AI.PA were left plain because
the code is also on the acronym list.
- A bracket that isn't a ticker ("NVDA (Q3 FY26)") no longer swallows the ticker before it.
- Share classes: BRK.B as the model writes it links to Yahoo's BRK-B.
- "AT&T (T)" and "Procter & Gamble (PG)" are bolded whole.
- Numeric holding codes (700 on HKEX) never link bare numbers, and keep their unsubscribe button.
- Index tickers that are also finance or tech acronyms (GM, UPS, IR, HD, USB, RF...) link only in a
marked form, as single letters already did.
- A refresh part that fails (a page down, Sharesight not up yet) is tried again in 30 minutes,
not 24 hours; a footnote in the table header no longer hides the ticker column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…the ASX 200 Wikipedia answered 403 to the page fetches: its policy refuses programs whose User-Agent names no contact. The lists now come through the action API (action=parse) with the repo URL in the User-Agent, and a refusal logs Wikimedia's reason. The S&P/ASX 200 is added: its codes link bare with .AX. Holdings on any known exchange count (NXT -> NXT.AX). A code in a US index and in the ASX 200 isn't linked bare unless a holding settles it. ASX codes that are everyday words or acronyms (GPT, WOW, ZIP, CAR...) need a marked form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
The "Nasdaq-100" article has no constituents table any more (0 tickers in production). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…gle Finance Google takes CODE:EXCHANGE (NXT.AX -> NXT:ASX, 0700.HK -> 700:HKG); a US ticker names no exchange (NASDAQ or NYSE), so it opens a Google Finance search. History compaction and the holding-news mute codes read either provider's links back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Google Finance quotes are CODE:EXCHANGE (JPM:NYSE, SPCX:NASDAQ, BRK.A:NYSE). A US ticker's exchange now comes from the text (NYSE:JPM, JPM (NYSE)), the Sharesight holdings, the NASDAQ-100 (all NASDAQ) or the S&P 500 list, whose ticker links point at nyse.com or nasdaq.com; kept in kv with the lists. Only a ticker none of these place opens a Google Finance search. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
TradingView pages are EXCHANGE-CODE (ASX-NXT, NYSE-BRK.A, NASDAQ-SPCX), with the US exchange from the same sources as Google's; a code it can't place opens TradingView's page for the bare code. The three sites' URL builders leave tickers.py (near its size limit) for their own module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Shows what the daily holding-news check (and movers, DM buttons, chat commands, filings) costs next to chat; every request was already recorded with its kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
/tokens and /credits still work. Telegram shows the menu commands in autocomplete, so admins now see /usage there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Each recipient used to get a separate searched request, so a stock two people hold was searched twice and every request paid its own overhead (holding news was the biggest cost in /usage). The daily check now asks once for the union of everyone's unmuted holdings, with everyone's recent items as the don't-repeat list, then gives each person the items about their own holdings; an item naming no holding goes to everyone. /holdingnews still checks just the asker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
… articles SHARESIGHT_HOLDING_NEWS_SKIP_DAYS (e.g. sun,mon) skips the daily check on those weekdays. Each check now searches back to the last one that got an answer (kv; at least 24 h, at most 7 days), so skipped days, a busy provider or an outage leave no gap; the prompt and the message header say how far (past 72h). Repeats: the don't-repeat list always reaches past the last check, and an item citing an article already sent is dropped whatever the model says. The feature becomes a package (holding_news/ with text.py) to stay under the module size limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
The old name is no longer read: rename it in the service's environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…AMES The daily known-ticker refresh now reads every portfolio list_portfolios returns (skipping consolidated views, which repeat the others): their holdings feed the known tickers and their names route portfolio questions in the gate. Both are kept in kv, so a restart has them at once. The PORTFOLIO_NAMES setting is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…arts Two charts from Yahoo's get_analyst_estimates (revenue, EPS), each with Low, Average and High lines fanning out from last fiscal year's actual through this and next fiscal year; the axis is labelled with real fiscal years from the latest annual statement. Drawn with the existing make_chart, no model. History uses the estimates' own year-ago figures, so EPS stays on the analysts' (adjusted) basis rather than mixing in GAAP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
…earch The combined daily check searches every holding in one request, and OpenRouter's search tool capped the whole request at 20 results, so it ran out part-way (and said so in a "Note:" bullet sent to everyone). Requests can now ask for a larger allowance (Request.search_results; OpenRouter's max_total_results) and the daily check asks for 60. ETFs and funds, found by name, are left out of the search. The prompt asks for news items only, and lines about the search are dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
The Yahoo server is now yahoo-finance2's own (npx -y -p yahoo-finance2 yahoo-finance-mcp): generic tools (chart, quote, quoteSummary, fundamentalsTimeSeries, search, ...) returning raw yahoo-finance2 JSON. The old tool names are gone, so what called them by name moves over: - Ticker charts and their period buttons fetch prices with `chart` (period1 from the period; times in the exchange's own time zone; 1D and 5D keep the last trading days, so a weekend isn't empty). The fetch passes `_raw`, a new internal flag (no slimming, no size cap, stripped from the model's calls like `_keep_sales`), so a day of 5-minute bars isn't thinned to 60 points. - /future reads quoteSummary's earningsTrend; the fiscal-year labels come from its end dates (a June year is right for ASX names), so the statements call is gone. - Quotes are trimmed to the fields a model uses (about 870 characters each instead of 3,700), so a ten-stock quote fits one result instead of being cut after three. - mcp_servers.json: the new command, an allowed_tools list (quote, quoteSummary, chart, fundamentalsTimeSeries, search: names that don't look read-only, and the rest left out), moduleOptions hidden, and a description naming the queryOptions the model needs. The Yahoo part of the prompt drops from about 1,120 to 725 tokens. - Fixtures are real output from the new server; tools/capture.py and the docs follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
A wrong URL, a refused, failed or unauthorised connection, a missing command or an MCP error is logged as "MCP server yahoo failed: HTTP 401 Unauthorized from http://127.0.0.1:3000/mcp", without the SDK's nested ExceptionGroup traceback (httpx's "For more information" line and a local server's stderr are folded into the same line). Anything else still gets its traceback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Besides off, on and auto (9 pm to 7 am), DARKMODE can now be a range in TZ, in 24-hour or am/pm form, with or without minutes; it may run past midnight and ends just before its last time. Settings.darkmode is now that range as minutes of the day (None = off), and the chart checks the minute, not just the hour. A value that isn't a range (18, 25-6, 6-6) stops startup with the accepted forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
The dark hours are now a range you set (21-7 is what auto was); auto is refused at startup with the accepted forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
robdevops
pushed a commit
that referenced
this pull request
Nov 11, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Portfolio names and holdings from Sharesight itself: drop PORTFOLIO_NAMES
PORTFOLIO_NAMESfed the tool gate and the known-ticker holdings; both now come from Sharesight'slist_portfolios(every portfolio except consolidated views), refreshed daily and kept in kv. The env var is gone./future TICKER
Analysts' low/average/high revenue and EPS for this and next fiscal year, as two line charts drawn without the model.
Holding news: search allowance
One combined request with a larger web-search allowance (60 results), ETFs and funds skipped, and the model's notes about its search dropped.
Move to the yahoo-finance2 MCP server
chart, in the exchange's own time zone. 1D/5D show the last trading days. A new internal_rawflag (no slimming or cap, stripped from the model's calls) keeps the bot's own fetch at full resolution.quoteSummaryearningsTrend. Fiscal-year labels come from its end dates.mcp_servers.json:allowed_tools(quote, quoteSummary, chart, fundamentalsTimeSeries, search).moduleOptionsis hidden from the model.MCP connection failures in one log line
A bad URL, a refused or unauthorised connection, a missing command or an MCP error is logged as one line, e.g.
MCP server yahoo failed: HTTP 401 Unauthorized from http://127.0.0.1:3000/mcp. Anything else keeps its traceback.DARKMODE takes the dark hours
off(default),on, or a range inTZ:18:00-6:00,18-6,6pm-6am. A range may run past midnight.autois removed: use21-7for the old behaviour.🤖 Generated with Claude Code
https://claude.ai/code/session_014k4kywyN5dAo39izUPvxbw