* feat: add fetch_latest_closed_rates_indexed and allow_whole_dollar_env opt-in (#43, #44)
Closes#43: add fetch_latest_closed_rates_indexed(client, *, symbol,
granularity, count) -> pd.DataFrame to mt5cli/trading.py. Internally
reuses fetch_latest_closed_rates_for_trading_client(), converts the
"time" column to a UTC-aware DatetimeIndex named "time", and drops the
original column. Exported from trading.__all__, mt5cli.__init__, and
STABLE_SDK_EXPORTS.
Closes#44: extend substitute_env_placeholders() with opt-in
allow_whole_dollar_env=False that expands whole-value $ENV_NAME strings
(entire string must be exactly $IDENTIFIER). Threaded through
build_config(), resolve_account_spec(), and resolve_account_specs() with
the same default=False. Partial strings like "plan$pass", "abc$ENV", or
"$ENV-suffix" are never expanded.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: align Markdown table columns in docs and skill file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: treat numeric (float64) epoch seconds as UTC in _rate_time_to_utc
After DataFrame concat or NA upcast the time column becomes float64, which
is still epoch seconds. Using is_numeric_dtype instead of is_integer_dtype
fixes the silent misalignment. Using series.to_numpy() before passing to
pd.to_datetime avoids the redundant pd.DatetimeIndex() wrapper and aligns
with how existing rate-time normalization in schemas.py handles numeric
timestamps.
Add test_converts_float_epoch_seconds_to_utc_datetime_index to cover the
regression. Add a doc note clarifying that build_config cannot expand
login since that parameter is int | None.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: reject NaT values after rate timestamp conversion in _rate_time_to_utc
pd.to_datetime() silently produces NaT for None/NaN inputs rather than
raising, so the function could return a DatetimeIndex containing NaT
despite documenting invalid timestamps as a ValueError. Check any(idx.isna())
after conversion and raise with a clear message.
Add test_raises_on_nat_time_column to cover the regression.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Bump version to v0.9.0
* fix: handle object numeric rate timestamps
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add stable SDK helpers for volume, margin, and closed bars (#39, #40, #41)
Expose generic trading utilities in the stable downstream SDK so applications
like mteor can drop local MT5 adapter code:
- normalize_order_volume() for broker step/min/max sizing
- estimate_order_margin() and calculate_positions_margin() for margin totals
- fetch_latest_closed_rates_for_trading_client() for closed bars from Mt5TradingClient
Update STABLE_SDK_EXPORTS, package-root exports, docs, and unit tests.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* chore: bump version to 0.8.3
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: address PR review feedback on volume cap, rate time, and margin grouping
- Re-apply volume_max after step normalization in normalize_order_volume()
- Drop misleading non-time index reset branch in _ensure_rate_time_column()
- Group positions by (symbol, side) before margin estimation
- Add branch-coverage tests for tick price validation and volume cap edge case
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: address remaining PR review threads on docs and DatetimeIndex
- Rename unnamed DatetimeIndex column to time after reset_index()
- Guard estimate_order_margin example on positive normalized volume
- Document calculate_positions_margin skip vs error propagation behavior
- Add test for unnamed DatetimeIndex branch coverage
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: harden stable SDK margin, rate fetch, and volume normalization
- Wrap order_calc_margin conversion and reject None/non-numeric results
- Validate fetched rate objects are DataFrames before time normalization
- Return 0.0 for non-finite volume inputs and constraints in normalize_order_volume
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: reject non-finite volumes in margin estimation helpers
Use _is_positive_finite_number() in estimate_order_margin() and
calculate_positions_margin() so NaN/inf volumes never reach broker calls.
Add focused tests and document non-finite volume skipping in trading.md.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: guard symbol filter in calculate_positions_margin for empty frames
Return 0.0 before filtering when positions are empty or lack a symbol column.
Add regression tests for filtered calls on malformed position frames.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Add trading session helpers and extend ThrottledHistoryUpdater
Introduce mt5cli.trading with mt5_trading_session() for Mt5TradingClient
lifecycle management and reusable operational helpers for position-side
detection, margin/volume sizing, and protective order price derivation.
Extend ThrottledHistoryUpdater to validate inputs before updates and to
optionally suppress ValueError, OSError, and missing-method errors without
advancing the throttle timestamp.
Export the new helpers from mt5cli.__init__, add unit tests with mocked
clients, and document migration guidance for downstream projects such as
mteor.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Narrow ThrottledHistoryUpdater suppress_errors handling (#27)
* Narrow ThrottledHistoryUpdater suppress_errors for MT5 capability only
Remove broad AttributeError/TypeError handling from recoverable errors.
Add _is_mt5_client_capability_error() to detect missing history API methods
or non-callable client attributes by message and attribute name.
Generic AttributeError/TypeError values always propagate even when
suppress_errors=True. Update docs and tests accordingly.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Detect non-callable history client methods in suppress_errors
Address review feedback: when a history API attribute exists but is not
callable, Python raises a generic TypeError. Inspect the traceback for
mt5cli.history client call sites so these capability mismatches are still
suppressed without matching all TypeError values.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Address PR review feedback on trading helpers
- Resolve history module path once at import time
- Only treat non-callable TypeErrors as capability errors at the raise site
- Validate SL/TP ratios in determine_order_limits
- Add tests for margin_free edge cases, body-raise shutdown, and internal TypeError propagation
- Clarify ThrottledHistoryUpdater suppress_errors docs
- Split README migration example into trading vs read-only history sessions
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Tighten protective ratio validation and clamp negative margin_free
Add _require_protective_ratio enforcing 0 <= ratio < 1 for SL/TP limits so
a ratio of 1.0 cannot produce zero protective prices. Clamp negative
margin_free to 0.0 in calculate_margin_and_volume before sizing.
Add boundary and negative-margin tests; document constraints in trading API
docs.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>