PositionSizing 1.0.0
See the version list below for details.
dotnet add package PositionSizing --version 1.0.0
NuGet\Install-Package PositionSizing -Version 1.0.0
<PackageReference Include="PositionSizing" Version="1.0.0" />
<PackageVersion Include="PositionSizing" Version="1.0.0" />
<PackageReference Include="PositionSizing" />
paket add PositionSizing --version 1.0.0
#r "nuget: PositionSizing, 1.0.0"
#:package PositionSizing@1.0.0
#addin nuget:?package=PositionSizing&version=1.0.0
#tool nuget:?package=PositionSizing&version=1.0.0
PositionSizing
A small, dependency-free .NET library covering the questions every systematic trading strategy has to answer on every trade — not just "how big should this bet be," but the whole arc from placing the initial stop, through sizing, through managing the stop while the trade is open, to grading the outcome once it's closed.
It has no dependencies beyond the .NET base class library, and no notion of a broker, a market, or a strategy — just the arithmetic. Reference it as a normal package, or copy the files you need directly into your project.
What's in it
Placing the initial stop and target
TargetPriceCalculator — places a stop-loss and take-profit level from an entry price, a
volatility measure (typically ATR), a stop-distance multiplier, and a risk:reward ratio. The
target distance is always a multiple of the stop distance, so it scales proportionally with
however wide or tight the stop is set.
Sizing the position — three independent philosophies
PositionSizeCalculator — the standard risk-budget approach: sizes a position under three
constraints (how much you're willing to lose if the stop is hit, a hard cap on capital per
trade, and how much portfolio exposure headroom remains) and returns the tightest one. A
position with no headroom left for even one unit returns null rather than a
zero-or-negative-sized position — "there's no size that fits" is a distinct, real outcome from
"here's a valid size."
KellyCriterion — a different starting point: given a measured edge (win probability and
payoff ratio), computes the fraction of equity that maximizes long-run growth, rather than a
fraction chosen independent of that edge. Includes fractional-Kelly (half-Kelly by default), the
standard practitioner adjustment for the large drawdowns full Kelly produces under real-world
estimation error.
VolatilityTargetSizing — a third approach, from risk-parity and vol-targeting practice:
sizes a position so its expected day-to-day P&L swing matches a fixed target, independent of any
stop-loss distance. A calm instrument gets a bigger position and a volatile one a smaller
position, for the same risk contribution.
Managing the stop while the trade is open
ChandelierStop — an ATR-based trailing stop that ratchets toward price as a trend extends,
tightening only, never loosening — a favorable move can't be undone by a later, less favorable
one pulling the stop back out.
BreakevenStop — the "once this trade can no longer be a loser" rule: moves the stop to
entry once price has moved a given multiple of the initial risk in your favor. Composes with
ChandelierStop: both only ever tighten, so calling either (or both, taking whichever is
tighter) as new bars arrive is always safe.
After the trade closes
RMultipleCalculator — grades a closed trade as a multiple of its initial risk (a "+2.4R"
winner or a "-0.6R" loser), the standard way to compare trade outcomes independent of how big any
particular position happened to be.
ScaleOutPlan — splits a position across profit-taking levels you supply (this library has
no opinion on which R-multiples or percentages make a good plan), correctly handling the
integer-rounding remainder so legs never lose or double-count a unit, and separately reporting
any quantity you deliberately left unassigned to run.
Instruments traded in lots
PositionSizeCalculator.ComputeForLotSize — a thin wrapper around the same risk-budget sizing
for instruments traded in fixed contract sizes (index futures and options): pass a lot size, and
the returned quantity is already expressed in whole lots, with capital already correctly scaled
to real notional value.
Install
Reference the project directly, or build and reference the package:
dotnet add reference path/to/PositionSizing.csproj
or pack it and add it as a local NuGet feed:
dotnet pack src/PositionSizing -o ./nupkg
dotnet add package PositionSizing --source ./nupkg
Usage
The core lifecycle: place, size, trail, grade
using PositionSizing;
// 1. Place the initial stop and target from ATR.
var levels = TargetPriceCalculator.Compute(
entryPrice: 100m, atrValue: 2.0, isShort: false,
atrStopMultiplier: 1.5m, riskRewardRatio: 2.0m);
// levels.StopLossPrice = 97, levels.TargetPrice = 106, levels.StopDistance = 3
// 2. Size the position.
var size = PositionSizeCalculator.Compute(
accountEquity: 1_000_000m, currentPrice: 100m, stopDistance: levels.StopDistance,
riskPerTradePercent: 0.005m, maxCapitalPerTradePercent: 0.25m,
openExposurePercent: 0.30m, maxExposurePercent: 0.60m);
if (size is null) { /* no exposure headroom left — skip the trade */ }
// 3. As new bars arrive, trail the stop (never loosens) and check the breakeven rule.
decimal stop = levels.StopLossPrice;
stop = ChandelierStop.Compute(stop, extremePriceSinceEntry: runningHighSinceEntry, atrValue: latestAtr, isShort: false);
stop = BreakevenStop.Compute(stop, entryPrice: 100m, currentPrice: latestPrice, initialStopDistance: levels.StopDistance, isShort: false);
// 4. Once closed, grade the outcome.
double? rMultiple = RMultipleCalculator.Compute(
entryPrice: 100m, exitPrice: 108m, initialStopDistance: levels.StopDistance, isShort: false);
Kelly sizing, from a measured edge
// From your own trade history: 55% win rate, average win 1.8x average loss.
double halfKellyFraction = KellyCriterion.ComputeFractional(winProbability: 0.55, payoffRatio: 1.8);
decimal riskBudget = accountEquity * (decimal)halfKellyFraction;
Volatility-targeted sizing, independent of a stop
decimal? quantity = VolatilityTargetSizing.Compute(
accountEquity: 1_000_000m, currentPrice: 100m,
dailyVolatilityPercent: 0.02m, // e.g. ATR / price
targetDailyRiskPercent: 0.01m); // this position should swing ~1% of equity on a normal day
Scaling out at caller-chosen levels
var (legs, remainder) = ScaleOutPlan.Compute(
totalQuantity: 100m,
levels: new[] { (RMultiple: 1.0m, ExitPercent: 0.5m) }); // half off at 1R, let the rest run
// legs[0].Quantity = 50, remainder = 50 (deliberately left to run)
Sizing a lot-based instrument (e.g. index options)
var lotSize = PositionSizeCalculator.ComputeForLotSize(
accountEquity: 1_000_000m,
underlyingPrice: 200m, // premium per unit, not multiplied by lot size
stopDistancePerUnit: 4m,
lotSize: 25,
riskPerTradePercent: 0.01m, maxCapitalPerTradePercent: 0.5m,
openExposurePercent: 0m, maxExposurePercent: 0.6m);
// lotSize.Quantity is already in LOTS; lotSize.CapitalDeployed is already correct notional value.
What this library does not do
- It does not decide whether to take the trade. Confidence thresholds, signal scoring, and regime filters are strategy decisions that belong upstream of this library, not inside it.
- It does not enforce policy-level portfolio gates like a daily loss limit or a maximum
position count. Those are one-line checks (
dailyPnL <= -dailyLossLimit,openPositionCount >= maxPositions) that differ enough in exact semantics between systems (does the loss limit include unrealized P&L? does it reset intraday?) that it isn't a clean shared primitive the way stop placement and capital sizing are — write the check that matches your own system's semantics. - It does not model margin or SPAN requirements.
ComputeForLotSize's capital figure is notional value (accurate real capital for a long option's premium), not a margin requirement — for futures or short options, actual capital consumed is a broker- and instrument-specific margin figure smaller than full notional, which this does not compute. - It does not know about correlation across positions. Exposure headroom sums capital across positions as if they were independent; two highly correlated positions carry more combined risk than two uncorrelated ones of the same size, and that's a portfolio-optimization problem (needing a real covariance estimate) well beyond what a small, dependency-free library should attempt.
- It does not fetch data. ATR, win rates, account equity, and open exposure are all inputs you provide.
Provenance
TargetPriceCalculator and PositionSizeCalculator's core sizing formulas were extracted from a
private trading-research platform's risk-management gate, where they were the one part of a much
larger, strategy-specific gate (AI-confidence thresholds, composite-score gates, regime filters,
session time windows) that had no dependency on any of that — pure arithmetic over equity, price,
and exposure. KellyCriterion, ChandelierStop, BreakevenStop, VolatilityTargetSizing,
RMultipleCalculator, ScaleOutPlan, and ComputeForLotSize were not part of that platform and
were written directly for this package, to round out the sizing, stop-management, and
trade-grading lifecycle around the extracted core.
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.