Compare commits
2 Commits
0ecbcad0b0
...
ead2e5bd1b
| Author | SHA1 | Date | |
|---|---|---|---|
| ead2e5bd1b | |||
| 6d8ac852ab |
@@ -1,6 +1,6 @@
|
||||
import { signal } from "@preact/signals";
|
||||
import { useCallback, useEffect } from "preact/hooks";
|
||||
import {trpc} from '../../trpc.js';
|
||||
import { trpc } from "../../trpc.js";
|
||||
|
||||
const availableUnderlyings = signal([]);
|
||||
const chosenUnderlying = signal(null);
|
||||
@@ -17,54 +17,86 @@ const chosenStrike = signal(null);
|
||||
const optionContractUplotData = signal([]);
|
||||
const underlyingUplotData = signal([]);
|
||||
|
||||
function chooseUnderlying(underlying: string) {
|
||||
chosenUnderlying.value = underlying;
|
||||
trpc.getAvailableAsOfDates
|
||||
.query({ underlying: underlying })
|
||||
.then((getAvailableAsOfDatesResponse) => {
|
||||
availableAsOfDates.value = getAvailableAsOfDatesResponse;
|
||||
chooseAsOfDate(getAvailableAsOfDatesResponse[0]);
|
||||
});
|
||||
trpc.getOpensForUnderlying
|
||||
.query({ underlying: underlying })
|
||||
.then((getOpensForUnderlyingResponse) => {
|
||||
underlyingUplotData.value = getOpensForUnderlyingResponse;
|
||||
});
|
||||
}
|
||||
|
||||
function chooseAsOfDate(asOfDate: string) {
|
||||
chosenAsOfDate.value = asOfDate;
|
||||
trpc.getExpirationsForUnderlying
|
||||
.query({
|
||||
underlying: chosenUnderlying.value,
|
||||
asOfDate: chosenAsOfDate.value,
|
||||
})
|
||||
.then((getExpirationsForUnderlyingResponse) => {
|
||||
availableExpirations.value = getExpirationsForUnderlyingResponse;
|
||||
chooseExpiration(getExpirationsForUnderlyingResponse[0]);
|
||||
});
|
||||
}
|
||||
|
||||
function chooseExpiration(expiration: string) {
|
||||
chosenExpiration.value = expiration;
|
||||
trpc.getStrikesForUnderlying
|
||||
.query({
|
||||
underlying: chosenUnderlying.value,
|
||||
asOfDate: chosenAsOfDate.value,
|
||||
expirationDate: expiration,
|
||||
})
|
||||
.then((getStrikesForUnderlyingResponse) => {
|
||||
availableStrikes.value = getStrikesForUnderlyingResponse;
|
||||
chooseStrike(getStrikesForUnderlyingResponse[0]);
|
||||
});
|
||||
}
|
||||
|
||||
function chooseStrike(strike: string) {
|
||||
chosenStrike.value = strike;
|
||||
trpc.getOpensForOptionContract
|
||||
.query({
|
||||
underlying: chosenUnderlying.value,
|
||||
expirationDate: chosenExpiration.value,
|
||||
strike: parseFloat(strike),
|
||||
})
|
||||
.then((getOpensForOptionContractResponse) => {
|
||||
optionContractUplotData.value = getOpensForOptionContractResponse;
|
||||
});
|
||||
}
|
||||
|
||||
export function CalendarOptimizer() {
|
||||
const handleInit = useCallback(() => {
|
||||
trpc.getAvailableUnderlyings
|
||||
.query()
|
||||
.then((availableUnderlyingsResponse) => {
|
||||
availableUnderlyings.value = availableUnderlyingsResponse;
|
||||
// load first underlying in list:
|
||||
chooseUnderlying(availableUnderlyingsResponse[0]);
|
||||
});
|
||||
}, []);
|
||||
const handleUnderlyingChange = useCallback((e) => {
|
||||
console.log(`Chose Underlying: ${e.target.value}`);
|
||||
chosenUnderlying.value = e.target.value;
|
||||
trpc.getAvailableAsOfDates
|
||||
.query({underlying:e.target.value})
|
||||
.then((getAvailableAsOfDatesResponse)=>{
|
||||
availableAsOfDates.value = getAvailableAsOfDatesResponse;
|
||||
});
|
||||
trpc.getOpensForUnderlying
|
||||
.query({underlying:e.target.value})
|
||||
.then((getOpensForUnderlyingResponse)=>{
|
||||
underlyingUplotData.value = getOpensForUnderlyingResponse;
|
||||
});
|
||||
chooseUnderlying(e.target.value);
|
||||
}, []);
|
||||
const handleAsOfDateChange = useCallback((e) => {
|
||||
console.log(`Chose Date: ${e.target.value}`);
|
||||
chosenAsOfDate.value = e.target.value;
|
||||
trpc.getExpirationsForUnderlying
|
||||
.query({underlying:chosenUnderlying.value, asOfDate:chosenAsOfDate.value})
|
||||
.then((getExpirationsForUnderlyingResponse)=>{
|
||||
availableExpirations.value = getExpirationsForUnderlyingResponse;
|
||||
});
|
||||
chooseAsOfDate(e.target.value);
|
||||
}, []);
|
||||
const handleExpirationChange = useCallback((e) => {
|
||||
console.log(`Chose Expiration: ${e.target.value}`);
|
||||
chosenExpiration.value = e.target.value;
|
||||
trpc.getStrikesForUnderlying
|
||||
.query({underlying:chosenUnderlying.value, asOfDate:chosenAsOfDate.value, expirationDate: e.target.value})
|
||||
.then((getStrikesForUnderlyingResponse)=>{
|
||||
availableStrikes.value = getStrikesForUnderlyingResponse;
|
||||
});
|
||||
chooseExpiration(e.target.value);
|
||||
}, []);
|
||||
const handleStrikeChange = useCallback((e) => {
|
||||
console.log(`Chose Strike: ${e.target.value}`);
|
||||
chosenStrike.value = e.target.value;
|
||||
trpc.getOpensForOptionContract
|
||||
.query({underlying:chosenUnderlying.value, expirationDate:chosenExpiration.value, strike:parseFloat(e.target.value)})
|
||||
.then((getOpensForOptionContractResponse)=>{
|
||||
optionContractUplotData.value = getOpensForOptionContractResponse;
|
||||
});
|
||||
chooseStrike(e.target.value);
|
||||
}, []);
|
||||
|
||||
useEffect(handleInit, []);
|
||||
@@ -73,51 +105,51 @@ export function CalendarOptimizer(){
|
||||
<div>
|
||||
<div>
|
||||
<label>Available Underlyings</label>
|
||||
{
|
||||
availableUnderlyings.value.length === 0
|
||||
? "Loading..."
|
||||
: <select onChange={handleUnderlyingChange}>
|
||||
{availableUnderlyings.value.length === 0 ? (
|
||||
"Loading..."
|
||||
) : (
|
||||
<select onChange={handleUnderlyingChange}>
|
||||
{availableUnderlyings.value.map((availableUnderlying) => (
|
||||
<option value={availableUnderlying}>{availableUnderlying}</option>
|
||||
))}
|
||||
</select>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label>Available "As-of" Dates</label>
|
||||
{
|
||||
availableAsOfDates.value.length === 0
|
||||
? "Loading..."
|
||||
: <select onChange={handleAsOfDateChange}>
|
||||
{availableAsOfDates.value.length === 0 ? (
|
||||
"Loading..."
|
||||
) : (
|
||||
<select onChange={handleAsOfDateChange}>
|
||||
{availableAsOfDates.value.map((availableAsOfDate) => (
|
||||
<option value={availableAsOfDate}>{availableAsOfDate}</option>
|
||||
))}
|
||||
</select>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label>Available Expirations</label>
|
||||
{
|
||||
availableExpirations.value.length === 0
|
||||
? "Loading..."
|
||||
: <select onChange={handleExpirationChange}>
|
||||
{availableExpirations.value.length === 0 ? (
|
||||
"Loading..."
|
||||
) : (
|
||||
<select onChange={handleExpirationChange}>
|
||||
{availableExpirations.value.map((availableExpiration) => (
|
||||
<option value={availableExpiration}>{availableExpiration}</option>
|
||||
))}
|
||||
</select>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label>Available Strikes</label>
|
||||
{
|
||||
availableStrikes.value.length === 0
|
||||
? "Loading..."
|
||||
: <select onChange={handleStrikeChange}>
|
||||
{availableStrikes.value.length === 0 ? (
|
||||
"Loading..."
|
||||
) : (
|
||||
<select onChange={handleStrikeChange}>
|
||||
{availableStrikes.value.map((availableStrike) => (
|
||||
<option value={availableStrike}>{availableStrike}</option>
|
||||
))}
|
||||
</select>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{/* {chosenUnderlying.value!==null && underlyingUplotData.value.length>0
|
||||
? <UPlot data={underlyingUplotData.value} title="Underlying" opts={uplotOpts}/>
|
||||
|
||||
+177
-73
@@ -1,12 +1,17 @@
|
||||
import _ from './env';
|
||||
import { publicProcedure, router } from './trpc.js';
|
||||
import { query } from './clickhouse.js';
|
||||
import { createHTTPHandler } from '@trpc/server/adapters/standalone';
|
||||
import cors from 'cors';
|
||||
import { Object as ObjectT, String as StringT, TSchema, Number as NumberT } from '@sinclair/typebox';
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { createServer } from 'http';
|
||||
import _ from "./env";
|
||||
import { publicProcedure, router } from "./trpc.js";
|
||||
import { query } from "./clickhouse.js";
|
||||
import { createHTTPHandler } from "@trpc/server/adapters/standalone";
|
||||
import cors from "cors";
|
||||
import {
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
TSchema,
|
||||
Number as NumberT,
|
||||
} from "@sinclair/typebox";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { createServer } from "http";
|
||||
|
||||
/**
|
||||
* Generate a TRPC-compatible validator function given a Typebox schema.
|
||||
@@ -15,94 +20,128 @@ import { createServer } from 'http';
|
||||
* @returns A TRPC-compatible validator function
|
||||
*/
|
||||
export function RpcType<T extends TSchema>(schema: T) {
|
||||
const check = TypeCompiler.Compile(schema)
|
||||
const check = TypeCompiler.Compile(schema);
|
||||
return (value: unknown) => {
|
||||
if (check.Check(value)) return value
|
||||
const { path, message } = check.Errors(value).First()!
|
||||
throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' })
|
||||
if (check.Check(value)) return value;
|
||||
const { path, message } = check.Errors(value).First()!;
|
||||
throw new TRPCError({
|
||||
message: `${message} for ${path}`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const appRouter = router({
|
||||
getAvailableUnderlyings: publicProcedure
|
||||
.query(async (opts) => {
|
||||
return (await query<{symbol:string}>(`
|
||||
SELECT DISTINCT(symbol) as symbol FROM option_contracts
|
||||
`))
|
||||
.map(({symbol})=>symbol);
|
||||
getAvailableUnderlyings: publicProcedure.query(async (opts) => {
|
||||
// return (await query<{symbol:string}>(`
|
||||
// SELECT DISTINCT(symbol) as symbol FROM option_contracts
|
||||
// `))
|
||||
// .map(({symbol})=>symbol);
|
||||
return ["GOOGL", "AAPL", "NFLX", "AMD", "MSFT"];
|
||||
}),
|
||||
getAvailableAsOfDates: publicProcedure
|
||||
.input(RpcType(ObjectT({ underlying: StringT() })))
|
||||
.query(async (opts) => {
|
||||
const underlying = opts.input.underlying;
|
||||
return (await query<{asOfDate:string}>(`
|
||||
return (
|
||||
await query<{ asOfDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(asOfDate) as asOfDate
|
||||
FROM option_contracts
|
||||
WHERE symbol = '${underlying}'
|
||||
`))
|
||||
.map(({asOfDate})=>asOfDate);
|
||||
`)
|
||||
).map(({ asOfDate }) => asOfDate);
|
||||
}),
|
||||
getExpirationsForUnderlying: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
asOfDate:StringT()
|
||||
})))
|
||||
asOfDate: StringT(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, asOfDate } = opts.input;
|
||||
return (await query<{expirationDate:string}>(`
|
||||
return (
|
||||
await query<{ expirationDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(expirationDate)
|
||||
FROM option_contracts
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
`))
|
||||
.map(({expirationDate})=>expirationDate);
|
||||
`)
|
||||
).map(({ expirationDate }) => expirationDate);
|
||||
}),
|
||||
getStrikesForUnderlying: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
asOfDate: StringT(),
|
||||
expirationDate: StringT(),
|
||||
})))
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, asOfDate, expirationDate } = opts.input;
|
||||
return (await query<{strike:string}>(`
|
||||
return (
|
||||
await query<{ strike: string }>(`
|
||||
SELECT
|
||||
DISTINCT(strike)
|
||||
FROM option_contracts
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
`))
|
||||
.map(({strike})=>strike);
|
||||
`)
|
||||
).map(({ strike }) => strike);
|
||||
}),
|
||||
getOpensForUnderlying: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
underlying:StringT({maxLength:5})
|
||||
})))
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
return (
|
||||
await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart),
|
||||
open
|
||||
FROM stock_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
ORDER BY tsStart ASC
|
||||
`,'JSONCompactEachRow'))
|
||||
.reduce((columns, row)=>{ columns[0].push(row[0]); columns[1].push(row[1]); return columns; },[[],[]]);
|
||||
`,
|
||||
"JSONCompactEachRow"
|
||||
)
|
||||
).reduce(
|
||||
(columns, row) => {
|
||||
columns[0].push(row[0]);
|
||||
columns[1].push(row[1]);
|
||||
return columns;
|
||||
},
|
||||
[[], []]
|
||||
);
|
||||
}),
|
||||
getOpensForOptionContract: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
expirationDate: StringT(),
|
||||
strike:NumberT()
|
||||
})))
|
||||
strike: NumberT(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, expirationDate, strike } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
return (
|
||||
await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart),
|
||||
open
|
||||
@@ -110,22 +149,43 @@ const appRouter = router({
|
||||
WHERE symbol = '${underlying}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
AND strike = ${strike}
|
||||
AND optionType = 'call'
|
||||
AND type = 'call'
|
||||
ORDER BY tsStart ASC
|
||||
`,'JSONCompactEachRow'))
|
||||
.reduce((columns, row)=>{ columns[0].push(row[0]); columns[1].push(row[1]); return columns; },[[],[]]);
|
||||
`,
|
||||
"JSONCompactEachRow"
|
||||
)
|
||||
).reduce(
|
||||
(columns, row) => {
|
||||
columns[0].push(row[0]);
|
||||
columns[1].push(row[1]);
|
||||
return columns;
|
||||
},
|
||||
[[], []]
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarPrices: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
daysToFrontExpiration: NumberT(),
|
||||
daysBetweenFrontAndBackExpiration: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMin: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
|
||||
})))
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, strikePercentageFromUnderlyingPriceRangeMin, strikePercentageFromUnderlyingPriceRangeMax, } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
strikePercentageFromUnderlyingPriceRangeMin,
|
||||
strikePercentageFromUnderlyingPriceRangeMax,
|
||||
} = opts.input;
|
||||
return (
|
||||
await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as asOfTs,
|
||||
calendarPrice
|
||||
@@ -135,18 +195,32 @@ const appRouter = router({
|
||||
AND strikePercentageFromUnderlyingPrice >= ${strikePercentageFromUnderlyingPriceRangeMin}
|
||||
AND strikePercentageFromUnderlyingPrice <= ${strikePercentageFromUnderlyingPriceRangeMax}
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
`,'JSONCompactEachRow'))
|
||||
.reduce((columns, row)=>{ columns[0].push(row[0]); columns[1].push(row[1]); return columns; },[[],[]]);
|
||||
`,
|
||||
"JSONCompactEachRow"
|
||||
)
|
||||
).reduce(
|
||||
(columns, row) => {
|
||||
columns[0].push(row[0]);
|
||||
columns[1].push(row[1]);
|
||||
return columns;
|
||||
},
|
||||
[[], []]
|
||||
);
|
||||
}),
|
||||
getHistoricalStockQuoteChartData: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
lookbackPeriodStart: StringT(),
|
||||
lookbackPeriodEnd: StringT(),
|
||||
})))
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, lookbackPeriodStart, lookbackPeriodEnd } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
return await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
@@ -155,10 +229,14 @@ const appRouter = router({
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
ORDER BY x ASC
|
||||
`,'JSONEachRow'));
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarQuoteChartData: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
daysToFrontExpiration: NumberT(),
|
||||
daysBetweenFrontAndBackExpiration: NumberT(),
|
||||
@@ -166,10 +244,21 @@ const appRouter = router({
|
||||
strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
|
||||
lookbackPeriodStart: StringT(),
|
||||
lookbackPeriodEnd: StringT(),
|
||||
})))
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, strikePercentageFromUnderlyingPriceRangeMin, strikePercentageFromUnderlyingPriceRangeMax, lookbackPeriodStart, lookbackPeriodEnd, } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
strikePercentageFromUnderlyingPriceRangeMin,
|
||||
strikePercentageFromUnderlyingPriceRangeMax,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
calendarPrice as y
|
||||
@@ -181,19 +270,34 @@ const appRouter = router({
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
`,'JSONEachRow'));
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarExitQuoteChartData: publicProcedure
|
||||
.input(RpcType(ObjectT({
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
daysToFrontExpiration: NumberT(),
|
||||
daysBetweenFrontAndBackExpiration: NumberT(),
|
||||
lookbackPeriodStart:StringT({pattern:'[0-9]{4}\-[0-9]{2}\-[0-9]{2}'}),
|
||||
lookbackPeriodEnd:StringT({pattern:'[0-9]{4}\-[0-9]{2}\-[0-9]{2}'}),
|
||||
})))
|
||||
lookbackPeriodStart: StringT({
|
||||
pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}",
|
||||
}),
|
||||
lookbackPeriodEnd: StringT({ pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}" }),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, lookbackPeriodStart, lookbackPeriodEnd, } = opts.input;
|
||||
return (await query<[number,number]>(`
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
FLOOR(strikePercentageFromUnderlyingPrice, 1) as x,
|
||||
calendarPrice as y
|
||||
@@ -206,7 +310,9 @@ const appRouter = router({
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
ORDER BY x ASC
|
||||
`,'JSONEachRow'));
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -214,7 +320,6 @@ const appRouter = router({
|
||||
// NOT the router itself.
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
|
||||
const handler = createHTTPHandler({
|
||||
middleware: cors(),
|
||||
router: appRouter,
|
||||
@@ -227,8 +332,7 @@ const server = createServer((req, res)=>{
|
||||
if (req.url.startsWith("/healthz")) {
|
||||
res.statusCode = 200;
|
||||
res.end("OK");
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
handler(req, res);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user