Add _valid_tick_price() internal helper that returns a positive finite float
from a tick dict or None for any invalid value (missing, None, NaN, infinite,
zero, negative, or unsupported type). Refactor five existing bid/ask validation
sites in trading.py to use it, removing duplicated isinstance/isfinite checks.
Add calculate_positions_margin_by_symbol() which computes margin per unique
symbol independently using the existing strict calculate_positions_margin(),
with first-seen deduplication and configurable error suppression
(Mt5TradingError, Mt5RuntimeError, AttributeError) via suppress_errors=.
Add calculate_positions_margin_safe() as a thin sum wrapper with
suppress_errors=True, returning 0.0 on empty or fully-failed inputs.
Both new helpers are exported from mt5cli, added to STABLE_SDK_EXPORTS, and
documented in docs/api/public-contract.md. Existing strict behavior of
calculate_positions_margin() is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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>
* feat: add injectable update_backend to ThrottledHistoryUpdater
Allow downstream applications to substitute the history update backend via
the ThrottledHistoryUpdater constructor without monkey-patching
mt5cli.sdk.update_history. Defaults to update_history for backward
compatibility.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* chore: fix lint and format after QA
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* chore: bump version to 0.8.2
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: use explicit None check for ThrottledHistoryUpdater backend
Only None selects the default update_history backend so falsy callable
objects with __bool__ returning False are preserved as custom backends.
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>