@lacspace/market 1.0.3 → 1.1.1

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.
package/README.md CHANGED
@@ -108,10 +108,38 @@ formatINR(1234567.5); // "₹12,34,567.50"
108
108
  | [`@lacspace/market-clock`](https://www.npmjs.com/package/@lacspace/market-clock) | Is the market open? holidays |
109
109
  | [`@lacspace/paper-trade`](https://www.npmjs.com/package/@lacspace/paper-trade) | Headless paper-trading engine |
110
110
 
111
+ ## New in 1.1 — options greeks & portfolio analytics
112
+
113
+ ```ts
114
+ import { blackScholes, impliedVolatility, sharpe, sortino, maxDrawdown, volatility, formatCompactINR } from "@lacspace/market";
115
+
116
+ // Black-Scholes price + greeks for a European option
117
+ const g = blackScholes({ type: "call", spot: 100, strike: 100, timeYears: 30/365, rate: 0.07, volatility: 0.25 });
118
+ // → { price, delta, gamma, theta, vega, rho }
119
+ impliedVolatility(marketPrice, { type: "call", spot: 100, strike: 100, timeYears: 30/365, rate: 0.07 });
120
+
121
+ // Portfolio stats from a returns / equity series
122
+ sharpe(returns); // annualized
123
+ sortino(returns); // downside-only
124
+ maxDrawdown(equityCurve); // { maxDrawdown: 0.25, peakIndex, troughIndex }
125
+
126
+ formatCompactINR(12345678); // "₹1.23 Cr"
127
+ ```
128
+
111
129
  ## Licensing
112
130
 
113
131
  This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
114
132
 
115
133
  Not every Lacspace package is free. We also offer **Commercial** (paid), **Client-specific**, and **Private** (proprietary) packages under separate terms. See the full **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
116
134
 
135
+ ---
136
+
137
+ <div align="center">
138
+
139
+ **Part of the Lacspace ecosystem — 35 zero-dependency, isomorphic TypeScript packages.**
140
+
141
+ [All packages ↗](https://lacspace.com/packages) · [npm org ↗](https://www.npmjs.com/org/lacspace) · [Licence Centre ↗](https://lacspace.com/licenses) · [GitHub ↗](https://github.com/lacspace/npm-packages)
142
+
143
+ </div>
144
+
117
145
  <div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · powers <a href="https://stockyatra.com">StockYatra</a> · Lacspace Free Licence · <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
package/dist/index.cjs CHANGED
@@ -161,18 +161,160 @@ function charges(input, config = IN_DISCOUNT_BROKER) {
161
161
  breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty)
162
162
  };
163
163
  }
164
+ function trimTo2(n) {
165
+ return Number(n.toFixed(2)).toString();
166
+ }
167
+ function formatCompactINR(amount, opts = {}) {
168
+ const { symbol = "\u20B9" } = opts;
169
+ const neg = amount < 0;
170
+ const a = Math.abs(amount);
171
+ const units = [
172
+ [1e7, "Cr"],
173
+ [1e5, "L"],
174
+ [1e3, "K"]
175
+ ];
176
+ const u = units.find(([size]) => a >= size);
177
+ const out = u ? `${trimTo2(a / u[0])} ${u[1]}` : trimTo2(a);
178
+ return `${neg ? "-" : ""}${symbol}${out}`;
179
+ }
180
+ function normPdf(x) {
181
+ return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
182
+ }
183
+ function normCdf(x) {
184
+ const t = 1 / (1 + 0.2316419 * Math.abs(x));
185
+ const d = 0.3989422804014327 * Math.exp(-x * x / 2);
186
+ const p = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));
187
+ return x > 0 ? 1 - p : p;
188
+ }
189
+ function blackScholes(o) {
190
+ const { type, spot: S, strike: K, timeYears: t, rate: r } = o;
191
+ const q = o.dividendYield ?? 0;
192
+ const sigma = o.volatility;
193
+ const sqrtT = Math.sqrt(t);
194
+ const d1 = (Math.log(S / K) + (r - q + sigma * sigma / 2) * t) / (sigma * sqrtT);
195
+ const d2 = d1 - sigma * sqrtT;
196
+ const disc = Math.exp(-r * t);
197
+ const dq = Math.exp(-q * t);
198
+ const nd1 = normPdf(d1);
199
+ const gamma = dq * nd1 / (S * sigma * sqrtT);
200
+ const vega = S * dq * nd1 * sqrtT;
201
+ if (type === "call") {
202
+ const Nd1 = normCdf(d1);
203
+ const Nd2 = normCdf(d2);
204
+ return {
205
+ price: S * dq * Nd1 - K * disc * Nd2,
206
+ delta: dq * Nd1,
207
+ gamma,
208
+ theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) - r * K * disc * Nd2 + q * S * dq * Nd1,
209
+ vega,
210
+ rho: K * t * disc * Nd2
211
+ };
212
+ }
213
+ const Nnd1 = normCdf(-d1);
214
+ const Nnd2 = normCdf(-d2);
215
+ return {
216
+ price: K * disc * Nnd2 - S * dq * Nnd1,
217
+ delta: -dq * Nnd1,
218
+ gamma,
219
+ theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) + r * K * disc * Nnd2 - q * S * dq * Nnd1,
220
+ vega,
221
+ rho: -K * t * disc * Nnd2
222
+ };
223
+ }
224
+ function impliedVolatility(marketPrice, o, opts = {}) {
225
+ const tol = opts.tolerance ?? 1e-6;
226
+ const maxIter = opts.maxIterations ?? 100;
227
+ let lo = 1e-4;
228
+ let hi = 5;
229
+ for (let i = 0; i < maxIter; i++) {
230
+ const mid = (lo + hi) / 2;
231
+ const price = blackScholes({ ...o, volatility: mid }).price;
232
+ const diff = price - marketPrice;
233
+ if (Math.abs(diff) < tol) return mid;
234
+ if (diff > 0) hi = mid;
235
+ else lo = mid;
236
+ }
237
+ return null;
238
+ }
239
+ function simpleReturns(series) {
240
+ const out = [];
241
+ for (let i = 1; i < series.length; i++) {
242
+ const prev = series[i - 1];
243
+ if (prev !== 0) out.push((series[i] - prev) / prev);
244
+ }
245
+ return out;
246
+ }
247
+ function mean(xs) {
248
+ return xs.length ? xs.reduce((s, x) => s + x, 0) / xs.length : 0;
249
+ }
250
+ function stdev(xs) {
251
+ if (xs.length < 2) return 0;
252
+ const m = mean(xs);
253
+ return Math.sqrt(xs.reduce((s, x) => s + (x - m) ** 2, 0) / (xs.length - 1));
254
+ }
255
+ function volatility(returns, opts = {}) {
256
+ const sd = stdev(returns);
257
+ return opts.annualize === false ? sd : sd * Math.sqrt(opts.periodsPerYear ?? 252);
258
+ }
259
+ function sharpe(returns, opts = {}) {
260
+ const ppy = opts.periodsPerYear ?? 252;
261
+ const rfPerPeriod = (opts.riskFree ?? 0) / ppy;
262
+ const excess = returns.map((r) => r - rfPerPeriod);
263
+ const sd = stdev(excess);
264
+ if (sd === 0) return 0;
265
+ return mean(excess) / sd * Math.sqrt(ppy);
266
+ }
267
+ function sortino(returns, opts = {}) {
268
+ const ppy = opts.periodsPerYear ?? 252;
269
+ const rfPerPeriod = (opts.riskFree ?? 0) / ppy;
270
+ const excess = returns.map((r) => r - rfPerPeriod);
271
+ const downside = excess.filter((r) => r < 0);
272
+ if (!downside.length) return 0;
273
+ const dd = Math.sqrt(downside.reduce((s, r) => s + r * r, 0) / downside.length);
274
+ if (dd === 0) return 0;
275
+ return mean(excess) / dd * Math.sqrt(ppy);
276
+ }
277
+ function maxDrawdown(equity) {
278
+ let peak = equity[0] ?? 0;
279
+ let peakIdx = 0;
280
+ let maxDd = 0;
281
+ let ddPeak = 0;
282
+ let ddTrough = 0;
283
+ for (let i = 0; i < equity.length; i++) {
284
+ const v = equity[i];
285
+ if (v > peak) {
286
+ peak = v;
287
+ peakIdx = i;
288
+ }
289
+ const dd = peak > 0 ? (peak - v) / peak : 0;
290
+ if (dd > maxDd) {
291
+ maxDd = dd;
292
+ ddPeak = peakIdx;
293
+ ddTrough = i;
294
+ }
295
+ }
296
+ return { maxDrawdown: maxDd, peakIndex: ddPeak, troughIndex: ddTrough };
297
+ }
164
298
 
165
299
  exports.IN_DISCOUNT_BROKER = IN_DISCOUNT_BROKER;
166
300
  exports.averagePrice = averagePrice;
301
+ exports.blackScholes = blackScholes;
167
302
  exports.cagr = cagr;
168
303
  exports.changePercent = changePercent;
169
304
  exports.charges = charges;
170
305
  exports.circuitLimits = circuitLimits;
306
+ exports.formatCompactINR = formatCompactINR;
171
307
  exports.formatINR = formatINR;
308
+ exports.impliedVolatility = impliedVolatility;
309
+ exports.maxDrawdown = maxDrawdown;
172
310
  exports.pnl = pnl;
173
311
  exports.pnlPercent = pnlPercent;
174
312
  exports.positionSize = positionSize;
175
313
  exports.roundToTick = roundToTick;
314
+ exports.sharpe = sharpe;
315
+ exports.simpleReturns = simpleReturns;
316
+ exports.sortino = sortino;
317
+ exports.volatility = volatility;
176
318
  exports.xirr = xirr;
177
319
  //# sourceMappingURL=index.cjs.map
