| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- import crypto from 'crypto';
- import iorKeys from './iorKeys.js';
- import eventSolutions from './eventSolutions.js';
- /**
- * 精确浮点数字
- * @param {number} number
- * @param {number} x
- * @returns {number}
- */
- const fixFloat = (number, x=3) => {
- return parseFloat(number.toFixed(x));
- }
- /**
- * 盘口排序
- */
- const priority = { polymarket: 1, pinnacle: 2 };
- const getPriority = (p) => {
- return priority[p] ?? 99;
- }
- const sortCpr = (cpr) => {
- const temp = [...cpr];
- temp.sort((a, b) => getPriority(a.p) - getPriority(b.p));
- return temp;
- }
- /**
- * 获取平台类型
- */
- const getPlatformKey = (cpr) => {
- const platforms = sortCpr(cpr).map(item => item.p);
- return [...new Set(platforms)].join('_');
- }
- const cartesianOdds = (selection) => {
- const [a, b, c] = selection;
- return a.flatMap(itemA => b.flatMap(itemB => c.map(itemC => [itemA, itemB, itemC])));
- }
- const getOptimalSelections = (odds, rules) => {
- const results = [];
- rules.forEach((rule, ruleIndex) => {
- const validOptions = [];
- const selection = [];
- let isValid = true;
- for (let i = 0; i < 3; i++) {
- const key = rule[i];
- let item = odds[key];
- if (key === '-') {
- item = { no: { v: 1 } };
- }
- if (!item) {
- isValid = false;
- break;
- }
- const candidates = Object.keys(item);
- if (candidates.length === 0) {
- isValid = false;
- break;
- }
- selection.push(candidates.map(k => ({
- k: key,
- p: k,
- o: item,
- ...item[k],
- })));
- }
- if (isValid) {
- const cartesian = cartesianOdds(selection);
- cartesian.forEach(iors => {
- const iorsCount = new Set(iors.filter(ior => ior.p !== 'no').map(ior => ior.p));
- if (iorsCount.size > 1) {
- validOptions.push(iors);
- }
- });
- }
- validOptions.forEach(iors => {
- results.push({ rule, iors, ruleIndex });
- });
- });
- return results;
- }
- export const getPassableEvents = (relations) => {
- return relations.map(({ platforms }) => {
- const eventsMap = {};
- const oddsMap = {};
- Object.keys(platforms).forEach(platform => {
- const { id, leagueName, teamHomeName, teamAwayName, timestamp, evtime, odds } = platforms[platform] ?? {};
- if (!odds) {
- return;
- }
- if (platform === 'polymarket') {
- eventsMap['info'] = { id, leagueName, teamHomeName, teamAwayName, timestamp };
- }
- Object.keys(odds).forEach(ior => {
- if (!oddsMap[ior]) {
- oddsMap[ior] = {};
- }
- oddsMap[ior][platform] = odds[ior]
- });
- });
- eventsMap['odds'] = oddsMap;
- return eventsMap;
- })
- .filter(item => item?.info);
- }
- export const eventsCombination = (passableEvents) => {
- const solutions = [];
- passableEvents.forEach(events => {
- const { odds, info } = events;
- Object.keys(iorKeys).forEach(iorGroup => {
- const rules = iorKeys[iorGroup];
- const optimalSelections = getOptimalSelections(odds, rules);
- optimalSelections.forEach(selection => {
- const { iors, rule, ruleIndex } = selection;
- const [, , , crossType] = rule;
- const [ oddsSideA, oddsSideB, oddsSideC ] = iors;
- const baseIndex = iors.reduce((minIdx, cur, idx) => cur.v < iors[minIdx].v ? idx : minIdx, 0);
- if (!oddsSideA || !oddsSideB || !oddsSideC) {
- return;
- }
- if (oddsSideA.v <= 1 || oddsSideB.v <= 1) {
- return;
- }
- const cpr = [oddsSideA, oddsSideB, oddsSideC];
- const betInfo = {
- cross_type: crossType,
- base_index: baseIndex,
- base_stake: 10000,
- odds_side_a: fixFloat(oddsSideA.v - 1),
- odds_side_b: fixFloat(oddsSideB.v - 1),
- odds_side_c: fixFloat(oddsSideC.v - 1),
- };
- const sol = eventSolutions(betInfo, true);
- if (cpr[2].k == '-') {
- cpr.pop();
- }
- if (!isNaN(sol?.win_average)) {
- const id = info.id;
- const keys = cpr.map(item => `${item.k}-${item.p}`).join('##');
- const sid = crypto.createHash('sha1').update(`${id}-${keys}`).digest('hex');
- const hasLower = cpr.some(item => item.q === 0);
- const platformKey = getPlatformKey(cpr);
- const timestamp = Date.now();
- solutions.push({sid, sol, cpr, cross: platformKey, info, lower: hasLower, rule: `${iorGroup}:${ruleIndex}`, timestamp});
- }
- });
- });
- });
- return solutions.sort((a, b) => b.sol.win_profit_rate - a.sol.win_profit_rate);
- }
|