aider: implement simple cli in `ink` for running backtests

main
avraham 9 months ago
parent d134385bd7
commit 82915fb0b5

@ -2,10 +2,11 @@
"private": true,
"type": "module",
"scripts": {
"build": "esbuild src/*.ts src/**/*.ts src/**/**/*.ts --platform=node --outdir=dist --format=esm",
"build": "esbuild src/*.ts src/*.tsx src/**/*.ts src/**/**/*.ts --platform=node --outdir=dist --format=esm",
"dev:node": "node --watch dist/index.js",
"dev:esbuild": "pnpm run build --watch",
"dev": "run-p dev:*"
"dev": "run-p dev:*",
"cli": "tsx src/cli.tsx"
},
"dependencies": {
"@clickhouse/client": "^0.2.7",
@ -14,18 +15,22 @@
"@trpc/server": "^10.45.0",
"cors": "^2.8.5",
"execa": "^9.3.0",
"ink": "^4.1.0",
"ink-text-input": "^5.0.1",
"lmdbx": "^0.5.0",
"p-all": "^5.0.0",
"p-queue": "^8.0.1",
"p-retry": "^6.2.0",
"p-series": "^3.0.0",
"p-throttle": "^6.1.0",
"react": "^18.2.0",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/node": "^20.10.7",
"@types/react": "^18.0.0",
"esbuild": "^0.19.11",
"npm-run-all": "^4.1.5",
"tsx": "^4.17.0",

@ -26,6 +26,12 @@ importers:
execa:
specifier: ^9.3.0
version: 9.3.0
ink:
specifier: ^4.1.0
version: 4.4.1(@types/react@18.3.3)(react@18.3.1)
ink-text-input:
specifier: ^5.0.1
version: 5.0.1(ink@4.4.1(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)
lmdbx:
specifier: ^0.5.0
version: 0.5.0
@ -44,6 +50,9 @@ importers:
p-throttle:
specifier: ^6.1.0
version: 6.1.0
react:
specifier: ^18.2.0
version: 18.3.1
sqlite:
specifier: ^5.1.1
version: 5.1.1
@ -57,6 +66,9 @@ importers:
'@types/node':
specifier: ^20.10.7
version: 20.10.7
'@types/react':
specifier: ^18.0.0
version: 18.3.3
esbuild:
specifier: ^0.19.11
version: 0.19.11
@ -72,6 +84,10 @@ importers:
packages:
'@alcalzone/ansi-tokenize@0.1.3':
resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==}
engines: {node: '>=14.13.1'}
'@clickhouse/client-common@0.2.7':
resolution: {integrity: sha512-vgZm+8c5Cu1toIx1/xplF5dEHlCPw+7pJDOOEtLv2CIUVZ0Bl6nGVZ43EWxRdHeah9ivTfoRWhN1zI1PxjH0xQ==}
@ -428,6 +444,12 @@ packages:
'@types/node@20.10.7':
resolution: {integrity: sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==}
'@types/prop-types@15.7.12':
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
'@types/react@18.3.3':
resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}
'@types/retry@0.12.2':
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
@ -446,14 +468,26 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
ansi-escapes@6.2.1:
resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==}
engines: {node: '>=14.16'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
@ -469,6 +503,10 @@ packages:
resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
engines: {node: '>= 0.4'}
auto-bind@5.0.1:
resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
available-typed-arrays@1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
@ -502,6 +540,10 @@ packages:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
chalk@5.3.0:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@ -509,10 +551,30 @@ packages:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
cli-boxes@3.0.0:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
engines: {node: '>=10'}
cli-cursor@4.0.0:
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
cli-truncate@3.1.0:
resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
code-excerpt@4.0.0:
resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
@ -529,6 +591,10 @@ packages:
console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
convert-to-spaces@2.0.1:
resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@ -541,6 +607,9 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
debug@4.3.5:
resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
engines: {node: '>=6.0'}
@ -573,9 +642,15 @@ packages:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
encoding@0.1.13:
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
@ -618,6 +693,10 @@ packages:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
escape-string-regexp@2.0.0:
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
engines: {node: '>=8'}
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
@ -765,6 +844,10 @@ packages:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
indent-string@5.0.0:
resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
engines: {node: '>=12'}
infer-owner@1.0.4:
resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
@ -778,6 +861,26 @@ packages:
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
ink-text-input@5.0.1:
resolution: {integrity: sha512-crnsYJalG4EhneOFnr/q+Kzw1RgmXI2KsBaLFE6mpiIKxAtJLUnvygOF2IUKO8z4nwkSkveGRBMd81RoYdRSag==}
engines: {node: '>=14.16'}
peerDependencies:
ink: ^4.0.0
react: ^18.0.0
ink@4.4.1:
resolution: {integrity: sha512-rXckvqPBB0Krifk5rn/5LvQGmyXwCUpBfmTwbkQNBY9JY8RSl3b8OftBNEYxg4+SWUhEKcPifgope28uL9inlA==}
engines: {node: '>=14.16'}
peerDependencies:
'@types/react': '>=18.0.0'
react: '>=18.0.0'
react-devtools-core: ^4.19.1
peerDependenciesMeta:
'@types/react':
optional: true
react-devtools-core:
optional: true
internal-slot@1.0.6:
resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
engines: {node: '>= 0.4'}
@ -803,6 +906,10 @@ packages:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
is-ci@3.0.1:
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
hasBin: true
is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
@ -814,9 +921,16 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-fullwidth-code-point@4.0.0:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
engines: {node: '>=12'}
is-lambda@1.0.1:
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
is-lower-case@2.0.2:
resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==}
is-negative-zero@2.0.2:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
engines: {node: '>= 0.4'}
@ -860,6 +974,9 @@ packages:
resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==}
engines: {node: '>=18'}
is-upper-case@2.0.2:
resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==}
is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
@ -869,6 +986,9 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
jsbn@1.1.0:
resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
@ -882,6 +1002,13 @@ packages:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
engines: {node: '>=4'}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@ -894,6 +1021,10 @@ packages:
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
engines: {node: '>= 0.10.0'}
mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
@ -1031,6 +1162,10 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
ordered-binary@1.5.1:
resolution: {integrity: sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==}
@ -1074,6 +1209,10 @@ packages:
resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
engines: {node: '>=18'}
patch-console@2.0.0:
resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
@ -1134,6 +1273,16 @@ packages:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
react-reconciler@0.29.2:
resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==}
engines: {node: '>=0.10.0'}
peerDependencies:
react: ^18.3.1
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
read-pkg@3.0.0:
resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}
engines: {node: '>=4'}
@ -1153,6 +1302,10 @@ packages:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
restore-cursor@4.0.0:
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
retry@0.12.0:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
engines: {node: '>= 4'}
@ -1179,6 +1332,9 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@ -1234,6 +1390,14 @@ packages:
simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
slice-ansi@5.0.0:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'}
slice-ansi@6.0.0:
resolution: {integrity: sha512-6bn4hRfkTvDfUoEQYkERg0BVF1D0vrX9HEkMl08uDiNWvVvjylLHvZFZWkDo6wjT8tUctbYl1nCOuE66ZTaUtA==}
engines: {node: '>=14.16'}
smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
@ -1271,10 +1435,18 @@ packages:
resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
engines: {node: '>= 8'}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
string.prototype.padend@3.1.5:
resolution: {integrity: sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==}
engines: {node: '>= 0.4'}
@ -1296,6 +1468,10 @@ packages:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.1.0:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
@ -1327,6 +1503,9 @@ packages:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
tsx@4.17.0:
resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==}
engines: {node: '>=18.0.0'}
@ -1335,6 +1514,14 @@ packages:
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
type-fest@0.12.0:
resolution: {integrity: sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==}
engines: {node: '>=10'}
type-fest@3.13.1:
resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
engines: {node: '>=14.16'}
typed-array-buffer@1.0.0:
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
engines: {node: '>= 0.4'}
@ -1399,9 +1586,29 @@ packages:
wide-align@1.1.5:
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
widest-line@4.0.1:
resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
engines: {node: '>=12'}
wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
@ -1409,8 +1616,16 @@ packages:
resolution: {integrity: sha512-FsQpXXeOEe05tcJN4Z2eicuC6+6KiJdBbPOAChanSkwwjZ277XGsh8wh/HaPuGeifTiw/7dgAzabitu2bnDvRg==}
engines: {node: '>=18'}
yoga-wasm-web@0.3.3:
resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==}
snapshots:
'@alcalzone/ansi-tokenize@0.1.3':
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 4.0.0
'@clickhouse/client-common@0.2.7': {}
'@clickhouse/client@0.2.7':
@ -1612,6 +1827,13 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/prop-types@15.7.12': {}
'@types/react@18.3.3':
dependencies:
'@types/prop-types': 15.7.12
csstype: 3.1.3
'@types/retry@0.12.2': {}
abbrev@1.1.1:
@ -1635,13 +1857,19 @@ snapshots:
indent-string: 4.0.0
optional: true
ansi-escapes@6.2.1: {}
ansi-regex@5.0.1:
optional: true
ansi-regex@6.0.1: {}
ansi-styles@3.2.1:
dependencies:
color-convert: 1.9.3
ansi-styles@6.2.1: {}
aproba@2.0.0:
optional: true
@ -1666,6 +1894,8 @@ snapshots:
is-array-buffer: 3.0.2
is-shared-array-buffer: 1.0.2
auto-bind@5.0.1: {}
available-typed-arrays@1.0.5: {}
balanced-match@1.0.2: {}
@ -1728,13 +1958,32 @@ snapshots:
escape-string-regexp: 1.0.5
supports-color: 5.5.0
chalk@5.3.0: {}
chownr@1.1.4: {}
chownr@2.0.0: {}
ci-info@3.9.0: {}
clean-stack@2.2.0:
optional: true
cli-boxes@3.0.0: {}
cli-cursor@4.0.0:
dependencies:
restore-cursor: 4.0.0
cli-truncate@3.1.0:
dependencies:
slice-ansi: 5.0.0
string-width: 5.1.2
code-excerpt@4.0.0:
dependencies:
convert-to-spaces: 2.0.1
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
@ -1749,6 +1998,8 @@ snapshots:
console-control-strings@1.1.0:
optional: true
convert-to-spaces@2.0.1: {}
cors@2.8.5:
dependencies:
object-assign: 4.1.1
@ -1768,6 +2019,8 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
csstype@3.1.3: {}
debug@4.3.5:
dependencies:
ms: 2.1.2
@ -1796,9 +2049,13 @@ snapshots:
detect-libc@2.0.3: {}
eastasianwidth@0.2.0: {}
emoji-regex@8.0.0:
optional: true
emoji-regex@9.2.2: {}
encoding@0.1.13:
dependencies:
iconv-lite: 0.6.3
@ -1927,6 +2184,8 @@ snapshots:
escape-string-regexp@1.0.5: {}
escape-string-regexp@2.0.0: {}
eventemitter3@5.0.1: {}
execa@9.3.0:
@ -2099,6 +2358,8 @@ snapshots:
indent-string@4.0.0:
optional: true
indent-string@5.0.0: {}
infer-owner@1.0.4:
optional: true
@ -2112,6 +2373,47 @@ snapshots:
ini@1.3.8: {}
ink-text-input@5.0.1(ink@4.4.1(@types/react@18.3.3)(react@18.3.1))(react@18.3.1):
dependencies:
chalk: 5.3.0
ink: 4.4.1(@types/react@18.3.3)(react@18.3.1)
react: 18.3.1
type-fest: 3.13.1
ink@4.4.1(@types/react@18.3.3)(react@18.3.1):
dependencies:
'@alcalzone/ansi-tokenize': 0.1.3
ansi-escapes: 6.2.1
auto-bind: 5.0.1
chalk: 5.3.0
cli-boxes: 3.0.0
cli-cursor: 4.0.0
cli-truncate: 3.1.0
code-excerpt: 4.0.0
indent-string: 5.0.0
is-ci: 3.0.1
is-lower-case: 2.0.2
is-upper-case: 2.0.2
lodash: 4.17.21
patch-console: 2.0.0
react: 18.3.1
react-reconciler: 0.29.2(react@18.3.1)
scheduler: 0.23.2
signal-exit: 3.0.7
slice-ansi: 6.0.0
stack-utils: 2.0.6
string-width: 5.1.2
type-fest: 0.12.0
widest-line: 4.0.1
wrap-ansi: 8.1.0
ws: 8.18.0
yoga-wasm-web: 0.3.3
optionalDependencies:
'@types/react': 18.3.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
internal-slot@1.0.6:
dependencies:
get-intrinsic: 1.2.2
@ -2143,6 +2445,10 @@ snapshots:
is-callable@1.2.7: {}
is-ci@3.0.1:
dependencies:
ci-info: 3.9.0
is-core-module@2.13.1:
dependencies:
hasown: 2.0.0
@ -2154,9 +2460,15 @@ snapshots:
is-fullwidth-code-point@3.0.0:
optional: true
is-fullwidth-code-point@4.0.0: {}
is-lambda@1.0.1:
optional: true
is-lower-case@2.0.2:
dependencies:
tslib: 2.6.3
is-negative-zero@2.0.2: {}
is-network-error@1.0.1: {}
@ -2192,6 +2504,10 @@ snapshots:
is-unicode-supported@2.0.0: {}
is-upper-case@2.0.2:
dependencies:
tslib: 2.6.3
is-weakref@1.0.2:
dependencies:
call-bind: 1.0.5
@ -2200,6 +2516,8 @@ snapshots:
isexe@2.0.0: {}
js-tokens@4.0.0: {}
jsbn@1.1.0:
optional: true
@ -2220,6 +2538,12 @@ snapshots:
pify: 3.0.0
strip-bom: 3.0.0
lodash@4.17.21: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
lru-cache@6.0.0:
dependencies:
yallist: 4.0.0
@ -2250,6 +2574,8 @@ snapshots:
memorystream@0.3.1: {}
mimic-fn@2.1.0: {}
mimic-response@3.1.0: {}
minimatch@3.1.2:
@ -2416,6 +2742,10 @@ snapshots:
dependencies:
wrappy: 1.0.2
onetime@5.1.2:
dependencies:
mimic-fn: 2.1.0
ordered-binary@1.5.1: {}
p-all@5.0.0:
@ -2453,6 +2783,8 @@ snapshots:
parse-ms@4.0.0: {}
patch-console@2.0.0: {}
path-is-absolute@1.0.1:
optional: true
@ -2512,6 +2844,16 @@ snapshots:
minimist: 1.2.8
strip-json-comments: 2.0.1
react-reconciler@0.29.2(react@18.3.1):
dependencies:
loose-envify: 1.4.0
react: 18.3.1
scheduler: 0.23.2
react@18.3.1:
dependencies:
loose-envify: 1.4.0
read-pkg@3.0.0:
dependencies:
load-json-file: 4.0.0
@ -2538,6 +2880,11 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
restore-cursor@4.0.0:
dependencies:
onetime: 5.1.2
signal-exit: 3.0.7
retry@0.12.0:
optional: true
@ -2566,6 +2913,10 @@ snapshots:
safer-buffer@2.1.2:
optional: true
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
semver@5.7.2: {}
semver@7.6.2: {}
@ -2606,8 +2957,7 @@ snapshots:
get-intrinsic: 1.2.2
object-inspect: 1.13.1
signal-exit@3.0.7:
optional: true
signal-exit@3.0.7: {}
signal-exit@4.1.0: {}
@ -2619,6 +2969,16 @@ snapshots:
once: 1.4.0
simple-concat: 1.0.1
slice-ansi@5.0.0:
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 4.0.0
slice-ansi@6.0.0:
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 4.0.0
smart-buffer@4.2.0:
optional: true
@ -2673,6 +3033,10 @@ snapshots:
minipass: 3.3.6
optional: true
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@ -2680,6 +3044,12 @@ snapshots:
strip-ansi: 6.0.1
optional: true
string-width@5.1.2:
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
string.prototype.padend@3.1.5:
dependencies:
call-bind: 1.0.5
@ -2713,6 +3083,10 @@ snapshots:
ansi-regex: 5.0.1
optional: true
strip-ansi@7.1.0:
dependencies:
ansi-regex: 6.0.1
strip-bom@3.0.0: {}
strip-final-newline@4.0.0: {}
@ -2749,6 +3123,8 @@ snapshots:
mkdirp: 1.0.4
yallist: 4.0.0
tslib@2.6.3: {}
tsx@4.17.0:
dependencies:
esbuild: 0.23.0
@ -2760,6 +3136,10 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
type-fest@0.12.0: {}
type-fest@3.13.1: {}
typed-array-buffer@1.0.0:
dependencies:
call-bind: 1.0.5
@ -2848,8 +3228,22 @@ snapshots:
string-width: 4.2.3
optional: true
widest-line@4.0.1:
dependencies:
string-width: 5.1.2
wrap-ansi@8.1.0:
dependencies:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
wrappy@1.0.2: {}
ws@8.18.0: {}
yallist@4.0.0: {}
yoctocolors@2.1.0: {}
yoga-wasm-web@0.3.3: {}

