From b02c71d2063222f3d8a89c39e238fddf94723065 Mon Sep 17 00:00:00 2001 From: Avraham Sakal Date: Sun, 18 Jun 2023 14:44:24 -0400 Subject: [PATCH] factor-out pickers into jotai atoms --- dist/index.js | 170 ++++++++++++++--------- src/App.tsx | 50 ++++--- src/HistoricalImpliedVolatilityChart.tsx | 27 ++++ src/Picker.tsx | 44 ++++++ src/QuoteDatePicker.tsx | 32 ----- 5 files changed, 205 insertions(+), 118 deletions(-) create mode 100644 src/HistoricalImpliedVolatilityChart.tsx create mode 100644 src/Picker.tsx delete mode 100644 src/QuoteDatePicker.tsx diff --git a/dist/index.js b/dist/index.js index 3b7e51c..c898a52 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1088,7 +1088,7 @@ var require_react_development = __commonJS({ } return dispatcher.useContext(Context); } - function useState2(initialState) { + function useState(initialState) { var dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); } @@ -1890,7 +1890,7 @@ var require_react_development = __commonJS({ exports.useMemo = useMemo; exports.useReducer = useReducer2; exports.useRef = useRef3; - exports.useState = useState2; + exports.useState = useState; exports.useSyncExternalStore = useSyncExternalStore; exports.useTransition = useTransition; exports.version = ReactVersion; @@ -24379,11 +24379,11 @@ var require_react_jsx_runtime_development = __commonJS({ return jsxWithValidation(type, props, key, false); } } - var jsx5 = jsxWithValidationDynamic; - var jsxs3 = jsxWithValidationStatic; + var jsx6 = jsxWithValidationDynamic; + var jsxs4 = jsxWithValidationStatic; exports.Fragment = REACT_FRAGMENT_TYPE; - exports.jsx = jsx5; - exports.jsxs = jsxs3; + exports.jsx = jsx6; + exports.jsxs = jsxs4; })(); } } @@ -24404,6 +24404,18 @@ var require_jsx_runtime = __commonJS({ // src/index.tsx var import_client = __toESM(require_client(), 1); +// src/Header.tsx +var import_jsx_runtime = __toESM(require_jsx_runtime(), 1); +function Header() { + return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "header", children: [ + /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Choose Probabilities" }), + /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: "X-Axis = Ticks, Y-Axis = Underlying Price" }), + /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: "Begin with 2 Ticks, T_0 and T_1. Can add more in-between or at ends. Do not have to be equidistant." }), + /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: 'Begin with 2 Nodes to the "right" of each Node representing what can happen in the next tick. Can add more. No not need to be equidistant.' }) + ] }); +} +var Header_default = Header; + // node_modules/@kurkle/color/dist/color.esm.js function round(v) { return v + 0.5 | 0; @@ -35977,19 +35989,36 @@ function createTypedChart(type, registerables) { } var Line = /* @__PURE__ */ createTypedChart("line", LineController); -// src/Header.tsx -var import_jsx_runtime = __toESM(require_jsx_runtime(), 1); -function Header() { - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "header", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "Choose Probabilities" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: "X-Axis = Ticks, Y-Axis = Underlying Price" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: "Begin with 2 Ticks, T_0 and T_1. Can add more in-between or at ends. Do not have to be equidistant." }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { children: 'Begin with 2 Nodes to the "right" of each Node representing what can happen in the next tick. Can add more. No not need to be equidistant.' }) - ] }); +// src/HistoricalImpliedVolatilityChart.tsx +var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); +Chart.register(LineElement, plugin_tooltip, plugin_legend, CategoryScale, LinearScale, PointElement); +function HistoricalImpliedVolatilityChart() { + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + Line, + { + datasetIdKey: "id", + data: { + labels: ["Jun", "Jul", "Aug"], + datasets: [ + { + //@ts-ignore + id: 1, + label: "", + data: [5, 6, 7] + }, + { + //@ts-ignore + id: 2, + label: "", + data: [3, 2, 1] + } + ] + } + } + ) }); } -var Header_default = Header; -// src/QuoteDatePicker.tsx +// src/Picker.tsx var import_react3 = __toESM(require_react(), 1); // node_modules/jotai/esm/vanilla.mjs @@ -36637,63 +36666,78 @@ function useAtom(atom2, options) { ]; } -// src/QuoteDatePicker.tsx -var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); -var $dates = atom([]); -var $isLoading = atom(false); -function QuoteDatePicker() { - const [dates, setDates] = useAtom($dates); - const [isLoading, setIsLoading] = useAtom($isLoading); - (0, import_react3.useEffect)(() => { - fetch("http://127.0.0.1:8234/option_quotes/AAPL/quote_dates").then((x) => x.json()).catch((err) => ["2021-01-02", "2021-01-03", "2021-01-04"]).then((dates_) => { - setDates(dates_); - setIsLoading(false); - }); - }, []); - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { children: dates.map( - (date) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: date, children: date }, date) - ) }) }); +// src/Picker.tsx +var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1); +function create$Picker({ $options = atom([]), $isLoading = atom(false), $selectedOption = atom(""), $url = atom(""), $isEnabled = atom(true) }) { + return atom({ + $options, + $isLoading, + $selectedOption, + $url, + Picker: () => { + const [url, setUrl] = useAtom($url); + const [options, setOptions2] = useAtom($options); + const [isLoading, setIsLoading] = useAtom($isLoading); + const [selectedOption, setSelectedOption] = useAtom($selectedOption); + const [isEnabled, setIsEnabled] = useAtom($isEnabled); + (0, import_react3.useEffect)(() => { + if (isEnabled) { + fetch(url).then((x) => x.json()).catch((err) => ["AAPL", "MSFT", "GOOG"]).then((underlyings_) => { + setOptions2(underlyings_); + setIsLoading(false); + }); + } + }, [isEnabled, url]); + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: selectedOption, onChange: (e) => { + setSelectedOption(e.target.value); + }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "" }, ""), + options.map( + (date) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: date, children: date }, date) + ) + ] }) }); + } + }); } // src/App.tsx -var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1); -Chart.register(LineElement, plugin_tooltip, plugin_legend, CategoryScale, LinearScale, PointElement); +var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1); +var baseUrl = "http://127.0.0.1:8234"; +var $underlyingsUrl = atom(`${baseUrl}/option_quotes/underlyings`); +var $underlyingPicker = create$Picker({ $url: $underlyingsUrl }); +var $quoteDatePicker = create$Picker({ + $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/quote_dates`), + $isEnabled: atom((get) => get(get($underlyingPicker).$selectedOption) !== "") +}); +var $frontMonthPicker = create$Picker({ + $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), + $isEnabled: atom((get) => get(get($quoteDatePicker).$selectedOption) !== "" && get(get($underlyingPicker).$selectedOption) !== "") +}); +var $backMonthPicker = create$Picker({ + $url: atom((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), + $isEnabled: atom((get) => get(get($quoteDatePicker).$selectedOption) !== "" && get(get($underlyingPicker).$selectedOption) !== "") +}); function App() { - return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Header_default, {}), - /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(QuoteDatePicker, {}), - /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( - Line, - { - datasetIdKey: "id", - data: { - labels: ["Jun", "Jul", "Aug"], - datasets: [ - { - //@ts-ignore - id: 1, - label: "", - data: [5, 6, 7] - }, - { - //@ts-ignore - id: 2, - label: "", - data: [3, 2, 1] - } - ] - } - } - ) + const { Picker: UnderlyingPicker, $selectedOption } = useAtomValue($underlyingPicker); + const { Picker: QuoteDatePicker } = useAtomValue($quoteDatePicker); + const { Picker: FrontMonthPicker } = useAtomValue($frontMonthPicker); + const { Picker: BackMonthPicker } = useAtomValue($backMonthPicker); + return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Header_default, {}), + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UnderlyingPicker, {}), + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QuoteDatePicker, {}), + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FrontMonthPicker, {}), + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BackMonthPicker, {}), + /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HistoricalImpliedVolatilityChart, {}) ] }); } var App_default = App; // src/index.tsx -var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1); +var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1); var rootEl = document.getElementById("app"); var Root = (0, import_client.createRoot)(rootEl); -Root.render(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(App_default, {})); +Root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(App_default, {})); /*! Bundled license information: react/cjs/react.development.js: diff --git a/src/App.tsx b/src/App.tsx index 3c2755b..6193277 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,38 @@ -//import Chart from 'chart.js/auto'; -import { Chart as ChartJS, Tooltip, Legend, LineElement, CategoryScale, LinearScale, PointElement } from "chart.js"; -import { Line } from 'react-chartjs-2'; import Header from './Header'; -import { QuoteDatePicker } from "./QuoteDatePicker"; +import { HistoricalImpliedVolatilityChart } from "./HistoricalImpliedVolatilityChart"; +import { create$Picker } from './Picker'; +import { atom as $, useAtomValue } from 'jotai'; -ChartJS.register(LineElement, Tooltip, Legend, CategoryScale, LinearScale, PointElement); +const baseUrl = 'http://127.0.0.1:8234'; +const $underlyingsUrl = $(`${baseUrl}/option_quotes/underlyings`); +const $underlyingPicker = create$Picker({$url:$underlyingsUrl}); +const $quoteDatePicker = create$Picker({ + $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/quote_dates`), + $isEnabled: $((get) => get(get($underlyingPicker).$selectedOption)!=='') +}); +const $frontMonthPicker = create$Picker({ + $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), + $isEnabled: $((get) => get(get($quoteDatePicker).$selectedOption)!=='' && get(get($underlyingPicker).$selectedOption)!=='') +}); +const $backMonthPicker = create$Picker({ + $url: $((get) => `${baseUrl}/option_quotes/${get(get($underlyingPicker).$selectedOption)}/${get(get($quoteDatePicker).$selectedOption)}/expirations`), + $isEnabled: $((get) => get(get($quoteDatePicker).$selectedOption)!=='' && get(get($underlyingPicker).$selectedOption)!=='') +}); function App() { + const {Picker:UnderlyingPicker, $selectedOption} = useAtomValue($underlyingPicker); + const {Picker:QuoteDatePicker} = useAtomValue($quoteDatePicker); + const {Picker:FrontMonthPicker} = useAtomValue($frontMonthPicker); + const {Picker:BackMonthPicker} = useAtomValue($backMonthPicker); + return (
+ - + + +
); } diff --git a/src/HistoricalImpliedVolatilityChart.tsx b/src/HistoricalImpliedVolatilityChart.tsx new file mode 100644 index 0000000..1cc0894 --- /dev/null +++ b/src/HistoricalImpliedVolatilityChart.tsx @@ -0,0 +1,27 @@ +import { Chart as ChartJS, Tooltip, Legend, LineElement, CategoryScale, LinearScale, PointElement } from "chart.js"; +import { Line } from 'react-chartjs-2'; + +ChartJS.register(LineElement, Tooltip, Legend, CategoryScale, LinearScale, PointElement); + +export function HistoricalImpliedVolatilityChart(){ + return (
+ +
); +} \ No newline at end of file diff --git a/src/Picker.tsx b/src/Picker.tsx new file mode 100644 index 0000000..d74a011 --- /dev/null +++ b/src/Picker.tsx @@ -0,0 +1,44 @@ +import { useEffect } from "react"; +import { atom as $, useAtom, Atom, PrimitiveAtom } from 'jotai'; + +type PickerInput = { $options?:PrimitiveAtom>, $isLoading?:PrimitiveAtom, $selectedOption?:PrimitiveAtom, $url:Atom, $isEnabled?:Atom }; +export function create$Picker({ $options=$([]), $isLoading=$(false), $selectedOption=$(''), $url=$(''), $isEnabled=$(true) }: PickerInput){ + return $({ + $options, + $isLoading, + $selectedOption, + $url, + Picker: ()=>{ + const [url, setUrl] = useAtom($url); + const [options, setOptions] = useAtom($options); + const [isLoading, setIsLoading] = useAtom($isLoading); + const [selectedOption, setSelectedOption] = useAtom($selectedOption); + const [isEnabled, setIsEnabled] = useAtom($isEnabled); + + useEffect(()=>{ + if(isEnabled){ + fetch(url) + .then(x=>x.json()) + .catch((err)=>['AAPL', 'MSFT', 'GOOG']) + .then((underlyings_)=>{ setOptions(underlyings_); setIsLoading(false); }) + } + },[isEnabled, url]) + + return ( +
+ {isLoading + ? + Loading... + : + + } +
+ ); + } + }); +} \ No newline at end of file diff --git a/src/QuoteDatePicker.tsx b/src/QuoteDatePicker.tsx deleted file mode 100644 index 658dfb8..0000000 --- a/src/QuoteDatePicker.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useState } from "react"; -import { atom as $, useAtom } from 'jotai'; - -const $dates = $>([]); -const $isLoading = $(false); - -export function QuoteDatePicker(){ - const [dates, setDates] = useAtom($dates); - const [isLoading, setIsLoading] = useAtom($isLoading); - - useEffect(()=>{ - fetch('http://127.0.0.1:8234/option_quotes/AAPL/quote_dates') - .then(x=>x.json()) - .catch((err)=>['2021-01-02', '2021-01-03', '2021-01-04']) - .then((dates_)=>{ setDates(dates_); setIsLoading(false); }) - },[]) - - return ( -
- {isLoading - ? - Loading... - : - - } -
- ); -} \ No newline at end of file