From da03fa8804133f9936a097ad1222d4b329d5ca61 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Tue, 10 May 2022 01:15:18 +0400 Subject: [PATCH] refactor: stars calculation moved to platforms --- src/backend/platforms/_example.ts | 9 +- src/backend/platforms/betfair.ts | 29 +- src/backend/platforms/fantasyscotus.ts | 5 +- src/backend/platforms/foretold.ts | 15 +- src/backend/platforms/givewellopenphil.ts | 14 +- src/backend/platforms/goodjudgment.ts | 14 +- src/backend/platforms/goodjudgmentopen.ts | 50 ++-- src/backend/platforms/guesstimate.ts | 19 +- src/backend/platforms/index.ts | 38 ++- src/backend/platforms/infer.ts | 13 +- src/backend/platforms/kalshi.ts | 44 ++- src/backend/platforms/manifold.ts | 30 +- src/backend/platforms/metaculus.ts | 17 +- src/backend/platforms/polymarket.ts | 40 ++- src/backend/platforms/predictit.ts | 11 +- src/backend/platforms/rootclaim.ts | 11 +- src/backend/platforms/smarkets.ts | 14 +- src/backend/platforms/wildeford.ts | 16 +- src/backend/platforms/xrisk.ts | 1 + src/backend/utils/getSecrets.ts | 2 +- src/backend/utils/stars.ts | 330 ---------------------- src/utils.ts | 3 + 22 files changed, 272 insertions(+), 453 deletions(-) delete mode 100644 src/backend/utils/stars.ts diff --git a/src/backend/platforms/_example.ts b/src/backend/platforms/_example.ts index 897a151..e41113c 100644 --- a/src/backend/platforms/_example.ts +++ b/src/backend/platforms/_example.ts @@ -1,7 +1,6 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -26,7 +25,7 @@ async function processPredictions(predictions) { let results = await predictions.map((prediction) => { const id = `${platformName}-${prediction.id}`; const probability = prediction.probability; - const options = [ + const options: FetchedQuestion["options"] = [ { name: "Yes", probability: probability, @@ -45,9 +44,6 @@ async function processPredictions(predictions) { description: prediction.description, options, qualityindicators: { - stars: calculateStars(platformName, { - /* some: somex, factors: factors */ - }), // other: prediction.otherx, // indicators: prediction.indicatorx, }, @@ -68,4 +64,7 @@ export const example: Platform = { let results = await processPredictions(data); // somehow needed return results; }, + calculateStars(data) { + return 2; + }, }; diff --git a/src/backend/platforms/betfair.ts b/src/backend/platforms/betfair.ts index 2fec2cf..aa950d8 100644 --- a/src/backend/platforms/betfair.ts +++ b/src/backend/platforms/betfair.ts @@ -2,7 +2,7 @@ import axios from "axios"; import https from "https"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; const platformName = "betfair"; @@ -121,17 +121,13 @@ async function processPredictions(data) { if (title.includes("of the named")) { title = prediction.marketName + ": " + title; } - const result = { + const result: FetchedQuestion = { id, title, url: `https://www.betfair.com/exchange/plus/politics/market/${prediction.marketId}`, - platform: platformName, description, options, qualityindicators: { - stars: calculateStars(platformName, { - volume: prediction.totalMatched, - }), volume: prediction.totalMatched, }, }; @@ -149,4 +145,25 @@ export const betfair: Platform = { const results = await processPredictions(data); // somehow needed return results; }, + calculateStars(data) { + const volume = data.qualityindicators.volume || 0; + let nuno = () => (volume > 10000 ? 4 : volume > 1000 ? 3 : 2); + let eli = () => (volume > 10000 ? null : null); + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + + const firstOption = data.options[0]; + + // Substract 1 star if probability is above 90% or below 10% + if ( + firstOption && + ((firstOption.probability || 0) < 0.1 || + (firstOption.probability || 0) > 0.9) + ) { + starsDecimal = starsDecimal - 1; + } + + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/fantasyscotus.ts b/src/backend/platforms/fantasyscotus.ts index 4a74614..7527599 100644 --- a/src/backend/platforms/fantasyscotus.ts +++ b/src/backend/platforms/fantasyscotus.ts @@ -1,7 +1,6 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; import { FetchedQuestion, Platform } from "./"; const platformName = "fantasyscotus"; @@ -100,7 +99,6 @@ async function processData(data) { ], qualityindicators: { numforecasts: Number(predictionData.numForecasts), - stars: calculateStars(platformName, {}), }, }; results.push(eventObject); @@ -120,4 +118,7 @@ export const fantasyscotus: Platform = { let results = await processData(rawData); return results; }, + calculateStars(data) { + return 2; + }, }; diff --git a/src/backend/platforms/foretold.ts b/src/backend/platforms/foretold.ts index 97e63fd..0673fae 100644 --- a/src/backend/platforms/foretold.ts +++ b/src/backend/platforms/foretold.ts @@ -1,7 +1,7 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -68,7 +68,8 @@ export const foretold: Platform = { questions = questions.filter((question) => question.previousAggregate); // Questions without any predictions questions.forEach((question) => { let id = `${platformName}-${question.id}`; - let options = []; + + let options: FetchedQuestion["options"] = []; if (question.valueType == "PERCENTAGE") { let probability = question.previousAggregate.value.percentage; options = [ @@ -84,6 +85,7 @@ export const foretold: Platform = { }, ]; } + const result: FetchedQuestion = { id, title: question.name, @@ -92,7 +94,6 @@ export const foretold: Platform = { options, qualityindicators: { numforecasts: Math.floor(Number(question.measurementCount) / 2), - stars: calculateStars(platformName, {}), }, /*liquidity: liquidity.toFixed(2), tradevolume: tradevolume.toFixed(2), @@ -104,4 +105,12 @@ export const foretold: Platform = { } return results; }, + calculateStars(data) { + let nuno = () => 2; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/givewellopenphil.ts b/src/backend/platforms/givewellopenphil.ts index e8d9754..48db8ca 100644 --- a/src/backend/platforms/givewellopenphil.ts +++ b/src/backend/platforms/givewellopenphil.ts @@ -2,7 +2,7 @@ import axios from "axios"; import fs from "fs"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { Platform } from "./"; const platformName = "givewellopenphil"; @@ -53,9 +53,7 @@ async function main1() { platform: platformName, description, options: [], - qualityindicators: { - stars: calculateStars(platformName, {}), - }, + qualityindicators: {}, }; // Note: This requires some processing afterwards // console.log(result) results.push(result); @@ -84,4 +82,12 @@ export const givewellopenphil: Platform = { })); return dataWithDate; }, + calculateStars(data) { + let nuno = () => 2; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/goodjudgment.ts b/src/backend/platforms/goodjudgment.ts index 5f4b0df..ab487bf 100644 --- a/src/backend/platforms/goodjudgment.ts +++ b/src/backend/platforms/goodjudgment.ts @@ -3,8 +3,8 @@ import axios from "axios"; import { Tabletojson } from "tabletojson"; import tunnel from "tunnel"; +import { average } from "../../utils"; import { hash } from "../utils/hash"; -import { calculateStars } from "../utils/stars"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -103,9 +103,7 @@ export const goodjudgment: Platform = { url: endpoint, description, options, - qualityindicators: { - stars: calculateStars(platformName, {}), - }, + qualityindicators: {}, extra: { superforecastercommentary: analysis || "", }, @@ -120,4 +118,12 @@ export const goodjudgment: Platform = { return results; }, + calculateStars(data) { + let nuno = () => 4; + let eli = () => 4; + let misha = () => 3.5; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/goodjudgmentopen.ts b/src/backend/platforms/goodjudgmentopen.ts index a4e1087..05c7a8d 100644 --- a/src/backend/platforms/goodjudgmentopen.ts +++ b/src/backend/platforms/goodjudgmentopen.ts @@ -2,16 +2,16 @@ import axios from "axios"; import { Tabletojson } from "tabletojson"; +import { average } from "../../utils"; import { applyIfSecretExists } from "../utils/getSecrets"; -import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "goodjudgmentopen"; -let htmlEndPoint = "https://www.gjopen.com/questions?page="; -let annoyingPromptUrls = [ +const htmlEndPoint = "https://www.gjopen.com/questions?page="; +const annoyingPromptUrls = [ "https://www.gjopen.com/questions/1933-what-forecasting-questions-should-we-ask-what-questions-would-you-like-to-forecast-on-gjopen", "https://www.gjopen.com/questions/1779-are-there-any-forecasting-tips-tricks-and-experiences-you-would-like-to-share-and-or-discuss-with-your-fellow-forecasters", "https://www.gjopen.com/questions/2246-are-there-any-forecasting-tips-tricks-and-experiences-you-would-like-to-share-and-or-discuss-with-your-fellow-forecasters-2022-thread", @@ -22,7 +22,7 @@ const id = () => 0; /* Support functions */ -async function fetchPage(page, cookie) { +async function fetchPage(page: number, cookie: string) { let response = await axios({ url: htmlEndPoint + page, method: "GET", @@ -35,7 +35,7 @@ async function fetchPage(page, cookie) { return response; } -async function fetchStats(questionUrl, cookie) { +async function fetchStats(questionUrl: string, cookie: string) { let response = await axios({ url: questionUrl + "/stats", method: "GET", @@ -50,7 +50,7 @@ async function fetchStats(questionUrl, cookie) { // Is binary? let isbinary = response.includes("binary?":true"); - let options = []; + let options: FetchedQuestion["options"] = []; if (isbinary) { // Crowd percentage let htmlElements = response.split("\n"); @@ -107,21 +107,12 @@ async function fetchStats(questionUrl, cookie) { .split(",")[0]; //console.log(numpredictors) - // Calculate the stars - let minProbability = Math.min(...options.map((option) => option.probability)); - let maxProbability = Math.max(...options.map((option) => option.probability)); - let result = { - description: description, - options: options, + description, + options, qualityindicators: { numforecasts: Number(numforecasts), numforecasters: Number(numforecasters), - stars: calculateStars("Good Judgment Open", { - numforecasts, - minProbability, - maxProbability, - }), }, // this mismatches the code below, and needs to be fixed, but I'm doing typescript conversion and don't want to touch any logic for now } as any; @@ -129,7 +120,7 @@ async function fetchStats(questionUrl, cookie) { return result; } -function isSignedIn(html) { +function isSignedIn(html: string) { let isSignedInBool = !( html.includes("You need to sign in or sign up before continuing") || html.includes("Sign up") @@ -157,7 +148,7 @@ function sleep(ms: number) { /* Body */ -async function goodjudgmentopen_inner(cookie) { +async function goodjudgmentopen_inner(cookie: string) { let i = 1; let response = await fetchPage(i, cookie); @@ -243,6 +234,23 @@ export const goodjudgmentopen: Platform = { color: "#002455", async fetcher() { let cookie = process.env.GOODJUDGMENTOPENCOOKIE; - return await applyIfSecretExists(cookie, goodjudgmentopen_inner); + return (await applyIfSecretExists(cookie, goodjudgmentopen_inner)) || null; + }, + calculateStars(data) { + let minProbability = Math.min( + ...data.options.map((option) => option.probability || 0) + ); + let maxProbability = Math.max( + ...data.options.map((option) => option.probability || 0) + ); + + let nuno = () => ((data.qualityindicators.numforecasts || 0) > 100 ? 3 : 2); + let eli = () => 3; + let misha = () => + minProbability > 0.1 || maxProbability < 0.9 ? 3.1 : 2.5; + + let starsDecimal = average([nuno(), eli(), misha()]); + let starsInteger = Math.round(starsDecimal); + return starsInteger; }, }; diff --git a/src/backend/platforms/guesstimate.ts b/src/backend/platforms/guesstimate.ts index d61ed7e..b517215 100644 --- a/src/backend/platforms/guesstimate.ts +++ b/src/backend/platforms/guesstimate.ts @@ -1,12 +1,10 @@ import axios from "axios"; -import { parseISO } from "date-fns"; -/* Imports */ import { Question } from "@prisma/client"; import { AlgoliaQuestion } from "../../backend/utils/algolia"; import { prisma } from "../database/prisma"; -import { Platform } from "./"; +import { FetchedQuestion, Platform, prepareQuestion } from "./"; /* Definitions */ const searchEndpoint = @@ -14,25 +12,20 @@ const searchEndpoint = const apiEndpoint = "https://guesstimate.herokuapp.com"; -/* Body */ - const modelToQuestion = (model: any): Question => { const { description } = model; // const description = model.description // ? model.description.replace(/\n/g, " ").replace(/ /g, " ") // : ""; - const stars = description.length > 250 ? 2 : 1; - const timestamp = parseISO(model.created_at); - const q: Question = { + // const timestamp = parseISO(model.created_at); + const fq: FetchedQuestion = { id: `guesstimate-${model.id}`, title: model.name, url: `https://www.getguesstimate.com/models/${model.id}`, - timestamp, - platform: "guesstimate", + // timestamp, description, options: [], qualityindicators: { - stars, numforecasts: 1, numforecasters: 1, }, @@ -41,6 +34,7 @@ const modelToQuestion = (model: any): Question => { }, // ranking: 10 * (index + 1) - 0.5, //(model._rankingInfo - 1*index)// hack }; + const q = prepareQuestion(fq, guesstimate); return q; }; @@ -68,7 +62,7 @@ async function search(query: string): Promise { }); // filter for duplicates. Surprisingly common. - let uniqueTitles = []; + let uniqueTitles: string[] = []; let uniqueModels: AlgoliaQuestion[] = []; for (let model of mappedModels) { if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) { @@ -100,4 +94,5 @@ export const guesstimate: Platform & { color: "#223900", search, fetchQuestion, + calculateStars: (q) => (q.description.length > 250 ? 2 : 1), }; diff --git a/src/backend/platforms/index.ts b/src/backend/platforms/index.ts index 7016060..d9127ed 100644 --- a/src/backend/platforms/index.ts +++ b/src/backend/platforms/index.ts @@ -41,11 +41,16 @@ export interface QualityIndicators { export type FetchedQuestion = Omit< Question, - "extra" | "qualityindicators" | "timestamp" | "platform" + "extra" | "qualityindicators" | "timestamp" | "platform" | "options" > & { timestamp?: Date; extra?: object; // required in DB but annoying to return empty; also this is slightly stricter than Prisma's JsonValue - qualityindicators: QualityIndicators; // slightly stronger type than Prisma's JsonValue + options: { + name?: string; + probability?: number; + type: "PROBABILITY"; + }[]; // stronger type than Prisma's JsonValue + qualityindicators: Omit; // slightly stronger type than Prisma's JsonValue }; // fetcher should return null if platform failed to fetch questions for some reason @@ -56,6 +61,7 @@ export interface Platform { label: string; // longer name for displaying on frontend etc., e.g. "X-risk estimates" color: string; // used on frontend fetcher?: PlatformFetcher; + calculateStars: (question: FetchedQuestion) => number; } // draft for the future callback-based streaming/chunking API: @@ -86,6 +92,22 @@ export const platforms: Platform[] = [ xrisk, ]; +export const prepareQuestion = ( + q: FetchedQuestion, + platform: Platform +): Question => { + return { + extra: {}, + timestamp: new Date(), + ...q, + platform: platform.name, + qualityindicators: { + ...q.qualityindicators, + stars: platform.calculateStars(q), + }, + }; +}; + export const processPlatform = async (platform: Platform) => { if (!platform.fetcher) { console.log(`Platform ${platform.name} doesn't have a fetcher, skipping`); @@ -97,16 +119,6 @@ export const processPlatform = async (platform: Platform) => { return; } - const prepareQuestion = (q: FetchedQuestion): Question => { - return { - extra: {}, - timestamp: new Date(), - ...q, - platform: platform.name, - qualityindicators: q.qualityindicators as object, // fighting typescript - }; - }; - const oldQuestions = await prisma.question.findMany({ where: { platform: platform.name, @@ -123,7 +135,7 @@ export const processPlatform = async (platform: Platform) => { const updatedQuestions: Question[] = []; const deletedIds = oldIds.filter((id) => !fetchedIdsSet.has(id)); - for (const q of fetchedQuestions.map((q) => prepareQuestion(q))) { + for (const q of fetchedQuestions.map((q) => prepareQuestion(q, platform))) { if (oldIdsSet.has(q.id)) { updatedQuestions.push(q); } else { diff --git a/src/backend/platforms/infer.ts b/src/backend/platforms/infer.ts index 1516a93..b004917 100644 --- a/src/backend/platforms/infer.ts +++ b/src/backend/platforms/infer.ts @@ -1,9 +1,9 @@ /* Imports */ import axios from "axios"; +import { average } from "../../utils"; import { applyIfSecretExists } from "../utils/getSecrets"; import { measureTime } from "../utils/measureTime"; -import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -106,7 +106,6 @@ async function fetchStats(questionUrl, cookie) { numforecasts: Number(numforecasts), numforecasters: Number(numforecasters), comments_count: Number(comments_count), - stars: calculateStars(platformName, { numforecasts }), }, }; // console.log(JSON.stringify(result, null, 4)); @@ -177,9 +176,7 @@ async function infer_inner(cookie: string) { let question: FetchedQuestion = { id: id, title: title, - description: moreinfo.description, url: url, - options: moreinfo.options, ...moreinfo, }; console.log(JSON.stringify(question, null, 4)); @@ -236,4 +233,12 @@ export const infer: Platform = { let cookie = process.env.INFER_COOKIE; return await applyIfSecretExists(cookie, infer_inner); }, + calculateStars(data) { + let nuno = () => 2; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/kalshi.ts b/src/backend/platforms/kalshi.ts index c7e88be..665f0e6 100644 --- a/src/backend/platforms/kalshi.ts +++ b/src/backend/platforms/kalshi.ts @@ -1,7 +1,7 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -23,7 +23,7 @@ async function processMarkets(markets) { markets = markets.filter((market) => market.close_date > dateNow); let results = await markets.map((market) => { const probability = market.last_price / 100; - const options = [ + const options: FetchedQuestion["options"] = [ { name: "Yes", probability: probability, @@ -43,29 +43,26 @@ async function processMarkets(markets) { description: `${market.settle_details}. The resolution source is: ${market.ranged_group_name} (${market.settle_source_url})`, options, qualityindicators: { - stars: calculateStars(platformName, { - shares_volume: market.volume, - interest: market.open_interest, - }), yes_bid: market.yes_bid, yes_ask: market.yes_ask, spread: Math.abs(market.yes_bid - market.yes_ask), shares_volume: market.volume, // Assuming that half of all buys are for yes and half for no, which is a big if. // "open_interest": market.open_interest, also in shares }, + extra: { + open_interest: market.open_interest, + }, }; return result; }); - //console.log(results.length) - // console.log(results.map(result => result.title)) - // console.log(results.map(result => result.title).length) + console.log([...new Set(results.map((result) => result.title))]); console.log( "Number of unique questions: ", [...new Set(results.map((result) => result.title))].length ); - // console.log([...new Set(results.map(result => result.title))].length) - return results; //resultsProcessed + + return results; } export const kalshi: Platform = { @@ -76,4 +73,29 @@ export const kalshi: Platform = { let markets = await fetchAllMarkets(); return await processMarkets(markets); }, + calculateStars(data) { + let nuno = () => + ((data.extra as any)?.open_interest || 0) > 500 && + data.qualityindicators.shares_volume > 10000 + ? 4 + : data.qualityindicators.shares_volume > 2000 + ? 3 + : 2; + // let eli = (data) => data.interest > 10000 ? 5 : 4 + // let misha = (data) => 4 + let starsDecimal = average([nuno()]); //, eli(data), misha(data)]) + + // Substract 1 star if probability is above 90% or below 10% + if ( + data.options instanceof Array && + data.options[0] && + ((data.options[0].probability || 0) < 0.1 || + (data.options[0].probability || 0) > 0.9) + ) { + starsDecimal = starsDecimal - 1; + } + + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/manifold.ts b/src/backend/platforms/manifold.ts index f221bc6..b735470 100644 --- a/src/backend/platforms/manifold.ts +++ b/src/backend/platforms/manifold.ts @@ -1,7 +1,7 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -25,16 +25,16 @@ async function fetchData() { function showStatistics(results: FetchedQuestion[]) { console.log(`Num unresolved markets: ${results.length}`); - let sum = (arr) => arr.reduce((tally, a) => tally + a, 0); + let sum = (arr: number[]) => arr.reduce((tally, a) => tally + a, 0); let num2StarsOrMore = results.filter( - (result) => result.qualityindicators.stars >= 2 + (result) => manifold.calculateStars(result) >= 2 ); console.log( `Manifold has ${num2StarsOrMore.length} markets with 2 stars or more` ); console.log( `Mean volume: ${ - sum(results.map((result) => result.qualityindicators.volume7Days)) / + sum(results.map((result) => result.qualityindicators.volume7Days || 0)) / results.length }; mean pool: ${ sum(results.map((result) => result.qualityindicators.pool)) / @@ -47,7 +47,7 @@ async function processPredictions(predictions) { let results: FetchedQuestion[] = await predictions.map((prediction) => { let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name let probability = prediction.probability; - let options = [ + let options: FetchedQuestion["options"] = [ { name: "Yes", probability: probability, @@ -64,13 +64,8 @@ async function processPredictions(predictions) { title: prediction.question, url: prediction.url, description: prediction.description, - options: options, + options, qualityindicators: { - stars: calculateStars(platformName, { - volume7Days: prediction.volume7Days, - volume24Hours: prediction.volume24Hours, - pool: prediction.pool, - }), createdTime: prediction.createdTime, volume7Days: prediction.volume7Days, volume24Hours: prediction.volume24Hours, @@ -99,4 +94,17 @@ export const manifold: Platform = { showStatistics(results); return results; }, + calculateStars(data) { + let nuno = () => + (data.qualityindicators.volume7Days || 0) > 250 || + ((data.qualityindicators.pool || 0) > 500 && + (data.qualityindicators.volume7Days || 0) > 100) + ? 2 + : 1; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(data), misha(data)]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/metaculus.ts b/src/backend/platforms/metaculus.ts index 5d54b10..0327ca6 100644 --- a/src/backend/platforms/metaculus.ts +++ b/src/backend/platforms/metaculus.ts @@ -1,7 +1,7 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -131,7 +131,7 @@ export const metaculus: Platform = { let description = descriptionprocessed2; let isbinary = result.possibilities.type == "binary"; - let options = []; + let options: FetchedQuestion["options"] = []; if (isbinary) { let probability = Number(result.community_prediction.full.q2); options = [ @@ -156,9 +156,6 @@ export const metaculus: Platform = { options, qualityindicators: { numforecasts: Number(result.number_of_predictions), - stars: calculateStars(platformName, { - numforecasts: result.number_of_predictions, - }), }, extra: { resolution_data: { @@ -193,4 +190,14 @@ export const metaculus: Platform = { return all_questions; }, + calculateStars(data) { + const { numforecasts } = data.qualityindicators; + let nuno = () => + (numforecasts || 0) > 300 ? 4 : (numforecasts || 0) > 100 ? 3 : 2; + let eli = () => 3; + let misha = () => 3; + let starsDecimal = average([nuno(), eli(), misha()]); + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/polymarket.ts b/src/backend/platforms/polymarket.ts index e00db69..395b60e 100644 --- a/src/backend/platforms/polymarket.ts +++ b/src/backend/platforms/polymarket.ts @@ -1,7 +1,7 @@ /* Imports */ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -96,8 +96,8 @@ export const polymarket: Platform = { let options = []; for (let outcome in moreMarketInfo.outcomeTokenPrices) { options.push({ - name: marketInfo.outcomes[outcome], - probability: moreMarketInfo.outcomeTokenPrices[outcome], + name: String(marketInfo.outcomes[outcome]), + probability: Number(moreMarketInfo.outcomeTokenPrices[outcome]), type: "PROBABILITY", }); } @@ -112,11 +112,6 @@ export const polymarket: Platform = { numforecasts: numforecasts.toFixed(0), liquidity: liquidity.toFixed(2), tradevolume: tradevolume.toFixed(2), - stars: calculateStars(platformName, { - liquidity, - option: options[0], - volume: tradevolume, - }), }, extra: { address: marketInfo.address, @@ -132,4 +127,33 @@ export const polymarket: Platform = { } return results; }, + calculateStars(data) { + // let nuno = (data) => (data.volume > 10000 ? 4 : data.volume > 1000 ? 3 : 2); + // let eli = (data) => data.liquidity > 10000 ? 5 : 4 + // let misha = (data) => 4 + + const liquidity = data.qualityindicators.liquidity || 0; + const volume = data.qualityindicators.tradevolume || 0; + + let nuno = () => + liquidity > 1000 && volume > 10000 + ? 4 + : liquidity > 500 && volume > 1000 + ? 3 + : 2; + let starsDecimal = average([nuno()]); //, eli(data), misha(data)]) + + // Substract 1 star if probability is above 90% or below 10% + if ( + data.options instanceof Array && + data.options[0] && + ((data.options[0].probability || 0) < 0.1 || + (data.options[0].probability || 0) > 0.9) + ) { + starsDecimal = starsDecimal - 1; + } + + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/predictit.ts b/src/backend/platforms/predictit.ts index 9f5fed2..550a622 100644 --- a/src/backend/platforms/predictit.ts +++ b/src/backend/platforms/predictit.ts @@ -1,6 +1,6 @@ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -103,7 +103,6 @@ export const predictit: Platform = { description, options, qualityindicators: { - stars: calculateStars(platformName, {}), shares_volume, }, }; @@ -113,4 +112,12 @@ export const predictit: Platform = { return results; }, + calculateStars(data) { + let nuno = () => 3; + let eli = () => 3.5; + let misha = () => 2.5; + let starsDecimal = average([nuno(), eli(), misha()]); + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/rootclaim.ts b/src/backend/platforms/rootclaim.ts index 0999e73..374e38e 100644 --- a/src/backend/platforms/rootclaim.ts +++ b/src/backend/platforms/rootclaim.ts @@ -1,7 +1,7 @@ import axios from "axios"; import { JSDOM } from "jsdom"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import toMarkdown from "../utils/toMarkdown"; import { FetchedQuestion, Platform } from "./"; @@ -79,11 +79,18 @@ export const rootclaim: Platform = { options: options, qualityindicators: { numforecasts: 1, - stars: calculateStars(platformName, {}), }, }; results.push(obj); } return results; }, + calculateStars(data) { + let nuno = () => 4; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno() /*, eli(data), misha(data)*/]); + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/smarkets.ts b/src/backend/platforms/smarkets.ts index 19cb449..52b6059 100644 --- a/src/backend/platforms/smarkets.ts +++ b/src/backend/platforms/smarkets.ts @@ -1,6 +1,6 @@ import axios from "axios"; -import { calculateStars } from "../utils/stars"; +import { average } from "../../utils"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -166,9 +166,7 @@ export const smarkets: Platform = { description: market.description, options: options, timestamp: new Date(), - qualityindicators: { - stars: calculateStars(platformName, {}), - }, + qualityindicators: {}, }; VERBOSE ? console.log(result) : empty(); results.push(result); @@ -176,4 +174,12 @@ export const smarkets: Platform = { VERBOSE ? console.log(results) : empty(); return results; }, + calculateStars(data) { + let nuno = () => 2; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/wildeford.ts b/src/backend/platforms/wildeford.ts index 0b39eaa..5dee63d 100644 --- a/src/backend/platforms/wildeford.ts +++ b/src/backend/platforms/wildeford.ts @@ -1,9 +1,9 @@ /* Imports */ import { GoogleSpreadsheet } from "google-spreadsheet"; +import { average } from "../../utils"; import { applyIfSecretExists } from "../utils/getSecrets"; import { hash } from "../utils/hash"; -import { calculateStars } from "../utils/stars"; import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -76,7 +76,7 @@ async function processPredictions(predictions) { let title = prediction["Prediction"].replace(" [update]", ""); let id = `${platformName}-${hash(title)}`; let probability = Number(prediction["Odds"].replace("%", "")) / 100; - let options = [ + let options: FetchedQuestion["options"] = [ { name: "Yes", probability: probability, @@ -95,9 +95,7 @@ async function processPredictions(predictions) { description: prediction["Notes"] || "", options, timestamp: new Date(Date.parse(prediction["Prediction Date"] + "Z")), - qualityindicators: { - stars: calculateStars(platformName, null), - }, + qualityindicators: {}, }; return result; }); @@ -125,4 +123,12 @@ export const wildeford: Platform = { const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY; // See: https://developers.google.com/sheets/api/guides/authorizing#APIKey return await applyIfSecretExists(GOOGLE_API_KEY, wildeford_inner); }, + calculateStars(data) { + let nuno = () => 3; + let eli = () => null; + let misha = () => null; + let starsDecimal = average([nuno()]); //, eli(), misha()]) + let starsInteger = Math.round(starsDecimal); + return starsInteger; + }, }; diff --git a/src/backend/platforms/xrisk.ts b/src/backend/platforms/xrisk.ts index 768e2be..d5ab360 100644 --- a/src/backend/platforms/xrisk.ts +++ b/src/backend/platforms/xrisk.ts @@ -25,4 +25,5 @@ export const xrisk: Platform = { }); return results; }, + calculateStars: () => 2, }; diff --git a/src/backend/utils/getSecrets.ts b/src/backend/utils/getSecrets.ts index 2a73260..37c6d06 100644 --- a/src/backend/utils/getSecrets.ts +++ b/src/backend/utils/getSecrets.ts @@ -1,5 +1,5 @@ export async function applyIfSecretExists( - cookie: string, + cookie: string | undefined, fun: (cookie: string) => T ) { if (cookie) { diff --git a/src/backend/utils/stars.ts b/src/backend/utils/stars.ts deleted file mode 100644 index 5150394..0000000 --- a/src/backend/utils/stars.ts +++ /dev/null @@ -1,330 +0,0 @@ -let average = (array: number[]) => - array.reduce((a, b) => a + b, 0) / array.length; - -function calculateStarsAstralCodexTen(data) { - let nuno = (data) => 3; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsBetfair(data) { - let nuno = (data) => (data.volume > 10000 ? 4 : data.volume > 1000 ? 3 : 2); - let eli = (data) => (data.volume > 10000 ? null : null); - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - // Substract 1 star if probability is above 90% or below 10% - if ( - data.option && - (data.option.probability < 0.1 || data.option.probability > 0.9) - ) { - starsDecimal = starsDecimal - 1; - } - - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsCoupCast(data) { - let nuno = (data) => 3; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsCSETForetell(data) { - let nuno = (data) => (data.numforecasts > 100 ? 3 : 2); - let eli = (data) => 3; - let misha = (data) => 2; - let starsDecimal = average([nuno(data), eli(data), misha(data)]); - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsElicit(data) { - let nuno = (data) => 1; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsEstimize(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsForetold(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsGiveWellOpenPhil(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsGoodJudgment(data) { - let nuno = (data) => 4; - let eli = (data) => 4; - let misha = (data) => 3.5; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsGoodJudgmentOpen(data) { - let nuno = (data) => (data.numforecasts > 100 ? 3 : 2); - let eli = (data) => 3; - let misha = (data) => - data.minProbability > 0.1 || data.maxProbability < 0.9 ? 3.1 : 2.5; - let starsDecimal = average([nuno(data), eli(data), misha(data)]); - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsHypermind(data) { - let nuno = (data) => 3; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsInfer(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsKalshi(data) { - let nuno = (data) => - data.interest > 500 && data.shares_volume > 10000 - ? 4 - : data.shares_volume > 2000 - ? 3 - : 2; - // let eli = (data) => data.interest > 10000 ? 5 : 4 - // let misha = (data) => 4 - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - // Substract 1 star if probability is above 90% or below 10% - if ( - data.option && - (data.option.probability < 0.1 || data.option.probability > 0.9) - ) { - starsDecimal = starsDecimal - 1; - } - - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsLadbrokes(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsManifold(data) { - let nuno = (data) => - data.volume7Days > 250 || (data.pool > 500 && data.volume7Days > 100) - ? 2 - : 1; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - // console.log(data); - // console.log(starsInteger); - return starsInteger; -} - -function calculateStarsMetaculus(data) { - let nuno = (data) => - data.numforecasts > 300 ? 4 : data.numforecasts > 100 ? 3 : 2; - let eli = (data) => 3; - let misha = (data) => 3; - let starsDecimal = average([nuno(data), eli(data), misha(data)]); - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsOmen(data) { - let nuno = (data) => 1; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsPolymarket(data) { - // let nuno = (data) => (data.volume > 10000 ? 4 : data.volume > 1000 ? 3 : 2); - // let eli = (data) => data.liquidity > 10000 ? 5 : 4 - // let misha = (data) => 4 - - let nuno = (data) => - data.liquidity > 1000 && data.volume > 10000 - ? 4 - : data.liquidity > 500 && data.volume > 1000 - ? 3 - : 2; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - // Substract 1 star if probability is above 90% or below 10% - if ( - data.option && - (data.option.probability < 0.1 || data.option.probability > 0.9) - ) { - starsDecimal = starsDecimal - 1; - } - - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsPredictIt(data) { - let nuno = (data) => 3; - let eli = (data) => 3.5; - let misha = (data) => 2.5; - let starsDecimal = average([nuno(data), eli(data), misha(data)]); - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsRootclaim(data) { - let nuno = (data) => 4; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data) /*, eli(data), misha(data)*/]); - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsSmarkets(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsWildeford(data) { - let nuno = (data) => 3; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -function calculateStarsWilliamHill(data) { - let nuno = (data) => 2; - let eli = (data) => null; - let misha = (data) => null; - let starsDecimal = average([nuno(data)]); //, eli(data), misha(data)]) - let starsInteger = Math.round(starsDecimal); - return starsInteger; -} - -export function calculateStars(platform: string, data) { - let stars = 2; - switch (platform) { - case "betfair": - stars = calculateStarsBetfair(data); - break; - case "infer": - stars = calculateStarsInfer(data); - break; - case "foretold": - stars = calculateStarsForetold(data); - break; - case "givewellopenphil": - stars = calculateStarsGiveWellOpenPhil(data); - break; - case "goodjudgment": - stars = calculateStarsGoodJudgment(data); - break; - case "goodjudgmentopen": - stars = calculateStarsGoodJudgmentOpen(data); - break; - case "kalshi": - stars = calculateStarsKalshi(data); - break; - case "manifold": - stars = calculateStarsManifold(data); - break; - case "metaculus": - stars = calculateStarsMetaculus(data); - break; - case "polymarket": - stars = calculateStarsPolymarket(data); - break; - case "predictit": - stars = calculateStarsPredictIt(data); - break; - case "rootclaim": - stars = calculateStarsRootclaim(data); - break; - case "smarkets": - stars = calculateStarsSmarkets(data); - break; - case "wildeford": - stars = calculateStarsWildeford(data); - break; - - // deprecated - case "AstralCodexTen": - stars = calculateStarsAstralCodexTen(data); - break; - case "CoupCast": - stars = calculateStarsCoupCast(data); - break; - case "CSET-foretell": - stars = calculateStarsCSETForetell(data); - break; - case "Elicit": - stars = calculateStarsElicit(data); - break; - case "Estimize": - stars = calculateStarsEstimize(data); - break; - case "Hypermind": - stars = calculateStarsHypermind(data); - break; - case "Ladbrokes": - stars = calculateStarsLadbrokes(data); - break; - case "Omen": - stars = calculateStarsOmen(data); - break; - case "WilliamHill": - stars = calculateStarsWilliamHill(data); - break; - default: - stars = 2; - } - return stars; -} diff --git a/src/utils.ts b/src/utils.ts index 649b2a7..5b42717 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,3 +6,6 @@ export const shuffleArray = (array: T[]): T[] => { } return array; }; + +export const average = (array: number[]) => + array.reduce((a, b) => a + b, 0) / array.length;