refactor
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { query } from "./lib/clickhouse";
|
||||
import { publicProcedure, RpcType, router } from "./trpc";
|
||||
import {
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
Number as NumberT,
|
||||
} from "@sinclair/typebox";
|
||||
|
||||
/** Gets a list of symbols that have at least one option contract */
|
||||
export const getAvailableUnderlyings = publicProcedure.query(async (opts) => {
|
||||
// return (
|
||||
// await query<{ symbol: string }>(`
|
||||
// SELECT DISTINCT(symbol) as symbol FROM option_contract_existences WHERE asOfDate = (SELECT max(asOfDate) FROM option_contract_existences)
|
||||
// `)
|
||||
// ).map(({ symbol }) => symbol);
|
||||
return ["AAPL", "AMD", "GOOGL", "MSFT", "NFLX"];
|
||||
});
|
||||
|
||||
export const getAvailableAsOfDates = publicProcedure
|
||||
.input(RpcType(ObjectT({ underlying: StringT() })))
|
||||
.query(async (opts) => {
|
||||
const underlying = opts.input.underlying;
|
||||
return (
|
||||
await query<{ asOfDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(asOfDate) as asOfDate
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
ORDER BY asOfDate
|
||||
`)
|
||||
).map(({ asOfDate }) => asOfDate);
|
||||
});
|
||||
|
||||
export const getExpirationsForUnderlying = publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
asOfDate: StringT(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, asOfDate } = opts.input;
|
||||
return (
|
||||
await query<{ expirationDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(expirationDate) as expirationDate
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
ORDER BY expirationDate
|
||||
`)
|
||||
).map(({ expirationDate }) => expirationDate);
|
||||
});
|
||||
|
||||
export const getStrikesForUnderlying = publicProcedure
|
||||
.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 }>(`
|
||||
SELECT
|
||||
DISTINCT(strike) as strike
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
ORDER BY strike
|
||||
`)
|
||||
).map(({ strike }) => strike);
|
||||
});
|
||||
|
||||
export default router({
|
||||
getAvailableUnderlyings,
|
||||
getAvailableAsOfDates,
|
||||
getExpirationsForUnderlying,
|
||||
getStrikesForUnderlying,
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { query } from "./lib/clickhouse";
|
||||
import { publicProcedure, RpcType, router } from "./trpc";
|
||||
import {
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
Number as NumberT,
|
||||
} from "@sinclair/typebox";
|
||||
|
||||
export const getChartData = publicProcedure
|
||||
.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}" }),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number, number]>(
|
||||
`
|
||||
SELECT
|
||||
FLOOR(strikePercentageFromUnderlyingPrice, 1) as x,
|
||||
FLOOR(calendarPrice, 1) as y,
|
||||
count(*) as n
|
||||
FROM calendar_histories
|
||||
WHERE symbol = '${underlying}'
|
||||
AND daysToFrontExpiration = ${daysToFrontExpiration}
|
||||
AND strikePercentageFromUnderlyingPrice >= -5.0
|
||||
AND strikePercentageFromUnderlyingPrice <= 5.0
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
GROUP BY x, y
|
||||
ORDER BY x ASC, y ASC
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
});
|
||||
|
||||
export default router({
|
||||
getChartData,
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { query } from "./lib/clickhouse";
|
||||
import { publicProcedure, RpcType, router } from "./trpc";
|
||||
import {
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
Number as NumberT,
|
||||
} from "@sinclair/typebox";
|
||||
|
||||
/** Returns prices for all matching calendars (i.e. those with similar
|
||||
* characteristics to those given) */
|
||||
export const getChartData = publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
daysToFrontExpiration: NumberT(),
|
||||
daysBetweenFrontAndBackExpiration: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMin: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
|
||||
lookbackPeriodStart: StringT(),
|
||||
lookbackPeriodEnd: StringT(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
strikePercentageFromUnderlyingPriceRangeMin,
|
||||
strikePercentageFromUnderlyingPriceRangeMax,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
truncate(calendarPrice, 2) as y
|
||||
FROM calendar_histories
|
||||
WHERE symbol = '${underlying}'
|
||||
AND daysToFrontExpiration = ${daysToFrontExpiration}
|
||||
AND strikePercentageFromUnderlyingPrice >= ${strikePercentageFromUnderlyingPriceRangeMin}
|
||||
AND strikePercentageFromUnderlyingPrice <= ${strikePercentageFromUnderlyingPriceRangeMax}
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
});
|
||||
|
||||
export default router({
|
||||
getChartData,
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { query } from "./lib/clickhouse";
|
||||
import { publicProcedure, RpcType, router } from "./trpc";
|
||||
import { Object as ObjectT, String as StringT } from "@sinclair/typebox";
|
||||
|
||||
export const getChartData = publicProcedure
|
||||
.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]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM stock_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
ORDER BY x ASC
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
});
|
||||
|
||||
export default router({
|
||||
getChartData,
|
||||
});
|
||||
+76
-305
@@ -1,311 +1,82 @@
|
||||
import { publicProcedure, router } from "./trpc.js";
|
||||
import { publicProcedure, router, RpcType } from "./trpc.js";
|
||||
import { query } from "./lib/clickhouse.js";
|
||||
import { createHTTPHandler } from "@trpc/server/adapters/standalone";
|
||||
import cors from "cors";
|
||||
import {
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
type TSchema,
|
||||
Number as NumberT,
|
||||
Object as ObjectT,
|
||||
String as StringT,
|
||||
Number as NumberT,
|
||||
} from "@sinclair/typebox";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { createServer } from "node:http";
|
||||
import { Env } from "@humanwhocodes/env";
|
||||
import UnderlyingPriceChart from "./UnderlyingPriceChart.js";
|
||||
import SimilarCalendarPriceChart from "./SimilarCalendarPriceChart.js";
|
||||
import CalendarExitPriceChart from "./CalendarExitPriceChart.js";
|
||||
import CalendarCharacteristicsForm from "./CalendarCharacteristicsForm.js";
|
||||
|
||||
const env = new Env();
|
||||
|
||||
const LISTEN_PORT = env.get("LISTEN_PORT", 3005);
|
||||
|
||||
/**
|
||||
* Generate a TRPC-compatible validator function given a Typebox schema.
|
||||
* This was copied from [https://github.com/sinclairzx81/typebox/blob/6cfcdc02cc813af2f1be57407c771fc4fadfc34a/example/trpc/readme.md].
|
||||
* @param schema A Typebox schema
|
||||
* @returns A TRPC-compatible validator function
|
||||
*/
|
||||
export function RpcType<T extends TSchema>(schema: T) {
|
||||
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",
|
||||
});
|
||||
};
|
||||
}
|
||||
export const getOpensForUnderlying = publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying } = opts.input;
|
||||
return await query<{ x: number; y: number }>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM stock_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
ORDER BY tsStart ASC
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
});
|
||||
|
||||
export const getOpensForOptionContract = publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
expirationDate: StringT(),
|
||||
strike: NumberT(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, expirationDate, strike } = opts.input;
|
||||
return await query<{ x: number; y: number }>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM option_contract_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
AND strike = ${strike}
|
||||
AND type = 'call'
|
||||
ORDER BY tsStart ASC
|
||||
`,
|
||||
"JSONEachRow"
|
||||
);
|
||||
});
|
||||
|
||||
const appRouter = router({
|
||||
getAvailableUnderlyings: publicProcedure.query(async (opts) => {
|
||||
// return (
|
||||
// await query<{ symbol: string }>(`
|
||||
// SELECT DISTINCT(symbol) as symbol FROM option_contract_existences WHERE asOfDate = (SELECT max(asOfDate) FROM option_contract_existences)
|
||||
// `)
|
||||
// ).map(({ symbol }) => symbol);
|
||||
return ["AAPL", "AMD", "GOOGL", "MSFT", "NFLX"];
|
||||
}),
|
||||
getAvailableAsOfDates: publicProcedure
|
||||
.input(RpcType(ObjectT({ underlying: StringT() })))
|
||||
.query(async (opts) => {
|
||||
const underlying = opts.input.underlying;
|
||||
return (
|
||||
await query<{ asOfDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(asOfDate) as asOfDate
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
ORDER BY asOfDate
|
||||
`)
|
||||
).map(({ asOfDate }) => asOfDate);
|
||||
}),
|
||||
getExpirationsForUnderlying: publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
asOfDate: StringT(),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, asOfDate } = opts.input;
|
||||
return (
|
||||
await query<{ expirationDate: string }>(`
|
||||
SELECT
|
||||
DISTINCT(expirationDate) as expirationDate
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
ORDER BY expirationDate
|
||||
`)
|
||||
).map(({ expirationDate }) => expirationDate);
|
||||
}),
|
||||
getStrikesForUnderlying: publicProcedure
|
||||
.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 }>(`
|
||||
SELECT
|
||||
DISTINCT(strike) as strike
|
||||
FROM option_contract_existences
|
||||
WHERE symbol = '${underlying}'
|
||||
AND asOfDate = '${asOfDate}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
ORDER BY strike
|
||||
`)
|
||||
).map(({ strike }) => strike);
|
||||
}),
|
||||
getOpensForUnderlying: publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying } = opts.input;
|
||||
return await query<{ x: number; y: number }>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM stock_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
ORDER BY tsStart ASC
|
||||
`,
|
||||
"JSONEachRow",
|
||||
);
|
||||
}),
|
||||
getOpensForOptionContract: publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
expirationDate: StringT(),
|
||||
strike: NumberT(),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const { underlying, expirationDate, strike } = opts.input;
|
||||
return await query<{ x: number; y: number }>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM option_contract_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
AND expirationDate = '${expirationDate}'
|
||||
AND strike = ${strike}
|
||||
AND type = 'call'
|
||||
ORDER BY tsStart ASC
|
||||
`,
|
||||
"JSONEachRow",
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarPrices: publicProcedure
|
||||
.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]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as asOfTs,
|
||||
calendarPrice
|
||||
FROM calendar_histories
|
||||
WHERE symbol = '${underlying}'
|
||||
AND daysToFrontExpiration = ${daysToFrontExpiration}
|
||||
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;
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
}),
|
||||
getHistoricalStockQuoteChartData: publicProcedure
|
||||
.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]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
open as y
|
||||
FROM stock_aggregates
|
||||
WHERE symbol = '${underlying}'
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
ORDER BY x ASC
|
||||
`,
|
||||
"JSONEachRow",
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarQuoteChartData: publicProcedure
|
||||
.input(
|
||||
RpcType(
|
||||
ObjectT({
|
||||
underlying: StringT({ maxLength: 5 }),
|
||||
daysToFrontExpiration: NumberT(),
|
||||
daysBetweenFrontAndBackExpiration: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMin: NumberT(),
|
||||
strikePercentageFromUnderlyingPriceRangeMax: NumberT(),
|
||||
lookbackPeriodStart: StringT(),
|
||||
lookbackPeriodEnd: StringT(),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
strikePercentageFromUnderlyingPriceRangeMin,
|
||||
strikePercentageFromUnderlyingPriceRangeMax,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number]>(
|
||||
`
|
||||
SELECT
|
||||
toUnixTimestamp(tsStart) as x,
|
||||
truncate(calendarPrice, 2) as y
|
||||
FROM calendar_histories
|
||||
WHERE symbol = '${underlying}'
|
||||
AND daysToFrontExpiration = ${daysToFrontExpiration}
|
||||
AND strikePercentageFromUnderlyingPrice >= ${strikePercentageFromUnderlyingPriceRangeMin}
|
||||
AND strikePercentageFromUnderlyingPrice <= ${strikePercentageFromUnderlyingPriceRangeMax}
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
`,
|
||||
"JSONEachRow",
|
||||
);
|
||||
}),
|
||||
getHistoricalCalendarExitQuoteChartData: publicProcedure
|
||||
.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}" }),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.query(async (opts) => {
|
||||
const {
|
||||
underlying,
|
||||
daysToFrontExpiration,
|
||||
daysBetweenFrontAndBackExpiration,
|
||||
lookbackPeriodStart,
|
||||
lookbackPeriodEnd,
|
||||
} = opts.input;
|
||||
return await query<[number, number, number]>(
|
||||
`
|
||||
SELECT
|
||||
FLOOR(strikePercentageFromUnderlyingPrice, 1) as x,
|
||||
FLOOR(calendarPrice, 1) as y,
|
||||
count(*) as n
|
||||
FROM calendar_histories
|
||||
WHERE symbol = '${underlying}'
|
||||
AND daysToFrontExpiration = ${daysToFrontExpiration}
|
||||
AND strikePercentageFromUnderlyingPrice >= -5.0
|
||||
AND strikePercentageFromUnderlyingPrice <= 5.0
|
||||
AND daysBetweenFrontAndBackExpiration = ${daysBetweenFrontAndBackExpiration}
|
||||
AND tsStart >= '${lookbackPeriodStart} 00:00:00'
|
||||
AND tsStart <= '${lookbackPeriodEnd} 00:00:00'
|
||||
GROUP BY x, y
|
||||
ORDER BY x ASC, y ASC
|
||||
`,
|
||||
"JSONEachRow",
|
||||
);
|
||||
}),
|
||||
CalendarCharacteristicsForm,
|
||||
UnderlyingPriceChart,
|
||||
SimilarCalendarPriceChart,
|
||||
CalendarExitPriceChart,
|
||||
|
||||
getOpensForUnderlying,
|
||||
getOpensForOptionContract,
|
||||
});
|
||||
|
||||
// Export type router type signature,
|
||||
@@ -313,20 +84,20 @@ const appRouter = router({
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
const handler = createHTTPHandler({
|
||||
middleware: cors(),
|
||||
router: appRouter,
|
||||
createContext() {
|
||||
return {};
|
||||
},
|
||||
middleware: cors(),
|
||||
router: appRouter,
|
||||
createContext() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
if (req.url.startsWith("/healthz")) {
|
||||
res.statusCode = 200;
|
||||
res.end("OK");
|
||||
} else {
|
||||
handler(req, res);
|
||||
}
|
||||
if (req.url.startsWith("/healthz")) {
|
||||
res.statusCode = 200;
|
||||
res.end("OK");
|
||||
} else {
|
||||
handler(req, res);
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(Number.parseInt(LISTEN_PORT));
|
||||
|
||||
+25
-4
@@ -1,14 +1,35 @@
|
||||
import { initTRPC } from '@trpc/server';
|
||||
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
|
||||
/**
|
||||
* Initialization of tRPC backend
|
||||
* Should be done only once per backend!
|
||||
*/
|
||||
const t = initTRPC.create();
|
||||
|
||||
|
||||
/**
|
||||
* Export reusable router and procedure helpers
|
||||
* that can be used throughout the router
|
||||
*/
|
||||
export const router = t.router;
|
||||
export const publicProcedure = t.procedure;
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
/**
|
||||
* Generate a TRPC-compatible validator function given a Typebox schema.
|
||||
* This was copied from [https://github.com/sinclairzx81/typebox/blob/6cfcdc02cc813af2f1be57407c771fc4fadfc34a/example/trpc/readme.md].
|
||||
* @param schema A Typebox schema
|
||||
* @returns A TRPC-compatible validator function
|
||||
*/
|
||||
export function RpcType<T extends TSchema>(schema: T) {
|
||||
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",
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user