* test: add explicit unit tests for calculate_positions_margin_by_symbol and calculate_positions_margin_safe (#50)
Covers all acceptance criteria: partial failure with warning log, all-fail,
empty symbol list with no-broker-call assertion, duplicate deduplication,
successful aggregation with first-seen key order, suppress_errors=False
propagation, and three calculate_positions_margin_safe cases (partial skip,
all-fail → 0.0, empty list → 0.0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: fix warning log assertion and parametrize suppress_errors=False test
- Use record.getMessage() + levelno check instead of record.message, which
is only populated after formatting and can return an empty string.
- Parametrize test_one_symbol_fails_suppress_errors_false over all three
exception types caught by the implementation (Mt5TradingError,
Mt5RuntimeError, AttributeError) so any future narrowing of the except
tuple would be caught by tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style: shorten docstring to fit 88-char line limit
* style: shorten docstring to fit 88-char line limit
---------
Co-authored-by: agent <agent@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces manual isinstance/<=0 checks in calculate_spread_ratio() and
determine_order_limits() with _valid_tick_price(), ensuring NaN, inf,
-inf, zero, negative, bool, and invalid-string tick values are
consistently rejected across all trading helpers.
Adds regression tests covering numeric-string acceptance and every
invalid-value category for both functions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
* fix: re-verify normalized volume margin before returning from calculate_volume_by_margin
For CFDs, index products, and tiered-margin instruments, the initial
min-lot margin estimate can be optimistic; the normalized stepped volume
may require more margin than available_margin. After computing the
normalized volume, step down by volume_step until order_calc_margin
confirms affordability, or return 0.0 if no step is affordable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: fix ruff line-length violations in calculate_volume_by_margin tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use integer step index and add actual>0 guard in calculate_volume_by_margin
Replace float-subtraction loop with integer step index to eliminate
accumulation rounding error and add `actual > 0` guard so a broker
returning zero/negative margin is never accepted as affordable.
Inline `capped` to keep local-variable count within Ruff PLR0914 limit.
Update docstring to reflect re-verification behaviour and 0.0 fallback.
Tighten test assertion from `volume > 0` to the symbol's valid range.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Bump version to v0.9.1
* perf: replace linear step-down scan with binary search in calculate_volume_by_margin
Resolves the P2 review finding: the previous O(n) loop called
order_calc_margin once per volume step, making sizing appear hung for
symbols with a large step range or small volume_step.
Binary search over the integer step index finds the largest affordable
step in O(log n) IPC calls (≈17 for a 99 999-step range vs up to 99 999
in the worst case). Monotonicity of broker margin with volume is assumed,
which holds for standard linear margin schedules.
To stay within the PLR0914 local-variable limit the steps variable is
inlined into hi and the tick temporary is eliminated by accessing the
snapshot dict directly. Error messages still go via msg to satisfy EM102.
Two existing tests are updated to match the binary-search call sequence.
A new regression test (volume_min=0.01, volume_max=1000.0) configures
a tiered-margin mock with its threshold at step 50000 and asserts that
the total order_calc_margin call count does not exceed 25.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove obsolete TC003 per-file-ignore for history.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: agent <agent@localhost>
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>
* feat: add stable MT5Client public API and infrastructure layer
Introduce a reusable public API for downstream trading applications:
- MT5Client as the primary client abstraction with order_check/order_send
- schemas module with DataKind contracts, validation, and normalization
- converters, exceptions, retry, and storage facade modules
- CLI order commands now route through MT5Client
- connected_client made public; retry logic centralized
- Contract tests for API surface, schemas, and storage round-trips
- README and docs updated with Python API usage examples
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: correct time coercion, broker-safe symbols, and execution docs
- Normalize MT5 time columns with correct second/millisecond units
- Coerce all present known MT5 time fields, including optional order times
- Preserve broker symbol casing in normalize_symbol()
- Document order_send() as a live execution primitive with clear scope boundaries
- Add contract tests for timestamp and symbol normalization behavior
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>
* Consolidate duplicated export and history streaming helpers.
Reduce repeated CLI export plumbing, shared per-symbol SQLite writes, and test mock setup without changing public behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Bump version from 0.7.0 to 0.7.1.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Refactor MT5 constant parsing to delegate to pdmt5 >= 0.3.0
Replace local TIMEFRAME_MAP, TICK_FLAG_MAP, and parser helpers with thin
compatibility wrappers around pdmt5. COPY_TICKS flags now use real MT5 values
(ALL=-1, INFO=1, TRADE=2). Click parameter types validate all inputs through
the wrappers. Update tests and docs to describe the pdmt5/mt5cli/mt5api layering.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Fix timeframe defaults and COPY_TICKS flag defaults after pdmt5 migration
Use short timeframe aliases for default history collection and granularity
naming via pdmt5.get_timeframe_name. Set CLI/SDK default tick flags to ALL
(-1) instead of the legacy mt5cli-only value.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Address CI lint failure and PR review feedback
Fix ruff import ordering in history.py. Use ALL string defaults for CLI tick
flags, isolate TICK_FLAG_MAP as a dict snapshot, derive flag names from pdmt5,
reuse TIMEFRAME_NAMES for default history timeframes, and add tests for prefix
stripping and TIMEFRAME_ key filtering.
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* Bump version to 0.7.0
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>
* Add closed-bar rate helpers and bump version to 0.6.0.
Expose drop_forming_rate_bar and multi-account collectors so downstream apps no longer need count+1 fetches and manual bar trimming.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Bump pygments to 2.20.0 to fix CVE-2026-4539 ReDoS advisory.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback on closed-bar rate collection.
Validate count and start_pos before MT5 fetches, avoid redundant frame copies, clarify empty-series errors, and expand test coverage.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Include symbol and timeframe in empty closed-rate error messages.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Guard dedup scopes by written columns
* Address dedup scope review feedback
* Remove legacy dedup scope support
* Remove stale legacy descriptions
* chore: bump version from 0.5.1 to 0.5.2
* Add public helpers to resolve rate compatibility view names.
Expose resolve_rate_view_name and resolve_rate_view_names in mt5cli.history so consumers can derive mt5cli-managed SQLite view names from stored rates metadata without reimplementing the naming rules.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add reusable export, tick-window, and margin helpers for downstream tools.
Expose SQLite append/dedup export, recent tick retrieval, and minimum margin
summary through the SDK and CLI so projects like mteor can depend on mt5cli
instead of duplicating MT5 data plumbing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Bump version to 0.4.3.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback for rate view resolution and SDK helpers.
Harden SQLite read-only connections, tighten view discovery, improve recent_ticks
fetch efficiency, default SQLite export to append, and expand tests and docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix read-only SQLite URI construction on Windows.
Use Path.as_uri() so encoded file URIs work cross-platform with mode=ro.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Rename sqlite_history module to history.
Drop the sqlite-specific prefix now that history collection is the primary module name across SDK, tests, and docs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback for history module rename.
Add a sqlite_history compatibility shim, clarify docs naming, and align the
module docstring with the collect-history SQLite scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove sqlite_history compatibility shim.
The rename to mt5cli.history is intentionally breaking; downstream code
should update imports rather than rely on a deprecated re-export path.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add incremental SQLite history SDK for automated pipelines.
Extract sqlite history helpers into a dedicated module and expose update_history APIs that resume from existing MAX(time) values instead of re-fetching fixed date ranges.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix incremental history deals and stale rate view cleanup.
Fetch account events once during incremental updates, drop stale rate_* views when timeframes change, and avoid SQLite variable limits on wide frames.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix incremental deal filtering edge cases
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback for incremental SQLite history.
Make rate views collision-free, batch incremental resume queries, scope deduplication to appended boundaries, validate before opening MT5, use atomic SQLite transactions, and expand docs/tests for the new helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Document collect-history SQLite schema with ER diagram.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix account-event filtering and drop legacy rates resume.
Account events must follow only account_event_start, not per-symbol trade
cursors. Require normalized rates schema and fail fast when timeframe is missing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Validate normalized rates schema before incremental resume.
Require symbol, timeframe, and time on existing rates tables with clear
ValueError messages, and add regression tests for malformed schemas.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Refactor cli.py into cli and utils modules
Extract constants, enums, Click parameter types, and parse/export utility
functions into a new mt5cli/utils.py module, keeping the typer app, commands,
and collect-history SQLite helpers in cli.py.
https://claude.ai/code/session_016JwSEhPyq6phXySktQ1FGU
* Address review comments
* Add programmatic SDK layer for read-only MT5 data collection.
Expose Mt5CliClient and collect_history through the package API while keeping CLI commands as thin adapters over the SDK.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Harden SDK connection lifecycle and scope internal helpers as private.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Export build_config in the public API and bump version to 0.4.0.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove duplicate scripts/ in favor of local-qa skill script.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add collect-history command for bulk SQLite export
Bundles rates, ticks, history-orders, and history-deals for one or more
symbols into a single SQLite database. Tick collection uses
copy_ticks_range_as_df with a --flags option defaulting to ALL. With
--with-views, optional cash_events and positions_reconstructed views are
derived from history_deals when the required columns are present.
* Extend collect-history with datasets, if-exists, timeframe, view fixes
- Fetch history-orders and history-deals per symbol so --symbol applies
consistently across all four datasets.
- Add repeatable --dataset (rates, ticks, history-orders, history-deals)
so ticks are no longer required and any subset can be collected.
- Add --if-exists append|replace|fail to control SQLite table conflict
behavior instead of hard-coding replace.
- Record the requested timeframe in a timeframe column on the rates
table so appended runs at different timeframes stay distinguishable.
- Fix positions_reconstructed to exclude positions with no closing
deals, use volume-weighted open/close prices, and report reversal
deals (DEAL_ENTRY_INOUT) via volume_reversal / reversal_count without
contributing to weighted prices.
- Update tests, README, docs, and skill to match.
* Address collect-history review feedback
* Stream collect-history writes per symbol
* Address PR cleanup for collect-history
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Add CLI commands for remaining pdmt5.dataframe methods
Add the following subcommands so the CLI fully covers pdmt5.dataframe's
public *_as_df methods:
- version
- last-error
- symbol-info-tick
- market-book
- order-check (request via inline JSON or @path/to/file.json)
- order-send (request via inline JSON or @path/to/file.json)
Also export a new parse_request helper for parsing JSON order requests,
add tests for every new command, and update the README and docs command
tables.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bump version to 0.2.0
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* Address PR review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Harden CLI error assertions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Add pyarrow dependency for parquet export support
- Add docstring to tests/__init__.py (ruff D104)
- Disable reportMissingTypeStubs for pdmt5 in pyright config
- Exclude __main__.py from coverage measurement
- Format markdown tables with prettier
- Add uv.lock
https://claude.ai/code/session_01YW3YHru8wRH9dvHnBX7xf1
Create a standalone CLI package that uses pdmt5 as a dependency to export
MetaTrader 5 data to CSV, JSON, Parquet, and SQLite3 formats. Includes
12 subcommands for rates, ticks, account/terminal info, symbols, orders,
positions, and trading history, along with comprehensive test coverage.
https://claude.ai/code/session_01YW3YHru8wRH9dvHnBX7xf1