178
320
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["r"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/market\n * The money & mechanics toolkit every stock-market app re-implements.\n *\n * P&L, returns, CAGR, XIRR, tick-size rounding, circuit limits, position sizing\n * — plus a real Indian brokerage & charges calculator (STT, GST, SEBI, stamp,\n * exchange txn) with discount-broker presets.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\n/* ------------------------------------------------------------------ *\n * Formatting\n * ------------------------------------------------------------------ */\n\nexport interface FormatMoneyOptions {\n symbol?: string;\n decimals?: number;\n}\n\n/**\n * Format a number in the Indian numbering system (lakh / crore grouping).\n * @example formatINR(1234567.5) // \"₹12,34,567.50\"\n */\nexport function formatINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\", decimals = 2 } = opts;\n const neg = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart = \"0\", frac = \"\"] = fixed.split(\".\");\n const last3 = intPart.slice(-3);\n const rest = intPart.slice(0, -3);\n const grouped = rest\n ? rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3\n : last3;\n return `${neg ? \"-\" : \"\"}${symbol}${grouped}${decimals > 0 ? \".\" + frac : \"\"}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Returns & P&L\n * ------------------------------------------------------------------ */\n\n/** Absolute profit/loss for a round-trip. */\nexport function pnl(o: { buy: number; sell: number; qty: number }): number {\n return (o.sell - o.buy) * o.qty;\n}\n\n/** Percentage change from `reference` to `current` (e.g. LTP vs prev close). */\nexport function changePercent(current: number, reference: number): number {\n if (reference === 0) return 0;\n return ((current - reference) / reference) * 100;\n}\n\n/** Profit/loss as a percentage of the buy price. */\nexport function pnlPercent(o: { buy: number; sell: number }): number {\n return changePercent(o.sell, o.buy);\n}\n\n/** Compound Annual Growth Rate as a fraction (0.15 = 15%). */\nexport function cagr(begin: number, end: number, years: number): number {\n if (begin <= 0 || years <= 0) return NaN;\n return Math.pow(end / begin, 1 / years) - 1;\n}\n\nexport interface CashFlow {\n /** Negative = money out (investment), positive = money in (redemption). */\n amount: number;\n date: Date | string | number;\n}\n\nfunction toMillis(d: Date | string | number): number {\n if (d instanceof Date) return d.getTime();\n if (typeof d === \"number\") return d;\n return new Date(d).getTime();\n}\n\n/**\n * Extended Internal Rate of Return for irregularly-spaced cash flows.\n * Returns an annualised rate as a fraction. Uses Newton–Raphson.\n * @example xirr([{amount:-10000, date:\"2024-01-01\"}, {amount:12000, date:\"2025-01-01\"}]) // ~0.20\n */\nexport function xirr(flows: CashFlow[], guess = 0.1): number {\n if (flows.length < 2) return NaN;\n const cf = flows\n .map((f) => ({ amount: f.amount, t: toMillis(f.date) }))\n .sort((a, b) => a.t - b.t);\n const t0 = cf[0]!.t;\n const yearFrac = (t: number) => (t - t0) / (365 * 24 * 3600 * 1000);\n const npv = (r: number) =>\n cf.reduce((s, c) => s + c.amount / Math.pow(1 + r, yearFrac(c.t)), 0);\n const dnpv = (r: number) =>\n cf.reduce((s, c) => {\n const y = yearFrac(c.t);\n return s - (y * c.amount) / Math.pow(1 + r, y + 1);\n }, 0);\n\n let r = guess;\n for (let i = 0; i < 100; i++) {\n const f = npv(r);\n if (Math.abs(f) < 1e-7) return r;\n const d = dnpv(r);\n if (d === 0) break;\n const next = r - f / d;\n if (!isFinite(next)) break;\n if (Math.abs(next - r) < 1e-10) return next;\n r = next;\n }\n return r;\n}\n\n/* ------------------------------------------------------------------ *\n * Position mechanics\n * ------------------------------------------------------------------ */\n\n/** Volume-weighted average price across a set of trades. */\nexport function averagePrice(trades: { price: number; qty: number }[]): number {\n let qty = 0;\n let value = 0;\n for (const t of trades) {\n qty += t.qty;\n value += t.price * t.qty;\n }\n return qty === 0 ? 0 : value / qty;\n}\n\n/**\n * Risk-based position sizing. Returns the whole-share quantity so that a stop-out\n * costs at most `riskPercent` of capital.\n * @example positionSize({ capital: 100000, riskPercent: 1, entry: 500, stop: 480 }) // 50\n */\nexport function positionSize(o: {\n capital: number;\n riskPercent: number;\n entry: number;\n stop: number;\n}): number {\n const riskAmount = o.capital * (o.riskPercent / 100);\n const perShareRisk = Math.abs(o.entry - o.stop);\n if (perShareRisk === 0) return 0;\n return Math.floor(riskAmount / perShareRisk);\n}\n\n/** Round a price to the nearest exchange tick (default ₹0.05 for NSE equity). */\nexport function roundToTick(price: number, tick = 0.05): number {\n if (tick <= 0) return price;\n return Number((Math.round(price / tick) * tick).toFixed(4));\n}\n\n/** Upper & lower circuit price for a given previous close and band percent. */\nexport function circuitLimits(\n prevClose: number,\n percent: number,\n): { upper: number; lower: number } {\n const delta = prevClose * (percent / 100);\n return {\n upper: roundToTick(prevClose + delta),\n lower: roundToTick(prevClose - delta),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Brokerage & statutory charges (India)\n * ------------------------------------------------------------------ */\n\nexport type Segment = \"delivery\" | \"intraday\" | \"futures\" | \"options\";\n\nexport interface SegmentRates {\n /** Brokerage as a fraction of turnover per side (0.0003 = 0.03%). */\n brokeragePct: number;\n /** Per-order brokerage cap (₹). */\n brokerageCap: number;\n /** Flat per-order brokerage (₹) — overrides the pct/cap model when set. */\n brokerageFlat?: number;\n sttBuy: number;\n sttSell: number;\n exchangeTxn: number;\n stampBuy: number;\n /** Depository (DP) charge per scrip on the sell leg (₹). */\n dpPerScrip: number;\n}\n\nexport interface ChargeConfig {\n segments: Record<Segment, SegmentRates>;\n sebi: number;\n gst: number;\n}\n\n/**\n * Default rates approximating an Indian discount broker (Zerodha-style) as of\n * FY2024–25. Statutory rates change — override any field via the `config`\n * argument of {@link charges} and always verify against the live rate card.\n */\nexport const IN_DISCOUNT_BROKER: ChargeConfig = {\n segments: {\n delivery: {\n brokeragePct: 0,\n brokerageCap: 0,\n sttBuy: 0.001,\n sttSell: 0.001,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00015,\n dpPerScrip: 13.5,\n },\n intraday: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.00025,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n futures: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.0002,\n exchangeTxn: 0.0000173,\n stampBuy: 0.00002,\n dpPerScrip: 0,\n },\n options: {\n brokeragePct: 0,\n brokerageCap: 20,\n brokerageFlat: 20,\n sttBuy: 0,\n sttSell: 0.001,\n exchangeTxn: 0.0003503,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n },\n sebi: 0.000001, // ₹10 per crore\n gst: 0.18,\n};\n\nexport interface ChargeInput {\n segment: Segment;\n /** Buy price per unit. Omit / 0 for a sell-only leg. */\n buy: number;\n /** Sell price per unit. Omit / 0 for a buy-only leg. */\n sell: number;\n qty: number;\n}\n\nexport interface ChargeBreakdown {\n turnover: number;\n brokerage: number;\n stt: number;\n exchangeTxn: number;\n sebi: number;\n stamp: number;\n gst: number;\n dp: number;\n totalCharges: number;\n grossPnl: number;\n netPnl: number;\n /** Per-share price move needed just to break even on charges. */\n breakeven: number;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nfunction legBrokerage(turnover: number, r: SegmentRates): number {\n if (turnover <= 0) return 0;\n if (r.brokerageFlat !== undefined) return r.brokerageFlat;\n if (r.brokeragePct === 0) return 0;\n return Math.min(turnover * r.brokeragePct, r.brokerageCap);\n}\n\n/**\n * Full brokerage + statutory charges breakdown for a trade, Indian market.\n * @example\n * charges({ segment: \"intraday\", buy: 100, sell: 102, qty: 500 });\n * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }\n */\nexport function charges(\n input: ChargeInput,\n config: ChargeConfig = IN_DISCOUNT_BROKER,\n): ChargeBreakdown {\n const r = config.segments[input.segment];\n const buyVal = (input.buy || 0) * input.qty;\n const sellVal = (input.sell || 0) * input.qty;\n const turnover = buyVal + sellVal;\n\n const brokerage = legBrokerage(buyVal, r) + legBrokerage(sellVal, r);\n const stt = buyVal * r.sttBuy + sellVal * r.sttSell;\n const exchangeTxn = turnover * r.exchangeTxn;\n const sebi = turnover * config.sebi;\n const stamp = buyVal * r.stampBuy;\n const gst = (brokerage + exchangeTxn + sebi) * config.gst;\n const dp = sellVal > 0 ? r.dpPerScrip : 0;\n\n const totalCharges = brokerage + stt + exchangeTxn + sebi + stamp + gst + dp;\n const grossPnl = sellVal - buyVal;\n\n return {\n turnover: round2(turnover),\n brokerage: round2(brokerage),\n stt: round2(stt),\n exchangeTxn: round2(exchangeTxn),\n sebi: round2(sebi),\n stamp: round2(stamp),\n gst: round2(gst),\n dp: round2(dp),\n totalCharges: round2(totalCharges),\n grossPnl: round2(grossPnl),\n netPnl: round2(grossPnl - totalCharges),\n breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["r"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/market\n * The money & mechanics toolkit every stock-market app re-implements.\n *\n * P&L, returns, CAGR, XIRR, tick-size rounding, circuit limits, position sizing\n * — plus a real Indian brokerage & charges calculator (STT, GST, SEBI, stamp,\n * exchange txn) with discount-broker presets.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\n/* ------------------------------------------------------------------ *\n * Formatting\n * ------------------------------------------------------------------ */\n\nexport interface FormatMoneyOptions {\n symbol?: string;\n decimals?: number;\n}\n\n/**\n * Format a number in the Indian numbering system (lakh / crore grouping).\n * @example formatINR(1234567.5) // \"₹12,34,567.50\"\n */\nexport function formatINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\", decimals = 2 } = opts;\n const neg = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart = \"0\", frac = \"\"] = fixed.split(\".\");\n const last3 = intPart.slice(-3);\n const rest = intPart.slice(0, -3);\n const grouped = rest\n ? rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3\n : last3;\n return `${neg ? \"-\" : \"\"}${symbol}${grouped}${decimals > 0 ? \".\" + frac : \"\"}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Returns & P&L\n * ------------------------------------------------------------------ */\n\n/** Absolute profit/loss for a round-trip. */\nexport function pnl(o: { buy: number; sell: number; qty: number }): number {\n return (o.sell - o.buy) * o.qty;\n}\n\n/** Percentage change from `reference` to `current` (e.g. LTP vs prev close). */\nexport function changePercent(current: number, reference: number): number {\n if (reference === 0) return 0;\n return ((current - reference) / reference) * 100;\n}\n\n/** Profit/loss as a percentage of the buy price. */\nexport function pnlPercent(o: { buy: number; sell: number }): number {\n return changePercent(o.sell, o.buy);\n}\n\n/** Compound Annual Growth Rate as a fraction (0.15 = 15%). */\nexport function cagr(begin: number, end: number, years: number): number {\n if (begin <= 0 || years <= 0) return NaN;\n return Math.pow(end / begin, 1 / years) - 1;\n}\n\nexport interface CashFlow {\n /** Negative = money out (investment), positive = money in (redemption). */\n amount: number;\n date: Date | string | number;\n}\n\nfunction toMillis(d: Date | string | number): number {\n if (d instanceof Date) return d.getTime();\n if (typeof d === \"number\") return d;\n return new Date(d).getTime();\n}\n\n/**\n * Extended Internal Rate of Return for irregularly-spaced cash flows.\n * Returns an annualised rate as a fraction. Uses Newton–Raphson.\n * @example xirr([{amount:-10000, date:\"2024-01-01\"}, {amount:12000, date:\"2025-01-01\"}]) // ~0.20\n */\nexport function xirr(flows: CashFlow[], guess = 0.1): number {\n if (flows.length < 2) return NaN;\n const cf = flows\n .map((f) => ({ amount: f.amount, t: toMillis(f.date) }))\n .sort((a, b) => a.t - b.t);\n const t0 = cf[0]!.t;\n const yearFrac = (t: number) => (t - t0) / (365 * 24 * 3600 * 1000);\n const npv = (r: number) =>\n cf.reduce((s, c) => s + c.amount / Math.pow(1 + r, yearFrac(c.t)), 0);\n const dnpv = (r: number) =>\n cf.reduce((s, c) => {\n const y = yearFrac(c.t);\n return s - (y * c.amount) / Math.pow(1 + r, y + 1);\n }, 0);\n\n let r = guess;\n for (let i = 0; i < 100; i++) {\n const f = npv(r);\n if (Math.abs(f) < 1e-7) return r;\n const d = dnpv(r);\n if (d === 0) break;\n const next = r - f / d;\n if (!isFinite(next)) break;\n if (Math.abs(next - r) < 1e-10) return next;\n r = next;\n }\n return r;\n}\n\n/* ------------------------------------------------------------------ *\n * Position mechanics\n * ------------------------------------------------------------------ */\n\n/** Volume-weighted average price across a set of trades. */\nexport function averagePrice(trades: { price: number; qty: number }[]): number {\n let qty = 0;\n let value = 0;\n for (const t of trades) {\n qty += t.qty;\n value += t.price * t.qty;\n }\n return qty === 0 ? 0 : value / qty;\n}\n\n/**\n * Risk-based position sizing. Returns the whole-share quantity so that a stop-out\n * costs at most `riskPercent` of capital.\n * @example positionSize({ capital: 100000, riskPercent: 1, entry: 500, stop: 480 }) // 50\n */\nexport function positionSize(o: {\n capital: number;\n riskPercent: number;\n entry: number;\n stop: number;\n}): number {\n const riskAmount = o.capital * (o.riskPercent / 100);\n const perShareRisk = Math.abs(o.entry - o.stop);\n if (perShareRisk === 0) return 0;\n return Math.floor(riskAmount / perShareRisk);\n}\n\n/** Round a price to the nearest exchange tick (default ₹0.05 for NSE equity). */\nexport function roundToTick(price: number, tick = 0.05): number {\n if (tick <= 0) return price;\n return Number((Math.round(price / tick) * tick).toFixed(4));\n}\n\n/** Upper & lower circuit price for a given previous close and band percent. */\nexport function circuitLimits(\n prevClose: number,\n percent: number,\n): { upper: number; lower: number } {\n const delta = prevClose * (percent / 100);\n return {\n upper: roundToTick(prevClose + delta),\n lower: roundToTick(prevClose - delta),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Brokerage & statutory charges (India)\n * ------------------------------------------------------------------ */\n\nexport type Segment = \"delivery\" | \"intraday\" | \"futures\" | \"options\";\n\nexport interface SegmentRates {\n /** Brokerage as a fraction of turnover per side (0.0003 = 0.03%). */\n brokeragePct: number;\n /** Per-order brokerage cap (₹). */\n brokerageCap: number;\n /** Flat per-order brokerage (₹) — overrides the pct/cap model when set. */\n brokerageFlat?: number;\n sttBuy: number;\n sttSell: number;\n exchangeTxn: number;\n stampBuy: number;\n /** Depository (DP) charge per scrip on the sell leg (₹). */\n dpPerScrip: number;\n}\n\nexport interface ChargeConfig {\n segments: Record<Segment, SegmentRates>;\n sebi: number;\n gst: number;\n}\n\n/**\n * Default rates approximating an Indian discount broker (Zerodha-style) as of\n * FY2024–25. Statutory rates change — override any field via the `config`\n * argument of {@link charges} and always verify against the live rate card.\n */\nexport const IN_DISCOUNT_BROKER: ChargeConfig = {\n segments: {\n delivery: {\n brokeragePct: 0,\n brokerageCap: 0,\n sttBuy: 0.001,\n sttSell: 0.001,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00015,\n dpPerScrip: 13.5,\n },\n intraday: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.00025,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n futures: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.0002,\n exchangeTxn: 0.0000173,\n stampBuy: 0.00002,\n dpPerScrip: 0,\n },\n options: {\n brokeragePct: 0,\n brokerageCap: 20,\n brokerageFlat: 20,\n sttBuy: 0,\n sttSell: 0.001,\n exchangeTxn: 0.0003503,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n },\n sebi: 0.000001, // ₹10 per crore\n gst: 0.18,\n};\n\nexport interface ChargeInput {\n segment: Segment;\n /** Buy price per unit. Omit / 0 for a sell-only leg. */\n buy: number;\n /** Sell price per unit. Omit / 0 for a buy-only leg. */\n sell: number;\n qty: number;\n}\n\nexport interface ChargeBreakdown {\n turnover: number;\n brokerage: number;\n stt: number;\n exchangeTxn: number;\n sebi: number;\n stamp: number;\n gst: number;\n dp: number;\n totalCharges: number;\n grossPnl: number;\n netPnl: number;\n /** Per-share price move needed just to break even on charges. */\n breakeven: number;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nfunction legBrokerage(turnover: number, r: SegmentRates): number {\n if (turnover <= 0) return 0;\n if (r.brokerageFlat !== undefined) return r.brokerageFlat;\n if (r.brokeragePct === 0) return 0;\n return Math.min(turnover * r.brokeragePct, r.brokerageCap);\n}\n\n/**\n * Full brokerage + statutory charges breakdown for a trade, Indian market.\n * @example\n * charges({ segment: \"intraday\", buy: 100, sell: 102, qty: 500 });\n * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }\n */\nexport function charges(\n input: ChargeInput,\n config: ChargeConfig = IN_DISCOUNT_BROKER,\n): ChargeBreakdown {\n const r = config.segments[input.segment];\n const buyVal = (input.buy || 0) * input.qty;\n const sellVal = (input.sell || 0) * input.qty;\n const turnover = buyVal + sellVal;\n\n const brokerage = legBrokerage(buyVal, r) + legBrokerage(sellVal, r);\n const stt = buyVal * r.sttBuy + sellVal * r.sttSell;\n const exchangeTxn = turnover * r.exchangeTxn;\n const sebi = turnover * config.sebi;\n const stamp = buyVal * r.stampBuy;\n const gst = (brokerage + exchangeTxn + sebi) * config.gst;\n const dp = sellVal > 0 ? r.dpPerScrip : 0;\n\n const totalCharges = brokerage + stt + exchangeTxn + sebi + stamp + gst + dp;\n const grossPnl = sellVal - buyVal;\n\n return {\n turnover: round2(turnover),\n brokerage: round2(brokerage),\n stt: round2(stt),\n exchangeTxn: round2(exchangeTxn),\n sebi: round2(sebi),\n stamp: round2(stamp),\n gst: round2(gst),\n dp: round2(dp),\n totalCharges: round2(totalCharges),\n grossPnl: round2(grossPnl),\n netPnl: round2(grossPnl - totalCharges),\n breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Compact formatting\n * ------------------------------------------------------------------ */\n\nfunction trimTo2(n: number): string {\n return Number(n.toFixed(2)).toString();\n}\n\n/** Compact INR: `formatCompactINR(12345678)` → `\"₹1.23 Cr\"`. */\nexport function formatCompactINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\" } = opts;\n const neg = amount < 0;\n const a = Math.abs(amount);\n const units: [number, string][] = [\n [1e7, \"Cr\"],\n [1e5, \"L\"],\n [1e3, \"K\"],\n ];\n const u = units.find(([size]) => a >= size);\n const out = u ? `${trimTo2(a / u[0])} ${u[1]}` : trimTo2(a);\n return `${neg ? \"-\" : \"\"}${symbol}${out}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Options — Black-Scholes greeks\n * ------------------------------------------------------------------ */\n\nfunction normPdf(x: number): number {\n return Math.exp((-x * x) / 2) / Math.sqrt(2 * Math.PI);\n}\n\n/** Standard normal CDF (Abramowitz & Stegun 7.1.26 approximation). */\nfunction normCdf(x: number): number {\n const t = 1 / (1 + 0.2316419 * Math.abs(x));\n const d = 0.3989422804014327 * Math.exp((-x * x) / 2);\n const p = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));\n return x > 0 ? 1 - p : p;\n}\n\nexport interface OptionInput {\n type: \"call\" | \"put\";\n /** Spot price of the underlying. */\n spot: number;\n strike: number;\n /** Time to expiry in years (e.g. 30 days ≈ 30/365). */\n timeYears: number;\n /** Risk-free rate (annual, decimal — e.g. 0.07). */\n rate: number;\n /** Volatility (annual, decimal — e.g. 0.25). */\n volatility: number;\n /** Continuous dividend yield (annual, decimal). Default 0. */\n dividendYield?: number;\n}\n\nexport interface Greeks {\n price: number;\n delta: number;\n gamma: number;\n /** Per-year theta (divide by 365 for per-day). */\n theta: number;\n /** Per 1.00 change in volatility (divide by 100 for per 1%). */\n vega: number;\n /** Per 1.00 change in rate (divide by 100 for per 1%). */\n rho: number;\n}\n\n/** Black-Scholes price + greeks for a European option. */\nexport function blackScholes(o: OptionInput): Greeks {\n const { type, spot: S, strike: K, timeYears: t, rate: r } = o;\n const q = o.dividendYield ?? 0;\n const sigma = o.volatility;\n const sqrtT = Math.sqrt(t);\n const d1 = (Math.log(S / K) + (r - q + (sigma * sigma) / 2) * t) / (sigma * sqrtT);\n const d2 = d1 - sigma * sqrtT;\n const disc = Math.exp(-r * t);\n const dq = Math.exp(-q * t);\n const nd1 = normPdf(d1);\n const gamma = (dq * nd1) / (S * sigma * sqrtT);\n const vega = S * dq * nd1 * sqrtT;\n if (type === \"call\") {\n const Nd1 = normCdf(d1);\n const Nd2 = normCdf(d2);\n return {\n price: S * dq * Nd1 - K * disc * Nd2,\n delta: dq * Nd1,\n gamma,\n theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) - r * K * disc * Nd2 + q * S * dq * Nd1,\n vega,\n rho: K * t * disc * Nd2,\n };\n }\n const Nnd1 = normCdf(-d1);\n const Nnd2 = normCdf(-d2);\n return {\n price: K * disc * Nnd2 - S * dq * Nnd1,\n delta: -dq * Nnd1,\n gamma,\n theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) + r * K * disc * Nnd2 - q * S * dq * Nnd1,\n vega,\n rho: -K * t * disc * Nnd2,\n };\n}\n\n/** Solve implied volatility from an observed option price (bisection). */\nexport function impliedVolatility(\n marketPrice: number,\n o: Omit<OptionInput, \"volatility\">,\n opts: { tolerance?: number; maxIterations?: number } = {},\n): number | null {\n const tol = opts.tolerance ?? 1e-6;\n const maxIter = opts.maxIterations ?? 100;\n let lo = 1e-4;\n let hi = 5;\n for (let i = 0; i < maxIter; i++) {\n const mid = (lo + hi) / 2;\n const price = blackScholes({ ...o, volatility: mid }).price;\n const diff = price - marketPrice;\n if (Math.abs(diff) < tol) return mid;\n if (diff > 0) hi = mid;\n else lo = mid;\n }\n return null;\n}\n\n/* ------------------------------------------------------------------ *\n * Portfolio analytics\n * ------------------------------------------------------------------ */\n\n/** Period-over-period simple returns from a price/equity series. */\nexport function simpleReturns(series: number[]): number[] {\n const out: number[] = [];\n for (let i = 1; i < series.length; i++) {\n const prev = series[i - 1]!;\n if (prev !== 0) out.push((series[i]! - prev) / prev);\n }\n return out;\n}\n\nfunction mean(xs: number[]): number {\n return xs.length ? xs.reduce((s, x) => s + x, 0) / xs.length : 0;\n}\n\nfunction stdev(xs: number[]): number {\n if (xs.length < 2) return 0;\n const m = mean(xs);\n return Math.sqrt(xs.reduce((s, x) => s + (x - m) ** 2, 0) / (xs.length - 1));\n}\n\n/** Volatility (stdev of returns); annualized by default. */\nexport function volatility(returns: number[], opts: { annualize?: boolean; periodsPerYear?: number } = {}): number {\n const sd = stdev(returns);\n return opts.annualize === false ? sd : sd * Math.sqrt(opts.periodsPerYear ?? 252);\n}\n\n/** Annualized Sharpe ratio from a return series. */\nexport function sharpe(returns: number[], opts: { riskFree?: number; periodsPerYear?: number } = {}): number {\n const ppy = opts.periodsPerYear ?? 252;\n const rfPerPeriod = (opts.riskFree ?? 0) / ppy;\n const excess = returns.map((r) => r - rfPerPeriod);\n const sd = stdev(excess);\n if (sd === 0) return 0;\n return (mean(excess) / sd) * Math.sqrt(ppy);\n}\n\n/** Annualized Sortino ratio (downside-deviation only). */\nexport function sortino(returns: number[], opts: { riskFree?: number; periodsPerYear?: number } = {}): number {\n const ppy = opts.periodsPerYear ?? 252;\n const rfPerPeriod = (opts.riskFree ?? 0) / ppy;\n const excess = returns.map((r) => r - rfPerPeriod);\n const downside = excess.filter((r) => r < 0);\n if (!downside.length) return 0;\n const dd = Math.sqrt(downside.reduce((s, r) => s + r * r, 0) / downside.length);\n if (dd === 0) return 0;\n return (mean(excess) / dd) * Math.sqrt(ppy);\n}\n\n/** Maximum drawdown of an equity curve, as a positive fraction (0.2 = −20%). */\nexport function maxDrawdown(equity: number[]): { maxDrawdown: number; peakIndex: number; troughIndex: number } {\n let peak = equity[0] ?? 0;\n let peakIdx = 0;\n let maxDd = 0;\n let ddPeak = 0;\n let ddTrough = 0;\n for (let i = 0; i < equity.length; i++) {\n const v = equity[i]!;\n if (v > peak) {\n peak = v;\n peakIdx = i;\n }\n const dd = peak > 0 ? (peak - v) / peak : 0;\n if (dd > maxDd) {\n maxDd = dd;\n ddPeak = peakIdx;\n ddTrough = i;\n }\n }\n return { maxDrawdown: maxDd, peakIndex: ddPeak, troughIndex: ddTrough };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -122,5 +122,62 @@ interface ChargeBreakdown {
122
122
  * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }
