b878a61c07
* 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>
183 lines
4.9 KiB
TOML
183 lines
4.9 KiB
TOML
[project]
|
|
name = "mt5cli"
|
|
version = "0.9.1"
|
|
description = "Generic MT5 data and execution infrastructure for Python applications"
|
|
authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
|
maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
|
license = "MIT"
|
|
license-files = ["LICENSE"]
|
|
readme = "README.md"
|
|
requires-python = ">= 3.11, < 3.14"
|
|
dependencies = [
|
|
"pdmt5>=0.3.0",
|
|
"click >= 8.1.0",
|
|
"pyarrow >= 19.0.0",
|
|
"typer >= 0.15.0",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 3 - Alpha",
|
|
"Environment :: Console",
|
|
"License :: OSI Approved :: MIT License",
|
|
"Operating System :: Microsoft :: Windows",
|
|
"Programming Language :: Python",
|
|
"Programming Language :: Python :: 3",
|
|
"Intended Audience :: Financial and Insurance Industry",
|
|
"Topic :: Office/Business :: Financial :: Investment",
|
|
]
|
|
|
|
[project.scripts]
|
|
mt5cli = "mt5cli.cli:main"
|
|
|
|
[project.urls]
|
|
Repository = "https://github.com/dceoy/mt5cli.git"
|
|
|
|
[tool.uv]
|
|
required-environments = ["platform_system == 'Windows'"]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"ruff >= 0.11.0",
|
|
"pyright >= 1.1.407",
|
|
"pytest>=9.0.3",
|
|
"pytest-mock >= 3.12.0",
|
|
"pytest-cov >= 5.0.0",
|
|
"pandas-stubs >= 2.2.3.250527",
|
|
"mkdocs >= 1.6.1",
|
|
"mkdocs-material >= 9.7.6",
|
|
"mkdocstrings[python] >= 1.0.4",
|
|
"pymdown-extensions >= 10.21.2",
|
|
]
|
|
|
|
[tool.ruff]
|
|
line-length = 88
|
|
exclude = ["build", ".venv"]
|
|
preview = true
|
|
|
|
[tool.ruff.lint]
|
|
select = [
|
|
"F", # Pyflakes (F)
|
|
"E", # pycodestyle error (E)
|
|
"W", # pycodestyle warning (W)
|
|
"C90", # mccabe (C90)
|
|
"I", # isort (I)
|
|
"N", # pep8-naming (N)
|
|
"D", # pydocstyle (D)
|
|
"UP", # pyupgrade (UP)
|
|
"S", # flake8-bandit (S)
|
|
"B", # flake8-bugbear (B)
|
|
"C4", # flake8-comprehensions (C4)
|
|
"SIM", # flake8-simplify (SIM)
|
|
"ARG", # flake8-unused-arguments (ARG)
|
|
"PD", # pandas-vet (PD)
|
|
"PLC", # Pylint convention (PLC)
|
|
"PLE", # Pylint error (PLE)
|
|
"PLR", # Pylint refactor (PLR)
|
|
"PLW", # Pylint warning (PLW)
|
|
"FLY", # flynt (FLY)
|
|
"NPY", # NumPy-specific rules (NPY)
|
|
"PERF", # Perflint (PERF)
|
|
"FURB", # refurb (FURB)
|
|
"RUF", # Ruff-specific rules (RUF)
|
|
"YTT", # flake8-2020 (YTT)
|
|
"ANN", # flake8-annotations (ANN)
|
|
"ASYNC", # flake8-async (ASYNC)
|
|
"BLE", # flake8-blind-except (BLE)
|
|
"FBT", # flake8-boolean-trap (FBT)
|
|
"A", # flake8-builtins (A)
|
|
"COM", # flake8-commas (COM)
|
|
"DTZ", # flake8-datetimez (DTZ)
|
|
"T10", # flake8-debugger (T10)
|
|
"DJ", # flake8-django (DJ)
|
|
"EM", # flake8-errmsg (EM)
|
|
"EXE", # flake8-executable (EXE)
|
|
"FA", # flake8-future-annotations (FA)
|
|
"ISC", # flake8-implicit-str-concat (ISC)
|
|
"ICN", # flake8-import-conventions (ICN)
|
|
"LOG", # flake8-logging (LOG)
|
|
"G", # flake8-logging-format (G)
|
|
"INP", # flake8-no-pep420 (INP)
|
|
"PIE", # flake8-pie (PIE)
|
|
"T20", # flake8-print (T20)
|
|
"PYI", # flake8-pyi (PYI)
|
|
"PT", # flake8-pytest-style (PT)
|
|
"Q", # flake8-quotes (Q)
|
|
"RSE", # flake8-raise (RSE)
|
|
"SLF", # flake8-self (SLF)
|
|
"SLOT", # flake8-slots (SLOT)
|
|
"TID", # flake8-tidy-imports (TID)
|
|
"TCH", # flake8-type-checking (TCH)
|
|
"INT", # flake8-gettext (INT)
|
|
"PTH", # flake8-use-pathlib (PTH)
|
|
"TD", # flake8-todos (TD)
|
|
"FIX", # flake8-fixme (FIX)
|
|
"ERA", # eradicate (ERA)
|
|
"PGH", # pygrep-hooks (PGH)
|
|
"TRY", # tryceratops (TRY)
|
|
"FAST", # FastAPI (FAST)
|
|
"AIR", # Airflow (AIR)
|
|
"DOC", # pydoclint (DOC)
|
|
]
|
|
ignore = [
|
|
"COM812", # missing-trailing-comma
|
|
"FBT001", # boolean-type-hint-positional-argument
|
|
"FBT002", # boolean-default-value-positional-argument
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/**/*.py" = [
|
|
"DOC201", # Missing return documentation
|
|
"DOC501", # Raised exception missing from docstring
|
|
"PLC2701", # Private name import
|
|
"PLR0904", # Too many public methods
|
|
"PLR2004", # Magic value used in comparison
|
|
"PLR6301", # Method could be function/static/classmethod
|
|
"S101", # Use of assert (acceptable in tests)
|
|
"S106", # Possible hardcoded password
|
|
"SLF001", # Private member accessed
|
|
]
|
|
|
|
[tool.ruff.lint.pydocstyle]
|
|
convention = "google"
|
|
|
|
[tool.ruff.lint.pylint]
|
|
max-args = 10
|
|
max-public-methods = 40
|
|
|
|
[tool.pyright]
|
|
exclude = ["build", ".venv"]
|
|
venvPath = "."
|
|
venv = ".venv"
|
|
typeCheckingMode = "strict"
|
|
reportMissingTypeStubs = false
|
|
|
|
[tool.pytest.ini_options]
|
|
addopts = [
|
|
"--cov=mt5cli",
|
|
"--cov-branch",
|
|
"--doctest-modules",
|
|
"--capture=no",
|
|
]
|
|
pythonpath = ["."]
|
|
testpaths = ["tests"]
|
|
python_files = ["test_*.py", "*_test.py"]
|
|
python_classes = ["Test*"]
|
|
python_functions = ["test_*"]
|
|
minversion = "6.0"
|
|
|
|
[tool.coverage.run]
|
|
source = ["mt5cli"]
|
|
omit = [
|
|
"**/__init__.py",
|
|
"**/__main__.py",
|
|
"tests/**",
|
|
]
|
|
|
|
[tool.coverage.report]
|
|
show_missing = true
|
|
fail_under = 100
|
|
exclude_lines = ["if TYPE_CHECKING:"]
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|