IndianTradingCosts 1.1.0

dotnet add package IndianTradingCosts --version 1.1.0
                    
NuGet\Install-Package IndianTradingCosts -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IndianTradingCosts" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IndianTradingCosts" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="IndianTradingCosts" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add IndianTradingCosts --version 1.1.0
                    
#r "nuget: IndianTradingCosts, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package IndianTradingCosts@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=IndianTradingCosts&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=IndianTradingCosts&version=1.1.0
                    
Install as a Cake Tool

IndianTradingCosts

A small, dependency-free .NET library that correctly computes real NSE equity and F&O transaction costs — brokerage, STT, stamp duty, exchange charges, SEBI fees, GST, and volatility-scaled slippage.

It exists because most retail backtesting tools and cost calculators get a handful of specific, easy-to-miss things wrong — and getting them wrong doesn't fail loudly. It just quietly makes a losing strategy look profitable, or a real edge look smaller than it is. This library encodes the correct rules once, with the reasoning written down next to the numbers, so you don't have to rediscover them the hard way.

It has no dependencies beyond the .NET base class library. You can drop the two source files into any project, or reference it as a normal library.

What it gets right that's easy to get wrong

Intraday (MIS) and delivery (CNC) are not the same cost structure, and the gap is large. STT alone is 8x higher on delivery (0.1% both sides) than on intraday (0.025%, sell-side only), and delivery adds a DP charge intraday never pays. A cost model that applies one flat rate to both product types will misprice every delivery trade it touches — often understating the real cost by more than half.

Buying and selling an option are not symmetric. STT applies to premium on SELL orders only — buying an option costs no STT at all, but pays stamp duty instead (a smaller charge, on a different base). A model that charges STT on every option leg regardless of direction will overcharge every long leg and get the total wrong for any multi-leg structure (spreads, straddles, collars).

An option that expires in-the-money is not "sold" — it's exercised, and the base the tax is charged on changes. Exercise-settlement STT is levied on the contract's intrinsic value, not on a premium, and it is charged to the option's holder (the buyer), never its writer.

That base is what makes it a cliff. Both rates are 0.15% since 1 April 2026 (the exercise rate used to be the higher of the two at 0.125% against 0.1%, but the Union Budget 2026 revision levelled them), so the sting no longer comes from the rate — it comes from what the rate is applied to. A leg sitting 500 points in the money has an intrinsic value far larger than the premium it would have cost to buy back, so riding to expiry is still materially more expensive than squaring off, even at an identical rate.

The party matters as much as the base: a short leg that finishes in-the-money is already a pure P&L loss for whoever sold it, and charging it exercise STT on top would double-count the same event from the wrong side of the trade. This one is easy to get backwards, and getting it backwards silently overstates costs for premium-sellers and understates them for premium-buyers.

Slippage is not a flat number — it should rise with real volatility. A flat "0.05% slippage" assumption is a reasonable guess on a calm day and a real understatement on a stressed one, which matters most for exactly the trades you'd most want to cost correctly: the ones that go through it. CalculateRoundTrip accepts an optional volatilityRatio (the trade's ATR divided by its price) and scales slippage up above a calm-day baseline, capped so one bad data point can't blow up the estimate.

Install

Reference the project directly, or build and reference the package:

dotnet add reference path/to/IndianTradingCosts.csproj

or pack it and add it as a local NuGet feed:

dotnet pack src/IndianTradingCosts -o ./nupkg
dotnet add package IndianTradingCosts --source ./nupkg

Usage

Equity, intraday (MIS)

using IndianTradingCosts;

// Bought 100 shares at ₹1,000, sold at ₹1,010, same day.
decimal cost = IndianMarketCosts.CalculateRoundTrip(
    entryPrice: 1_000m, exitPrice: 1_010m, quantity: 100m, isDelivery: false);

decimal grossPnl = (1_010m - 1_000m) * 100m; // ₹1,000
decimal netPnl   = grossPnl - cost;

Equity, delivery (CNC) — and why it matters to say so