123
123
  */
124
124
  declare function charges(input: ChargeInput, config?: ChargeConfig): ChargeBreakdown;
125
+ /** Compact INR: `formatCompactINR(12345678)` → `"₹1.23 Cr"`. */
126
+ declare function formatCompactINR(amount: number, opts?: FormatMoneyOptions): string;
127
+ interface OptionInput {
128
+ type: "call" | "put";
129
+ /** Spot price of the underlying. */
130
+ spot: number;
131
+ strike: number;
132
+ /** Time to expiry in years (e.g. 30 days ≈ 30/365). */
133
+ timeYears: number;
134
+ /** Risk-free rate (annual, decimal — e.g. 0.07). */
135
+ rate: number;
136
+ /** Volatility (annual, decimal — e.g. 0.25). */
137
+ volatility: number;
138
+ /** Continuous dividend yield (annual, decimal). Default 0. */
139
+ dividendYield?: number;
140
+ }
141
+ interface Greeks {
142
+ price: number;
143
+ delta: number;
144
+ gamma: number;
145
+ /** Per-year theta (divide by 365 for per-day). */
146
+ theta: number;
147
+ /** Per 1.00 change in volatility (divide by 100 for per 1%). */
148
+ vega: number;
149
+ /** Per 1.00 change in rate (divide by 100 for per 1%). */
150
+ rho: number;
151
+ }
152
+ /** Black-Scholes price + greeks for a European option. */
153
+ declare function blackScholes(o: OptionInput): Greeks;
154
+ /** Solve implied volatility from an observed option price (bisection). */
155
+ declare function impliedVolatility(marketPrice: number, o: Omit<OptionInput, "volatility">, opts?: {
156
+ tolerance?: number;
157
+ maxIterations?: number;
158
+ }): number | null;
159
+ /** Period-over-period simple returns from a price/equity series. */
160
+ declare function simpleReturns(series: number[]): number[];
161
+ /** Volatility (stdev of returns); annualized by default. */
162
+ declare function volatility(returns: number[], opts?: {
163
+ annualize?: boolean;
164
+ periodsPerYear?: number;
165
+ }): number;
166
+ /** Annualized Sharpe ratio from a return series. */
167
+ declare function sharpe(returns: number[], opts?: {
168
+ riskFree?: number;
169
+ periodsPerYear?: number;
170
+ }): number;
171
+ /** Annualized Sortino ratio (downside-deviation only). */
172
+ declare function sortino(returns: number[], opts?: {
173
+ riskFree?: number;
174
+ periodsPerYear?: number;
175
+ }): number;
176
+ /** Maximum drawdown of an equity curve, as a positive fraction (0.2 = −20%). */
177
+ declare function maxDrawdown(equity: number[]): {
178
+ maxDrawdown: number;
179
+ peakIndex: number;
180
+ troughIndex: number;
181
+ };
125
182
 
