@uzh-bf/react-option-charts
Version:
Option pricing and payoff charts in React
124 lines • 6.91 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import * as React from "react";
import { XAxis, YAxis, } from "@react-financial-charts/axes";
import { CurrentCoordinate, MouseCoordinateX, } from "@react-financial-charts/coordinates";
import { ChartCanvas, Chart, GenericChartComponent, } from "@react-financial-charts/core";
import { LineSeries, } from "@react-financial-charts/series";
import { SingleValueTooltip, } from '@react-financial-charts/tooltip';
import { withSize, withDeviceRatio, } from "@react-financial-charts/utils";
import { blackScholes } from "black-scholes";
import { scaleLinear } from "d3-scale";
import { MouseCoordinateYAccessor } from "./MouseCoordinateYAccessor.js";
import { format, formatUSD, rangeMinSize } from "./utils.js";
;
;
const calcLegValue = (o, underlyingPrice, r) => blackScholes(underlyingPrice, o.k, o.t, o.v, r, o.callPut) || 0;
const calcValue = (optionLegs, underlyingPrice, r) => {
let total = 0;
const optionLegValues = optionLegs.reduce((acc, o, i) => {
const legValue = calcLegValue(o, underlyingPrice, r) * (o.quantity || 1);
total += legValue;
acc.push(legValue);
return acc;
}, []);
return {
total,
optionLegValues,
};
};
const OptionPayoffChart = (props) => {
const { s, r, strategies, children, onCurrentValueChanged } = props, chartCanvasProps = __rest(props, ["s", "r", "strategies", "children", "onCurrentValueChanged"]);
const [lastX, setLastX] = React.useState(s);
const strategyByName = strategies.reduce((acc, strategy) => {
const strat = Object.assign(Object.assign({}, strategy), { value: calcValue(strategy.optionLegs, s, r) });
acc[strat.name] = strat;
if (strat.showPayoff) {
const minT = Math.min(...strat.optionLegs.map(o => o.t));
const title = format(strat.payoffTitle || "{0} payoff", strat.name);
acc[title] = Object.assign(Object.assign({}, strat), { color: strat.payoffColor || strat.color, optionLegs: strat.optionLegs.map(o => {
return Object.assign(Object.assign({}, o), { t: o.t - minT });
}) });
}
return acc;
}, {});
const strategyNames = Object.keys(strategyByName);
const strategyValues = strategyNames.reduce((acc, strategyName) => {
acc[strategyName] = strategyByName[strategyName].value;
return acc;
}, {});
React.useEffect(() => {
onCurrentValueChanged && onCurrentValueChanged(s, strategyValues, strategyValues);
}, [s, r, strategies]);
const allLegs = strategies.flatMap(strat => strat.optionLegs);
const v = Math.max(...allLegs.map(o => o.v));
const keyXs = allLegs.map(o => o.k);
if (s)
keyXs.push(s);
let minX = Math.floor(Math.min(...keyXs) * Math.max(0, 1 - v));
let maxX = Math.ceil(Math.max(...keyXs) * Math.min(2, 1 + v));
if (maxX - minX == 1) {
minX = Math.max(0, minX - 1);
maxX += 1;
}
const data = rangeMinSize(maxX - minX, minX).map(x => {
return strategyNames.reduce((acc, strategyName) => {
var _a;
const strat = strategyByName[strategyName];
acc[strategyName] = calcValue(strat.optionLegs, x, r).total - (((_a = strat.value) === null || _a === void 0 ? void 0 : _a.total) || 0);
return acc;
}, { x });
});
const xExtents = [minX, maxX];
const xScale = scaleLinear([0, maxX - minX], [minX, maxX]);
const xAccessor = (data) => data.x;
const yExtents = (data) => {
const minY = Math.min(...strategyNames.map(strategyName => data[strategyName]));
const maxY = Math.max(...strategyNames.map(strategyName => data[strategyName]));
return [minY - Math.abs(minY * .2), maxY + Math.abs(maxY * .2)];
};
const yAccessor = (strategyName) => (data) => data[strategyName];
const series = strategyNames.map(strategyName => (React.createElement(LineSeries, { key: `series-${strategyName}`, strokeStyle: strategyByName[strategyName].color, yAccessor: yAccessor(strategyName) })));
const coords = strategyNames.map(strategyName => (React.createElement(CurrentCoordinate, { key: `coords-${strategyName}`, fillStyle: strategyByName[strategyName].color, strokeStyle: strategyByName[strategyName].color, yAccessor: yAccessor(strategyName) })));
const edges = strategyNames.map(strategyName => (React.createElement(MouseCoordinateYAccessor, { key: `edges-${strategyName}`, at: "right", orient: "right", displayFormat: formatUSD, yAccessor: yAccessor(strategyName) })));
const tooltips = strategyNames.map((strategyName, i) => (React.createElement(SingleValueTooltip, { key: `tooltips-${strategyName}`, yAccessor: yAccessor(strategyName), yLabel: strategyName, yDisplayFormat: formatUSD, labelFill: strategyByName[strategyName].color, origin: [8, (i + 1) * 16] })));
const currentValueChanged = (ctx, { currentItem }) => {
var _a;
const _b = !!currentItem && currentItem, { x } = _b, strategies = __rest(_b, ["x"]);
if (x === lastX)
return;
else
setLastX(x);
const currentValues = (_a = Object.keys(strategies)) === null || _a === void 0 ? void 0 : _a.reduce((acc, strategyName) => {
if (strategyByName[strategyName]) {
acc[strategyName] = calcValue(strategyByName[strategyName].optionLegs, x, r);
}
return acc;
}, {});
onCurrentValueChanged && onCurrentValueChanged(x, strategyValues, currentValues);
};
return (React.createElement(ChartCanvas, Object.assign({}, chartCanvasProps, { data: data, margin: { left: 0, right: 50, top: 0, bottom: 30 }, xScale: xScale, xAccessor: xAccessor, xExtents: xExtents }),
React.createElement(Chart, { id: 1, yExtents: yExtents },
React.createElement(XAxis, null),
React.createElement(YAxis, { ticks: 5 }),
React.createElement(MouseCoordinateX, { at: "bottom", orient: "bottom", displayFormat: formatUSD }),
onCurrentValueChanged && React.createElement(GenericChartComponent, { clip: false, canvasDraw: currentValueChanged, drawOn: ["mousemove"] }),
series,
coords,
edges,
tooltips,
children)));
};
export default withSize({ style: { minHeight: 300 } })(withDeviceRatio()(
// @ts-ignore
OptionPayoffChart));
//# sourceMappingURL=OptionPayoffChart.js.map