// The SAME trade, held overnight instead of squared off intraday, pays a materially
// different cost — mainly because STT jumps from sell-side-only to both-sides.
decimal cncCost = IndianMarketCosts.CalculateRoundTrip(
    entryPrice: 1_000m, exitPrice: 1_010m, quantity: 100m, isDelivery: true);

// cncCost is meaningfully higher than the MIS cost above for the identical trade.

Scaling slippage to real volatility instead of a flat guess

// ATR (14-day average true range) for the stock, divided by its current price.
decimal atr = 25m;
decimal price = 1_000m;
decimal volatilityRatio = atr / price; // 0.025 -> 2.5% ATR/price, above the calm baseline

decimal cost = IndianMarketCosts.CalculateRoundTrip(
    entryPrice: 1_000m, exitPrice: 990m, quantity: 100m,
    isDelivery: false, volatilityRatio: volatilityRatio);
// Slippage inside `cost` is scaled up versus a calm-day trade at the same price/quantity.

Options — one leg, one order

// Sold 1 lot (65 units) of a NIFTY put at ₹150 premium, opening a short leg.
decimal openCost = IndianOptionsCosts.OrderCost(premium: 150m, quantity: 65m, isSellOrder: true);

// Bought it back later at ₹90 to close the position.
decimal closeCost = IndianOptionsCosts.OrderCost(premium: 90m, quantity: 65m, isSellOrder: false);

decimal grossPnl = (150m - 90m) * 65m;         // credit collected, minus what it cost to buy back
decimal netPnl   = grossPnl - openCost - closeCost;

Options — a multi-leg structure (e.g. a credit spread)

// Short 1 lot 17500 PE @ 150 (sell to open), long 1 lot 17400 PE @ 90 (buy to open) —
// a defined-risk put credit spread. Each leg is its own order; sum them.
decimal shortLegOpen = IndianOptionsCosts.OrderCost(150m, 65m, isSellOrder: true);
decimal longLegOpen  = IndianOptionsCosts.OrderCost(90m,  65m, isSellOrder: false);
decimal totalOpenCost = shortLegOpen + longLegOpen;

decimal creditReceived = (150m - 90m) * 65m;

Options — a leg that expires in-the-money

// Only call this for the position's LONG (bought) leg, and only if it finished ITM at expiry.
// Never call it for a short/sold leg — that leg's ITM outcome is already a P&L loss, not an
// additional charge.
decimal intrinsicValue = 35m; // e.g. strike 17400 put, underlying settled at 17365
decimal exerciseCost = IndianOptionsCosts.ExerciseSettlementCost(intrinsicValue, quantity: 65m);

Rates change — override them instead of waiting for a package update

Every rate is overridable. This is not a nicety: options STT moved from 0.1% to 0.15% on 1 April 2026, and this library carried the stale figure for about five months, understating the largest regulatory charge on option selling by a third the whole time. Nobody using it should have to wait for a maintainer to notice.

EquityCostRates and OptionsCostRates hold the full rate table. Both expose a Default carrying the built-in snapshot (last verified 27 August 2026), and both are records — so you use a with expression to change only what actually differs, without restating a dozen figures you don't care about:

using IndianTradingCosts;

// The government moved intraday STT and your broker charges for delivery.
var myRates = EquityCostRates.Default with
{
    MisSttSellPct   = 0.0003m,   // whatever the current rate is
    CncBrokeragePct = 0.005m,    // 0.5% on delivery, capped per order
};

decimal cost = IndianMarketCosts.CalculateRoundTrip(
    entryPrice: 1_000m, exitPrice: 1_010m, quantity: 100m,
    isDelivery: false, volatilityRatio: 0m, rates: myRates);
// Options: pass null for slippagePct to take it from the rate table too, so the whole
// cost model comes from one place.
var optionRates = OptionsCostRates.Default with { SttSellPremiumPct = 0.002m };

decimal legCost = IndianOptionsCosts.OrderCost(
    premium: 150m, quantity: 65m, isSellOrder: true, slippagePct: null, rates: optionRates);

