@lacspace/market 1.0.2 → 1.1.0
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 +24 -0
- package/dist/index.cjs +142 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -1
- package/dist/index.d.ts +58 -1
- package/dist/index.js +135 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,4 +108,28 @@ 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
|
+
|
|
129
|
+
## Licensing
|
|
130
|
+
|
|
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.
|
|
132
|
+
|
|
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)**.
|
|
134
|
+
|
|
111
135
|
<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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
+
"version": "1.1.0",
|
|
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",
|