126
- export { type CashFlow, type ChargeBreakdown, type ChargeConfig, type ChargeInput, type FormatMoneyOptions, IN_DISCOUNT_BROKER, type Segment, type SegmentRates, averagePrice, cagr, changePercent, charges, circuitLimits, formatINR, pnl, pnlPercent, positionSize, roundToTick, xirr };
183
+ export { type CashFlow, type ChargeBreakdown, type ChargeConfig, type ChargeInput, type FormatMoneyOptions, type Greeks, IN_DISCOUNT_BROKER, type OptionInput, type Segment, type SegmentRates, averagePrice, blackScholes, cagr, changePercent, charges, circuitLimits, formatCompactINR, formatINR, impliedVolatility, maxDrawdown, pnl, pnlPercent, positionSize, roundToTick, sharpe, simpleReturns, sortino, volatility, xirr };
package/dist/index.d.ts CHANGED
@@ -122,5 +122,62 @@ interface ChargeBreakdown {
122
122
  * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }
123
123
  */
124
124
  declare function charges(input: ChargeInput, config?: ChargeConfig): ChargeBreakdown;
125
+ /** Compact INR: `formatCompactINR(12345678)` → `"₹1.23 Cr"`. */
126
+ declare function formatCompactINR(amount: number, opts?: FormatMoneyOptions): string;
127
+ interface OptionInput {
128
+ type: "call" | "put";
129
+ /** Spot price of the underlying. */
130
+ spot: number;
131
+ strike: number;
132
+ /** Time to expiry in years (e.g. 30 days ≈ 30/365). */
133
+ timeYears: number;
134
+ /** Risk-free rate (annual, decimal — e.g. 0.07). */
135
+ rate: number;
136
+ /** Volatility (annual, decimal — e.g. 0.25). */
137
+ volatility: number;
138
+ /** Continuous dividend yield (annual, decimal). Default 0. */
139
+ dividendYield?: number;
140
+ }
141
+ interface Greeks {
142
+ price: number;
143
+ delta: number;
144
+ gamma: number;
145
+ /** Per-year theta (divide by 365 for per-day). */
146
+ theta: number;
147
+ /** Per 1.00 change in volatility (divide by 100 for per 1%). */
148
+ vega: number;
149
+ /** Per 1.00 change in rate (divide by 100 for per 1%). */
150
+ rho: number;
151
+ }
152
+ /** Black-Scholes price + greeks for a European option. */
153
+ declare function blackScholes(o: OptionInput): Greeks;
154
+ /** Solve implied volatility from an observed option price (bisection). */
155
+ declare function impliedVolatility(marketPrice: number, o: Omit<OptionInput, "volatility">, opts?: {
156
+ tolerance?: number;
157
+ maxIterations?: number;
158
+ }): number | null;
159
+ /** Period-over-period simple returns from a price/equity series. */
160
+ declare function simpleReturns(series: number[]): number[];
161
+ /** Volatility (stdev of returns); annualized by default. */
162
+ declare function volatility(returns: number[], opts?: {
163
+ annualize?: boolean;
164
+ periodsPerYear?: number;
165
+ }): number;
166
+ /** Annualized Sharpe ratio from a return series. */
167
+ declare function sharpe(returns: number[], opts?: {
168
+ riskFree?: number;
169
+ periodsPerYear?: number;
170
+ }): number;
171
+ /** Annualized Sortino ratio (downside-deviation only). */
172
+ declare function sortino(returns: number[], opts?: {
173
+ riskFree?: number;
174
+ periodsPerYear?: number;
175
+ }): number;
176
+ /** Maximum drawdown of an equity curve, as a positive fraction (0.2 = −20%). */
177
+ declare function maxDrawdown(equity: number[]): {
178
+ maxDrawdown: number;
179
+ peakIndex: number;
180
+ troughIndex: number;
181
+ };
125
182
 
