riskkit-quant 0.4.0__py3-none-any.whl

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/validator.py ADDED
@@ -0,0 +1,197 @@
1
+ """Pre-trade validator — the final gate before an order goes out.
2
+
3
+ A composable checklist that runs every rule against a proposed trade and vetoes
4
+ it if *any* rule fails. The point is to make "should I take this trade?" a
5
+ single, auditable function call whose output records exactly which rules passed
6
+ and which failed.
7
+
8
+ The checks span market quality, position sizing, risk limits, signal quality,
9
+ and timing. The validator is framework-agnostic: you assemble a
10
+ :class:`TradeProposal` from whatever your system knows, and you get back a
11
+ :class:`ValidationResult`.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass, field
16
+ from typing import Mapping
17
+
18
+
19
+ @dataclass
20
+ class CheckResult:
21
+ name: str
22
+ ok: bool
23
+ details: str = ""
24
+
25
+
26
+ @dataclass
27
+ class ValidationResult:
28
+ passed: bool
29
+ failures: list[CheckResult]
30
+ details: list[CheckResult] = field(default_factory=list)
31
+ market_quality_failed: bool = False # if True, the caller may retry shortly
32
+
33
+
34
+ @dataclass
35
+ class TradeProposal:
36
+ symbol: str
37
+ side: str # "long" | "short"
38
+ entry_price: float
39
+ stop_price: float
40
+ target_price: float
41
+ size_units: float
42
+ notional: float
43
+ strategy: str
44
+ score: int # signal-quality score (0-100)
45
+ regime: str = ""
46
+
47
+ # Market-quality snapshot
48
+ spread_pct: float = 0.0
49
+ orderbook_depth: float = float("inf") # quote-currency depth near touch
50
+ recent_atr_spike_x: float = 1.0 # current_atr / baseline_atr
51
+ last_quote_age_sec: float = 0.0
52
+
53
+ # Portfolio state
54
+ equity: float = 0.0
55
+ free_balance: float = float("inf")
56
+ current_total_exposure_pct: float = 0.0
57
+ current_portfolio_heat_pct: float = 0.0 # open risk-at-stop, excl. this trade
58
+ sector: str = "" # sector / asset-class tag (for per-sector cap)
59
+ current_sector_exposure_pct: float = 0.0 # this sector's open notional %, excl. this trade
60
+ open_concurrent_positions: int = 0
61
+ daily_loss_pct: float = 0.0
62
+ daily_trade_count: int = 0
63
+ drawdown_halted: bool = False
64
+ cooldown_active: bool = False
65
+ correlation_blocked: bool = False
66
+ at_max_concurrent: bool = False
67
+ seconds_since_last_trade: float = float("inf")
68
+
69
+
70
+ class PreTradeValidator:
71
+ def __init__(
72
+ self,
73
+ max_spread_pct_tight: float = 0.05,
74
+ max_spread_pct_default: float = 0.1,
75
+ tight_spread_symbols: set[str] | None = None,
76
+ depth_multiplier: float = 2.0,
77
+ max_recent_atr_spike: float = 3.0,
78
+ max_quote_age_sec: float = 120.0,
79
+ max_notional_pct: float = 4.0,
80
+ max_total_exposure_pct: float = 10.0,
81
+ max_portfolio_heat_pct: float = float("inf"),
82
+ max_exposure_per_sector_pct: float = float("inf"),
83
+ max_daily_loss_pct: float = 1.5,
84
+ max_daily_trades: int = 5,
85
+ min_score: int = 70,
86
+ min_rr_ratio: float = 2.0,
87
+ min_seconds_between_trades: float = 15 * 60,
88
+ regime_strategies: Mapping[str, set[str]] | None = None,
89
+ ) -> None:
90
+ self.max_spread_tight = max_spread_pct_tight
91
+ self.max_spread_default = max_spread_pct_default
92
+ self.tight_spread_symbols = tight_spread_symbols or set()
93
+ self.depth_mult = depth_multiplier
94
+ self.max_atr_spike = max_recent_atr_spike
95
+ self.max_quote_age = max_quote_age_sec
96
+ self.max_notional_pct = max_notional_pct
97
+ self.max_total_exposure_pct = max_total_exposure_pct
98
+ self.max_portfolio_heat_pct = max_portfolio_heat_pct
99
+ self.max_sector_exposure_pct = max_exposure_per_sector_pct
100
+ self.max_daily_loss_pct = max_daily_loss_pct
101
+ self.max_daily_trades = max_daily_trades
102
+ self.min_score = min_score
103
+ self.min_rr = min_rr_ratio
104
+ self.min_secs_between = min_seconds_between_trades
105
+ self.regime_strategies = regime_strategies
106
+
107
+ def validate(self, p: TradeProposal, *, min_score_override: int | None = None) -> ValidationResult:
108
+ results: list[CheckResult] = []
109
+ min_score = max(min_score_override or 0, self.min_score)
110
+
111
+ # ── MARKET QUALITY ──
112
+ max_spread = (
113
+ self.max_spread_tight if p.symbol in self.tight_spread_symbols
114
+ else self.max_spread_default
115
+ )
116
+ results.append(CheckResult("spread_ok", p.spread_pct < max_spread,
117
+ f"spread={p.spread_pct:.4f}% < {max_spread}%"))
118
+ results.append(CheckResult("orderbook_depth_ok",
119
+ p.orderbook_depth >= self.depth_mult * p.notional,
120
+ f"depth={p.orderbook_depth:.0f} vs {self.depth_mult}x notional {p.notional:.0f}"))
121
+ results.append(CheckResult("no_recent_volatility_spike",
122
+ p.recent_atr_spike_x < self.max_atr_spike,
123
+ f"atr_spike_x={p.recent_atr_spike_x:.2f} < {self.max_atr_spike}"))
124
+ results.append(CheckResult("data_fresh", p.last_quote_age_sec < self.max_quote_age,
125
+ f"quote_age={p.last_quote_age_sec:.0f}s"))
126
+
127
+ # ── POSITION SIZING ──
128
+ results.append(CheckResult("size_positive", p.size_units > 0 and p.notional > 0,
129
+ "size must be > 0"))
130
+ notional_pct = (p.notional / p.equity * 100.0) if p.equity else 100.0
131
+ results.append(CheckResult("notional_cap", notional_pct <= self.max_notional_pct,
132
+ f"notional {notional_pct:.2f}% of equity vs cap {self.max_notional_pct}%"))
133
+ projected = p.current_total_exposure_pct + notional_pct
134
+ results.append(CheckResult("total_exposure_cap", projected <= self.max_total_exposure_pct,
135
+ f"projected exposure {projected:.2f}% vs cap {self.max_total_exposure_pct}%"))
136
+ # Per-sector exposure: keep any single sector / asset-class from dominating the
137
+ # book. Only checked when a cap is configured and the trade carries a sector tag.
138
+ if self.max_sector_exposure_pct != float("inf") and p.sector:
139
+ projected_sector = p.current_sector_exposure_pct + notional_pct
140
+ results.append(CheckResult(
141
+ "sector_exposure_ok",
142
+ projected_sector <= self.max_sector_exposure_pct,
143
+ f"sector '{p.sector}' projected {projected_sector:.2f}% vs cap {self.max_sector_exposure_pct}%"))
144
+ # Portfolio heat: total risk-at-stop across open positions plus this one.
145
+ # Only checked when a cap is configured (off by default).
146
+ if self.max_portfolio_heat_pct != float("inf"):
147
+ trade_risk = abs(p.entry_price - p.stop_price) * p.size_units
148
+ trade_risk_pct = (trade_risk / p.equity * 100.0) if p.equity else 0.0
149
+ projected_heat = p.current_portfolio_heat_pct + trade_risk_pct
150
+ results.append(CheckResult("portfolio_heat_ok",
151
+ projected_heat <= self.max_portfolio_heat_pct,
152
+ f"projected heat {projected_heat:.2f}% vs cap {self.max_portfolio_heat_pct}%"))
153
+ results.append(CheckResult("sufficient_balance", p.free_balance >= p.notional * 1.01,
154
+ f"free={p.free_balance:.2f} need={p.notional * 1.01:.2f}"))
155
+
156
+ # ── RISK LIMITS ──
157
+ results.append(CheckResult("daily_loss_ok", p.daily_loss_pct < self.max_daily_loss_pct,
158
+ f"daily_loss={p.daily_loss_pct:.2f}% < {self.max_daily_loss_pct}%"))
159
+ results.append(CheckResult("daily_trade_count_ok", p.daily_trade_count < self.max_daily_trades,
160
+ f"trades_today={p.daily_trade_count} < {self.max_daily_trades}"))
161
+ results.append(CheckResult("not_in_drawdown_halt", not p.drawdown_halted,
162
+ "drawdown halt active" if p.drawdown_halted else "ok"))
163
+ results.append(CheckResult("not_in_cooldown", not p.cooldown_active,
164
+ "in cooldown" if p.cooldown_active else "ok"))
165
+ results.append(CheckResult("correlation_ok", not p.correlation_blocked,
166
+ "correlated position open" if p.correlation_blocked else "ok"))
167
+ results.append(CheckResult("max_concurrent_ok", not p.at_max_concurrent,
168
+ f"open={p.open_concurrent_positions} at cap" if p.at_max_concurrent else "ok"))
169
+
170
+ # ── SIGNAL QUALITY ──
171
+ results.append(CheckResult("score_ok", p.score >= min_score,
172
+ f"score={p.score} >= {min_score}"))
173
+ risk = abs(p.entry_price - p.stop_price)
174
+ reward = abs(p.target_price - p.entry_price)
175
+ rr = reward / risk if risk else 0.0
176
+ results.append(CheckResult("rr_ratio_ok", rr >= self.min_rr,
177
+ f"R:R {rr:.2f} >= {self.min_rr}"))
178
+ if self.regime_strategies is not None:
179
+ allowed = p.strategy in self.regime_strategies.get(p.regime, set())
180
+ results.append(CheckResult("regime_allows_strategy", allowed,
181
+ f"strategy={p.strategy} regime={p.regime}"))
182
+
183
+ # ── TIMING ──
184
+ results.append(CheckResult("min_time_between_trades",
185
+ p.seconds_since_last_trade >= self.min_secs_between,
186
+ f"{p.seconds_since_last_trade:.0f}s >= {self.min_secs_between}s"))
187
+
188
+ failures = [r for r in results if not r.ok]
189
+ market_quality_names = {
190
+ "spread_ok", "orderbook_depth_ok", "no_recent_volatility_spike", "data_fresh",
191
+ }
192
+ return ValidationResult(
193
+ passed=len(failures) == 0,
194
+ failures=failures,
195
+ details=results,
196
+ market_quality_failed=any(r.name in market_quality_names for r in failures),
197
+ )
@@ -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
+ [![CI](https://github.com/HasibDaddy/riskkit/actions/workflows/ci.yml/badge.svg)](https://github.com/HasibDaddy/riskkit/actions/workflows/ci.yml)
43
+ [![Docs](https://img.shields.io/badge/docs-mkdocs-1f6feb)](https://hasibdaddy.github.io/riskkit/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
45
+ ![Python](https://img.shields.io/badge/python-3.9%E2%80%933.12-blue.svg)
46
+ ![Dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen.svg)
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,18 @@
1
+ riskkit/__init__.py,sha256=9gsMtPovsgwKT25gyGnBVbPy2lIrb_KK5Z05qnP-3mo,2651
2
+ riskkit/correlation.py,sha256=FHmCYuyhdmZzCT695cIZOTSgdPV9W3Rve3YkE9kU-UQ,4230
3
+ riskkit/drawdown.py,sha256=kfmeQord_vl9rHjikEvvWrQa7LHFCbUISX74pyayJxI,5977
4
+ riskkit/manager.py,sha256=649JeReCiIgxaFllhx0kX5g6yJXtM29Vq4-vMVVItvA,25677
5
+ riskkit/metrics.py,sha256=D9EOwwCEHKgWIEVRnJAbx-kxnFfbr532cwZTy7kIUNU,1694
6
+ riskkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ riskkit/session.py,sha256=mAqcOQybTl6zkk4cbZ1o9aDe9SV4mr0xALCIZbP4Bd8,6877
8
+ riskkit/sizing.py,sha256=0y02WR0itWQyJAdfhUOFAV8B_5pqGhnpFpxOWBzSb5U,10157
9
+ riskkit/stops.py,sha256=im_vObTohLqJiQfYxatdTZWcrTNib-8ChHp5t7z8AVI,10192
10
+ riskkit/validator.py,sha256=MmjIkebfcIA6I18GuIGNi6jZ-7ycX5vPY9M67v0okdo,9923
11
+ riskkit/adapters/__init__.py,sha256=-a58ATahRvWrRRNb8WLv12s_Se_RbXeXpisTeQKljkg,295
12
+ riskkit/adapters/backtesting.py,sha256=uJRthhAJDLFvbmd6bobBT9XPZ1cHOl4X-Tl6Epg0Hlc,6837
13
+ riskkit/adapters/freqtrade.py,sha256=1rDBz4aGuT_Bfc_2y3pzCY3hjFGL5emPjulBYHZ5smY,6188
14
+ riskkit/adapters/vectorbt.py,sha256=Ii1ip2yL0QrrMxPzBtf3L8Y5Av0jamhHnqZ5RY5IhLY,4711
15
+ riskkit_quant-0.4.0.dist-info/METADATA,sha256=dkBA_wEqtgOVAPo0Rbb4WIDQSxOIDZ6Zv6uoza8t13U,11441
16
+ riskkit_quant-0.4.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
+ riskkit_quant-0.4.0.dist-info/licenses/LICENSE,sha256=yNlpPgqTSHw3GbzxIlF5ndbtwb1eEJmTlyet0zxILH4,1062
18
+ riskkit_quant-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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.