temporarily limit available underlyings

main
Avraham Sakal 1 year ago
parent 0ecbcad0b0
commit 6d8ac852ab

@ -1,12 +1,17 @@
import _ from './env'; import _ from "./env";
import { publicProcedure, router } from './trpc.js'; import { publicProcedure, router } from "./trpc.js";
import { query } from './clickhouse.js'; import { query } from "./clickhouse.js";
import { createHTTPHandler } from '@trpc/server/adapters/standalone'; import { createHTTPHandler } from "@trpc/server/adapters/standalone";
import cors from 'cors'; import cors from "cors";
import { Object as ObjectT, String as StringT, TSchema, Number as NumberT } from '@sinclair/typebox'; import {
import { TypeCompiler } from '@sinclair/typebox/compiler'; Object as ObjectT,
import { TRPCError } from '@trpc/server'; String as StringT,
import { createServer } from 'http'; 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. * Generate a TRPC-compatible validator function given a Typebox schema.
@ -15,94 +20,128 @@ import { createServer } from 'http';
* @returns A TRPC-compatible validator function * @returns A TRPC-compatible validator function
*/ */
export function RpcType<T extends TSchema>(schema: T) { export function RpcType<T extends TSchema>(schema: T) {
const check = TypeCompiler.Compile(schema) const check = TypeCompiler.Compile(schema);
return (value: unknown) => { return (value: unknown) => {
if (check.Check(value)) return value if (check.Check(value)) return value;
const { path, message } = check.Errors(value).First()! const { path, message } = check.Errors(value).First()!;
throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) throw new TRPCError({
} message: `${message} for ${path}`,
code: "BAD_REQUEST",
});
};
} }
const appRouter = router({ const appRouter = router({
getAvailableUnderlyings: publicProcedure getAvailableUnderlyings: publicProcedure.query(async (opts) => {
.query(async (opts) => { // return (await query<{symbol:string}>(`
return (await query<{symbol:string}>(` // SELECT DISTINCT(symbol) as symbol FROM option_contracts
SELECT DISTINCT(symbol) as symbol FROM option_contracts // `))
`)) // .map(({symbol})=>symbol);
.map(({symbol})=>symbol); return ["GOOGL", "AAPL", "NFLX", "AMD", "MSFT"];
}), }),
getAvailableAsOfDates: publicProcedure getAvailableAsOfDates: publicProcedure
.input(RpcType(ObjectT({underlying:StringT()}))) .input(RpcType(ObjectT({ underlying: StringT() })))
.query(async (opts) => { .query(async (opts) => {
const underlying = opts.input.underlying; const underlying = opts.input.underlying;
return (await query<{asOfDate:string}>(` return (
await query<{ asOfDate: string }>(`
SELECT SELECT
DISTINCT(asOfDate) as asOfDate DISTINCT(asOfDate) as asOfDate
FROM option_contracts FROM option_contracts
WHERE symbol = '${underlying}' WHERE symbol = '${underlying}'
`)) `)
.map(({asOfDate})=>asOfDate); ).map(({ asOfDate }) => asOfDate);
}), }),
getExpirationsForUnderlying: publicProcedure getExpirationsForUnderlying: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
asOfDate:StringT() ObjectT({
}))) underlying: StringT({ maxLength: 5 }),
.query(async (opts)=>{ asOfDate: StringT(),
const {underlying, asOfDate} = opts.input; })
return (await query<{expirationDate:string}>(` )
)
.query(async (opts) => {
const { underlying, asOfDate } = opts.input;
return (
await query<{ expirationDate: string }>(`
SELECT SELECT
DISTINCT(expirationDate) DISTINCT(expirationDate)
FROM option_contracts FROM option_contracts
WHERE symbol = '${underlying}' WHERE symbol = '${underlying}'
AND asOfDate = '${asOfDate}' AND asOfDate = '${asOfDate}'
`)) `)
.map(({expirationDate})=>expirationDate); ).map(({ expirationDate }) => expirationDate);
}), }),
getStrikesForUnderlying: publicProcedure getStrikesForUnderlying: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
asOfDate:StringT(), ObjectT({
expirationDate:StringT(), underlying: StringT({ maxLength: 5 }),
}))) asOfDate: StringT(),
.query(async (opts)=>{ expirationDate: StringT(),
const {underlying, asOfDate, expirationDate} = opts.input; })
return (await query<{strike:string}>(` )
)
.query(async (opts) => {
const { underlying, asOfDate, expirationDate } = opts.input;
return (
await query<{ strike: string }>(`
SELECT SELECT
DISTINCT(strike) DISTINCT(strike)
FROM option_contracts FROM option_contracts
WHERE symbol = '${underlying}' WHERE symbol = '${underlying}'
AND asOfDate = '${asOfDate}' AND asOfDate = '${asOfDate}'
AND expirationDate = '${expirationDate}' AND expirationDate = '${expirationDate}'
`)) `)
.map(({strike})=>strike); ).map(({ strike }) => strike);
}), }),
getOpensForUnderlying: publicProcedure getOpensForUnderlying: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}) RpcType(
}))) ObjectT({
.query(async (opts)=>{ underlying: StringT({ maxLength: 5 }),
const {underlying} = opts.input; })
return (await query<[number,number]>(` )
)
.query(async (opts) => {
const { underlying } = opts.input;
return (
await query<[number, number]>(
`
SELECT SELECT
toUnixTimestamp(tsStart), toUnixTimestamp(tsStart),
open open
FROM stock_aggregates FROM stock_aggregates
WHERE symbol = '${underlying}' WHERE symbol = '${underlying}'
ORDER BY tsStart ASC 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 getOpensForOptionContract: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
expirationDate:StringT(), ObjectT({
strike:NumberT() underlying: StringT({ maxLength: 5 }),
}))) expirationDate: StringT(),
.query(async (opts)=>{ strike: NumberT(),
const {underlying, expirationDate, strike} = opts.input; })
return (await query<[number,number]>(` )
)
.query(async (opts) => {
const { underlying, expirationDate, strike } = opts.input;
return (
await query<[number, number]>(
`
SELECT SELECT
toUnixTimestamp(tsStart), toUnixTimestamp(tsStart),
open open
@ -112,20 +151,41 @@ const appRouter = router({
AND strike = ${strike} AND strike = ${strike}
AND optionType = 'call' AND optionType = 'call'
ORDER BY tsStart ASC 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 getHistoricalCalendarPrices: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
daysToFrontExpiration:NumberT(), ObjectT({
daysBetweenFrontAndBackExpiration:NumberT(), underlying: StringT({ maxLength: 5 }),
strikePercentageFromUnderlyingPriceRangeMin:NumberT(), daysToFrontExpiration: NumberT(),
strikePercentageFromUnderlyingPriceRangeMax:NumberT(), daysBetweenFrontAndBackExpiration: NumberT(),
}))) strikePercentageFromUnderlyingPriceRangeMin: NumberT(),
.query(async (opts)=>{ strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, strikePercentageFromUnderlyingPriceRangeMin, strikePercentageFromUnderlyingPriceRangeMax, } = opts.input; })
return (await query<[number,number]>(` )
)
.query(async (opts) => {
const {
underlying,
daysToFrontExpiration,
daysBetweenFrontAndBackExpiration,
strikePercentageFromUnderlyingPriceRangeMin,
strikePercentageFromUnderlyingPriceRangeMax,
} = opts.input;
return (
await query<[number, number]>(
`
SELECT SELECT
toUnixTimestamp(tsStart) as asOfTs, toUnixTimestamp(tsStart) as asOfTs,
calendarPrice calendarPrice
@ -135,18 +195,32 @@ const appRouter = router({
AND strikePercentageFromUnderlyingPrice >= ${strikePercentageFromUnderlyingPriceRangeMin} AND strikePercentageFromUnderlyingPrice >= ${strikePercentageFromUnderlyingPriceRangeMin}
AND strikePercentageFromUnderlyingPrice <= ${strikePercentageFromUnderlyingPriceRangeMax} AND strikePercentageFromUnderlyingPrice <= ${strikePercentageFromUnderlyingPriceRangeMax}
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration} 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 getHistoricalStockQuoteChartData: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
lookbackPeriodStart:StringT(), ObjectT({
lookbackPeriodEnd:StringT(), underlying: StringT({ maxLength: 5 }),
}))) lookbackPeriodStart: StringT(),
.query(async (opts)=>{ lookbackPeriodEnd: StringT(),
})
)
)
.query(async (opts) => {
const { underlying, lookbackPeriodStart, lookbackPeriodEnd } = opts.input; const { underlying, lookbackPeriodStart, lookbackPeriodEnd } = opts.input;
return (await query<[number,number]>(` return await query<[number, number]>(
`
SELECT SELECT
toUnixTimestamp(tsStart) as x, toUnixTimestamp(tsStart) as x,
open as y open as y
@ -155,21 +229,36 @@ const appRouter = router({
AND tsStart >= '${lookbackPeriodStart} 00:00:00' AND tsStart >= '${lookbackPeriodStart} 00:00:00'
AND tsStart <= '${lookbackPeriodEnd} 00:00:00' AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
ORDER BY x ASC ORDER BY x ASC
`,'JSONEachRow')); `,
"JSONEachRow"
);
}), }),
getHistoricalCalendarQuoteChartData: publicProcedure getHistoricalCalendarQuoteChartData: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
daysToFrontExpiration:NumberT(), ObjectT({
daysBetweenFrontAndBackExpiration:NumberT(), underlying: StringT({ maxLength: 5 }),
strikePercentageFromUnderlyingPriceRangeMin:NumberT(), daysToFrontExpiration: NumberT(),
strikePercentageFromUnderlyingPriceRangeMax:NumberT(), daysBetweenFrontAndBackExpiration: NumberT(),
lookbackPeriodStart:StringT(), strikePercentageFromUnderlyingPriceRangeMin: NumberT(),
lookbackPeriodEnd:StringT(), strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
}))) lookbackPeriodStart: StringT(),
.query(async (opts)=>{ lookbackPeriodEnd: StringT(),
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, strikePercentageFromUnderlyingPriceRangeMin, strikePercentageFromUnderlyingPriceRangeMax, lookbackPeriodStart, lookbackPeriodEnd, } = opts.input; })
return (await query<[number,number]>(` )
)
.query(async (opts) => {
const {
underlying,
daysToFrontExpiration,
daysBetweenFrontAndBackExpiration,
strikePercentageFromUnderlyingPriceRangeMin,
strikePercentageFromUnderlyingPriceRangeMax,
lookbackPeriodStart,
lookbackPeriodEnd,
} = opts.input;
return await query<[number, number]>(
`
SELECT SELECT
toUnixTimestamp(tsStart) as x, toUnixTimestamp(tsStart) as x,
calendarPrice as y calendarPrice as y
@ -181,19 +270,34 @@ const appRouter = router({
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration} AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
AND tsStart >= '${lookbackPeriodStart} 00:00:00' AND tsStart >= '${lookbackPeriodStart} 00:00:00'
AND tsStart <= '${lookbackPeriodEnd} 00:00:00' AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
`,'JSONEachRow')); `,
"JSONEachRow"
);
}), }),
getHistoricalCalendarExitQuoteChartData: publicProcedure getHistoricalCalendarExitQuoteChartData: publicProcedure
.input(RpcType(ObjectT({ .input(
underlying:StringT({maxLength:5}), RpcType(
daysToFrontExpiration:NumberT(), ObjectT({
daysBetweenFrontAndBackExpiration:NumberT(), underlying: StringT({ maxLength: 5 }),
lookbackPeriodStart:StringT({pattern:'[0-9]{4}\-[0-9]{2}\-[0-9]{2}'}), daysToFrontExpiration: NumberT(),
lookbackPeriodEnd:StringT({pattern:'[0-9]{4}\-[0-9]{2}\-[0-9]{2}'}), daysBetweenFrontAndBackExpiration: NumberT(),
}))) lookbackPeriodStart: StringT({
.query(async (opts)=>{ pattern: "[0-9]{4}-[0-9]{2}-[0-9]{2}",
const {underlying, daysToFrontExpiration, daysBetweenFrontAndBackExpiration, lookbackPeriodStart, lookbackPeriodEnd, } = opts.input; }),
return (await query<[number,number]>(` 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]>(
`
SELECT SELECT
FLOOR(strikePercentageFromUnderlyingPrice, 1) as x, FLOOR(strikePercentageFromUnderlyingPrice, 1) as x,
calendarPrice as y calendarPrice as y
@ -206,7 +310,9 @@ const appRouter = router({
AND tsStart >= '${lookbackPeriodStart} 00:00:00' AND tsStart >= '${lookbackPeriodStart} 00:00:00'
AND tsStart <= '${lookbackPeriodEnd} 00:00:00' AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
ORDER BY x ASC ORDER BY x ASC
`,'JSONEachRow')); `,
"JSONEachRow"
);
}), }),
}); });
@ -214,7 +320,6 @@ const appRouter = router({
// NOT the router itself. // NOT the router itself.
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;
const handler = createHTTPHandler({ const handler = createHTTPHandler({
middleware: cors(), middleware: cors(),
router: appRouter, router: appRouter,
@ -223,14 +328,13 @@ const handler = createHTTPHandler({
}, },
}); });
const server = createServer((req, res)=>{ const server = createServer((req, res) => {
if(req.url.startsWith("/healthz")){ if (req.url.startsWith("/healthz")) {
res.statusCode = 200; res.statusCode = 200;
res.end("OK"); res.end("OK");
} } else {
else{
handler(req, res); handler(req, res);
} }
}); });
server.listen(parseInt(process.env.LISTEN_PORT) || 3005); server.listen(parseInt(process.env.LISTEN_PORT) || 3005);

Loading…
Cancel
Save