Commit Graph
100 Commits
Author SHA1 Message Date
santhreal 0157409f0d fix(options-engine): tolerate malformed trade PnL and disclose exclusions (#907)
Trade records carrying pnl=None or a non-numeric value raised TypeError
while computing trade statistics. Skip those records instead, and — since
trade_count still reports every record — append a warning naming how many
were excluded so win_rate and profit_loss_ratio are never silently
derived from a subset.
2026-07-30 13:01:53 +08:00
santhreal e816448e30 fix(regime): return NaN edge density when there are no asset pairs (#908)
compute_edge_density divided by n_pairs, which is zero for a zero- or
one-column frame. Correlation edge density is undefined without at least
one distinct asset pair, so return an index-aligned all-NaN series. The
timeline entrypoint already requires two fetched assets, so this does not
mask a user-facing error.

Supersedes the duplicate guard bundled in #912.
2026-07-30 13:01:53 +08:00
santhreal 58d3026171 fix(risk-xray): raise when surviving symbols have zero total weight (#909)
The input validator requires the original basket total to be positive but
permits individual zero weights, so the history filter can leave only
zero-weight survivors and the renormalization divided by zero. Raise a
clear ValueError instead: allocating capital to zero-weight survivors
would invent holdings the user does not own, and an all-zero x-ray would
contradict the fully-invested-basket contract.
2026-07-30 13:01:52 +08:00
Santh a9e50e9bc1 fix(channels): preserve leading indent when splitting messages (#867)
* fix(channels): preserve leading indent when splitting messages

After a newline cut, lstrip() removed indentation on the next chunk, so
code blocks and nested markdown lost spaces across Discord/Slack/Telegram
splits. Drop only the separator character.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(channels): keep Signal style offsets aligned after indent-preserving splits

split_message no longer blanket-lstrips; mirror that in _partition_styles so
indented chunks keep UTF-16 style ranges.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(channels): advance style cursor on space separator preceding indented chunk

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-29 15:20:08 +08:00
Santh 72f6985825 fix(skills): parse frontmatter when closing fence is at EOF (#861)
* fix(skills): parse frontmatter when closing fence is at EOF

SKILL.md / memory files that end at the closing --- without a trailing
newline (common from editors and LLM skill saves) were treated as plain
text, so name/description were dropped. Empty ---/--- blocks had the
same miss. Accept EOF or empty meta between fences.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(skills): require newline before closing frontmatter fence

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-29 15:19:35 +08:00
santhreal 5ed1eb5472 fix(journal): treat BTCUSDT-style symbols as crypto markets
Concatenated pairs like BTCUSDT are alphabetic, so isalpha() labeled them
as US equities. Recognize USDT/USDC/BUSD suffixes before that branch.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:55:00 -07:00
santhreal 0a59f57f9e fix(journal): strip currency symbols before float coerce
Broker CSV cells like $1,234.56 or ¥1000 failed float() after only commas
were stripped, so price/fee silently became 0.0. Strip common currency
tokens and unicode minus too.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:52:41 -07:00
santhreal 8a4f6260bb fix(tools): decode UTF-16 BOM text before latin-1 fallback
Notepad/Excel Unicode text exports start with a UTF-16 BOM; latin-1 never
raises so those bytes were accepted as mojibake. Detect BOM and decode as
utf-16 first.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:50:16 -07:00
santhreal 34a9fd4302 fix(metrics): annualize lowercase 1h/1d and yahoo like loaders
After loaders started accepting 1h/4h/1d, calc_bars_per_year still keyed
only 1H/1D, so hourly runs were annualised as daily (Sharpe/vol wrong).
Normalize interval case and alias yahoo→yfinance / binance→ccxt.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:48:25 -07:00
santhreal fe04869194 fix(skills): keep CJK characters in skill directory slugs
Non-ASCII skill names such as 动量策略 were scrubbed to "--", so distinct
Chinese names collided into one directory and overwrote each other.
Allow the same non-Latin ranges as persistent memory, and hash when the
slug would otherwise be only hyphens.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:46:55 -07:00
santhreal b13243afd6 fix(journal): accept UTF-16 BOM CSV exports in load_dataframe
Excel "CSV UTF-16" / Unicode CSV writes a BOM that utf-8/gbk cannot
decode, so analyze_trade_journal rejected the file. Try utf-16 in the
encoding fallback before raising.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 21:46:53 -07:00
Santh 3f7e9a1c2a fix(tools): keep empty cells when parsing report_audit tables (#807)
Dropping blank cells shifted later values onto earlier column headers.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:31:32 +08:00
Santh e109ae69c8 fix(channels): keep empty edge columns in Slack md tables (#801)
str.strip('|') dropped leading/trailing empty cells, so Slack table
conversion mislabeled row-label columns and dropped trailing cell values.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:31:25 +08:00
Santh 8374fc4cf1 fix(channels): keep empty edge columns in Signal md tables (#800)
str.strip('|') dropped leading/trailing empty cells in pipe tables, so
row-label columns and trailing blanks collapsed when rendering for Signal.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:31:21 +08:00
Santh 1b6420d804 fix(channels): keep empty edge columns in Telegram md tables (#799)
str.strip('|') dropped leading/trailing empty cells in pipe tables, so
row-label columns and trailing blanks collapsed when rendering for Telegram.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:31:17 +08:00
Santh e04c08bf79 fix(connectors): map MT5 1H/4H/1D periods to hour/day bars (#806)
Project-style period tokens were falling through to daily TIMEFRAME_D1.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:29:40 +08:00
Santh 96f7bc58b4 fix(connectors): map Binance 1H/4H periods to ccxt timeframes (#805)
Project-style hour tokens were passed raw to ccxt, which expects lowercase unified timeframes.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:29:37 +08:00
Santh 7b6d219a53 fix(loaders): keep lowercase 1h/4h/1w as ccxt hour/week bars (#804)
* fix(loaders): keep lowercase 1h/4h/1w as ccxt hour/week bars

Lowercase project tokens previously missed _INTERVAL_MAP and fell through to silent daily bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* style(test): drop narrating comment on ccxt interval aliases

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:29:33 +08:00
Santh 457ebebdaa fix(connectors): reject unsupported Dhan history periods (#803)
Unknown periods such as 1H were silently served as daily candles. Fail
closed with an explicit unsupported-period error instead.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:29:29 +08:00
Santh 4e8271a0b9 fix(connectors): map Futu 1H/4H periods to hour bars (#802)
Project-style 1H fell through to daily K lines, and 4h incorrectly mapped
to K_60M instead of K_240M.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:29:26 +08:00
Santh dfdbefd0ea fix(journal): normalize Excel serials in generic datetime columns (#810)
Generic CSV/XLSX loads stringify serial day numbers, which then failed
to parse and became NaT in the standardized journal frame.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:28:54 +08:00
Santh b71427a829 fix(journal): normalize stringified Excel serials in Futu dates (#809)
load_dataframe reads CSV/XLSX as strings, so Date serials never hit the
numeric Excel path and stayed as raw "45321.0".

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:27:57 +08:00
Santh ebc1ff8d85 fix(journal): normalize stringified Excel serials in Tonghuashun dates (#808)
load_dataframe reads CSV/XLSX as strings, so serial day numbers never
hit the numeric Excel path and stayed as raw "45321.375".

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-25 22:27:08 +08:00
Santh 830122cffa fix(journal): normalize Excel day-count serials in Eastmoney dates (#811)
load_dataframe reads cells as strings, so day-count serials like
"44941.0" skipped the YYYYMMDD float path and stayed as raw date text.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:59 +08:00
Santh e5a99c778e fix(loaders): map Yahoo 4H to 1h like yfinance (#812)
Yahoo chart has no 4h interval; project 4H was lowercased to 4h and
sent unsupported to the API. Match yfinance's 4H → 1h approximation.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:55 +08:00
Santh 6d9079c162 fix(loaders): reject unsupported BaoStock history intervals (#813)
Docs say daily-only, but 1H/4H still logged in and returned frequency=d
bars. Reject non-daily intervals before login (tencent/fmp parity).

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:50 +08:00
Santh 9fdb316f3a fix(loaders): reject unsupported Alpha Vantage history intervals (#814)
TIME_SERIES_DAILY is daily-only, but 1H/4H still hit the API and
returned day bars. Reject non-daily intervals before the request.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:43 +08:00
Santh 960f595f31 fix(loaders): reject unsupported Finnhub history intervals (#823)
Finnhub always called resolution=D even for 1H/4H, so intraday runs
silently got day bars and stopped the fallback chain.

Reject unsupported intervals like the other daily-only loaders.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:38 +08:00
Santh bcdaaf5e8f fix(loaders): reject unsupported Tiingo history intervals (#824)
Tiingo always hit the daily EOD endpoint even for 1H/4H, so intraday
runs silently got day bars and stopped the fallback chain.

Reject unsupported intervals like the other daily-only loaders.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:33 +08:00
Santh eac1e92755 fix(loaders): reject unsupported Stooq history intervals (#825)
Stooq always fetched the daily CSV even for 1H/4H, so intraday runs
silently got day bars and stopped the fallback chain.

Reject unsupported intervals like tencent/fmp/india-broker.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:28 +08:00
Santh 4ab478cb56 fix(loaders): map OKX lowercase 1h/4h instead of silent daily (#826)
Connector-style 1h/4h fell through the uppercase-only allowlist and were
substituted with 1D. Map the aliases like the OKX connector bar map, and
reject unknown intervals instead of silently fetching daily bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:24 +08:00
Santh e14e627d9b fix(loaders): treat QVeris 1m/5m/15m/30m as intraday granularity (#827)
Lowercasing first left 1m/5m/15m/30m with empty wanted tokens, so daily
OHLCV capabilities outranked minute ones. Match minute tokens
case-sensitively and keep 1M as monthly.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:17 +08:00
Santh a4e9222235 fix(loaders): reject non-daily intervals for AKShare US/HK/ETF/forex (#828)
* fix(loaders): reject non-daily intervals for AKShare US/HK/ETF/forex

A-share already raised on unsupported intervals, but US/HK/ETF/forex
always called the daily endpoints, so interval=1H returned day bars
under a 1H cache key and stopped the fallback chain.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(loaders): tighten AKShare daily-only interval gate comment

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:12 +08:00
Santh c3b284055f fix(loaders): accept lowercase 1h/1d in India broker interval map (#829)
Connector-style 1h/1d were rejected while project 1H/1D mapped to the
same broker tokens, so market_data and mixed-vocabulary callers got
empty frames for valid hour/day bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:07 +08:00
Santh bca8ecf91b fix(loaders): accept lowercase 1h/4h/1d in Futu interval map (#830)
Connector-style hour/day tokens raised unsupported while 1H/4H/1D
already mapped to K_60M/K_240M/K_DAY, so mixed-vocabulary callers lost
intraday Futu history.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:12:02 +08:00
Santh be32143bc4 fix(loaders): accept daily aliases in FMP interval gate (#831)
* fix(loaders): accept daily aliases in FMP interval gate

The gate compared interval != \"1D\" exactly, so connector-style 1d/day
returned empty while sina/tencent treat those as daily.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(loaders): drop narrating FMP daily-alias gate comment

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:57 +08:00
Santh ae1cb2d19e fix(loaders): accept lowercase 1h/4h in local resample rules (#832)
Connector-style 1h/4h missed _RESAMPLE_RULES and returned native bars
unchanged, while 1H/4H correctly downsampled.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:52 +08:00
Santh c4e73777fe fix(loaders): map yfinance lowercase 4h/1w like 4H/1W (#833)
Lowercase 4h/1w fell through to .lower() as invalid Yahoo tokens while 4H/1W already mapped to 1h/1wk.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:47 +08:00
Santh 211baefbf9 fix(loaders): accept lowercase 1d/1w in Longbridge interval map (#834)
Loader rejected 1d/1w while the connector already maps them to Day/Week.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:43 +08:00
Santh 32290a22c8 fix(loaders): map MT5 1W/1M and reject unknown intervals (#835)
* fix(loaders): map MT5 1W/1M and reject unknown intervals

Unknown intervals silently fell back to daily bars; week/month tokens missed the map while the connector already exposes W1/MN1.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(loaders): tighten MT5 unknown-interval reject comment

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:39 +08:00
Santh 7d4767c96d fix(loaders): accept lowercase 1d/1w in AKShare daily map (#836)
1d/1w missed the daily period map while 1D/1W already mapped to daily/weekly.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:33 +08:00
Santh 6c743bb4d2 fix(loaders): treat lowercase 1d as Tushare daily bars (#837)
* fix(loaders): treat lowercase 1d as Tushare daily bars

interval=1d missed the exact 1D daily gate and fell into the minute path, which returned empty.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>

* fix(loaders): tighten Tushare daily-alias gate comment

---------

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:29 +08:00
Santh 6d5c1678a6 fix(loaders): accept lowercase 1d/1w in Eastmoney KLT map (#838)
1d/1w missed KLT_BY_INTERVAL while 1D/1W already mapped to daily/weekly codes.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-24 22:11:24 +08:00
santhreal 0f7983e595 fix(journal): treat numpy int serials as Excel days in Futu Date
iterrows yields np.int64 for int64 Date columns; use is_number so Excel
day serials convert instead of stringifying or ns-epoch parsing.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:44:46 -07:00
santhreal b11ccc7c8b fix(journal): treat numpy int serials as Excel days in Tonghuashun
iterrows yields np.int64 for int64 成交时间 columns; isinstance(int)
missed them and bare pd.to_datetime treated the value as ns-epoch.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:44:43 -07:00
santhreal ae435887ee fix(journal): normalize Tonghuashun Excel serial 成交时间 to ISO
同花顺 exports opened via Excel leave 成交时间 as a float serial; stringifying
it produced values like "45321.375" instead of a real timestamp. Convert
Excel day serials with pd.to_datetime before storing datetime.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:39:35 -07:00
santhreal b7b942fb1d fix(journal): normalize Futu Excel serial Date/Time to ISO
Futu exports opened via Excel leave Date/Time as float serials; stringifying
them produced values like "45321.0 0.375" instead of a real timestamp.
Convert Excel day serials with pd.to_datetime before building datetime.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:39:33 -07:00
santhreal 98f8544ac6 fix(loaders): accept lowercase 1h/4h/1d MT5 intervals
The MT5 loader only listed 1H/4H/1D, so trading-convention 1h fell through
to the daily fallback while the MT5 connector already maps 1h to H1.
Add the lowercase aliases.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:39:32 -07:00
santhreal 64b751503e fix(longbridge): accept project-style 1H/4H period tokens
Connector _candlestick_enums only listed lowercase 1h/4h, so runner-style
1H fell through to Day while the Longbridge loader already maps 1H to
Min_60. Align the connector map with the loader.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:39:30 -07:00
santhreal 5e5c5bb7de fix(shoonya): map 4h/1H periods and reject unknown intervals
Documented connector periods 4h/1H fell through to daily D. Map them to
Noren 240/60 minute candles and return an error for unsupported tokens
such as 1w/1M instead of silently substituting daily bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-22 12:39:29 -07:00
santhreal a30271ddf9 merge(main): sync before thermo hold 2026-07-22 12:21:58 -07:00
santhreal 2dddec63f4 merge(main): sync before thermo hold 2026-07-22 12:21:57 -07:00
santhreal 9d05d5856e merge(main): sync before thermo hold 2026-07-22 12:21:31 -07:00
santhreal c2da481bff merge(main): sync before thermo hold 2026-07-22 12:21:29 -07:00
santhreal 98038eeae7 merge(main): sync before thermo hold 2026-07-22 12:21:27 -07:00
santhreal 74cdab3b28 merge(main): sync before thermo hold 2026-07-22 12:21:26 -07:00
santhreal a88de6caab merge(main): sync before thermo hold 2026-07-22 12:21:24 -07:00
santhreal 805a75713c merge(main): sync before thermo hold 2026-07-22 12:21:23 -07:00
santhreal 5432364770 merge(main): sync before thermo hold 2026-07-22 12:21:21 -07:00
santhreal 298e3ec1d0 merge(main): sync before thermo hold 2026-07-22 12:21:20 -07:00
santhreal c671be6fba merge(main): sync before thermo hold 2026-07-22 12:21:18 -07:00
santhreal 87cab5ffa5 merge(main): sync before thermo hold 2026-07-22 12:21:15 -07:00
santhreal 65e624ab8d fix(connectors): accept project 1H/4H periods on OKX bars
_BAR_MAP only listed lowercase 1h/4h, so runner-style 1H/4H fell
through to bar 1D and silently returned daily candles.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:27:34 -07:00
santhreal d4d4e2762b fix(connectors): accept project 1H/4H periods on Alpaca bars
_rest_timeframe only listed lowercase 1h/4h, so runner-style 1H/4H
fell through to 1Day and silently returned daily bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:27:33 -07:00
santhreal 92f0f6e35d fix(cli): keep empty edge columns when parsing markdown tables
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:26:40 -07:00
santhreal 90def21855 fix(feishu): keep empty edge columns when parsing markdown tables
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:26:39 -07:00
santhreal 3ab407d799 fix(loaders): reject unsupported akshare a-share intervals
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:26:37 -07:00
santhreal 78650f74aa fix(tiger): accept project-style 1H/4H period tokens
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:26:36 -07:00
santhreal d69be31f10 fix(loaders): map ccxt 1W/1M intervals instead of silent daily
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:26:34 -07:00
santhreal f14e701501 fix(loaders): map yfinance 1M to monthly bars, not minutes
Unmapped intervals were lowercased, so project monthly token 1M became
yfinance 1m (one minute). Map 1M/1W explicitly like the yahoo loader.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:25:16 -07:00
santhreal 19c1cbc6e4 fix(loaders): reject unsupported india broker intervals instead of daily
Unknown intervals (including runner 4H) fell through to broker period 1d,
so hourly requests silently returned daily bars. Reject and return no data.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:25:15 -07:00
santhreal 0f3a2d3aa0 fix(validation): tolerate blank holding_days cells in trades.csv
Blank holding_days become NaN under pandas and int(NaN) aborted the
validation CLI. Coerce non-numeric/blank values to 0 holding bars.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:25:13 -07:00
santhreal 9563c1a110 fix(tools): emit JSON-safe diffs when report audit hits a zero reported value
When reported_value is 0 and fetched_value is not, verdict used float('inf')
and json.dumps emitted non-RFC Infinity tokens. Record null percents instead
and serialize with allow_nan=False.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:25:12 -07:00
santhreal 4c6f23cb25 fix(journal): normalize Excel float YYYYMMDD dates in eastmoney parser
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:19:15 -07:00
santhreal 2b01bf46d0 fix(journal): normalize float-stringified A-share codes before qualifying
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:19:15 -07:00
santhreal 91c7cb23da fix(journal): normalize Excel float-stringified A-share codes
Numeric CSV/Excel cells often stringify as 600519.0; the '.' made
_qualify_a_share treat them as already-qualified and skip .SH/.SZ.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:18:35 -07:00
santhreal ab3e8076d6 fix(tools): accept unicode dashes in PDF page ranges
Word/LLM paste often uses en/em/minus dashes in ranges like 1–10;
those were silently dropped because only ASCII '-' was recognized.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:18:22 -07:00
santhreal c0f794c2c9 fix(api): treat export KEY= as KEY in dotenv settings helpers
Shell-style export KEY=value lines were read as a literal key named export KEY, so Settings missed configured secrets and upserts appended a duplicate KEY= instead of updating the export line. Strip the export prefix on read/write like python-dotenv.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:17:28 -07:00
santhreal e76e1dc6fb fix(factors): keep single-day IC std finite in factor analysis JSON
One IC observation makes Series.std() (ddof=1) NaN, which leaked into ic_summary.json and the tool envelope. Treat single-sample std as 0.0 (same guard as metrics vol) and dump with allow_nan=False.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:17:25 -07:00
santhreal d88a591272 fix(backtest): print scalar metrics as strict JSON
Explosive equity paths can set annual_return to +inf. The engine stdout
dump used default allow_nan=True and emitted bare Infinity tokens.
Sanitize non-finite scalars to null and dump with allow_nan=False,
matching options and run_card.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:16:54 -07:00
santhreal 3074b684e1 fix(metrics): keep Sharpe/Sortino finite when equity hits zero mid-path
A 100 → 0 → 50 equity path yields non-finite pct_change values that
poisoned Sharpe/Sortino/IR with NaN/inf. Skip those ratios (report 0)
when bar returns are non-finite, matching the options metrics path.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:16:52 -07:00
santhreal bd610d0b67 fix(pattern): emit strict JSON when trend_slope mean is undefined
Halt-style NaN gaps can leave every polyfit window empty, so mean_slope
became NaN and leaked a bare NaN token into the tool JSON. Treat that as
0.0 and serialize with allow_nan=False, matching options_pricing.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:16:48 -07:00
santhreal afd0046cbd fix(session): skip schema-bad messages.jsonl lines when reading
get_messages already skipped JSONDecodeError lines but still raised on
valid JSON that was not a Message mapping (null, extra keys). Skip those
too so later good lines remain readable.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:13:47 -07:00
santhreal 0b9df59cfe fix(session): skip corrupt session.json when listing sessions
One unreadable or schema-invalid session.json raised during list_sessions
and aborted the whole listing; skip that entry and keep siblings.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:13:46 -07:00
santhreal 13f71e5a6a fix(loaders): keep yahoo 1m as minute bars, not monthly
_to_yahoo_interval uppercased intervals before the map lookup, so project
interval 1m collided with monthly 1M and was sent to Yahoo as 1mo.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-21 21:13:46 -07:00
santhreal d84c00f778 fix(api): scan quoted dotenv values before stripping hash comments
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:35:01 -07:00
santhreal 1260ce4704 fix(options): clarify non-positive S/K intrinsic fallback comment
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:35:01 -07:00
santhreal 6ffdc220f4 fix(api): preserve trailing comments after closed dotenv quotes
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:24:40 -07:00
santhreal d1f83c096c fix(api): upsert last duplicate active dotenv key
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:20:47 -07:00
santhreal 3fda400b76 fix(pattern): reject window=1 for trend_line_slope
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:20:47 -07:00
santhreal be7cf96383 fix(api): unquote dotenv values before stripping hash comments
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:20:47 -07:00
santhreal b52f2edd74 fix(options): catch OverflowError in options metrics annualization
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 22:20:36 -07:00
santhreal a0546c5b19 fix(metrics): avoid OverflowError annualizing explosive equity paths
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 21:44:39 -07:00
santhreal aeff42d776 fix(options): treat non-positive spot/strike as intrinsic in BS helpers
Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 21:44:38 -07:00
santhreal a63a880c92 fix(ui): skip non-positive MA periods in indicator overlays
Planner/design artifacts can emit ma_window=0; build_indicator_series then divided by zero. Drop non-positive periods when inferring and when building series.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 20:53:14 -07:00
santhreal 497274c688 fix(channels): avoid hang splitting telegram fenced markdown chunks
Long or mid-size cuts inside an opening fence could land on the fence line and reopen the same chunk forever. Advance past the fence header and bail when reopen does not shrink the buffer.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-19 20:51:47 -07:00
santhreal a4c3652935 test(tools): fix journal filter fixture month window
Use Jan-Feb so the exclusive upper MonthBegin bound matches two rows.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-18 17:59:58 -07:00
santhreal 159bffbef9 fix(tools): reject inverted trade-journal date filters
filter_expr like "2026-03 to 2026-01" silently emptied the frame and
ran analysis on zero rows. Raise ValueError, matching period inversion.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-18 17:59:40 -07:00
santhreal b8ccf2bc3f fix(tools): reject inverted PDF page ranges in doc_reader
pages like 10-5 produced an empty index list and silently returned no
text. Raise ValueError, matching alpha_bench period inversion.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-18 17:53:50 -07:00
santhreal 78d51b4eb5 fix(channels): avoid hang in split_message when max_len <= 0
When max_len is 0 or negative, the cut pointer never advances and split_message loops forever.
Return the content unsplit so outbound IM adapters cannot wedge.

Signed-off-by: santhreal <64453045+santhreal@users.noreply.github.com>
2026-07-18 17:09:32 -07:00