126
- export { type CashFlow, type ChargeBreakdown, type ChargeConfig, type ChargeInput, type FormatMoneyOptions, IN_DISCOUNT_BROKER, type Segment, type SegmentRates, averagePrice, cagr, changePercent, charges, circuitLimits, formatINR, pnl, pnlPercent, positionSize, roundToTick, xirr };
183
+ export { type CashFlow, type ChargeBreakdown, type ChargeConfig, type ChargeInput, type FormatMoneyOptions, type Greeks, IN_DISCOUNT_BROKER, type OptionInput, type Segment, type SegmentRates, averagePrice, blackScholes, cagr, changePercent, charges, circuitLimits, formatCompactINR, formatINR, impliedVolatility, maxDrawdown, pnl, pnlPercent, positionSize, roundToTick, sharpe, simpleReturns, sortino, volatility, xirr };
package/dist/index.js CHANGED
@@ -159,7 +159,141 @@ function charges(input, config = IN_DISCOUNT_BROKER) {
159
159
  breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty)
160
160
  };
161
161
  }
162
+ function trimTo2(n) {
163
+ return Number(n.toFixed(2)).toString();
164
+ }
165
+ function formatCompactINR(amount, opts = {}) {
166
+ const { symbol = "\u20B9" } = opts;
167
+ const neg = amount < 0;
168
+ const a = Math.abs(amount);
169
+ const units = [
170
+ [1e7, "Cr"],
171
+ [1e5, "L"],
172
+ [1e3, "K"]
173
+ ];
174
+ const u = units.find(([size]) => a >= size);
175
+ const out = u ? `${trimTo2(a / u[0])} ${u[1]}` : trimTo2(a);
176
+ return `${neg ? "-" : ""}${symbol}${out}`;
177
+ }
178
+ function normPdf(x) {
179
+ return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
180
+ }
181
+ function normCdf(x) {
182
+ const t = 1 / (1 + 0.2316419 * Math.abs(x));
183
+ const d = 0.3989422804014327 * Math.exp(-x * x / 2);
184
+ const p = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));
185
+ return x > 0 ? 1 - p : p;
186
+ }
187
+ function blackScholes(o) {
188
+ const { type, spot: S, strike: K, timeYears: t, rate: r } = o;
189
+ const q = o.dividendYield ?? 0;
190
+ const sigma = o.volatility;
191
+ const sqrtT = Math.sqrt(t);
192
+ const d1 = (Math.log(S / K) + (r - q + sigma * sigma / 2) * t) / (sigma * sqrtT);
193
+ const d2 = d1 - sigma * sqrtT;
194
+ const disc = Math.exp(-r * t);
195
+ const dq = Math.exp(-q * t);
196
+ const nd1 = normPdf(d1);
197
+ const gamma = dq * nd1 / (S * sigma * sqrtT);
198
+ const vega = S * dq * nd1 * sqrtT;
199
+ if (type === "call") {
200
+ const Nd1 = normCdf(d1);
201
+ const Nd2 = normCdf(d2);
202
+ return {
203
+ price: S * dq * Nd1 - K * disc * Nd2,
204
+ delta: dq * Nd1,
205
+ gamma,
206
+ theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) - r * K * disc * Nd2 + q * S * dq * Nd1,
207
+ vega,
208
+ rho: K * t * disc * Nd2
209
+ };
210
+ }
211
+ const Nnd1 = normCdf(-d1);
212
+ const Nnd2 = normCdf(-d2);
213
+ return {
214
+ price: K * disc * Nnd2 - S * dq * Nnd1,
215
+ delta: -dq * Nnd1,
216
+ gamma,
217
+ theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) + r * K * disc * Nnd2 - q * S * dq * Nnd1,
218
+ vega,
219
+ rho: -K * t * disc * Nnd2
220
+ };
221
+ }
222
+ function impliedVolatility(marketPrice, o, opts = {}) {
223
+ const tol = opts.tolerance ?? 1e-6;
224
+ const maxIter = opts.maxIterations ?? 100;
225
+ let lo = 1e-4;
226
+ let hi = 5;
227
+ for (let i = 0; i < maxIter; i++) {
228
+ const mid = (lo + hi) / 2;
229
+ const price = blackScholes({ ...o, volatility: mid }).price;
230
+ const diff = price - marketPrice;
231
+ if (Math.abs(diff) < tol) return mid;
232
+ if (diff > 0) hi = mid;
233
+ else lo = mid;
234
+ }
235
+ return null;
236
+ }
237
+ function simpleReturns(series) {
238
+ const out = [];
239
+ for (let i = 1; i < series.length; i++) {
240
+ const prev = series[i - 1];
241
+ if (prev !== 0) out.push((series[i] - prev) / prev);
242
+ }
243
+ return out;
244
+ }
245
+ function mean(xs) {
246
+ return xs.length ? xs.reduce((s, x) => s + x, 0) / xs.length : 0;
247
+ }
248
+ function stdev(xs) {
249
+ if (xs.length < 2) return 0;
250
+ const m = mean(xs);
251
+ return Math.sqrt(xs.reduce((s, x) => s + (x - m) ** 2, 0) / (xs.length - 1));
252
+ }
253
+ function volatility(returns, opts = {}) {
254
+ const sd = stdev(returns);
255
+ return opts.annualize === false ? sd : sd * Math.sqrt(opts.periodsPerYear ?? 252);
256
+ }
257
+ function sharpe(returns, opts = {}) {
258
+ const ppy = opts.periodsPerYear ?? 252;
259
+ const rfPerPeriod = (opts.riskFree ?? 0) / ppy;
260
+ const excess = returns.map((r) => r - rfPerPeriod);
261
+ const sd = stdev(excess);
262
+ if (sd === 0) return 0;
263
+ return mean(excess) / sd * Math.sqrt(ppy);
264
+ }
265
+ function sortino(returns, opts = {}) {
266
+ const ppy = opts.periodsPerYear ?? 252;
267
+ const rfPerPeriod = (opts.riskFree ?? 0) / ppy;
268
+ const excess = returns.map((r) => r - rfPerPeriod);
269
+ const downside = excess.filter((r) => r < 0);
270
+ if (!downside.length) return 0;
271
+ const dd = Math.sqrt(downside.reduce((s, r) => s + r * r, 0) / downside.length);
272
+ if (dd === 0) return 0;
273
+ return mean(excess) / dd * Math.sqrt(ppy);
274
+ }
275
+ function maxDrawdown(equity) {
276
+ let peak = equity[0] ?? 0;
277
+ let peakIdx = 0;
278
+ let maxDd = 0;
279
+ let ddPeak = 0;
280
+ let ddTrough = 0;
281
+ for (let i = 0; i < equity.length; i++) {
282
+ const v = equity[i];
283
+ if (v > peak) {
284
+ peak = v;
285
+ peakIdx = i;
286
+ }
287
+ const dd = peak > 0 ? (peak - v) / peak : 0;
288
+ if (dd > maxDd) {
289
+ maxDd = dd;
290
+ ddPeak = peakIdx;
291
+ ddTrough = i;
292
+ }
293
+ }
294
+ return { maxDrawdown: maxDd, peakIndex: ddPeak, troughIndex: ddTrough };
295
+ }
162
296
 