@ -3,7 +3,7 @@ import { database as calendarDatabase } from "./AggregateDatabase/Calendar/optio
import type { CalendarKey } from "./AggregateDatabase/Calendar/interfaces.js";
import { nextDate } from "./lib/utils/nextDate.js";
type BacktestInput = {
export type BacktestInput = {
symbol: string;
startDate: string;
endDate: string;
@ -11,6 +11,7 @@ type BacktestInput = {
historicalProbabilityOfSuccess?: number;
initialBuyingPower?: number;
};
export async function backtest({
symbol,
startDate,
@ -138,16 +139,8 @@ export async function backtest({
}
}
console.log("Ending Buying Power:", buyingPower);
console.log("Portfolio:", portfolio.values());
}
const args = process.argv.slice(2);
if (args.length > 0) {
if (args.length < 3) {
console.error("Please provide at least 3 command line arguments.");
process.exit(1);
} else {
await backtest({ symbol: args[0], startDate: args[1], endDate: args[2] });
}
return {
endingBuyingPower: buyingPower,
portfolio: Array.from(portfolio.values()),
};
}

@ -0,0 +1,85 @@
import React, { useState, useEffect } from "react";
import { render, Box, Text, useInput, useApp } from "ink";
import TextInput from "ink-text-input";
import type { BacktestInput } from "./backtest.js";
import { backtest } from "./backtest.js";
const App = () => {
const [step, setStep] = useState(0);
const [input, setInput] = useState<Partial<BacktestInput>>({});
const [result, setResult] = useState<string>("");
const { exit } = useApp();
const steps = [
{ prompt: "Enter symbol:", key: "symbol" },
{ prompt: "Enter start date (YYYY-MM-DD):", key: "startDate" },
{ prompt: "Enter end date (YYYY-MM-DD):", key: "endDate" },
{
prompt: "Enter historical probability of success (0-1, default 0.8):",
key: "historicalProbabilityOfSuccess",
},
{
prompt: "Enter initial buying power (default 2000):",
key: "initialBuyingPower",
},
];
useInput((input, key) => {
if (key.escape) {
exit();
}
});
useEffect(() => {
if (step === steps.length) {
const runBacktest = async () => {
try {
const backtestResult = await backtest(input as BacktestInput);
setResult(JSON.stringify(backtestResult, null, 2));
} catch (error) {
setResult(`Error: ${error.message}`);
}
};
runBacktest();
}
}, [step, input]);
const handleSubmit = (value: string) => {
const currentStep = steps[step];
let parsedValue: string | number = value;
if (
currentStep.key === "historicalProbabilityOfSuccess" ||
currentStep.key === "initialBuyingPower"
) {
parsedValue = Number.parseFloat(value) || undefined;
}
setInput({ ...input, [currentStep.key]: parsedValue });
setStep(step + 1);
};
if (step < steps.length) {
return (
<Box flexDirection="column">
<Text>{steps[step].prompt}</Text>
<TextInput
value={(input[steps[step].key] as string) || ""}
onChange={(value) =>
setInput((prev) => ({ ...prev, [steps[step].key]: value }))
}
onSubmit={handleSubmit}
/>
</Box>
);
}
return (
<Box flexDirection="column">
<Text>Backtest Results:</Text>
<Text>{result}</Text>
</Box>
);
};
render(<App />);

@ -1,5 +1,6 @@
{
"compilerOptions": {
"jsx": "react",
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",

Loading…
Cancel
Save