riskkit-quant 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- riskkit_quant-0.4.0/.github/ISSUE_TEMPLATE/bug_report.md +21 -0
- riskkit_quant-0.4.0/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
- riskkit_quant-0.4.0/.github/pull_request_template.md +10 -0
- riskkit_quant-0.4.0/.github/workflows/ci.yml +26 -0
- riskkit_quant-0.4.0/.github/workflows/release.yml +28 -0
- riskkit_quant-0.4.0/.gitignore +25 -0
- riskkit_quant-0.4.0/CHANGELOG.md +101 -0
- riskkit_quant-0.4.0/CONTRIBUTING.md +42 -0
- riskkit_quant-0.4.0/LICENSE +21 -0
- riskkit_quant-0.4.0/PKG-INFO +260 -0
- riskkit_quant-0.4.0/PUBLISHING.md +62 -0
- riskkit_quant-0.4.0/README.md +221 -0
- riskkit_quant-0.4.0/ROADMAP.md +68 -0
- riskkit_quant-0.4.0/docs/components.md +93 -0
- riskkit_quant-0.4.0/docs/index.md +44 -0
- riskkit_quant-0.4.0/docs/integrations.md +112 -0
- riskkit_quant-0.4.0/docs/quickstart.md +111 -0
- riskkit_quant-0.4.0/examples/backtesting_py_strategy.py +61 -0
- riskkit_quant-0.4.0/examples/backtesting_riskmanager.py +46 -0
- riskkit_quant-0.4.0/examples/freqtrade_callbacks.py +58 -0
- riskkit_quant-0.4.0/examples/multi_asset_book.py +102 -0
- riskkit_quant-0.4.0/examples/pipeline.py +60 -0
- riskkit_quant-0.4.0/examples/risk_manager.py +76 -0
- riskkit_quant-0.4.0/examples/vectorbt_sizing.py +45 -0
- riskkit_quant-0.4.0/mkdocs.yml +33 -0
- riskkit_quant-0.4.0/pyproject.toml +54 -0
- riskkit_quant-0.4.0/src/riskkit/__init__.py +86 -0
- riskkit_quant-0.4.0/src/riskkit/adapters/__init__.py +7 -0
- riskkit_quant-0.4.0/src/riskkit/adapters/backtesting.py +165 -0
- riskkit_quant-0.4.0/src/riskkit/adapters/freqtrade.py +144 -0
- riskkit_quant-0.4.0/src/riskkit/adapters/vectorbt.py +129 -0
- riskkit_quant-0.4.0/src/riskkit/correlation.py +119 -0
- riskkit_quant-0.4.0/src/riskkit/drawdown.py +171 -0
- riskkit_quant-0.4.0/src/riskkit/manager.py +619 -0
- riskkit_quant-0.4.0/src/riskkit/metrics.py +41 -0
- riskkit_quant-0.4.0/src/riskkit/py.typed +0 -0
- riskkit_quant-0.4.0/src/riskkit/session.py +173 -0
- riskkit_quant-0.4.0/src/riskkit/sizing.py +263 -0
- riskkit_quant-0.4.0/src/riskkit/stops.py +228 -0
- riskkit_quant-0.4.0/src/riskkit/validator.py +197 -0
- riskkit_quant-0.4.0/tests/test_adapter_backtesting.py +56 -0
- riskkit_quant-0.4.0/tests/test_adapter_freqtrade.py +77 -0
- riskkit_quant-0.4.0/tests/test_adapter_vectorbt.py +49 -0
- riskkit_quant-0.4.0/tests/test_correlation.py +34 -0
- riskkit_quant-0.4.0/tests/test_drawdown.py +84 -0
- riskkit_quant-0.4.0/tests/test_manager.py +310 -0
- riskkit_quant-0.4.0/tests/test_metrics.py +46 -0
- riskkit_quant-0.4.0/tests/test_presets.py +73 -0
- riskkit_quant-0.4.0/tests/test_properties.py +207 -0
- riskkit_quant-0.4.0/tests/test_session.py +57 -0
- riskkit_quant-0.4.0/tests/test_sizing.py +155 -0
- riskkit_quant-0.4.0/tests/test_stops.py +120 -0
- riskkit_quant-0.4.0/tests/test_validator.py +88 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something behaves differently than documented
|
|
4
|
+
title: "[bug] "
|
|
5
|
+
labels: bug
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**What happened**
|
|
9
|
+
A clear description of the bug.
|
|
10
|
+
|
|
11
|
+
**Minimal repro**
|
|
12
|
+
```python
|
|
13
|
+
# the smallest snippet that shows it
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Expected vs actual**
|
|
17
|
+
What you expected, and what happened instead.
|
|
18
|
+
|
|
19
|
+
**Environment**
|
|
20
|
+
- riskkit version:
|
|
21
|
+
- Python version:
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a new rule, component, or integration
|
|
4
|
+
title: "[feature] "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**The risk problem this solves**
|
|
9
|
+
What gap or failure mode are you trying to guard against?
|
|
10
|
+
|
|
11
|
+
**Proposed API**
|
|
12
|
+
Roughly how would it look to call this?
|
|
13
|
+
|
|
14
|
+
**Alternatives**
|
|
15
|
+
Anything you've tried or considered.
|
|
16
|
+
|
|
17
|
+
**Scope check**
|
|
18
|
+
Does this stay framework-agnostic and anti-martingale? (See CONTRIBUTING.md.)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## What this changes
|
|
2
|
+
|
|
3
|
+
Briefly, what and why.
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] Tests added/updated and `pytest` passes locally
|
|
8
|
+
- [ ] Public API has docstrings
|
|
9
|
+
- [ ] `CHANGELOG.md` updated
|
|
10
|
+
- [ ] Stays framework-agnostic and anti-martingale (see CONTRIBUTING.md)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: pytest -v
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes when you push a version tag like v0.2.0.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token stored in the repo.
|
|
5
|
+
# One-time setup: on pypi.org add a "pending publisher" for project `riskkit`,
|
|
6
|
+
# owner `HasibDaddy`, repo `riskkit`, workflow `release.yml`, environment `pypi`.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-and-publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: pypi
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write # required for trusted publishing
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
- name: Build
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade build
|
|
26
|
+
python -m build
|
|
27
|
+
- name: Publish
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# Tooling
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.hypothesis/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# Docs build output (mkdocs)
|
|
20
|
+
site/
|
|
21
|
+
|
|
22
|
+
# OS / editor
|
|
23
|
+
.DS_Store
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. This project adheres to
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-06-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Three more **stops** in the `StopEngine` stack: **chandelier** (trails ATR from
|
|
12
|
+
the highest high / lowest low since entry — `StopStack(use_chandelier=True)`,
|
|
13
|
+
`StopEngine(chandelier_atr_multiplier=...)`), **structure** (ratchets to a swing
|
|
14
|
+
level you pass as `update(..., structure_level=...)`), and **PSAR** (ratchets to a
|
|
15
|
+
Parabolic SAR value you pass as `update(..., psar_value=...)`). All tighten-only;
|
|
16
|
+
`update()` gained optional `current_high` / `current_low` for the chandelier extreme.
|
|
17
|
+
- Standalone, composable **sizers** (pure functions): `kelly_fraction()` (edge-optimal
|
|
18
|
+
risk fraction, `fraction=0.5` for half-Kelly), `volatility_target_size()` (size a
|
|
19
|
+
position to a target volatility, notional-capped), and `inverse_vol_weights()` (naive
|
|
20
|
+
risk-parity weights ∝ 1/σ). `PositionSizer` now delegates its half-Kelly ceiling to
|
|
21
|
+
`kelly_fraction` (single source of truth).
|
|
22
|
+
- Portfolio-level **heat** cap — `RiskConfig(max_portfolio_heat_pct=...)` limits the
|
|
23
|
+
total capital at risk across open positions (Σ units × distance-to-stop), checked
|
|
24
|
+
by the validator and surfaced via `RiskManager.portfolio_heat_pct()`. Off by
|
|
25
|
+
default; the presets set it (conservative 4% / balanced 8% / aggressive 15%).
|
|
26
|
+
- Per-sector / asset-class **exposure** cap — `RiskConfig(max_exposure_per_sector_pct=...)`
|
|
27
|
+
keeps any one sector from dominating the book. Tag trades with `TradeIntent(sector=...)`;
|
|
28
|
+
the manager tracks open notional per sector (`RiskManager.sector_exposure_pct(sector)`
|
|
29
|
+
and `.sector_exposure()`) and the validator blocks an entry that would push its sector
|
|
30
|
+
over the cap. Untagged trades are never capped. Off by default; the presets set it
|
|
31
|
+
(conservative 4% / balanced 10% / aggressive 25%).
|
|
32
|
+
- Property-based tests (hypothesis) for the core invariants: a position never
|
|
33
|
+
exceeds its notional/risk caps, size never increases after losses or deeper
|
|
34
|
+
drawdown (anti-martingale), the per-sector exposure cap is never breached across
|
|
35
|
+
random fill sequences, and the standalone sizers stay within bounds (vol-target
|
|
36
|
+
≤ notional cap and falls as vol rises; inverse-vol weights sum to 1; Kelly within
|
|
37
|
+
`[0, fraction]` and rising with edge).
|
|
38
|
+
- `value_at_risk` / `conditional_value_at_risk` — historical VaR and expected
|
|
39
|
+
shortfall over a return series (positive loss magnitudes; CVaR ≥ VaR by
|
|
40
|
+
construction).
|
|
41
|
+
|
|
42
|
+
## [0.3.0] - 2026-06-22
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- `RiskManager` façade — wires all six components from a single `RiskConfig` and
|
|
46
|
+
turns one `TradeIntent` into a sized, validated `RiskDecision` in a single
|
|
47
|
+
`evaluate()` call. Tracks drawdown, session, and open-book state for you via
|
|
48
|
+
`on_equity()` / `on_fill()` / `on_close()`. See `examples/risk_manager.py`.
|
|
49
|
+
- backtesting.py adapter — `riskkit.adapters.backtesting.RiskkitStrategy`, a
|
|
50
|
+
`Strategy` mixin whose `risk_long()` / `risk_short()` size and validate every
|
|
51
|
+
entry through a `RiskManager` and feed closed trades back to the session. New
|
|
52
|
+
`backtesting` optional extra; see `examples/backtesting_riskmanager.py`.
|
|
53
|
+
- `RiskConfig` presets (`conservative` / `balanced` / `aggressive`, plus
|
|
54
|
+
`RiskConfig.preset(name)`) and loaders — `RiskConfig.from_dict()`,
|
|
55
|
+
`RiskConfig.to_dict()`, and `RiskConfig.from_yaml()` (new `yaml` extra).
|
|
56
|
+
- freqtrade adapter — `riskkit.adapters.freqtrade.FreqtradeRiskManager`, which
|
|
57
|
+
drives `custom_stake_amount` / `confirm_trade_entry` from a `RiskManager`
|
|
58
|
+
(framework-agnostic: it imports nothing from freqtrade).
|
|
59
|
+
- vectorbt adapter — `riskkit.adapters.vectorbt.size_signals`, which sizes an
|
|
60
|
+
array of entry signals with riskkit for `Portfolio.from_signals`.
|
|
61
|
+
- Runnable integration examples for backtesting.py and freqtrade.
|
|
62
|
+
- mkdocs documentation site (Home / Quickstart / Components / Integrations).
|
|
63
|
+
- PyPI trusted-publishing release workflow and `PUBLISHING.md` guide.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
- `PositionSizer` now reports `risk_pct` consistent with `risk_amount` when the
|
|
67
|
+
notional cap binds (previously `risk_pct` kept the pre-cap target while
|
|
68
|
+
`risk_amount` reflected the smaller, capped position).
|
|
69
|
+
|
|
70
|
+
## [0.2.0] - 2026-06-19
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
- `StopEngine` / `StopStack` — composable per-position stop stack (initial,
|
|
74
|
+
break-even, ATR-trailing, EMA-trailing, time, and volatility stops); tightest
|
|
75
|
+
stop wins and stops only ever move closer.
|
|
76
|
+
- `CorrelationGuard` — one open position per correlation group, from static
|
|
77
|
+
groups and/or a dynamically computed return-correlation matrix (pandas extra).
|
|
78
|
+
- `SessionManager` — daily trade/loss caps, profit-taking stops, minimum
|
|
79
|
+
spacing, escalating cooldowns, and tilt detection.
|
|
80
|
+
- `PreTradeValidator` — composable final-gate checklist across market quality,
|
|
81
|
+
sizing, risk limits, signal quality, and timing; returns a result per check.
|
|
82
|
+
- `examples/pipeline.py` end-to-end demo, contribution guide, and issue/PR
|
|
83
|
+
templates.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- Generalized the API to be framework-agnostic (e.g. `symbol`/`score` instead of
|
|
87
|
+
exchange-specific names; correlation groups and regime maps are now injected
|
|
88
|
+
rather than hardcoded).
|
|
89
|
+
|
|
90
|
+
## [0.1.0] - 2026-06-19
|
|
91
|
+
|
|
92
|
+
Initial release.
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
- `PositionSizer` — volatility-adjusted fixed-fractional position sizing with a
|
|
96
|
+
half-Kelly ceiling, a reduction ladder (losing streaks, drawdown tiers, daily
|
|
97
|
+
loss), a high-conviction bonus, and a hard notional cap. Every adjustment is
|
|
98
|
+
returned for auditing.
|
|
99
|
+
- `DrawdownManager` — high-water-mark drawdown tracking with a 5-tier ladder, a
|
|
100
|
+
one-step-at-a-time recovery ramp, and a rolling weekly-loss pause.
|
|
101
|
+
- Full type hints (`py.typed`) and a test suite covering both modules.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Contributing to riskkit
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. riskkit aims to be the dependable, framework-agnostic
|
|
4
|
+
risk layer for systematic trading — correctness and clarity matter more than
|
|
5
|
+
features.
|
|
6
|
+
|
|
7
|
+
## Dev setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/HasibDaddy/riskkit
|
|
11
|
+
cd riskkit
|
|
12
|
+
python -m venv .venv && source .venv/bin/activate
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
pytest
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Principles (please keep these)
|
|
18
|
+
|
|
19
|
+
- **No framework lock-in.** The core must not depend on any exchange SDK, data
|
|
20
|
+
provider, or backtesting framework. Heavy optional deps (like pandas) go
|
|
21
|
+
behind an extra and are imported lazily.
|
|
22
|
+
- **Every decision is auditable.** New rules should return *why* they fired, not
|
|
23
|
+
just a boolean.
|
|
24
|
+
- **Conservative defaults.** Floors, ceilings, and caps bound every knob.
|
|
25
|
+
- **Anti-martingale only.** Nothing in this library may increase risk after a
|
|
26
|
+
loss.
|
|
27
|
+
|
|
28
|
+
## Pull requests
|
|
29
|
+
|
|
30
|
+
1. Add or update tests — every behaviour change needs a test.
|
|
31
|
+
2. Keep the public API documented (docstrings render in the README examples).
|
|
32
|
+
3. Run `pytest` locally; CI runs the suite on Python 3.9–3.12.
|
|
33
|
+
4. Update `CHANGELOG.md` under an `## [Unreleased]` heading.
|
|
34
|
+
|
|
35
|
+
## Good first contributions
|
|
36
|
+
|
|
37
|
+
- Integration examples/adapters for a specific framework (backtesting.py,
|
|
38
|
+
vectorbt, freqtrade).
|
|
39
|
+
- A worked notebook walking through one component on real OHLCV data.
|
|
40
|
+
- Edge-case tests for the existing modules.
|
|
41
|
+
|
|
42
|
+
Open an issue to discuss anything larger before you build it.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hasib
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: riskkit-quant
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A framework-agnostic risk-management toolkit for systematic traders: position sizing, drawdown laddering, and more.
|
|
5
|
+
Project-URL: Homepage, https://github.com/HasibDaddy/riskkit
|
|
6
|
+
Project-URL: Repository, https://github.com/HasibDaddy/riskkit
|
|
7
|
+
Project-URL: Issues, https://github.com/HasibDaddy/riskkit/issues
|
|
8
|
+
Author: Hasib
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: algotrading,backtesting,drawdown,kelly-criterion,position-sizing,quant,risk-management,trading
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Provides-Extra: backtesting
|
|
25
|
+
Requires-Dist: backtesting>=0.3; extra == 'backtesting'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: backtesting>=0.3; extra == 'dev'
|
|
28
|
+
Requires-Dist: hypothesis>=6.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pandas>=1.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pyyaml>=5.0; extra == 'dev'
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
|
|
34
|
+
Provides-Extra: pandas
|
|
35
|
+
Requires-Dist: pandas>=1.0; extra == 'pandas'
|
|
36
|
+
Provides-Extra: yaml
|
|
37
|
+
Requires-Dist: pyyaml>=5.0; extra == 'yaml'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# riskkit
|
|
41
|
+
|
|
42
|
+
[](https://github.com/HasibDaddy/riskkit/actions/workflows/ci.yml)
|
|
43
|
+
[](https://hasibdaddy.github.io/riskkit/)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+

|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
**A framework-agnostic risk-management toolkit for systematic traders.**
|
|
49
|
+
|
|
50
|
+
Most open-source trading tools focus on the fun part — signals, indicators,
|
|
51
|
+
backtesting engines. They leave the part that actually decides whether you
|
|
52
|
+
survive thin or absent: *how big a position to take, when to cut size, and when
|
|
53
|
+
to stop trading altogether.* That's what blows up retail algo traders, not a
|
|
54
|
+
bad entry signal.
|
|
55
|
+
|
|
56
|
+
`riskkit` is that missing layer. The components are pure Python with **no
|
|
57
|
+
dependency on any exchange, data provider, or backtesting framework**. They
|
|
58
|
+
don't know what CCXT is. You feed them numbers; they hand back decisions you can
|
|
59
|
+
audit. Drop them into [backtesting.py](https://github.com/kernc/backtesting.py),
|
|
60
|
+
[vectorbt](https://github.com/polakowo/vectorbt), [backtrader](https://github.com/mementum/backtrader),
|
|
61
|
+
[freqtrade](https://github.com/freqtrade/freqtrade), or your own loop.
|
|
62
|
+
|
|
63
|
+
> ⚠️ **Not financial advice.** `riskkit` helps you *implement* a risk policy you
|
|
64
|
+
> have chosen. It does not choose one for you, and it cannot make a losing
|
|
65
|
+
> strategy profitable. Test everything on paper first.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install "git+https://github.com/HasibDaddy/riskkit.git"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Zero runtime dependencies. Python 3.9+. *(A PyPI release is on the way — until
|
|
76
|
+
then, install straight from GitHub with the line above.)*
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## What's in the box
|
|
81
|
+
|
|
82
|
+
| Component | What it does |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `PositionSizer` | Volatility-adjusted fixed-fractional sizing with an optional half-Kelly ceiling, a reduction ladder for losing streaks / drawdowns, and a hard notional cap. |
|
|
85
|
+
| `DrawdownManager` | Tracks high-water-mark drawdown, maps it onto a tier ladder (cut size → raise the bar → halt), with a recovery ramp and a rolling weekly-loss pause. |
|
|
86
|
+
| `StopEngine` | A composable stop *stack* per position — initial, break-even, ATR/EMA trailing, chandelier, structure (swing), PSAR, time, and volatility stops. The tightest one wins; stops only ever move closer. |
|
|
87
|
+
| `CorrelationGuard` | At most one open position per correlation group. Groups can be static (you define them) or computed dynamically from a rolling return-correlation matrix. |
|
|
88
|
+
| `SessionManager` | Daily trade/loss caps, profit-taking stops, minimum spacing, escalating cooldowns after losing streaks, and tilt detection. |
|
|
89
|
+
| `PreTradeValidator` | The composable final gate: runs every rule against a proposed trade and vetoes it if any fails — returning exactly which checks passed and failed. |
|
|
90
|
+
|
|
91
|
+
Every decision is **auditable** — the sizer returns which multipliers fired,
|
|
92
|
+
the stop engine logs each adjustment, and the validator returns a pass/fail line
|
|
93
|
+
for every single check.
|
|
94
|
+
|
|
95
|
+
Beyond the six components, riskkit ships a few **portfolio-level controls** and
|
|
96
|
+
**standalone sizers** you can reach for on their own:
|
|
97
|
+
|
|
98
|
+
- **Portfolio caps** (enforced by `RiskManager` as the book fills): total open
|
|
99
|
+
notional, total **heat** (risk-at-stop), and **per-sector / asset-class**
|
|
100
|
+
exposure — so no single sector can dominate the book.
|
|
101
|
+
- **Composable sizers** (pure functions): `volatility_target_size`,
|
|
102
|
+
`inverse_vol_weights` (naive risk parity), and `kelly_fraction`.
|
|
103
|
+
- **Risk metrics**: historical `value_at_risk` and `conditional_value_at_risk`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Quick start
|
|
108
|
+
|
|
109
|
+
### The whole stack, one call
|
|
110
|
+
|
|
111
|
+
`RiskManager` is the façade: wire all six components from a single config, push
|
|
112
|
+
equity in as your account moves, and ask one question per trade. It keeps the
|
|
113
|
+
drawdown, session, and open-position state in sync for you.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from riskkit import RiskManager, RiskConfig, TradeIntent
|
|
117
|
+
|
|
118
|
+
risk = RiskManager(RiskConfig(
|
|
119
|
+
base_risk_pct=1.0, max_notional_pct=4.0,
|
|
120
|
+
drawdown=dict(tier1_pct=3, halt_pct=10),
|
|
121
|
+
session=dict(max_trades_per_day=5),
|
|
122
|
+
correlation=dict(static_groups={"majors": {"BTC/USDT", "ETH/USDT"}}),
|
|
123
|
+
))
|
|
124
|
+
|
|
125
|
+
risk.on_equity(10_000) # refresh drawdown/session state
|
|
126
|
+
decision = risk.evaluate(TradeIntent(
|
|
127
|
+
symbol="BTC/USDT", side="long",
|
|
128
|
+
entry_price=100.0, stop_price=98.0, target_price=104.0,
|
|
129
|
+
score=82, atr=2.0, atr_baseline=2.0,
|
|
130
|
+
))
|
|
131
|
+
|
|
132
|
+
if decision.ok:
|
|
133
|
+
place(decision.units, decision.stop) # your execution layer
|
|
134
|
+
risk.on_fill(decision) # tell riskkit it filled
|
|
135
|
+
else:
|
|
136
|
+
print("skip:", *decision.reasons, sep="\n ") # every gate that vetoed it
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Reach past the façade to any single component when you need to — they're all
|
|
140
|
+
exposed (`risk.sizer`, `risk.drawdown`, `risk.stops`, …) and usable standalone.
|
|
141
|
+
|
|
142
|
+
Don't want to tune every knob? Start from a preset, or load policy from YAML:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
risk = RiskManager(RiskConfig.conservative()) # or .balanced() / .aggressive()
|
|
146
|
+
cfg = RiskConfig.from_yaml("risk.yaml") # needs riskkit[yaml]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Sizing a trade
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from riskkit import PositionSizer, SizingInputs
|
|
153
|
+
|
|
154
|
+
sizer = PositionSizer(
|
|
155
|
+
base_risk_pct=1.0, # risk 1% of equity per trade, before adjustments
|
|
156
|
+
max_risk_pct=1.5, # never risk more than 1.5%
|
|
157
|
+
max_notional_pct=4.0, # never let a position exceed 4% of equity
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
result = sizer.size(SizingInputs(
|
|
161
|
+
equity=10_000,
|
|
162
|
+
entry_price=100.0,
|
|
163
|
+
stop_price=98.0, # the stop distance defines your risk per unit
|
|
164
|
+
atr=2.5, # current volatility
|
|
165
|
+
atr_baseline=2.0, # "normal" volatility -> scales risk down when choppy
|
|
166
|
+
consecutive_losses=2, # reduction ladder kicks in
|
|
167
|
+
drawdown_pct=4.0,
|
|
168
|
+
))
|
|
169
|
+
|
|
170
|
+
if result.units > 0:
|
|
171
|
+
print(f"Buy {result.units:.4f} units (risk {result.risk_pct:.2%})")
|
|
172
|
+
print("adjustments:", result.multipliers_applied)
|
|
173
|
+
else:
|
|
174
|
+
print("Skip:", result.reason_for_zero)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Adapting to drawdown
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from riskkit import DrawdownManager
|
|
181
|
+
|
|
182
|
+
dm = DrawdownManager(tier1_pct=3, tier2_pct=5, tier3_pct=7, halt_pct=10)
|
|
183
|
+
|
|
184
|
+
state = dm.update(current_equity) # call once per equity refresh
|
|
185
|
+
if state.halted:
|
|
186
|
+
print("No new trades:", state.reason)
|
|
187
|
+
else:
|
|
188
|
+
size = base_size * state.size_multiplier # scale every position by the tier
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The two compose naturally: feed `DrawdownManager`'s `drawdown_pct` and
|
|
192
|
+
`size_multiplier` straight into the sizer.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Design principles
|
|
197
|
+
|
|
198
|
+
- **Framework-agnostic.** No exchange SDK, no pandas requirement in the core,
|
|
199
|
+
no global state. Just dataclasses in, dataclasses out.
|
|
200
|
+
- **Auditable, not magic.** Every adjustment is named and returned. You can log
|
|
201
|
+
the exact reason a trade was sized down or skipped.
|
|
202
|
+
- **Conservative by default.** Floors, ceilings, and hard caps bound every knob.
|
|
203
|
+
The math can recommend; it can never exceed the limits you set.
|
|
204
|
+
- **Anti-martingale.** Size goes *down* after losses and during drawdowns, never
|
|
205
|
+
up. There is no "average down" path anywhere in this library.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Integrations
|
|
210
|
+
|
|
211
|
+
riskkit slots into whatever you already use — the [examples](examples/) are
|
|
212
|
+
runnable:
|
|
213
|
+
|
|
214
|
+
- **backtesting.py** — subclass the `RiskkitStrategy` adapter
|
|
215
|
+
(`from riskkit.adapters.backtesting import RiskkitStrategy`) and call
|
|
216
|
+
`risk_long()` / `risk_short()`; every entry is sized **and** validated by one
|
|
217
|
+
`RiskConfig`, with closed trades fed back to the session manager. See
|
|
218
|
+
[`examples/backtesting_riskmanager.py`](examples/backtesting_riskmanager.py)
|
|
219
|
+
(full façade) or [`examples/backtesting_py_strategy.py`](examples/backtesting_py_strategy.py)
|
|
220
|
+
(just `PositionSizer`, by hand).
|
|
221
|
+
- **freqtrade** — `FreqtradeRiskManager`
|
|
222
|
+
(`from riskkit.adapters.freqtrade import FreqtradeRiskManager`) drives
|
|
223
|
+
`custom_stake_amount` + `confirm_trade_entry` from one `RiskConfig`; see
|
|
224
|
+
[`examples/freqtrade_callbacks.py`](examples/freqtrade_callbacks.py).
|
|
225
|
+
- **vectorbt** — `size_signals`
|
|
226
|
+
(`from riskkit.adapters.vectorbt import size_signals`) turns entry signals into
|
|
227
|
+
a riskkit-sized array for `Portfolio.from_signals`; see
|
|
228
|
+
[`examples/vectorbt_sizing.py`](examples/vectorbt_sizing.py).
|
|
229
|
+
- **your own loop** — [`examples/risk_manager.py`](examples/risk_manager.py)
|
|
230
|
+
drives the full `RiskManager` façade end-to-end;
|
|
231
|
+
[`examples/multi_asset_book.py`](examples/multi_asset_book.py) allocates,
|
|
232
|
+
vol-targets, and vets a cross-sector book through the portfolio caps;
|
|
233
|
+
[`examples/pipeline.py`](examples/pipeline.py) shows the same flow wired by hand.
|
|
234
|
+
|
|
235
|
+
📖 **Full docs: https://hasibdaddy.github.io/riskkit/**
|
|
236
|
+
|
|
237
|
+
## Roadmap
|
|
238
|
+
|
|
239
|
+
`riskkit` is extracted and generalized from a working risk-first trading bot.
|
|
240
|
+
The core six components are in place; next up is making them effortless to drop
|
|
241
|
+
into the popular frameworks:
|
|
242
|
+
|
|
243
|
+
- [x] `PositionSizer`, `DrawdownManager`, `StopEngine`
|
|
244
|
+
- [x] `CorrelationGuard`, `SessionManager`, `PreTradeValidator`
|
|
245
|
+
- [x] A single `RiskManager` façade that wires all six together with one config
|
|
246
|
+
- [x] Config presets (conservative / balanced / aggressive) + dict/YAML loading
|
|
247
|
+
- [x] First-class adapters for backtesting.py, freqtrade, and vectorbt
|
|
248
|
+
- [x] A [hosted docs site](https://hasibdaddy.github.io/riskkit/) with component recipes
|
|
249
|
+
- [x] Portfolio caps (total-exposure, heat, per-sector) + standalone sizers (vol-target, risk-parity, Kelly) + VaR/CVaR
|
|
250
|
+
- [ ] A PyPI release (`pip install riskkit-quant`, then `import riskkit`)
|
|
251
|
+
|
|
252
|
+
Feedback on the API is genuinely welcome — open an issue. See the full
|
|
253
|
+
[ROADMAP.md](ROADMAP.md), [CONTRIBUTING.md](CONTRIBUTING.md), and the
|
|
254
|
+
[examples](examples/).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT © 2026 Hasib. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Publishing riskkit to PyPI
|
|
2
|
+
|
|
3
|
+
**Status (2026-06-23):** v0.4.0 is **build-verified and ready to publish** —
|
|
4
|
+
`python -m build` succeeds, `twine check` passes, and a clean-venv install of the
|
|
5
|
+
wheel imports and runs the full v0.4 surface (core is zero-dependency). The only
|
|
6
|
+
steps left are the PyPI-account ones below.
|
|
7
|
+
|
|
8
|
+
**Distribution name:** `riskkit-quant`. The ideal `riskkit` is unregistered (API
|
|
9
|
+
404) but PyPI's pending-publisher form rejects it as **too similar to the existing
|
|
10
|
+
`risk-kit`**, and `riskkit-trading` was also rejected. The accepted name is
|
|
11
|
+
`riskkit-quant` (free, distinct). The *import* name is unchanged:
|
|
12
|
+
`pip install riskkit-quant` then `import riskkit` — exactly like `scikit-learn` →
|
|
13
|
+
`import sklearn`. Never re-upload a version once published.
|
|
14
|
+
|
|
15
|
+
Two ways to publish — the automated one (recommended) and the manual one.
|
|
16
|
+
|
|
17
|
+
## Recommended: Trusted Publishing (no tokens)
|
|
18
|
+
|
|
19
|
+
This uses the included `.github/workflows/release.yml`. You configure PyPI once
|
|
20
|
+
to trust your GitHub repo, then publishing is just pushing a tag.
|
|
21
|
+
|
|
22
|
+
1. Create a PyPI account at https://pypi.org and verify your email.
|
|
23
|
+
2. Go to **Your projects → Publishing → Add a pending publisher** and enter:
|
|
24
|
+
- PyPI project name: `riskkit-quant`
|
|
25
|
+
- Owner: `HasibDaddy`
|
|
26
|
+
- Repository name: `riskkit`
|
|
27
|
+
- Workflow name: `release.yml`
|
|
28
|
+
- Environment name: `pypi`
|
|
29
|
+
3. In your GitHub repo, create an environment named `pypi`
|
|
30
|
+
(Settings → Environments → New environment).
|
|
31
|
+
4. Tag and push a release:
|
|
32
|
+
```bash
|
|
33
|
+
git tag v0.4.0
|
|
34
|
+
git push origin v0.4.0
|
|
35
|
+
```
|
|
36
|
+
The workflow builds the package and publishes it. Done.
|
|
37
|
+
|
|
38
|
+
## Manual (one machine, with a token)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python -m pip install --upgrade build twine
|
|
42
|
+
python -m build # creates dist/*.whl and dist/*.tar.gz
|
|
43
|
+
python -m twine check dist/*
|
|
44
|
+
python -m twine upload dist/* # paste a PyPI API token when prompted
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Before the first publish — checklist
|
|
48
|
+
|
|
49
|
+
- [x] GitHub handle (`HasibDaddy`) filled in across `pyproject.toml`,
|
|
50
|
+
`mkdocs.yml`, `docs/`, and the workflow.
|
|
51
|
+
- [x] `version` matches in `pyproject.toml` and `src/riskkit/__init__.py` (`0.4.0`).
|
|
52
|
+
- [x] `pytest` green (118 tests) and CI passes on GitHub across Python 3.9–3.12.
|
|
53
|
+
- [x] `python -m build` succeeds and `twine check dist/*` passes; the wheel installs
|
|
54
|
+
and imports cleanly in a fresh venv with zero dependencies.
|
|
55
|
+
|
|
56
|
+
## After publishing
|
|
57
|
+
|
|
58
|
+
- Verify the install: `pip install riskkit-quant` in a clean venv (then `import riskkit`).
|
|
59
|
+
- Update the README install line from the GitHub URL to `pip install riskkit-trading`.
|
|
60
|
+
- Bump the version for the next change (never re-upload the same version).
|
|
61
|
+
- Docs are already live at https://hasibdaddy.github.io/riskkit/ (re-run
|
|
62
|
+
`mkdocs gh-deploy` after docs changes).
|