163
- export { IN_DISCOUNT_BROKER, averagePrice, cagr, changePercent, charges, circuitLimits, formatINR, pnl, pnlPercent, positionSize, roundToTick, xirr };
297
+ export { IN_DISCOUNT_BROKER, averagePrice, blackScholes, cagr, changePercent, charges, circuitLimits, formatCompactINR, formatINR, impliedVolatility, maxDrawdown, pnl, pnlPercent, positionSize, roundToTick, sharpe, simpleReturns, sortino, volatility, xirr };
164
298
  //# sourceMappingURL=index.js.map
165
299
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["r"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/market\n * The money & mechanics toolkit every stock-market app re-implements.\n *\n * P&L, returns, CAGR, XIRR, tick-size rounding, circuit limits, position sizing\n * — plus a real Indian brokerage & charges calculator (STT, GST, SEBI, stamp,\n * exchange txn) with discount-broker presets.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\n/* ------------------------------------------------------------------ *\n * Formatting\n * ------------------------------------------------------------------ */\n\nexport interface FormatMoneyOptions {\n symbol?: string;\n decimals?: number;\n}\n\n/**\n * Format a number in the Indian numbering system (lakh / crore grouping).\n * @example formatINR(1234567.5) // \"₹12,34,567.50\"\n */\nexport function formatINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\", decimals = 2 } = opts;\n const neg = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart = \"0\", frac = \"\"] = fixed.split(\".\");\n const last3 = intPart.slice(-3);\n const rest = intPart.slice(0, -3);\n const grouped = rest\n ? rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3\n : last3;\n return `${neg ? \"-\" : \"\"}${symbol}${grouped}${decimals > 0 ? \".\" + frac : \"\"}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Returns & P&L\n * ------------------------------------------------------------------ */\n\n/** Absolute profit/loss for a round-trip. */\nexport function pnl(o: { buy: number; sell: number; qty: number }): number {\n return (o.sell - o.buy) * o.qty;\n}\n\n/** Percentage change from `reference` to `current` (e.g. LTP vs prev close). */\nexport function changePercent(current: number, reference: number): number {\n if (reference === 0) return 0;\n return ((current - reference) / reference) * 100;\n}\n\n/** Profit/loss as a percentage of the buy price. */\nexport function pnlPercent(o: { buy: number; sell: number }): number {\n return changePercent(o.sell, o.buy);\n}\n\n/** Compound Annual Growth Rate as a fraction (0.15 = 15%). */\nexport function cagr(begin: number, end: number, years: number): number {\n if (begin <= 0 || years <= 0) return NaN;\n return Math.pow(end / begin, 1 / years) - 1;\n}\n\nexport interface CashFlow {\n /** Negative = money out (investment), positive = money in (redemption). */\n amount: number;\n date: Date | string | number;\n}\n\nfunction toMillis(d: Date | string | number): number {\n if (d instanceof Date) return d.getTime();\n if (typeof d === \"number\") return d;\n return new Date(d).getTime();\n}\n\n/**\n * Extended Internal Rate of Return for irregularly-spaced cash flows.\n * Returns an annualised rate as a fraction. Uses Newton–Raphson.\n * @example xirr([{amount:-10000, date:\"2024-01-01\"}, {amount:12000, date:\"2025-01-01\"}]) // ~0.20\n */\nexport function xirr(flows: CashFlow[], guess = 0.1): number {\n if (flows.length < 2) return NaN;\n const cf = flows\n .map((f) => ({ amount: f.amount, t: toMillis(f.date) }))\n .sort((a, b) => a.t - b.t);\n const t0 = cf[0]!.t;\n const yearFrac = (t: number) => (t - t0) / (365 * 24 * 3600 * 1000);\n const npv = (r: number) =>\n cf.reduce((s, c) => s + c.amount / Math.pow(1 + r, yearFrac(c.t)), 0);\n const dnpv = (r: number) =>\n cf.reduce((s, c) => {\n const y = yearFrac(c.t);\n return s - (y * c.amount) / Math.pow(1 + r, y + 1);\n }, 0);\n\n let r = guess;\n for (let i = 0; i < 100; i++) {\n const f = npv(r);\n if (Math.abs(f) < 1e-7) return r;\n const d = dnpv(r);\n if (d === 0) break;\n const next = r - f / d;\n if (!isFinite(next)) break;\n if (Math.abs(next - r) < 1e-10) return next;\n r = next;\n }\n return r;\n}\n\n/* ------------------------------------------------------------------ *\n * Position mechanics\n * ------------------------------------------------------------------ */\n\n/** Volume-weighted average price across a set of trades. */\nexport function averagePrice(trades: { price: number; qty: number }[]): number {\n let qty = 0;\n let value = 0;\n for (const t of trades) {\n qty += t.qty;\n value += t.price * t.qty;\n }\n return qty === 0 ? 0 : value / qty;\n}\n\n/**\n * Risk-based position sizing. Returns the whole-share quantity so that a stop-out\n * costs at most `riskPercent` of capital.\n * @example positionSize({ capital: 100000, riskPercent: 1, entry: 500, stop: 480 }) // 50\n */\nexport function positionSize(o: {\n capital: number;\n riskPercent: number;\n entry: number;\n stop: number;\n}): number {\n const riskAmount = o.capital * (o.riskPercent / 100);\n const perShareRisk = Math.abs(o.entry - o.stop);\n if (perShareRisk === 0) return 0;\n return Math.floor(riskAmount / perShareRisk);\n}\n\n/** Round a price to the nearest exchange tick (default ₹0.05 for NSE equity). */\nexport function roundToTick(price: number, tick = 0.05): number {\n if (tick <= 0) return price;\n return Number((Math.round(price / tick) * tick).toFixed(4));\n}\n\n/** Upper & lower circuit price for a given previous close and band percent. */\nexport function circuitLimits(\n prevClose: number,\n percent: number,\n): { upper: number; lower: number } {\n const delta = prevClose * (percent / 100);\n return {\n upper: roundToTick(prevClose + delta),\n lower: roundToTick(prevClose - delta),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Brokerage & statutory charges (India)\n * ------------------------------------------------------------------ */\n\nexport type Segment = \"delivery\" | \"intraday\" | \"futures\" | \"options\";\n\nexport interface SegmentRates {\n /** Brokerage as a fraction of turnover per side (0.0003 = 0.03%). */\n brokeragePct: number;\n /** Per-order brokerage cap (₹). */\n brokerageCap: number;\n /** Flat per-order brokerage (₹) — overrides the pct/cap model when set. */\n brokerageFlat?: number;\n sttBuy: number;\n sttSell: number;\n exchangeTxn: number;\n stampBuy: number;\n /** Depository (DP) charge per scrip on the sell leg (₹). */\n dpPerScrip: number;\n}\n\nexport interface ChargeConfig {\n segments: Record<Segment, SegmentRates>;\n sebi: number;\n gst: number;\n}\n\n/**\n * Default rates approximating an Indian discount broker (Zerodha-style) as of\n * FY2024–25. Statutory rates change — override any field via the `config`\n * argument of {@link charges} and always verify against the live rate card.\n */\nexport const IN_DISCOUNT_BROKER: ChargeConfig = {\n segments: {\n delivery: {\n brokeragePct: 0,\n brokerageCap: 0,\n sttBuy: 0.001,\n sttSell: 0.001,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00015,\n dpPerScrip: 13.5,\n },\n intraday: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.00025,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n futures: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.0002,\n exchangeTxn: 0.0000173,\n stampBuy: 0.00002,\n dpPerScrip: 0,\n },\n options: {\n brokeragePct: 0,\n brokerageCap: 20,\n brokerageFlat: 20,\n sttBuy: 0,\n sttSell: 0.001,\n exchangeTxn: 0.0003503,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n },\n sebi: 0.000001, // ₹10 per crore\n gst: 0.18,\n};\n\nexport interface ChargeInput {\n segment: Segment;\n /** Buy price per unit. Omit / 0 for a sell-only leg. */\n buy: number;\n /** Sell price per unit. Omit / 0 for a buy-only leg. */\n sell: number;\n qty: number;\n}\n\nexport interface ChargeBreakdown {\n turnover: number;\n brokerage: number;\n stt: number;\n exchangeTxn: number;\n sebi: number;\n stamp: number;\n gst: number;\n dp: number;\n totalCharges: number;\n grossPnl: number;\n netPnl: number;\n /** Per-share price move needed just to break even on charges. */\n breakeven: number;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nfunction legBrokerage(turnover: number, r: SegmentRates): number {\n if (turnover <= 0) return 0;\n if (r.brokerageFlat !== undefined) return r.brokerageFlat;\n if (r.brokeragePct === 0) return 0;\n return Math.min(turnover * r.brokeragePct, r.brokerageCap);\n}\n\n/**\n * Full brokerage + statutory charges breakdown for a trade, Indian market.\n * @example\n * charges({ segment: \"intraday\", buy: 100, sell: 102, qty: 500 });\n * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }\n */\nexport function charges(\n input: ChargeInput,\n config: ChargeConfig = IN_DISCOUNT_BROKER,\n): ChargeBreakdown {\n const r = config.segments[input.segment];\n const buyVal = (input.buy || 0) * input.qty;\n const sellVal = (input.sell || 0) * input.qty;\n const turnover = buyVal + sellVal;\n\n const brokerage = legBrokerage(buyVal, r) + legBrokerage(sellVal, r);\n const stt = buyVal * r.sttBuy + sellVal * r.sttSell;\n const exchangeTxn = turnover * r.exchangeTxn;\n const sebi = turnover * config.sebi;\n const stamp = buyVal * r.stampBuy;\n const gst = (brokerage + exchangeTxn + sebi) * config.gst;\n const dp = sellVal > 0 ? r.dpPerScrip : 0;\n\n const totalCharges = brokerage + stt + exchangeTxn + sebi + stamp + gst + dp;\n const grossPnl = sellVal - buyVal;\n\n return {\n turnover: round2(turnover),\n brokerage: round2(brokerage),\n stt: round2(stt),\n exchangeTxn: round2(exchangeTxn),\n sebi: round2(sebi),\n stamp: round2(stamp),\n gst: round2(gst),\n dp: round2(dp),\n totalCharges: round2(totalCharges),\n grossPnl: round2(grossPnl),\n netPnl: round2(grossPnl - totalCharges),\n breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["r"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/market\n * The money & mechanics toolkit every stock-market app re-implements.\n *\n * P&L, returns, CAGR, XIRR, tick-size rounding, circuit limits, position sizing\n * — plus a real Indian brokerage & charges calculator (STT, GST, SEBI, stamp,\n * exchange txn) with discount-broker presets.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\n/* ------------------------------------------------------------------ *\n * Formatting\n * ------------------------------------------------------------------ */\n\nexport interface FormatMoneyOptions {\n symbol?: string;\n decimals?: number;\n}\n\n/**\n * Format a number in the Indian numbering system (lakh / crore grouping).\n * @example formatINR(1234567.5) // \"₹12,34,567.50\"\n */\nexport function formatINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\", decimals = 2 } = opts;\n const neg = amount < 0;\n const fixed = Math.abs(amount).toFixed(decimals);\n const [intPart = \"0\", frac = \"\"] = fixed.split(\".\");\n const last3 = intPart.slice(-3);\n const rest = intPart.slice(0, -3);\n const grouped = rest\n ? rest.replace(/\\B(?=(\\d{2})+(?!\\d))/g, \",\") + \",\" + last3\n : last3;\n return `${neg ? \"-\" : \"\"}${symbol}${grouped}${decimals > 0 ? \".\" + frac : \"\"}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Returns & P&L\n * ------------------------------------------------------------------ */\n\n/** Absolute profit/loss for a round-trip. */\nexport function pnl(o: { buy: number; sell: number; qty: number }): number {\n return (o.sell - o.buy) * o.qty;\n}\n\n/** Percentage change from `reference` to `current` (e.g. LTP vs prev close). */\nexport function changePercent(current: number, reference: number): number {\n if (reference === 0) return 0;\n return ((current - reference) / reference) * 100;\n}\n\n/** Profit/loss as a percentage of the buy price. */\nexport function pnlPercent(o: { buy: number; sell: number }): number {\n return changePercent(o.sell, o.buy);\n}\n\n/** Compound Annual Growth Rate as a fraction (0.15 = 15%). */\nexport function cagr(begin: number, end: number, years: number): number {\n if (begin <= 0 || years <= 0) return NaN;\n return Math.pow(end / begin, 1 / years) - 1;\n}\n\nexport interface CashFlow {\n /** Negative = money out (investment), positive = money in (redemption). */\n amount: number;\n date: Date | string | number;\n}\n\nfunction toMillis(d: Date | string | number): number {\n if (d instanceof Date) return d.getTime();\n if (typeof d === \"number\") return d;\n return new Date(d).getTime();\n}\n\n/**\n * Extended Internal Rate of Return for irregularly-spaced cash flows.\n * Returns an annualised rate as a fraction. Uses Newton–Raphson.\n * @example xirr([{amount:-10000, date:\"2024-01-01\"}, {amount:12000, date:\"2025-01-01\"}]) // ~0.20\n */\nexport function xirr(flows: CashFlow[], guess = 0.1): number {\n if (flows.length < 2) return NaN;\n const cf = flows\n .map((f) => ({ amount: f.amount, t: toMillis(f.date) }))\n .sort((a, b) => a.t - b.t);\n const t0 = cf[0]!.t;\n const yearFrac = (t: number) => (t - t0) / (365 * 24 * 3600 * 1000);\n const npv = (r: number) =>\n cf.reduce((s, c) => s + c.amount / Math.pow(1 + r, yearFrac(c.t)), 0);\n const dnpv = (r: number) =>\n cf.reduce((s, c) => {\n const y = yearFrac(c.t);\n return s - (y * c.amount) / Math.pow(1 + r, y + 1);\n }, 0);\n\n let r = guess;\n for (let i = 0; i < 100; i++) {\n const f = npv(r);\n if (Math.abs(f) < 1e-7) return r;\n const d = dnpv(r);\n if (d === 0) break;\n const next = r - f / d;\n if (!isFinite(next)) break;\n if (Math.abs(next - r) < 1e-10) return next;\n r = next;\n }\n return r;\n}\n\n/* ------------------------------------------------------------------ *\n * Position mechanics\n * ------------------------------------------------------------------ */\n\n/** Volume-weighted average price across a set of trades. */\nexport function averagePrice(trades: { price: number; qty: number }[]): number {\n let qty = 0;\n let value = 0;\n for (const t of trades) {\n qty += t.qty;\n value += t.price * t.qty;\n }\n return qty === 0 ? 0 : value / qty;\n}\n\n/**\n * Risk-based position sizing. Returns the whole-share quantity so that a stop-out\n * costs at most `riskPercent` of capital.\n * @example positionSize({ capital: 100000, riskPercent: 1, entry: 500, stop: 480 }) // 50\n */\nexport function positionSize(o: {\n capital: number;\n riskPercent: number;\n entry: number;\n stop: number;\n}): number {\n const riskAmount = o.capital * (o.riskPercent / 100);\n const perShareRisk = Math.abs(o.entry - o.stop);\n if (perShareRisk === 0) return 0;\n return Math.floor(riskAmount / perShareRisk);\n}\n\n/** Round a price to the nearest exchange tick (default ₹0.05 for NSE equity). */\nexport function roundToTick(price: number, tick = 0.05): number {\n if (tick <= 0) return price;\n return Number((Math.round(price / tick) * tick).toFixed(4));\n}\n\n/** Upper & lower circuit price for a given previous close and band percent. */\nexport function circuitLimits(\n prevClose: number,\n percent: number,\n): { upper: number; lower: number } {\n const delta = prevClose * (percent / 100);\n return {\n upper: roundToTick(prevClose + delta),\n lower: roundToTick(prevClose - delta),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Brokerage & statutory charges (India)\n * ------------------------------------------------------------------ */\n\nexport type Segment = \"delivery\" | \"intraday\" | \"futures\" | \"options\";\n\nexport interface SegmentRates {\n /** Brokerage as a fraction of turnover per side (0.0003 = 0.03%). */\n brokeragePct: number;\n /** Per-order brokerage cap (₹). */\n brokerageCap: number;\n /** Flat per-order brokerage (₹) — overrides the pct/cap model when set. */\n brokerageFlat?: number;\n sttBuy: number;\n sttSell: number;\n exchangeTxn: number;\n stampBuy: number;\n /** Depository (DP) charge per scrip on the sell leg (₹). */\n dpPerScrip: number;\n}\n\nexport interface ChargeConfig {\n segments: Record<Segment, SegmentRates>;\n sebi: number;\n gst: number;\n}\n\n/**\n * Default rates approximating an Indian discount broker (Zerodha-style) as of\n * FY2024–25. Statutory rates change — override any field via the `config`\n * argument of {@link charges} and always verify against the live rate card.\n */\nexport const IN_DISCOUNT_BROKER: ChargeConfig = {\n segments: {\n delivery: {\n brokeragePct: 0,\n brokerageCap: 0,\n sttBuy: 0.001,\n sttSell: 0.001,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00015,\n dpPerScrip: 13.5,\n },\n intraday: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.00025,\n exchangeTxn: 0.0000297,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n futures: {\n brokeragePct: 0.0003,\n brokerageCap: 20,\n sttBuy: 0,\n sttSell: 0.0002,\n exchangeTxn: 0.0000173,\n stampBuy: 0.00002,\n dpPerScrip: 0,\n },\n options: {\n brokeragePct: 0,\n brokerageCap: 20,\n brokerageFlat: 20,\n sttBuy: 0,\n sttSell: 0.001,\n exchangeTxn: 0.0003503,\n stampBuy: 0.00003,\n dpPerScrip: 0,\n },\n },\n sebi: 0.000001, // ₹10 per crore\n gst: 0.18,\n};\n\nexport interface ChargeInput {\n segment: Segment;\n /** Buy price per unit. Omit / 0 for a sell-only leg. */\n buy: number;\n /** Sell price per unit. Omit / 0 for a buy-only leg. */\n sell: number;\n qty: number;\n}\n\nexport interface ChargeBreakdown {\n turnover: number;\n brokerage: number;\n stt: number;\n exchangeTxn: number;\n sebi: number;\n stamp: number;\n gst: number;\n dp: number;\n totalCharges: number;\n grossPnl: number;\n netPnl: number;\n /** Per-share price move needed just to break even on charges. */\n breakeven: number;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nfunction legBrokerage(turnover: number, r: SegmentRates): number {\n if (turnover <= 0) return 0;\n if (r.brokerageFlat !== undefined) return r.brokerageFlat;\n if (r.brokeragePct === 0) return 0;\n return Math.min(turnover * r.brokeragePct, r.brokerageCap);\n}\n\n/**\n * Full brokerage + statutory charges breakdown for a trade, Indian market.\n * @example\n * charges({ segment: \"intraday\", buy: 100, sell: 102, qty: 500 });\n * // { brokerage, stt, gst, sebi, stamp, exchangeTxn, totalCharges, netPnl, breakeven, ... }\n */\nexport function charges(\n input: ChargeInput,\n config: ChargeConfig = IN_DISCOUNT_BROKER,\n): ChargeBreakdown {\n const r = config.segments[input.segment];\n const buyVal = (input.buy || 0) * input.qty;\n const sellVal = (input.sell || 0) * input.qty;\n const turnover = buyVal + sellVal;\n\n const brokerage = legBrokerage(buyVal, r) + legBrokerage(sellVal, r);\n const stt = buyVal * r.sttBuy + sellVal * r.sttSell;\n const exchangeTxn = turnover * r.exchangeTxn;\n const sebi = turnover * config.sebi;\n const stamp = buyVal * r.stampBuy;\n const gst = (brokerage + exchangeTxn + sebi) * config.gst;\n const dp = sellVal > 0 ? r.dpPerScrip : 0;\n\n const totalCharges = brokerage + stt + exchangeTxn + sebi + stamp + gst + dp;\n const grossPnl = sellVal - buyVal;\n\n return {\n turnover: round2(turnover),\n brokerage: round2(brokerage),\n stt: round2(stt),\n exchangeTxn: round2(exchangeTxn),\n sebi: round2(sebi),\n stamp: round2(stamp),\n gst: round2(gst),\n dp: round2(dp),\n totalCharges: round2(totalCharges),\n grossPnl: round2(grossPnl),\n netPnl: round2(grossPnl - totalCharges),\n breakeven: input.qty === 0 ? 0 : round2(totalCharges / input.qty),\n };\n}\n\n/* ------------------------------------------------------------------ *\n * Compact formatting\n * ------------------------------------------------------------------ */\n\nfunction trimTo2(n: number): string {\n return Number(n.toFixed(2)).toString();\n}\n\n/** Compact INR: `formatCompactINR(12345678)` → `\"₹1.23 Cr\"`. */\nexport function formatCompactINR(amount: number, opts: FormatMoneyOptions = {}): string {\n const { symbol = \"₹\" } = opts;\n const neg = amount < 0;\n const a = Math.abs(amount);\n const units: [number, string][] = [\n [1e7, \"Cr\"],\n [1e5, \"L\"],\n [1e3, \"K\"],\n ];\n const u = units.find(([size]) => a >= size);\n const out = u ? `${trimTo2(a / u[0])} ${u[1]}` : trimTo2(a);\n return `${neg ? \"-\" : \"\"}${symbol}${out}`;\n}\n\n/* ------------------------------------------------------------------ *\n * Options — Black-Scholes greeks\n * ------------------------------------------------------------------ */\n\nfunction normPdf(x: number): number {\n return Math.exp((-x * x) / 2) / Math.sqrt(2 * Math.PI);\n}\n\n/** Standard normal CDF (Abramowitz & Stegun 7.1.26 approximation). */\nfunction normCdf(x: number): number {\n const t = 1 / (1 + 0.2316419 * Math.abs(x));\n const d = 0.3989422804014327 * Math.exp((-x * x) / 2);\n const p = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));\n return x > 0 ? 1 - p : p;\n}\n\nexport interface OptionInput {\n type: \"call\" | \"put\";\n /** Spot price of the underlying. */\n spot: number;\n strike: number;\n /** Time to expiry in years (e.g. 30 days ≈ 30/365). */\n timeYears: number;\n /** Risk-free rate (annual, decimal — e.g. 0.07). */\n rate: number;\n /** Volatility (annual, decimal — e.g. 0.25). */\n volatility: number;\n /** Continuous dividend yield (annual, decimal). Default 0. */\n dividendYield?: number;\n}\n\nexport interface Greeks {\n price: number;\n delta: number;\n gamma: number;\n /** Per-year theta (divide by 365 for per-day). */\n theta: number;\n /** Per 1.00 change in volatility (divide by 100 for per 1%). */\n vega: number;\n /** Per 1.00 change in rate (divide by 100 for per 1%). */\n rho: number;\n}\n\n/** Black-Scholes price + greeks for a European option. */\nexport function blackScholes(o: OptionInput): Greeks {\n const { type, spot: S, strike: K, timeYears: t, rate: r } = o;\n const q = o.dividendYield ?? 0;\n const sigma = o.volatility;\n const sqrtT = Math.sqrt(t);\n const d1 = (Math.log(S / K) + (r - q + (sigma * sigma) / 2) * t) / (sigma * sqrtT);\n const d2 = d1 - sigma * sqrtT;\n const disc = Math.exp(-r * t);\n const dq = Math.exp(-q * t);\n const nd1 = normPdf(d1);\n const gamma = (dq * nd1) / (S * sigma * sqrtT);\n const vega = S * dq * nd1 * sqrtT;\n if (type === \"call\") {\n const Nd1 = normCdf(d1);\n const Nd2 = normCdf(d2);\n return {\n price: S * dq * Nd1 - K * disc * Nd2,\n delta: dq * Nd1,\n gamma,\n theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) - r * K * disc * Nd2 + q * S * dq * Nd1,\n vega,\n rho: K * t * disc * Nd2,\n };\n }\n const Nnd1 = normCdf(-d1);\n const Nnd2 = normCdf(-d2);\n return {\n price: K * disc * Nnd2 - S * dq * Nnd1,\n delta: -dq * Nnd1,\n gamma,\n theta: -(S * dq * nd1 * sigma) / (2 * sqrtT) + r * K * disc * Nnd2 - q * S * dq * Nnd1,\n vega,\n rho: -K * t * disc * Nnd2,\n };\n}\n\n/** Solve implied volatility from an observed option price (bisection). */\nexport function impliedVolatility(\n marketPrice: number,\n o: Omit<OptionInput, \"volatility\">,\n opts: { tolerance?: number; maxIterations?: number } = {},\n): number | null {\n const tol = opts.tolerance ?? 1e-6;\n const maxIter = opts.maxIterations ?? 100;\n let lo = 1e-4;\n let hi = 5;\n for (let i = 0; i < maxIter; i++) {\n const mid = (lo + hi) / 2;\n const price = blackScholes({ ...o, volatility: mid }).price;\n const diff = price - marketPrice;\n if (Math.abs(diff) < tol) return mid;\n if (diff > 0) hi = mid;\n else lo = mid;\n }\n return null;\n}\n\n/* ------------------------------------------------------------------ *\n * Portfolio analytics\n * ------------------------------------------------------------------ */\n\n/** Period-over-period simple returns from a price/equity series. */\nexport function simpleReturns(series: number[]): number[] {\n const out: number[] = [];\n for (let i = 1; i < series.length; i++) {\n const prev = series[i - 1]!;\n if (prev !== 0) out.push((series[i]! - prev) / prev);\n }\n return out;\n}\n\nfunction mean(xs: number[]): number {\n return xs.length ? xs.reduce((s, x) => s + x, 0) / xs.length : 0;\n}\n\nfunction stdev(xs: number[]): number {\n if (xs.length < 2) return 0;\n const m = mean(xs);\n return Math.sqrt(xs.reduce((s, x) => s + (x - m) ** 2, 0) / (xs.length - 1));\n}\n\n/** Volatility (stdev of returns); annualized by default. */\nexport function volatility(returns: number[], opts: { annualize?: boolean; periodsPerYear?: number } = {}): number {\n const sd = stdev(returns);\n return opts.annualize === false ? sd : sd * Math.sqrt(opts.periodsPerYear ?? 252);\n}\n\n/** Annualized Sharpe ratio from a return series. */\nexport function sharpe(returns: number[], opts: { riskFree?: number; periodsPerYear?: number } = {}): number {\n const ppy = opts.periodsPerYear ?? 252;\n const rfPerPeriod = (opts.riskFree ?? 0) / ppy;\n const excess = returns.map((r) => r - rfPerPeriod);\n const sd = stdev(excess);\n if (sd === 0) return 0;\n return (mean(excess) / sd) * Math.sqrt(ppy);\n}\n\n/** Annualized Sortino ratio (downside-deviation only). */\nexport function sortino(returns: number[], opts: { riskFree?: number; periodsPerYear?: number } = {}): number {\n const ppy = opts.periodsPerYear ?? 252;\n const rfPerPeriod = (opts.riskFree ?? 0) / ppy;\n const excess = returns.map((r) => r - rfPerPeriod);\n const downside = excess.filter((r) => r < 0);\n if (!downside.length) return 0;\n const dd = Math.sqrt(downside.reduce((s, r) => s + r * r, 0) / downside.length);\n if (dd === 0) return 0;\n return (mean(excess) / dd) * Math.sqrt(ppy);\n}\n\n/** Maximum drawdown of an equity curve, as a positive fraction (0.2 = −20%). */\nexport function maxDrawdown(equity: number[]): { maxDrawdown: number; peakIndex: number; troughIndex: number } {\n let peak = equity[0] ?? 0;\n let peakIdx = 0;\n let maxDd = 0;\n let ddPeak = 0;\n let ddTrough = 0;\n for (let i = 0; i < equity.length; i++) {\n const v = equity[i]!;\n if (v > peak) {\n peak = v;\n peakIdx = i;\n }\n const dd = peak > 0 ? (peak - v) / peak : 0;\n if (dd > maxDd) {\n maxDd = dd;\n ddPeak = peakIdx;\n ddTrough = i;\n }\n }\n return { maxDrawdown: maxDd, peakIndex: ddPeak, troughIndex: ddTrough };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacspace/market",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "Stock-market money math — P&L, returns, CAGR, XIRR, tick-size rounding, circuit limits, position sizing and an Indian brokerage & charges calculator (STT, GST, SEBI, stamp). Zero-dependency.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -38,7 +38,13 @@
38
38
  "circuit-limits",
39
39
  "nse",
40
40
  "bse",
41
- "typescript"
41
+ "typescript",
42
+ "black-scholes",
43
+ "options-pricing",
44
+ "greeks",
45
+ "implied-volatility",
46
+ "sharpe-ratio",
47
+ "sortino"
42
48
  ],
43
49
  "author": "Lacspace <contact@lacspace.com>",
44
50
  "license": "SEE LICENSE IN LICENSE",
@@ -53,5 +59,8 @@
53
59
  },
54
60
  "engines": {
55
61
  "node": ">=18"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
56
65
  }
57
66
  }