refactor: stars calculation moved to platforms

This commit is contained in:
Vyacheslav Matyukhin 2022-05-10 01:15:18 +04:00
parent ac7b541896
commit da03fa8804
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
22 changed files with 272 additions and 453 deletions

View File

@ -1,7 +1,6 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -26,7 +25,7 @@ async function processPredictions(predictions) {
let results = await predictions.map((prediction) => { let results = await predictions.map((prediction) => {
const id = `${platformName}-${prediction.id}`; const id = `${platformName}-${prediction.id}`;
const probability = prediction.probability; const probability = prediction.probability;
const options = [ const options: FetchedQuestion["options"] = [
{ {
name: "Yes", name: "Yes",
probability: probability, probability: probability,
@ -45,9 +44,6 @@ async function processPredictions(predictions) {
description: prediction.description, description: prediction.description,
options, options,
qualityindicators: { qualityindicators: {
stars: calculateStars(platformName, {
/* some: somex, factors: factors */
}),
// other: prediction.otherx, // other: prediction.otherx,
// indicators: prediction.indicatorx, // indicators: prediction.indicatorx,
}, },
@ -68,4 +64,7 @@ export const example: Platform = {
let results = await processPredictions(data); // somehow needed let results = await processPredictions(data); // somehow needed
return results; return results;
}, },
calculateStars(data) {
return 2;
},
}; };

View File

@ -2,7 +2,7 @@
import axios from "axios"; import axios from "axios";
import https from "https"; import https from "https";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
const platformName = "betfair"; const platformName = "betfair";
@ -121,17 +121,13 @@ async function processPredictions(data) {
if (title.includes("of the named")) { if (title.includes("of the named")) {
title = prediction.marketName + ": " + title; title = prediction.marketName + ": " + title;
} }
const result = { const result: FetchedQuestion = {
id, id,
title, title,
url: `https://www.betfair.com/exchange/plus/politics/market/${prediction.marketId}`, url: `https://www.betfair.com/exchange/plus/politics/market/${prediction.marketId}`,
platform: platformName,
description, description,
options, options,
qualityindicators: { qualityindicators: {
stars: calculateStars(platformName, {
volume: prediction.totalMatched,
}),
volume: prediction.totalMatched, volume: prediction.totalMatched,
}, },
}; };
@ -149,4 +145,25 @@ export const betfair: Platform = {
const results = await processPredictions(data); // somehow needed const results = await processPredictions(data); // somehow needed
return results; 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;
},
}; };

View File

@ -1,7 +1,6 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
const platformName = "fantasyscotus"; const platformName = "fantasyscotus";
@ -100,7 +99,6 @@ async function processData(data) {
], ],
qualityindicators: { qualityindicators: {
numforecasts: Number(predictionData.numForecasts), numforecasts: Number(predictionData.numForecasts),
stars: calculateStars(platformName, {}),
}, },
}; };
results.push(eventObject); results.push(eventObject);
@ -120,4 +118,7 @@ export const fantasyscotus: Platform = {
let results = await processData(rawData); let results = await processData(rawData);
return results; return results;
}, },
calculateStars(data) {
return 2;
},
}; };

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -68,7 +68,8 @@ export const foretold: Platform = {
questions = questions.filter((question) => question.previousAggregate); // Questions without any predictions questions = questions.filter((question) => question.previousAggregate); // Questions without any predictions
questions.forEach((question) => { questions.forEach((question) => {
let id = `${platformName}-${question.id}`; let id = `${platformName}-${question.id}`;
let options = [];
let options: FetchedQuestion["options"] = [];
if (question.valueType == "PERCENTAGE") { if (question.valueType == "PERCENTAGE") {
let probability = question.previousAggregate.value.percentage; let probability = question.previousAggregate.value.percentage;
options = [ options = [
@ -84,6 +85,7 @@ export const foretold: Platform = {
}, },
]; ];
} }
const result: FetchedQuestion = { const result: FetchedQuestion = {
id, id,
title: question.name, title: question.name,
@ -92,7 +94,6 @@ export const foretold: Platform = {
options, options,
qualityindicators: { qualityindicators: {
numforecasts: Math.floor(Number(question.measurementCount) / 2), numforecasts: Math.floor(Number(question.measurementCount) / 2),
stars: calculateStars(platformName, {}),
}, },
/*liquidity: liquidity.toFixed(2), /*liquidity: liquidity.toFixed(2),
tradevolume: tradevolume.toFixed(2), tradevolume: tradevolume.toFixed(2),
@ -104,4 +105,12 @@ export const foretold: Platform = {
} }
return results; 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;
},
}; };

View File

@ -2,7 +2,7 @@
import axios from "axios"; import axios from "axios";
import fs from "fs"; import fs from "fs";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { Platform } from "./"; import { Platform } from "./";
const platformName = "givewellopenphil"; const platformName = "givewellopenphil";
@ -53,9 +53,7 @@ async function main1() {
platform: platformName, platform: platformName,
description, description,
options: [], options: [],
qualityindicators: { qualityindicators: {},
stars: calculateStars(platformName, {}),
},
}; // Note: This requires some processing afterwards }; // Note: This requires some processing afterwards
// console.log(result) // console.log(result)
results.push(result); results.push(result);
@ -84,4 +82,12 @@ export const givewellopenphil: Platform = {
})); }));
return dataWithDate; 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;
},
}; };

View File

@ -3,8 +3,8 @@ import axios from "axios";
import { Tabletojson } from "tabletojson"; import { Tabletojson } from "tabletojson";
import tunnel from "tunnel"; import tunnel from "tunnel";
import { average } from "../../utils";
import { hash } from "../utils/hash"; import { hash } from "../utils/hash";
import { calculateStars } from "../utils/stars";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -103,9 +103,7 @@ export const goodjudgment: Platform = {
url: endpoint, url: endpoint,
description, description,
options, options,
qualityindicators: { qualityindicators: {},
stars: calculateStars(platformName, {}),
},
extra: { extra: {
superforecastercommentary: analysis || "", superforecastercommentary: analysis || "",
}, },
@ -120,4 +118,12 @@ export const goodjudgment: Platform = {
return results; 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;
},
}; };

View File

@ -2,16 +2,16 @@
import axios from "axios"; import axios from "axios";
import { Tabletojson } from "tabletojson"; import { Tabletojson } from "tabletojson";
import { average } from "../../utils";
import { applyIfSecretExists } from "../utils/getSecrets"; import { applyIfSecretExists } from "../utils/getSecrets";
import { calculateStars } from "../utils/stars";
import toMarkdown from "../utils/toMarkdown"; import toMarkdown from "../utils/toMarkdown";
import { Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
const platformName = "goodjudgmentopen"; const platformName = "goodjudgmentopen";
let htmlEndPoint = "https://www.gjopen.com/questions?page="; const htmlEndPoint = "https://www.gjopen.com/questions?page=";
let annoyingPromptUrls = [ 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/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/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", "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 */ /* Support functions */
async function fetchPage(page, cookie) { async function fetchPage(page: number, cookie: string) {
let response = await axios({ let response = await axios({
url: htmlEndPoint + page, url: htmlEndPoint + page,
method: "GET", method: "GET",
@ -35,7 +35,7 @@ async function fetchPage(page, cookie) {
return response; return response;
} }
async function fetchStats(questionUrl, cookie) { async function fetchStats(questionUrl: string, cookie: string) {
let response = await axios({ let response = await axios({
url: questionUrl + "/stats", url: questionUrl + "/stats",
method: "GET", method: "GET",
@ -50,7 +50,7 @@ async function fetchStats(questionUrl, cookie) {
// Is binary? // Is binary?
let isbinary = response.includes("binary?&quot;:true"); let isbinary = response.includes("binary?&quot;:true");
let options = []; let options: FetchedQuestion["options"] = [];
if (isbinary) { if (isbinary) {
// Crowd percentage // Crowd percentage
let htmlElements = response.split("\n"); let htmlElements = response.split("\n");
@ -107,21 +107,12 @@ async function fetchStats(questionUrl, cookie) {
.split(",")[0]; .split(",")[0];
//console.log(numpredictors) //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 = { let result = {
description: description, description,
options: options, options,
qualityindicators: { qualityindicators: {
numforecasts: Number(numforecasts), numforecasts: Number(numforecasts),
numforecasters: Number(numforecasters), 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 // 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; } as any;
@ -129,7 +120,7 @@ async function fetchStats(questionUrl, cookie) {
return result; return result;
} }
function isSignedIn(html) { function isSignedIn(html: string) {
let isSignedInBool = !( let isSignedInBool = !(
html.includes("You need to sign in or sign up before continuing") || html.includes("You need to sign in or sign up before continuing") ||
html.includes("Sign up") html.includes("Sign up")
@ -157,7 +148,7 @@ function sleep(ms: number) {
/* Body */ /* Body */
async function goodjudgmentopen_inner(cookie) { async function goodjudgmentopen_inner(cookie: string) {
let i = 1; let i = 1;
let response = await fetchPage(i, cookie); let response = await fetchPage(i, cookie);
@ -243,6 +234,23 @@ export const goodjudgmentopen: Platform = {
color: "#002455", color: "#002455",
async fetcher() { async fetcher() {
let cookie = process.env.GOODJUDGMENTOPENCOOKIE; 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;
}, },
}; };

View File

@ -1,12 +1,10 @@
import axios from "axios"; import axios from "axios";
import { parseISO } from "date-fns";
/* Imports */
import { Question } from "@prisma/client"; import { Question } from "@prisma/client";
import { AlgoliaQuestion } from "../../backend/utils/algolia"; import { AlgoliaQuestion } from "../../backend/utils/algolia";
import { prisma } from "../database/prisma"; import { prisma } from "../database/prisma";
import { Platform } from "./"; import { FetchedQuestion, Platform, prepareQuestion } from "./";
/* Definitions */ /* Definitions */
const searchEndpoint = const searchEndpoint =
@ -14,25 +12,20 @@ const searchEndpoint =
const apiEndpoint = "https://guesstimate.herokuapp.com"; const apiEndpoint = "https://guesstimate.herokuapp.com";
/* Body */
const modelToQuestion = (model: any): Question => { const modelToQuestion = (model: any): Question => {
const { description } = model; const { description } = model;
// const description = model.description // const description = model.description
// ? model.description.replace(/\n/g, " ").replace(/ /g, " ") // ? model.description.replace(/\n/g, " ").replace(/ /g, " ")
// : ""; // : "";
const stars = description.length > 250 ? 2 : 1; // const timestamp = parseISO(model.created_at);
const timestamp = parseISO(model.created_at); const fq: FetchedQuestion = {
const q: Question = {
id: `guesstimate-${model.id}`, id: `guesstimate-${model.id}`,
title: model.name, title: model.name,
url: `https://www.getguesstimate.com/models/${model.id}`, url: `https://www.getguesstimate.com/models/${model.id}`,
timestamp, // timestamp,
platform: "guesstimate",
description, description,
options: [], options: [],
qualityindicators: { qualityindicators: {
stars,
numforecasts: 1, numforecasts: 1,
numforecasters: 1, numforecasters: 1,
}, },
@ -41,6 +34,7 @@ const modelToQuestion = (model: any): Question => {
}, },
// ranking: 10 * (index + 1) - 0.5, //(model._rankingInfo - 1*index)// hack // ranking: 10 * (index + 1) - 0.5, //(model._rankingInfo - 1*index)// hack
}; };
const q = prepareQuestion(fq, guesstimate);
return q; return q;
}; };
@ -68,7 +62,7 @@ async function search(query: string): Promise<AlgoliaQuestion[]> {
}); });
// filter for duplicates. Surprisingly common. // filter for duplicates. Surprisingly common.
let uniqueTitles = []; let uniqueTitles: string[] = [];
let uniqueModels: AlgoliaQuestion[] = []; let uniqueModels: AlgoliaQuestion[] = [];
for (let model of mappedModels) { for (let model of mappedModels) {
if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) { if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) {
@ -100,4 +94,5 @@ export const guesstimate: Platform & {
color: "#223900", color: "#223900",
search, search,
fetchQuestion, fetchQuestion,
calculateStars: (q) => (q.description.length > 250 ? 2 : 1),
}; };

View File

@ -41,11 +41,16 @@ export interface QualityIndicators {
export type FetchedQuestion = Omit< export type FetchedQuestion = Omit<
Question, Question,
"extra" | "qualityindicators" | "timestamp" | "platform" "extra" | "qualityindicators" | "timestamp" | "platform" | "options"
> & { > & {
timestamp?: Date; timestamp?: Date;
extra?: object; // required in DB but annoying to return empty; also this is slightly stricter than Prisma's JsonValue 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<QualityIndicators, "stars">; // slightly stronger type than Prisma's JsonValue
}; };
// fetcher should return null if platform failed to fetch questions for some reason // 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" label: string; // longer name for displaying on frontend etc., e.g. "X-risk estimates"
color: string; // used on frontend color: string; // used on frontend
fetcher?: PlatformFetcher; fetcher?: PlatformFetcher;
calculateStars: (question: FetchedQuestion) => number;
} }
// draft for the future callback-based streaming/chunking API: // draft for the future callback-based streaming/chunking API:
@ -86,6 +92,22 @@ export const platforms: Platform[] = [
xrisk, 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) => { export const processPlatform = async (platform: Platform) => {
if (!platform.fetcher) { if (!platform.fetcher) {
console.log(`Platform ${platform.name} doesn't have a fetcher, skipping`); console.log(`Platform ${platform.name} doesn't have a fetcher, skipping`);
@ -97,16 +119,6 @@ export const processPlatform = async (platform: Platform) => {
return; 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({ const oldQuestions = await prisma.question.findMany({
where: { where: {
platform: platform.name, platform: platform.name,
@ -123,7 +135,7 @@ export const processPlatform = async (platform: Platform) => {
const updatedQuestions: Question[] = []; const updatedQuestions: Question[] = [];
const deletedIds = oldIds.filter((id) => !fetchedIdsSet.has(id)); 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)) { if (oldIdsSet.has(q.id)) {
updatedQuestions.push(q); updatedQuestions.push(q);
} else { } else {

View File

@ -1,9 +1,9 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { average } from "../../utils";
import { applyIfSecretExists } from "../utils/getSecrets"; import { applyIfSecretExists } from "../utils/getSecrets";
import { measureTime } from "../utils/measureTime"; import { measureTime } from "../utils/measureTime";
import { calculateStars } from "../utils/stars";
import toMarkdown from "../utils/toMarkdown"; import toMarkdown from "../utils/toMarkdown";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
@ -106,7 +106,6 @@ async function fetchStats(questionUrl, cookie) {
numforecasts: Number(numforecasts), numforecasts: Number(numforecasts),
numforecasters: Number(numforecasters), numforecasters: Number(numforecasters),
comments_count: Number(comments_count), comments_count: Number(comments_count),
stars: calculateStars(platformName, { numforecasts }),
}, },
}; };
// console.log(JSON.stringify(result, null, 4)); // console.log(JSON.stringify(result, null, 4));
@ -177,9 +176,7 @@ async function infer_inner(cookie: string) {
let question: FetchedQuestion = { let question: FetchedQuestion = {
id: id, id: id,
title: title, title: title,
description: moreinfo.description,
url: url, url: url,
options: moreinfo.options,
...moreinfo, ...moreinfo,
}; };
console.log(JSON.stringify(question, null, 4)); console.log(JSON.stringify(question, null, 4));
@ -236,4 +233,12 @@ export const infer: Platform = {
let cookie = process.env.INFER_COOKIE; let cookie = process.env.INFER_COOKIE;
return await applyIfSecretExists(cookie, infer_inner); 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;
},
}; };

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -23,7 +23,7 @@ async function processMarkets(markets) {
markets = markets.filter((market) => market.close_date > dateNow); markets = markets.filter((market) => market.close_date > dateNow);
let results = await markets.map((market) => { let results = await markets.map((market) => {
const probability = market.last_price / 100; const probability = market.last_price / 100;
const options = [ const options: FetchedQuestion["options"] = [
{ {
name: "Yes", name: "Yes",
probability: probability, 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})`, description: `${market.settle_details}. The resolution source is: ${market.ranged_group_name} (${market.settle_source_url})`,
options, options,
qualityindicators: { qualityindicators: {
stars: calculateStars(platformName, {
shares_volume: market.volume,
interest: market.open_interest,
}),
yes_bid: market.yes_bid, yes_bid: market.yes_bid,
yes_ask: market.yes_ask, yes_ask: market.yes_ask,
spread: Math.abs(market.yes_bid - 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. 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 // "open_interest": market.open_interest, also in shares
}, },
extra: {
open_interest: market.open_interest,
},
}; };
return result; 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([...new Set(results.map((result) => result.title))]);
console.log( console.log(
"Number of unique questions: ", "Number of unique questions: ",
[...new Set(results.map((result) => result.title))].length [...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 = { export const kalshi: Platform = {
@ -76,4 +73,29 @@ export const kalshi: Platform = {
let markets = await fetchAllMarkets(); let markets = await fetchAllMarkets();
return await processMarkets(markets); 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;
},
}; };

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -25,16 +25,16 @@ async function fetchData() {
function showStatistics(results: FetchedQuestion[]) { function showStatistics(results: FetchedQuestion[]) {
console.log(`Num unresolved markets: ${results.length}`); 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( let num2StarsOrMore = results.filter(
(result) => result.qualityindicators.stars >= 2 (result) => manifold.calculateStars(result) >= 2
); );
console.log( console.log(
`Manifold has ${num2StarsOrMore.length} markets with 2 stars or more` `Manifold has ${num2StarsOrMore.length} markets with 2 stars or more`
); );
console.log( console.log(
`Mean volume: ${ `Mean volume: ${
sum(results.map((result) => result.qualityindicators.volume7Days)) / sum(results.map((result) => result.qualityindicators.volume7Days || 0)) /
results.length results.length
}; mean pool: ${ }; mean pool: ${
sum(results.map((result) => result.qualityindicators.pool)) / sum(results.map((result) => result.qualityindicators.pool)) /
@ -47,7 +47,7 @@ async function processPredictions(predictions) {
let results: FetchedQuestion[] = await predictions.map((prediction) => { let results: FetchedQuestion[] = await predictions.map((prediction) => {
let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name
let probability = prediction.probability; let probability = prediction.probability;
let options = [ let options: FetchedQuestion["options"] = [
{ {
name: "Yes", name: "Yes",
probability: probability, probability: probability,
@ -64,13 +64,8 @@ async function processPredictions(predictions) {
title: prediction.question, title: prediction.question,
url: prediction.url, url: prediction.url,
description: prediction.description, description: prediction.description,
options: options, options,
qualityindicators: { qualityindicators: {
stars: calculateStars(platformName, {
volume7Days: prediction.volume7Days,
volume24Hours: prediction.volume24Hours,
pool: prediction.pool,
}),
createdTime: prediction.createdTime, createdTime: prediction.createdTime,
volume7Days: prediction.volume7Days, volume7Days: prediction.volume7Days,
volume24Hours: prediction.volume24Hours, volume24Hours: prediction.volume24Hours,
@ -99,4 +94,17 @@ export const manifold: Platform = {
showStatistics(results); showStatistics(results);
return 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;
},
}; };

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import toMarkdown from "../utils/toMarkdown"; import toMarkdown from "../utils/toMarkdown";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
@ -131,7 +131,7 @@ export const metaculus: Platform = {
let description = descriptionprocessed2; let description = descriptionprocessed2;
let isbinary = result.possibilities.type == "binary"; let isbinary = result.possibilities.type == "binary";
let options = []; let options: FetchedQuestion["options"] = [];
if (isbinary) { if (isbinary) {
let probability = Number(result.community_prediction.full.q2); let probability = Number(result.community_prediction.full.q2);
options = [ options = [
@ -156,9 +156,6 @@ export const metaculus: Platform = {
options, options,
qualityindicators: { qualityindicators: {
numforecasts: Number(result.number_of_predictions), numforecasts: Number(result.number_of_predictions),
stars: calculateStars(platformName, {
numforecasts: result.number_of_predictions,
}),
}, },
extra: { extra: {
resolution_data: { resolution_data: {
@ -193,4 +190,14 @@ export const metaculus: Platform = {
return all_questions; 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;
},
}; };

View File

@ -1,7 +1,7 @@
/* Imports */ /* Imports */
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -96,8 +96,8 @@ export const polymarket: Platform = {
let options = []; let options = [];
for (let outcome in moreMarketInfo.outcomeTokenPrices) { for (let outcome in moreMarketInfo.outcomeTokenPrices) {
options.push({ options.push({
name: marketInfo.outcomes[outcome], name: String(marketInfo.outcomes[outcome]),
probability: moreMarketInfo.outcomeTokenPrices[outcome], probability: Number(moreMarketInfo.outcomeTokenPrices[outcome]),
type: "PROBABILITY", type: "PROBABILITY",
}); });
} }
@ -112,11 +112,6 @@ export const polymarket: Platform = {
numforecasts: numforecasts.toFixed(0), numforecasts: numforecasts.toFixed(0),
liquidity: liquidity.toFixed(2), liquidity: liquidity.toFixed(2),
tradevolume: tradevolume.toFixed(2), tradevolume: tradevolume.toFixed(2),
stars: calculateStars(platformName, {
liquidity,
option: options[0],
volume: tradevolume,
}),
}, },
extra: { extra: {
address: marketInfo.address, address: marketInfo.address,
@ -132,4 +127,33 @@ export const polymarket: Platform = {
} }
return results; 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;
},
}; };

View File

@ -1,6 +1,6 @@
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import toMarkdown from "../utils/toMarkdown"; import toMarkdown from "../utils/toMarkdown";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
@ -103,7 +103,6 @@ export const predictit: Platform = {
description, description,
options, options,
qualityindicators: { qualityindicators: {
stars: calculateStars(platformName, {}),
shares_volume, shares_volume,
}, },
}; };
@ -113,4 +112,12 @@ export const predictit: Platform = {
return results; 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;
},
}; };

View File

@ -1,7 +1,7 @@
import axios from "axios"; import axios from "axios";
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import toMarkdown from "../utils/toMarkdown"; import toMarkdown from "../utils/toMarkdown";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
@ -79,11 +79,18 @@ export const rootclaim: Platform = {
options: options, options: options,
qualityindicators: { qualityindicators: {
numforecasts: 1, numforecasts: 1,
stars: calculateStars(platformName, {}),
}, },
}; };
results.push(obj); results.push(obj);
} }
return results; 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;
},
}; };

View File

@ -1,6 +1,6 @@
import axios from "axios"; import axios from "axios";
import { calculateStars } from "../utils/stars"; import { average } from "../../utils";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -166,9 +166,7 @@ export const smarkets: Platform = {
description: market.description, description: market.description,
options: options, options: options,
timestamp: new Date(), timestamp: new Date(),
qualityindicators: { qualityindicators: {},
stars: calculateStars(platformName, {}),
},
}; };
VERBOSE ? console.log(result) : empty(); VERBOSE ? console.log(result) : empty();
results.push(result); results.push(result);
@ -176,4 +174,12 @@ export const smarkets: Platform = {
VERBOSE ? console.log(results) : empty(); VERBOSE ? console.log(results) : empty();
return results; 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;
},
}; };

View File

@ -1,9 +1,9 @@
/* Imports */ /* Imports */
import { GoogleSpreadsheet } from "google-spreadsheet"; import { GoogleSpreadsheet } from "google-spreadsheet";
import { average } from "../../utils";
import { applyIfSecretExists } from "../utils/getSecrets"; import { applyIfSecretExists } from "../utils/getSecrets";
import { hash } from "../utils/hash"; import { hash } from "../utils/hash";
import { calculateStars } from "../utils/stars";
import { FetchedQuestion, Platform } from "./"; import { FetchedQuestion, Platform } from "./";
/* Definitions */ /* Definitions */
@ -76,7 +76,7 @@ async function processPredictions(predictions) {
let title = prediction["Prediction"].replace(" [update]", ""); let title = prediction["Prediction"].replace(" [update]", "");
let id = `${platformName}-${hash(title)}`; let id = `${platformName}-${hash(title)}`;
let probability = Number(prediction["Odds"].replace("%", "")) / 100; let probability = Number(prediction["Odds"].replace("%", "")) / 100;
let options = [ let options: FetchedQuestion["options"] = [
{ {
name: "Yes", name: "Yes",
probability: probability, probability: probability,
@ -95,9 +95,7 @@ async function processPredictions(predictions) {
description: prediction["Notes"] || "", description: prediction["Notes"] || "",
options, options,
timestamp: new Date(Date.parse(prediction["Prediction Date"] + "Z")), timestamp: new Date(Date.parse(prediction["Prediction Date"] + "Z")),
qualityindicators: { qualityindicators: {},
stars: calculateStars(platformName, null),
},
}; };
return result; 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 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); 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;
},
}; };

View File

@ -25,4 +25,5 @@ export const xrisk: Platform = {
}); });
return results; return results;
}, },
calculateStars: () => 2,
}; };

View File

@ -1,5 +1,5 @@
export async function applyIfSecretExists<T>( export async function applyIfSecretExists<T>(
cookie: string, cookie: string | undefined,
fun: (cookie: string) => T fun: (cookie: string) => T
) { ) {
if (cookie) { if (cookie) {

View File

@ -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;
}

View File

@ -6,3 +6,6 @@ export const shuffleArray = <T>(array: T[]): T[] => {
} }
return array; return array;
}; };
export const average = (array: number[]) =>
array.reduce((a, b) => a + b, 0) / array.length;