Every overridable rate

EquityCostRates — defaults as of 27 August 2026:

Property Default Applies to
ExchangeChargePct 0.00307% NSE exchange charge, per side, on turnover
SebiFeesPct 0.0001% SEBI turnover fee, per side (₹10/crore)
GstRate 18% On brokerage + exchange + SEBI. Never on STT or stamp duty
SlippagePct 0.05% Calm-market slippage per side. An assumption, not a published rate
NormalDailyAtrRatio 1.5% ATR/price treated as calm; slippage scales up above it
MaxSlippageMultiplier 5.0 Ceiling on slippage scaling
MisBrokeragePct / MisBrokerageMaxPerOrder 0.03% / ₹20 Intraday brokerage — the lower of the two
MisSttSellPct 0.025% Intraday STT, sell side only
MisStampDutyBuyPct 0.003% Intraday stamp duty, buy side only
CncBrokeragePct / CncBrokerageMaxPerOrder 0% / ₹20 Delivery brokerage. Zero suits discount brokers; set it if yours charges
CncSttPctPerSide 0.1% Delivery STT, both sides
CncStampDutyBuyPct 0.015% Delivery stamp duty, buy side only
CncDpChargePerSell ₹15.34 Flat depository charge per delivery sell, GST-inclusive

OptionsCostRates:

Property Default Applies to
BrokeragePerOrder ₹20 Flat, per executed order
SttSellPremiumPct 0.15% STT on premium, sell orders only (raised from 0.1% on 1-Apr-2026)
SttExerciseIntrinsicPct 0.15% STT on intrinsic value at ITM expiry, charged to the holder
ExchangeChargePct 0.03553% NSE charge on premium turnover, both sides
StampDutyBuyPct 0.003% Stamp duty on buy-side premium only
SebiFeesPct 0.0001% SEBI turnover fee, per side
GstRate 18% On brokerage + exchange + SEBI
DefaultSlippagePct 0.2% Used when slippagePct is null. An assumption, not a rate

Three things worth knowing:

  • Existing calls are unaffected. The rate-aware methods are overloads, so every call written against the old signatures compiles, links and returns exactly the same number as before — there is a test asserting that.
  • A rate table is validated on use. A negative rate would flip a charge into a credit, so it throws rather than quietly paying you to trade. A MaxSlippageMultiplier below 1 throws too, since slippage scaling only ever raises the calm-market baseline.
  • There is deliberately no settable global default. A mutable static would let one test or backtest silently change the costs another one sees, and it would make comparing two rate regimes in the same process — pre- and post-revision, or two brokers — impossible. If you want one table application-wide, hold your own static readonly field and pass it.

This also makes rate history reproducible. To re-run a backtest at the rates that actually applied at the time, describe them:

var preApril2026 = OptionsCostRates.Default with
{
    SttSellPremiumPct       = 0.001m,
    SttExerciseIntrinsicPct = 0.00125m,
};

What this library does not do

  • It does not fetch live rates, margins, or market data — it is pure calculation from numbers you already have (prices, quantities, a volatility ratio if you want regime-aware slippage).
  • It does not model margin/SPAN requirements, capital blocking, or position sizing.
  • Rates change. STT, brokerage caps, exchange charges, and stamp duty are all set externally and revised periodically (STT on options last moved 1-Oct-2024; index F&O lot sizes have been revised more than once). This library gets the structure right — which side pays what, on what base, and why — but the constants are a snapshot as of the date noted in each file's doc comment. Check current rates before relying on this for a real trading decision, and update the constants when they change; the formulas around them are the part meant to last.

Provenance

Extracted from a private algorithmic-trading research platform, where this exact logic backs a real backtesting engine and has been checked by independent review more than once. The test suite in tests/IndianTradingCosts.Tests carries the same hand-verified worked examples that were validated there.

License

MIT — see LICENSE.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

Version Downloads Last Updated
1.1.0 142 8/31/2026
1.0.0 156 8/25/2026 1.0.0 is deprecated because it has critical bugs.