From 8986dc4ea96b50bc774c688e325f5abeb703d468 Mon Sep 17 00:00:00 2001 From: avraham Date: Sun, 29 Sep 2024 20:01:53 -0400 Subject: [PATCH] fully adopt `@mui/material`; refactor --- frontend/package.json | 10 +- frontend/pnpm-lock.yaml | 293 +++++++++------ .../src/pages/HistoricalCalendarPrices.tsx | 333 ++++-------------- ...tableDaysBetweenFrontAndBackExpiration.tsx | 38 ++ .../EditableDaysToFrontExpiration.tsx | 32 ++ .../EditableExitToFrontExpiration.tsx | 32 ++ .../EditableLookbackPeriodEnd.tsx | 38 ++ .../EditableLookbackPeriodStart.tsx | 38 ++ .../EditableStrike.tsx | 70 ++++ .../EditableUnderlying.tsx | 42 +++ .../EditableValue.tsx | 29 ++ .../pages/HistoricalCalendarPrices/actions.ts | 63 ++++ .../pages/HistoricalCalendarPrices/state.ts | 46 +++ 13 files changed, 678 insertions(+), 386 deletions(-) create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableDaysBetweenFrontAndBackExpiration.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableDaysToFrontExpiration.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableExitToFrontExpiration.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodEnd.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodStart.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableStrike.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableUnderlying.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/EditableValue.tsx create mode 100644 frontend/src/pages/HistoricalCalendarPrices/actions.ts create mode 100644 frontend/src/pages/HistoricalCalendarPrices/state.ts diff --git a/frontend/package.json b/frontend/package.json index e71b354..a4e1769 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,12 +7,10 @@ "preview": "vite preview" }, "dependencies": { - "@date-io/date-fns": "^3.0.0", - "@emotion/react": "^11.13.0", - "@emotion/styled": "^11.13.0", - "@mui/material": "^5.16.6", - "@mui/system": "^5.16.6", - "@mui/x-date-pickers": "^7.12.0", + "@mui/icons-material": "^6.1.1", + "@mui/material": "^6.1.1", + "@mui/system": "^6.1.1", + "@mui/x-date-pickers": "^7.18.0", "@preact/signals": "^1.2.2", "@trpc/client": "^10.45.0", "chart.js": "^4.4.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 85558bc..dc0507a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,24 +8,18 @@ importers: .: dependencies: - '@date-io/date-fns': - specifier: ^3.0.0 - version: 3.0.0(date-fns@3.6.0) - '@emotion/react': - specifier: ^11.13.0 - version: 11.13.0(@types/react@18.3.3)(react@18.3.1) - '@emotion/styled': - specifier: ^11.13.0 - version: 11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@mui/icons-material': + specifier: ^6.1.1 + version: 6.1.1(@mui/material@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) '@mui/material': - specifier: ^5.16.6 - version: 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^6.1.1 + version: 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/system': - specifier: ^5.16.6 - version: 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + specifier: ^6.1.1 + version: 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) '@mui/x-date-pickers': - specifier: ^7.12.0 - version: 7.12.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^7.18.0 + version: 7.18.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@preact/signals': specifier: ^1.2.2 version: 1.2.2(preact@10.19.3) @@ -178,6 +172,10 @@ packages: resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.25.6': + resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.22.15': resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -190,17 +188,6 @@ packages: resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} engines: {node: '>=6.9.0'} - '@date-io/core@3.0.0': - resolution: {integrity: sha512-S3j+IAQVBYNkQzchVVhX40eBkGDreBpScy9RXwTS5j2+k07+62pMVPisQ44Gq76Rqy5AOG/EZXCwBpY/jbemvA==} - - '@date-io/date-fns@3.0.0': - resolution: {integrity: sha512-hsLAbsdP8LKfi7OQ729cXMWfmHQEq0hn3ysXfAAoc92j6j6sBq0s0tplnkWu6O4iBUpVCYRPGuNjQQhTaOu2AA==} - peerDependencies: - date-fns: ^3.2.0 - peerDependenciesMeta: - date-fns: - optional: true - '@emotion/babel-plugin@11.12.0': resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} @@ -408,57 +395,71 @@ packages: '@kurkle/color@0.3.2': resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} - '@mui/core-downloads-tracker@5.16.6': - resolution: {integrity: sha512-kytg6LheUG42V8H/o/Ptz3olSO5kUXW9zF0ox18VnblX6bO2yif1FPItgc3ey1t5ansb1+gbe7SatntqusQupg==} + '@mui/core-downloads-tracker@6.1.1': + resolution: {integrity: sha512-VdQC1tPIIcZAnf62L2M1eQif0x2vlKg3YK4kGYbtijSH4niEgI21GnstykW1vQIs+Bc6L+Hua2GATYVjilJ22A==} - '@mui/material@5.16.6': - resolution: {integrity: sha512-0LUIKBOIjiFfzzFNxXZBRAyr9UQfmTAFzbt6ziOU2FDXhorNN2o3N9/32mNJbCA8zJo2FqFU6d3dtoqUDyIEfA==} - engines: {node: '>=12.0.0'} + '@mui/icons-material@6.1.1': + resolution: {integrity: sha512-sy/YKwcLPW8VcacNP2uWMYR9xyWuwO9NN9FXuGEU90bRshBXj8pdKk+joe3TCW7oviVS3zXLHlc94wQ0jNsQRQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.1.1 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@6.1.1': + resolution: {integrity: sha512-b+eULldTqtqTCbN++2BtBWCir/1LwEYw+2mIlOt2GiEUh1EBBw4/wIukGKKNt3xrCZqRA80yLLkV6tF61Lq3cA==} + engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + '@mui/material-pigment-css': ^6.1.1 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@emotion/react': optional: true '@emotion/styled': optional: true + '@mui/material-pigment-css': + optional: true '@types/react': optional: true - '@mui/private-theming@5.16.6': - resolution: {integrity: sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==} - engines: {node: '>=12.0.0'} + '@mui/private-theming@6.1.1': + resolution: {integrity: sha512-JlrjIdhyZUtewtdAuUsvi3ZnO0YS49IW4Mfz19ZWTlQ0sDGga6LNPVwHClWr2/zJK2we2BQx9/i8M32rgKuzrg==} + engines: {node: '>=14.0.0'} peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - '@mui/styled-engine@5.16.6': - resolution: {integrity: sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==} - engines: {node: '>=12.0.0'} + '@mui/styled-engine@6.1.1': + resolution: {integrity: sha512-HJyIoMpFb11fnHuRtUILOXgq6vj4LhIlE8maG4SwP/W+E5sa7HFexhnB3vOMT7bKys4UKNxhobC8jwWxYilGsA==} + engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.4.1 '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@emotion/react': optional: true '@emotion/styled': optional: true - '@mui/system@5.16.6': - resolution: {integrity: sha512-5xgyJjBIMPw8HIaZpfbGAaFYPwImQn7Nyh+wwKWhvkoIeDosQ1ZMVrbTclefi7G8hNmqhip04duYwYpbBFnBgw==} - engines: {node: '>=12.0.0'} + '@mui/system@6.1.1': + resolution: {integrity: sha512-PaYsCz2tUOcpu3T0okDEsSuP/yCDIj9JZ4Tox1JovRSKIjltHpXPsXZSGr3RiWdtM1MTQMFMCZzu0+CKbyy+Kw==} + engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.5.0 '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@emotion/react': optional: true @@ -467,10 +468,10 @@ packages: '@types/react': optional: true - '@mui/types@7.2.15': - resolution: {integrity: sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==} + '@mui/types@7.2.17': + resolution: {integrity: sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==} peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -485,14 +486,25 @@ packages: '@types/react': optional: true - '@mui/x-date-pickers@7.12.0': - resolution: {integrity: sha512-WU5C7QNfSpJ9cP8vl2sY7q35NW+0TUMgEy+sl98fcPhLckq3cgV1wnVxoZnQZ3BxVQAtx+7ag/MpefU03vJcVw==} + '@mui/utils@6.1.1': + resolution: {integrity: sha512-HlRrgdJSPbYDXPpoVMWZV8AE7WcFtAk13rWNWAEVWKSanzBBkymjz3km+Th/Srowsh4pf1fTSP1B0L116wQBYw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-date-pickers@7.18.0': + resolution: {integrity: sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 '@emotion/styled': ^11.8.1 - '@mui/material': ^5.15.14 - date-fns: ^2.25.0 || ^3.2.0 + '@mui/material': ^5.15.14 || ^6.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 + date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0 date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 dayjs: ^1.10.7 luxon: ^3.0.2 @@ -521,6 +533,12 @@ packages: moment-jalaali: optional: true + '@mui/x-internals@7.18.0': + resolution: {integrity: sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -573,8 +591,8 @@ packages: '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - '@types/react-transition-group@4.4.10': - resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} @@ -1107,6 +1125,10 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.25.6': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.22.15': dependencies: '@babel/code-frame': 7.23.5 @@ -1134,18 +1156,10 @@ snapshots: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - '@date-io/core@3.0.0': {} - - '@date-io/date-fns@3.0.0(date-fns@3.6.0)': - dependencies: - '@date-io/core': 3.0.0 - optionalDependencies: - date-fns: 3.6.0 - '@emotion/babel-plugin@11.12.0': dependencies: '@babel/helper-module-imports': 7.22.15 - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.6 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.0 @@ -1155,6 +1169,7 @@ snapshots: find-root: 1.1.0 source-map: 0.5.7 stylis: 4.2.0 + optional: true '@emotion/cache@11.13.1': dependencies: @@ -1164,17 +1179,19 @@ snapshots: '@emotion/weak-memoize': 0.4.0 stylis: 4.2.0 - '@emotion/hash@0.9.2': {} + '@emotion/hash@0.9.2': + optional: true '@emotion/is-prop-valid@1.3.0': dependencies: '@emotion/memoize': 0.9.0 + optional: true '@emotion/memoize@0.9.0': {} '@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.6 '@emotion/babel-plugin': 11.12.0 '@emotion/cache': 11.13.1 '@emotion/serialize': 1.3.0 @@ -1185,6 +1202,7 @@ snapshots: react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 + optional: true '@emotion/serialize@1.3.0': dependencies: @@ -1193,12 +1211,13 @@ snapshots: '@emotion/unitless': 0.9.0 '@emotion/utils': 1.4.0 csstype: 3.1.3 + optional: true '@emotion/sheet@1.4.0': {} '@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.6 '@emotion/babel-plugin': 11.12.0 '@emotion/is-prop-valid': 1.3.0 '@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1) @@ -1208,12 +1227,15 @@ snapshots: react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 + optional: true - '@emotion/unitless@0.9.0': {} + '@emotion/unitless@0.9.0': + optional: true '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': dependencies: react: 18.3.1 + optional: true '@emotion/utils@1.4.0': {} @@ -1304,17 +1326,25 @@ snapshots: '@kurkle/color@0.3.2': {} - '@mui/core-downloads-tracker@5.16.6': {} + '@mui/core-downloads-tracker@6.1.1': {} - '@mui/material@5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/icons-material@6.1.1(@mui/material@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 - '@mui/core-downloads-tracker': 5.16.6 - '@mui/system': 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) - '@mui/types': 7.2.15(@types/react@18.3.3) - '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) + '@babel/runtime': 7.25.6 + '@mui/material': 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/material@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/core-downloads-tracker': 6.1.1 + '@mui/system': 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@mui/types': 7.2.17(@types/react@18.3.3) + '@mui/utils': 6.1.1(@types/react@18.3.3)(react@18.3.1) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.10 + '@types/react-transition-group': 4.4.11 clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 @@ -1327,19 +1357,20 @@ snapshots: '@emotion/styled': 11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 - '@mui/private-theming@5.16.6(@types/react@18.3.3)(react@18.3.1)': + '@mui/private-theming@6.1.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 - '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) + '@babel/runtime': 7.25.6 + '@mui/utils': 6.1.1(@types/react@18.3.3)(react@18.3.1) prop-types: 15.8.1 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 - '@mui/styled-engine@5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)': + '@mui/styled-engine@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.6 '@emotion/cache': 11.13.1 + '@emotion/sheet': 1.4.0 csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 @@ -1347,13 +1378,13 @@ snapshots: '@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1) '@emotion/styled': 11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) - '@mui/system@5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': + '@mui/system@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 - '@mui/private-theming': 5.16.6(@types/react@18.3.3)(react@18.3.1) - '@mui/styled-engine': 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - '@mui/types': 7.2.15(@types/react@18.3.3) - '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) + '@babel/runtime': 7.25.6 + '@mui/private-theming': 6.1.1(@types/react@18.3.3)(react@18.3.1) + '@mui/styled-engine': 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.17(@types/react@18.3.3) + '@mui/utils': 6.1.1(@types/react@18.3.3)(react@18.3.1) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 @@ -1363,14 +1394,14 @@ snapshots: '@emotion/styled': 11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) '@types/react': 18.3.3 - '@mui/types@7.2.15(@types/react@18.3.3)': + '@mui/types@7.2.17(@types/react@18.3.3)': optionalDependencies: '@types/react': 18.3.3 '@mui/utils@5.16.6(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 - '@mui/types': 7.2.15(@types/react@18.3.3) + '@babel/runtime': 7.25.6 + '@mui/types': 7.2.17(@types/react@18.3.3) '@types/prop-types': 15.7.12 clsx: 2.1.1 prop-types: 15.8.1 @@ -1379,13 +1410,26 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 - '@mui/x-date-pickers@7.12.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/utils@6.1.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 - '@mui/material': 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 5.16.6(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@babel/runtime': 7.25.6 + '@mui/types': 7.2.17(@types/react@18.3.3) + '@types/prop-types': 15.7.12 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/x-date-pickers@7.18.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/material': 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.1.1(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) - '@types/react-transition-group': 4.4.10 + '@mui/x-internals': 7.18.0(@types/react@18.3.3)(react@18.3.1) + '@types/react-transition-group': 4.4.11 clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 @@ -1398,6 +1442,14 @@ snapshots: transitivePeerDependencies: - '@types/react' + '@mui/x-internals@7.18.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - '@types/react' + '@popperjs/core@2.11.8': {} '@preact/preset-vite@2.8.1(@babel/core@7.23.7)(preact@10.19.3)(vite@4.5.1)': @@ -1456,11 +1508,12 @@ snapshots: '@trpc/server@10.45.0': {} - '@types/parse-json@4.0.2': {} + '@types/parse-json@4.0.2': + optional: true '@types/prop-types@15.7.12': {} - '@types/react-transition-group@4.4.10': + '@types/react-transition-group@4.4.11': dependencies: '@types/react': 18.3.3 @@ -1475,9 +1528,10 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.6 cosmiconfig: 7.1.0 resolve: 1.22.8 + optional: true babel-plugin-transform-hook-names@1.0.2(@babel/core@7.23.7): dependencies: @@ -1492,7 +1546,8 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) - callsites@3.1.0: {} + callsites@3.1.0: + optional: true caniuse-lite@1.0.30001575: {} @@ -1514,7 +1569,8 @@ snapshots: color-name@1.1.3: {} - convert-source-map@1.9.0: {} + convert-source-map@1.9.0: + optional: true convert-source-map@2.0.0: {} @@ -1525,6 +1581,7 @@ snapshots: parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 + optional: true css-select@5.1.0: dependencies: @@ -1576,6 +1633,7 @@ snapshots: error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 + optional: true esbuild@0.18.20: optionalDependencies: @@ -1606,11 +1664,13 @@ snapshots: escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} + escape-string-regexp@4.0.0: + optional: true estree-walker@2.0.2: {} - find-root@1.1.0: {} + find-root@1.1.0: + optional: true fsevents@2.3.3: optional: true @@ -1632,13 +1692,16 @@ snapshots: hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 + optional: true import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 + optional: true - is-arrayish@0.2.1: {} + is-arrayish@0.2.1: + optional: true is-core-module@2.13.1: dependencies: @@ -1648,13 +1711,15 @@ snapshots: jsesc@2.5.2: {} - json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@2.3.1: + optional: true json5@2.2.3: {} kolorist@1.8.0: {} - lines-and-columns@1.2.4: {} + lines-and-columns@1.2.4: + optional: true loose-envify@1.4.0: dependencies: @@ -1688,6 +1753,7 @@ snapshots: parent-module@1.0.1: dependencies: callsites: 3.1.0 + optional: true parse-json@5.2.0: dependencies: @@ -1695,10 +1761,12 @@ snapshots: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + optional: true path-parse@1.0.7: {} - path-type@4.0.0: {} + path-type@4.0.0: + optional: true picocolors@1.0.0: {} @@ -1761,7 +1829,8 @@ snapshots: regenerator-runtime@0.14.1: {} - resolve-from@4.0.0: {} + resolve-from@4.0.0: + optional: true resolve@1.22.8: dependencies: @@ -1781,7 +1850,8 @@ snapshots: source-map-js@1.2.0: {} - source-map@0.5.7: {} + source-map@0.5.7: + optional: true stylis@4.2.0: {} @@ -1811,4 +1881,5 @@ snapshots: yallist@3.1.1: {} - yaml@1.10.2: {} + yaml@1.10.2: + optional: true diff --git a/frontend/src/pages/HistoricalCalendarPrices.tsx b/frontend/src/pages/HistoricalCalendarPrices.tsx index 57121ef..2310010 100644 --- a/frontend/src/pages/HistoricalCalendarPrices.tsx +++ b/frontend/src/pages/HistoricalCalendarPrices.tsx @@ -1,5 +1,5 @@ import { signal, computed } from "@preact/signals"; -import { useEffect } from "preact/hooks"; +import { useEffect, useRef } from "preact/hooks"; import { trpc } from "../trpc.js"; import { Chart as ChartJS, @@ -17,102 +17,42 @@ import { TextField, Select, MenuItem, - InputLabel, - FormControl, Paper, + Button, + Popper, + ClickAwayListener, + Slider, + Box, } from "@mui/material"; +import { + availableUnderlyings, + calendarExitPriceChartData, + isPopperOpen, + lookbackPeriodEnd, + lookbackPeriodStart, + maxChartPrice, + maxN, + popperAnchorEl, + popperContent, + similarCalendarPriceChartData, + stockPriceChartData, + underlying, +} from "./HistoricalCalendarPrices/state.js"; +import { EditableStrike } from "./HistoricalCalendarPrices/EditableStrike.js"; +import { + refreshcalendarExitPriceChartData, + refreshSimilarCalendarPriceChartData, + refreshStockPriceChartData, +} from "./HistoricalCalendarPrices/actions.js"; +import { EditableUnderlying } from "./HistoricalCalendarPrices/EditableUnderlying.js"; +import { EditableDaysToFrontExpiration } from "./HistoricalCalendarPrices/EditableDaysToFrontExpiration.js"; +import { EditableExitToFrontExpiration } from "./HistoricalCalendarPrices/EditableExitToFrontExpiration.js"; +import { EditableDaysBetweenFrontAndBackExpiration } from "./HistoricalCalendarPrices/EditableDaysBetweenFrontAndBackExpiration.js"; +import { EditableLookbackPeriodStart } from "./HistoricalCalendarPrices/EditableLookbackPeriodStart.js"; +import { EditableLookbackPeriodEnd } from "./HistoricalCalendarPrices/EditableLookbackPeriodEnd.js"; ChartJS.register(LinearScale, CategoryScale, PointElement, Tooltip, Title); -const availableUnderlyings = signal([]); -const underlying = signal(null); - -const daysToFrontExpiration = signal(14); - -const daysBetweenFrontAndBackExpiration = signal(14); - -const strikePercentageFromUnderlyingPrice = signal(1.4); -const strikePercentageFromUnderlyingPriceRadius = signal(0.05); - -const exitToFrontExpiration = signal(2); - -const stockPriceChartData = signal([]); - -const similarCalendarPriceChartData = signal([]); - -const calendarExitPriceChartData = signal([]); - -const lookbackPeriodStart = signal("2022-01-01"); -const lookbackPeriodEnd = signal("2024-01-01"); - -const maxChartPrice = computed(() => - Math.max( - Math.max.apply( - null, - similarCalendarPriceChartData.value.map((d) => d.y) - ), - Math.max.apply( - null, - calendarExitPriceChartData.value.map((d) => d.y) - ) - ) -); - -const maxN = computed(() => - Math.max.apply( - null, - calendarExitPriceChartData.value.map((d) => d.n) - ) -); - -const refreshStockPriceChartData = () => { - stockPriceChartData.value = []; - trpc.StockPriceChart.getChartData - .query({ - underlying: underlying.value, - lookbackPeriodStart: lookbackPeriodStart.value, - lookbackPeriodEnd: lookbackPeriodEnd.value, - }) - .then((getChartDataResponse) => { - stockPriceChartData.value = getChartDataResponse; - }); -}; -const refreshSimilarCalendarPriceChartData = () => { - similarCalendarPriceChartData.value = []; - trpc.SimilarCalendarPriceChart.getChartData - .query({ - underlying: underlying.value, - daysToFrontExpiration: daysToFrontExpiration.value, - daysBetweenFrontAndBackExpiration: - daysBetweenFrontAndBackExpiration.value, - strikePercentageFromUnderlyingPriceRangeMin: - strikePercentageFromUnderlyingPrice.value - - strikePercentageFromUnderlyingPriceRadius.value, - strikePercentageFromUnderlyingPriceRangeMax: - strikePercentageFromUnderlyingPrice.value + - strikePercentageFromUnderlyingPriceRadius.value, - lookbackPeriodStart: lookbackPeriodStart.value, - lookbackPeriodEnd: lookbackPeriodEnd.value, - }) - .then((getChartDataResponse) => { - similarCalendarPriceChartData.value = getChartDataResponse; - }); -}; -const refreshcalendarExitPriceChartData = () => { - calendarExitPriceChartData.value = []; - trpc.CalendarExitPriceChart.getChartData - .query({ - underlying: underlying.value, - daysToFrontExpiration: exitToFrontExpiration.value, - daysBetweenFrontAndBackExpiration: - daysBetweenFrontAndBackExpiration.value, - lookbackPeriodStart: lookbackPeriodStart.value, - lookbackPeriodEnd: lookbackPeriodEnd.value, - }) - .then((getChartDataResponse) => { - calendarExitPriceChartData.value = getChartDataResponse; - }); -}; const handleInit = () => { trpc.CalendarCharacteristicsForm.getAvailableUnderlyings .query() @@ -124,74 +64,6 @@ const handleInit = () => { refreshcalendarExitPriceChartData(); }); }; -const handleUnderlyingChange = (e) => { - if (underlying.value !== e.target.value) { - underlying.value = e.target.value; - refreshStockPriceChartData(); - refreshSimilarCalendarPriceChartData(); - refreshcalendarExitPriceChartData(); - } -}; -const handleDaysToFrontExpirationChange = (e) => { - if (daysToFrontExpiration.value !== Number.parseInt(e.target.value)) { - daysToFrontExpiration.value = Number.parseInt(e.target.value); - refreshSimilarCalendarPriceChartData(); - } -}; -const handleDaysBetweenFrontAndBackExpirationChange = (e) => { - if ( - daysBetweenFrontAndBackExpiration.value !== Number.parseInt(e.target.value) - ) { - daysBetweenFrontAndBackExpiration.value = Number.parseInt(e.target.value); - refreshSimilarCalendarPriceChartData(); - refreshcalendarExitPriceChartData(); - } -}; -const handleStrikePercentageFromUnderlyingPriceChange = (e) => { - if ( - strikePercentageFromUnderlyingPrice.value !== - Number.parseFloat(e.target.value) - ) { - strikePercentageFromUnderlyingPrice.value = Number.parseFloat( - e.target.value - ); - refreshSimilarCalendarPriceChartData(); - } -}; -const handleStrikePercentageFromUnderlyingPriceRadiusChange = (e) => { - if ( - strikePercentageFromUnderlyingPriceRadius.value !== - Number.parseFloat(e.target.value) - ) { - strikePercentageFromUnderlyingPriceRadius.value = Number.parseFloat( - e.target.value - ); - refreshSimilarCalendarPriceChartData(); - } -}; -const handleExitToFrontExpirationChange = (e) => { - if (exitToFrontExpiration.value !== Number.parseInt(e.target.value)) { - exitToFrontExpiration.value = Number.parseInt(e.target.value); - refreshcalendarExitPriceChartData(); - } -}; - -const handleLookbackPeriodStartChange = (e) => { - if (lookbackPeriodStart.value !== e.target.value) { - lookbackPeriodStart.value = e.target.value; - refreshStockPriceChartData(); - refreshSimilarCalendarPriceChartData(); - refreshcalendarExitPriceChartData(); - } -}; -const handleLookbackPeriodEndChange = (e) => { - if (lookbackPeriodEnd.value !== e.target.value) { - lookbackPeriodEnd.value = e.target.value; - refreshStockPriceChartData(); - refreshSimilarCalendarPriceChartData(); - refreshcalendarExitPriceChartData(); - } -}; export function HistoricalCalendarPrices() { useEffect(handleInit, []); @@ -201,113 +73,36 @@ export function HistoricalCalendarPrices() { - Historical Calendar Prices + : + + -Day Calendar @ + %-from-the-money + + Opening at DTE, Closing at{" "} + + DTE + + + - + + + { + isPopperOpen.value = false; + // refreshSimilarCalendarPriceChartData(); + console.log("clicked away"); + }} + > + + + {popperContent.value} + + + - - - - - - Available Underlyings - - - - - - - - - - - - - - - - - - - - - handleLookbackPeriodStartChange({ - target: { value: e.target.value }, - }) - } - InputLabelProps={{ shrink: true }} - /> - - - - handleLookbackPeriodEndChange({ - target: { value: e.target.value }, - }) - } - InputLabelProps={{ shrink: true }} - /> - - - - - - + + {underlying.value !== null && stockPriceChartData.value.length > 0 ? ( - - + + {underlying.value !== null && similarCalendarPriceChartData.value.length > 0 ? ( - - + + {underlying.value !== null && similarCalendarPriceChartData.value.length > 0 ? ( { + if ( + daysBetweenFrontAndBackExpiration.value !== Number.parseInt(e.target.value) + ) { + daysBetweenFrontAndBackExpiration.value = Number.parseInt(e.target.value); + refreshSimilarCalendarPriceChartData(); + refreshcalendarExitPriceChartData(); + } +}; + +function DaysBetweenFrontAndBackExpirationChooser() { + return ( + + ); +} + +export function EditableDaysBetweenFrontAndBackExpiration() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableDaysToFrontExpiration.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableDaysToFrontExpiration.tsx new file mode 100644 index 0000000..3ac60b7 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableDaysToFrontExpiration.tsx @@ -0,0 +1,32 @@ +import TextField from "@mui/material/TextField"; +import { refreshSimilarCalendarPriceChartData } from "./actions"; +import { EditableValue } from "./EditableValue"; +import { daysToFrontExpiration } from "./state"; + +const handleDaysToFrontExpirationChange = (e) => { + if (daysToFrontExpiration.value !== Number.parseInt(e.target.value)) { + daysToFrontExpiration.value = Number.parseInt(e.target.value); + refreshSimilarCalendarPriceChartData(); + } +}; + +function DaysToFrontExpirationChooser() { + return ( + + ); +} + +export function EditableDaysToFrontExpiration() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableExitToFrontExpiration.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableExitToFrontExpiration.tsx new file mode 100644 index 0000000..28d64e0 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableExitToFrontExpiration.tsx @@ -0,0 +1,32 @@ +import TextField from "@mui/material/TextField"; +import { EditableValue } from "./EditableValue"; +import { exitToFrontExpiration } from "./state"; +import { refreshcalendarExitPriceChartData } from "./actions"; + +const handleExitToFrontExpirationChange = (e) => { + if (exitToFrontExpiration.value !== Number.parseInt(e.target.value)) { + exitToFrontExpiration.value = Number.parseInt(e.target.value); + refreshcalendarExitPriceChartData(); + } +}; + +function ExitToFrontExpirationChooser() { + return ( + + ); +} + +export function EditableExitToFrontExpiration() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodEnd.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodEnd.tsx new file mode 100644 index 0000000..8022a16 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodEnd.tsx @@ -0,0 +1,38 @@ +import TextField from "@mui/material/TextField"; +import { + refreshcalendarExitPriceChartData, + refreshSimilarCalendarPriceChartData, + refreshStockPriceChartData, +} from "./actions"; +import { lookbackPeriodEnd } from "./state"; +import { EditableValue } from "./EditableValue"; + +const handleLookbackPeriodEndChange = (e) => { + if (lookbackPeriodEnd.value !== e.target.value) { + lookbackPeriodEnd.value = e.target.value; + refreshStockPriceChartData(); + refreshSimilarCalendarPriceChartData(); + refreshcalendarExitPriceChartData(); + } +}; + +function LookbackPeriodEndChooser() { + return ( + + ); +} + +export function EditableLookbackPeriodEnd() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodStart.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodStart.tsx new file mode 100644 index 0000000..23b4c5a --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableLookbackPeriodStart.tsx @@ -0,0 +1,38 @@ +import TextField from "@mui/material/TextField"; +import { + refreshcalendarExitPriceChartData, + refreshSimilarCalendarPriceChartData, + refreshStockPriceChartData, +} from "./actions"; +import { lookbackPeriodStart } from "./state"; +import { EditableValue } from "./EditableValue"; + +const handleLookbackPeriodStartChange = (e) => { + if (lookbackPeriodStart.value !== e.target.value) { + lookbackPeriodStart.value = e.target.value; + refreshStockPriceChartData(); + refreshSimilarCalendarPriceChartData(); + refreshcalendarExitPriceChartData(); + } +}; + +function LookbackPeriodStartChooser() { + return ( + + ); +} + +export function EditableLookbackPeriodStart() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableStrike.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableStrike.tsx new file mode 100644 index 0000000..fd2f69c --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableStrike.tsx @@ -0,0 +1,70 @@ +import Box from "@mui/material/Box"; +import { EditableValue } from "./EditableValue"; +import { + strikePercentageFromUnderlyingPrice, + strikePercentageFromUnderlyingPriceRadius, +} from "./state"; +import Slider from "@mui/material/Slider"; +import { refreshSimilarCalendarPriceChartData } from "./actions"; + +function StrikePercentageFromUnderlyingPriceChooser() { + return ( + { + strikePercentageFromUnderlyingPrice.value = value as number; + }} + onChangeCommitted={(e, value) => { + refreshSimilarCalendarPriceChartData(); + }} + InputProps={{ endAdornment: "%" }} + /> + ); +} + +function StrikePercentageFromUnderlyingPriceRadiusChooser() { + return ( + { + strikePercentageFromUnderlyingPriceRadius.value = value as number; + }} + onChangeCommitted={(e, value) => { + refreshSimilarCalendarPriceChartData(); + }} + InputProps={{ endAdornment: "%" }} + /> + ); +} + +/** This is its own component so that sliding the slider with the mouse is + * smoother. Preact detects reads from the "slider" signal values, and + * associates them with the component that read them and redraws that component. + * If this was not its own component, it would redraw the entire UI. It was very + * slow. */ +export function EditableStrike() { + return ( + + + + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableUnderlying.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableUnderlying.tsx new file mode 100644 index 0000000..e4d8d61 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableUnderlying.tsx @@ -0,0 +1,42 @@ +import Select from "@mui/material/Select"; +import { EditableValue } from "./EditableValue"; +import { availableUnderlyings, underlying } from "./state"; +import MenuItem from "@mui/material/MenuItem"; +import { + refreshcalendarExitPriceChartData, + refreshSimilarCalendarPriceChartData, + refreshStockPriceChartData, +} from "./actions"; + +const handleUnderlyingChange = (e) => { + if (underlying.value !== e.target.value) { + underlying.value = e.target.value; + refreshStockPriceChartData(); + refreshSimilarCalendarPriceChartData(); + refreshcalendarExitPriceChartData(); + } +}; + +function UnderlyingChooser() { + return ( + + ); +} + +export function EditableUnderlying() { + return ( + + + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/EditableValue.tsx b/frontend/src/pages/HistoricalCalendarPrices/EditableValue.tsx new file mode 100644 index 0000000..59fc936 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/EditableValue.tsx @@ -0,0 +1,29 @@ +import Button from "@mui/material/Button"; +import { isPopperOpen, popperAnchorEl, popperContent } from "./state"; + +export function EditableValue({ text, children }) { + return ( + + ); +} diff --git a/frontend/src/pages/HistoricalCalendarPrices/actions.ts b/frontend/src/pages/HistoricalCalendarPrices/actions.ts new file mode 100644 index 0000000..d3e7ca3 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/actions.ts @@ -0,0 +1,63 @@ +import { trpc } from "../../trpc"; +import { + calendarExitPriceChartData, + daysBetweenFrontAndBackExpiration, + daysToFrontExpiration, + exitToFrontExpiration, + lookbackPeriodEnd, + lookbackPeriodStart, + similarCalendarPriceChartData, + stockPriceChartData, + strikePercentageFromUnderlyingPrice, + strikePercentageFromUnderlyingPriceRadius, + underlying, +} from "./state"; + +export const refreshStockPriceChartData = () => { + stockPriceChartData.value = []; + trpc.StockPriceChart.getChartData + .query({ + underlying: underlying.value, + lookbackPeriodStart: lookbackPeriodStart.value, + lookbackPeriodEnd: lookbackPeriodEnd.value, + }) + .then((getChartDataResponse) => { + stockPriceChartData.value = getChartDataResponse; + }); +}; +export const refreshSimilarCalendarPriceChartData = () => { + similarCalendarPriceChartData.value = []; + trpc.SimilarCalendarPriceChart.getChartData + .query({ + underlying: underlying.value, + daysToFrontExpiration: daysToFrontExpiration.value, + daysBetweenFrontAndBackExpiration: + daysBetweenFrontAndBackExpiration.value, + strikePercentageFromUnderlyingPriceRangeMin: + strikePercentageFromUnderlyingPrice.value - + strikePercentageFromUnderlyingPriceRadius.value, + strikePercentageFromUnderlyingPriceRangeMax: + strikePercentageFromUnderlyingPrice.value + + strikePercentageFromUnderlyingPriceRadius.value, + lookbackPeriodStart: lookbackPeriodStart.value, + lookbackPeriodEnd: lookbackPeriodEnd.value, + }) + .then((getChartDataResponse) => { + similarCalendarPriceChartData.value = getChartDataResponse; + }); +}; +export const refreshcalendarExitPriceChartData = () => { + calendarExitPriceChartData.value = []; + trpc.CalendarExitPriceChart.getChartData + .query({ + underlying: underlying.value, + daysToFrontExpiration: exitToFrontExpiration.value, + daysBetweenFrontAndBackExpiration: + daysBetweenFrontAndBackExpiration.value, + lookbackPeriodStart: lookbackPeriodStart.value, + lookbackPeriodEnd: lookbackPeriodEnd.value, + }) + .then((getChartDataResponse) => { + calendarExitPriceChartData.value = getChartDataResponse; + }); +}; diff --git a/frontend/src/pages/HistoricalCalendarPrices/state.ts b/frontend/src/pages/HistoricalCalendarPrices/state.ts new file mode 100644 index 0000000..029ef26 --- /dev/null +++ b/frontend/src/pages/HistoricalCalendarPrices/state.ts @@ -0,0 +1,46 @@ +import { computed, signal } from "@preact/signals"; + +export const isPopperOpen = signal(false); +export const popperAnchorEl = signal(null); +export const popperContent = signal(null); + +export const availableUnderlyings = signal([]); +export const underlying = signal(null); + +export const daysToFrontExpiration = signal(14); + +export const daysBetweenFrontAndBackExpiration = signal(14); + +export const strikePercentageFromUnderlyingPrice = signal(1.4); +export const strikePercentageFromUnderlyingPriceRadius = signal(0.05); + +export const exitToFrontExpiration = signal(2); + +export const stockPriceChartData = signal([]); + +export const similarCalendarPriceChartData = signal([]); + +export const calendarExitPriceChartData = signal([]); + +export const lookbackPeriodStart = signal("2022-01-01"); +export const lookbackPeriodEnd = signal("2024-01-01"); + +export const maxChartPrice = computed(() => + Math.max( + Math.max.apply( + null, + similarCalendarPriceChartData.value.map((d) => d.y) + ), + Math.max.apply( + null, + calendarExitPriceChartData.value.map((d) => d.y) + ) + ) +); + +export const maxN = computed(() => + Math.max.apply( + null, + calendarExitPriceChartData.value.map((d) => d.n) + ) +);