const crypto = require('crypto'); const Logs = require('../libs/logs'); const IOR_KEYS_MAP = require('./iorKeys'); const { getSetting } = require('./settings'); const { eventSolutions } = require('./eventSolutions'); /** * 筛选最优赔率 */ function getOptimalSelections(odds, rules) { const results = []; const { obRebateRatio, hgRebateRatio } = getSetting(); const rebateMap = { ob: 1 + obRebateRatio / 100, hg: 1 + hgRebateRatio / 100, }; rules.forEach((rule, ruleIndex) => { let validOptions = []; for (let i = 0; i < 3; i++) { const innerIndex = i; const selection = []; let isValid = true; for (let j = 0; j < 3; j++) { const key = rule[j]; let item = odds[key]; if (key == '-') { item = { no: { v: 1 } }; } if (!item) { isValid = false; break; } if (j === innerIndex) { if (!('ps' in item)) { isValid = false; break; } selection.push({ k: key, p: 'ps', v: item.ps.v, r: item.ps.r, s: item.ps.s, o: item }); } else { const candidates = ['ob', 'hg', 'no'].filter((k) => k in item); if (candidates.length === 0) { isValid = false; break; } // Logs.out('candidates', candidates) const best = candidates.reduce((a, b) => { const aValue = (item[a].v-1)*rebateMap[a]; const bValue = (item[b].v-1)*rebateMap[b]; const seletcted = aValue > bValue ? a : b; return seletcted; }); // Logs.out('best', item, best) selection.push({ k: key, p: best, v: item[best].v, r: item[best].r, o: item }); } } if (isValid) { validOptions.push(selection); } } validOptions.forEach(iors => { results.push({ rule, iors, ruleIndex }); }); // if (validOptions.length > 0) { // const iors = validOptions.reduce((a, b) => { // const sumA = a.reduce((sum, x) => sum + x.v, 0); // const sumB = b.reduce((sum, x) => sum + x.v, 0); // return sumA > sumB ? a : b; // }); // results.push({ rule, iors, ruleIndex }); // } }); return results; } /** * 精确浮点数字 * @param {number} number * @param {number} x * @returns {number} */ const fixFloat = (number, x=2) => { return parseFloat(number.toFixed(x)); } /** * 盘口排序 */ const priority = { ps: 1, ob: 2, hg: 3 }; const sortCpr = (cpr) => { const temp = [...cpr]; temp.sort((a, b) => priority[a.p] - priority[b.p]); return temp; } /** * 添加返佣 */ const attachRebate = (ior) => { const { obRebateRatio, hgRebateRatio } = getSetting(); const { p } = ior; let rebate = 0; if (p == 'ps') { rebate = 0; } else if (p == 'ob') { rebate = obRebateRatio; } else if (p == 'hg') { rebate = hgRebateRatio; } return { ...ior, b: rebate }; } const eventsCombination = (passableEvents) => { const { minProfitAmount, innerDefaultAmount, innerRebateRatio } = getSetting(); const solutions = []; passableEvents.forEach(events => { const { odds, info } = events; Object.keys(IOR_KEYS_MAP).forEach(iorGroup => { const rules = IOR_KEYS_MAP[iorGroup]; const optimalSelections = getOptimalSelections(odds, rules); optimalSelections.forEach(selection => { const { rule, iors, ruleIndex } = selection; const [, , , crossType] = rule; const oddsSideA = attachRebate(iors[0]); const oddsSideB = attachRebate(iors[1]); const oddsSideC = attachRebate(iors[2]); const innerIndex = iors.findIndex(item => item.p == 'ps'); if (!oddsSideA || !oddsSideB || !oddsSideC) { return; } const cpr = [ oddsSideA, oddsSideB, oddsSideC ]; const betInfo = { cross_type: crossType, inner_index: innerIndex, inner_base: innerDefaultAmount, inner_rebate: fixFloat(innerRebateRatio / 100, 3), odds_side_a: fixFloat(oddsSideA.v - 1), odds_side_b: fixFloat(oddsSideB.v - 1), odds_side_c: fixFloat(oddsSideC.v - 1), rebate_side_a: parseFloat((oddsSideA.b / 100).toFixed(4)), rebate_side_b: parseFloat((oddsSideB.b / 100).toFixed(4)), rebate_side_c: parseFloat((oddsSideC.b / 100).toFixed(4)), }; const sol = eventSolutions(betInfo, true); if (cpr[2].k == '-') { cpr.pop(); } if (sol?.win_average > minProfitAmount) { const id = info.id; const sortedCpr = sortCpr(cpr); const keys = sortedCpr.map(item => `${item.k}`).join('_'); const sid = crypto.createHash('sha1').update(`${id}_${keys}`).digest('hex'); const crpGroup = `${id}_${sortedCpr[0].k}`; const timestamp = Date.now(); solutions.push({sid, sol, cpr, info, group: crpGroup, rule: `${iorGroup}:${ruleIndex}`, timestamp}); } }); }); }); return solutions.sort((a, b) => { return b.sol.win_average - a.sol.win_average; }); } module.exports = { eventsCombination };