| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- const Logs = require('../libs/logs');
- const { eventSolutions } = require('./eventSolutions');
- /**
- * 精确浮点数字
- * @param {number} number
- * @param {number} x
- * @returns {number}
- */
- const fixFloat = (number, x = 2) => {
- return parseFloat(number.toFixed(x));
- }
- /**
- * 计算输赢比例
- * 与其他方法中的输赢逻辑相反
- * 正数为输
- * 负数为赢
- */
- const CROSS_TYPE_MAP = { w: -1, l: 1, a: 1, h: 0.5, d: 0, r: 0, v: 0 };
- const lossProportion = (sol) => {
- const { cross_type, odds_side_a, odds_side_b, rebate_side_a, rebate_side_b } = sol;
- const typeList = cross_type.split('_').map(part => {
- return part.split('').map(key => CROSS_TYPE_MAP[key]);
- }).map(([a, b])=> a * b);
- let loss_proportion_a = 0, loss_proportion_b = 0;
- if (typeList[0] >= 0) {
- loss_proportion_a = typeList[0] * (1 - rebate_side_a);
- }
- else {
- loss_proportion_a = typeList[0] * odds_side_a * (1 + rebate_side_a);
- }
- if (typeList[1] >= 0) {
- loss_proportion_b = typeList[1] * (1 - rebate_side_b);
- }
- else {
- loss_proportion_b = typeList[1] * odds_side_b * (1 + rebate_side_b);
- }
- return { loss_proportion_a, loss_proportion_b };
- }
- /**
- * 不同组合的金额计算
- */
- const HandicapCalc = function (data) {
- const { i, g, a, b, c, A, B, C, w, l } = data;
- const t = w + l;
- const k1 = a * (1 + A);
- const k2 = 1 - A;
- const k3 = b * (1 + B);
- const k4 = 1 - B;
- const k5 = c * (1 + C);
- const k6 = 1 - C;
- const calcTemplate = (handlers) => {
- if (i > 2 || i < 0) {
- return {};
- }
- if (i === 2) {
- const z = g;
- const m = t + k6 * z;
- const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
- const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
- return { x, y, z };
- };
- return handlers[i]?.() ?? {};
- };
- return {
- la_wh_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const m = t + k2 * x;
- const z = m / (2 * k5 + k6);
- const y = (k5 + k6) * m / (k3 * k5 + k3 * k6 / 2);
- return { x, y, z };
- },
- () => {
- const y = g;
- const z = ((k1 + k2) * t + (k2 * k4 - k1 * k3 / 2) * y) / (k1 * k5 - k2 * k6);
- const x = ((k5 + k6) * t + (k4 * k5 - k3 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
- return { x, y, z };
- }
- ]);
- },
- la_dr_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const m = t + k2 * x;
- const z = m / k5;
- const y = (k5 + k6) * m / (k3 * k5);
- return { x, y, z };
- },
- () => {
- const y = g;
- const z = ((k1 + k2) * t + k2 * k4 * y) / (k1 * k5 - k2 * k6);
- const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6);
- return { x, y, z };
- }
- ]);
- },
- la_lh_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const m = t + k2 * x;
- const z = (2 * k3 + k4) * m / (2 * k3 * k5 - k4 * k6);
- const y = (k5 + k6) * m / (k3 * k5 - k4 * k6 / 2);
- return { x, y, z };
- },
- () => {
- const y = g;
- const z = ((k1 + k2) * t + (k2 * k4 + k1 * k4 / 2) * y) / (k1 * k5 - k2 * k6);
- const x = ((k5 + k6) * t + (k4 * k5 + k4 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
- return { x, y, z };
- }
- ]);
- },
- lh_dr_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const z = (t + k2 * x / 2) / k5;
- const y = ((k5 + k6) * t + (k2 * k5 + k2 * k4 / 2) * x) / (k3 * k5);
- return { x, y, z };
- },
- () => {
- const y = g;
- const z = ((2 * k1 + k2) * t + k2 * k4 * y) / (2 * k1 * k5 - k2 * k6);
- const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6 / 2);
- return { x, y, z };
- }
- ]);
- },
- lh_lh_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const z = ((2 * k3 + k4) * t + (k3 + k4) * k2 * x) / (2 * k3 * k5 - k4 * k6);
- const y = ((k5 + k6) * t + (k5 + k6 / 2) * k2 * x) / (k3 * k5 - k4 * k6 / 2);
- return { x, y, z };
- },
- () => {
- const y = g;
- const z = ((2 * k1 + k2) * t + (k1 + k2) * k4 * y) / (2 * k1 * k5 - k2 * k6);
- const x = ((k5 + k6) * t + (k5 + k6 / 2) * k4 * y) / (k1 * k5 - k2 * k6 / 2);
- return { x, y, z };
- }
- ]);
- },
- la_la_wa() {
- return calcTemplate([
- () => {
- const x = g;
- const m = t + k2 * x;
- const z = (k3 + k4) * m / (k3 * k5 - k4 * k6);
- const y = (k5 + k6) * m / (k3 * k5 - k4 * k6);
- return { x, y, z };
- },
- () => {
- const y = g;
- const m = t + k4 * y;
- const z = (k1 + k2) * m / (k1 * k5 - k2 * k6);
- const x = (k5 + k6) * m / (k1 * k5 - k2 * k6);
- return { x, y, z };
- }
- ]);
- },
- la_wa_rv() {
- return calcTemplate([
- () => {
- const x = g;
- const y = (k2 * x + t) / k3;
- const z = 0;
- return { x, y, z };
- },
- () => {
- const y = g;
- const x = (k4 * y + t) / k1;
- const z = 0;
- return { x, y, z };
- }
- ]);
- }
- }
- }
- /**
- * const z = g;
- const m = t + k6 * z;
- const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
- const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
- return { x, y, z };
- */
- /**
- * 根据预期盈利计算下注金额
- */
- const calcGoldsWithTarget = (data) => {
- const {
- inner_base: g,
- odds_side_a: a,
- odds_side_b: b,
- odds_side_c: c,
- rebate_side_a: A,
- rebate_side_b: B,
- rebate_side_c: C,
- inner_index: i,
- cross_type: t,
- win_target: w,
- loss_out: l = 0,
- } = data;
- const calc = new HandicapCalc({ i, g, a, b, c, A, B, C, w, l });
- const { x, y, z } = calc?.[t]() ?? {};
- return {
- gold_side_a: fixFloat(x),
- gold_side_b: fixFloat(y),
- gold_side_c: fixFloat(z),
- }
- }
- /**
- * 根据预期盈利计算金额和内盘盈亏
- */
- const calcWinResultWithTarget = (data) => {
- const { inner_base, inner_rebate, win_target, sol1, sol2 } = data;
- const {
- cross_type: crossType1,
- odds_side_a: oddsA1,
- odds_side_b: oddsB1,
- odds_side_c: oddsC1,
- rebate_side_a: rebateA1,
- rebate_side_b: rebateB1,
- rebate_side_c: rebateC1,
- inner_index: inner_index_1,
- } = sol1;
- const {
- gold_side_a: goldA1,
- gold_side_b: goldB1,
- gold_side_c: goldC1,
- } = calcGoldsWithTarget({ ...sol1, inner_base, win_target });
- let loss_out_1 = 0, win_inner_1 = 0;
- switch (inner_index_1) {
- case 0:
- loss_out_1 = goldB1 * (1 - rebateB1) + goldC1 * (1 - rebateC1);
- win_inner_1 = inner_base * (oddsA1 + 1);
- break;
- case 1:
- loss_out_1 = goldA1 * (1 - rebateA1) + goldC1 * (1 - rebateC1);
- win_inner_1 = inner_base * (oddsB1 + 1);
- break;
- case 2:
- const { loss_proportion_a: lpA1, loss_proportion_b: lpB1 } = lossProportion(sol1);
- loss_out_1 = goldA1 * lpA1 + goldB1 * lpB1;
- win_inner_1 = inner_base * (oddsC1 + 1)
- break;
- }
- win_inner_1 = fixFloat(win_inner_1);
- const {
- cross_type: crossType2,
- odds_side_a: oddsA2,
- odds_side_b: oddsB2,
- odds_side_c: oddsC2,
- rebate_side_a: rebateA2,
- rebate_side_b: rebateB2,
- rebate_side_c: rebateC2,
- inner_index: inner_index_2,
- } = sol2;
- const {
- gold_side_a: goldA2,
- gold_side_b: goldB2,
- gold_side_c: goldC2,
- } = calcGoldsWithTarget({ ...sol2, inner_base, win_target, loss_out: loss_out_1 });
- let loss_out_2 = 0, win_inner_2 = 0, inner_base_key;
- switch (inner_index_2) {
- case 0:
- inner_base_key = 'goldA2';
- loss_out_2 = inner_base + goldB2 * (1 - rebateB2) + goldC2 * (1 - rebateC2) + loss_out_1;
- win_inner_2 = win_inner_1 * (oddsA2 + 1);
- break;
- case 1:
- inner_base_key = 'goldB2';
- loss_out_2 = inner_base + goldA2 * (1 - rebateA2) + goldC2 * (1 - rebateC2) + loss_out_1;
- win_inner_2 = win_inner_1 * (oddsB2 + 1);
- break;
- case 2:
- const { loss_proportion_a: lpA2, loss_proportion_b: lpB2 } = lossProportion(sol2);
- inner_base_key = 'goldC2';
- loss_out_2 = inner_base + goldA2 * lpA2 + goldB2 * lpB2 + loss_out_1;
- win_inner_2 = win_inner_1 * (oddsC2 + 1);
- break;
- }
- const win_inner = fixFloat(win_inner_2 - loss_out_2);
- const goldsInfo = { goldA1, goldB1, goldC1, goldA2, goldB2, goldC2 }
- if (goldsInfo[inner_base_key]) {
- goldsInfo[inner_base_key] = win_inner_1;
- }
- const result = {
- bet_info: [
- {
- cross_type: crossType1,
- gold_side_a: goldsInfo.goldA1,
- gold_side_b: goldsInfo.goldB1,
- gold_side_c: goldsInfo.goldC1,
- odds_side_a: oddsA1,
- odds_side_b: oddsB1,
- odds_side_c: oddsC1,
- rebate_side_a: rebateA1,
- rebate_side_b: rebateB1,
- rebate_side_c: rebateC1,
- inner_index: inner_index_1,
- },
- {
- cross_type: crossType2,
- gold_side_a: goldsInfo.goldA2,
- gold_side_b: goldsInfo.goldB2,
- gold_side_c: goldsInfo.goldC2,
- odds_side_a: oddsA2,
- odds_side_b: oddsB2,
- odds_side_c: oddsC2,
- rebate_side_a: rebateA2,
- rebate_side_b: rebateB2,
- rebate_side_c: rebateC2,
- inner_index: inner_index_2,
- }
- ],
- win_inner,
- inner_base,
- inner_rebate,
- }
- return result;
- }
- /**
- * 根据单关盈亏计算综合利润
- */
- const calcTotalProfit = (sol1, sol2, inner_base, inner_rebate) => {
- const rebateInner = inner_base * inner_rebate;
- const winTarget1 = fixFloat(sol1.win_average - rebateInner);
- const winTarget2 = fixFloat(sol2.win_average - rebateInner);
- const winTarget = fixFloat(Math.min(winTarget1, winTarget2));
- const win1 = calcWinResultWithTarget({ inner_base, inner_rebate, win_target: winTarget1, sol1, sol2 })?.win_inner;
- const win2 = calcWinResultWithTarget({ inner_base, inner_rebate, win_target: winTarget2, sol1, sol2 })?.win_inner;
- const win_inner = fixFloat(Math.max(win1, win2), 2);
- const start = Math.max(winTarget, win_inner);
- const end = Math.min(winTarget, win_inner);
- const result = [];
- for (let i = start; i > end; i--) {
- const win_target = i;
- const goldsInfo = calcWinResultWithTarget({ inner_base, inner_rebate, win_target, sol1, sol2 });
- const { win_inner, ...goldsRest } = goldsInfo;
- const win_diff = Math.abs(fixFloat(win_target - goldsInfo.win_inner));
- const lastResult = result.at(-1);
- if (!lastResult?.win_diff || win_diff < lastResult.win_diff) {
- result.push({ win_target: fixFloat(win_target + rebateInner), win_diff, win_inner: fixFloat(win_inner + rebateInner), ...goldsRest });
- }
- else {
- break;
- }
- }
- return result.at(-1);
- }
- /**
- * ----------------------------
- * 计算第一关锁定之后的新利润
- * 第一关过关后,重新计算第二关金额
- * 结合第一关亏损计算第二关新利润
- * ----------------------------
- */
- /**
- * 计算第二关利润
- */
- const calcSecondProfit = (betInfo) => {
- const {
- inner_index, inner_odds_first,
- odds_side_a: a, odds_side_b: b, odds_side_c: c,
- } = betInfo;
- let odds_side_a, odds_side_b, odds_side_c;
- switch (inner_index) {
- case 0:
- odds_side_a = fixFloat((a+1) * (inner_odds_first+1) - 1);
- odds_side_b = b;
- odds_side_c = c;
- break;
- case 1:
- odds_side_a = a;
- odds_side_b = fixFloat((b+1) * (inner_odds_first+1) - 1);
- odds_side_c = c;
- break;
- case 2:
- odds_side_a = a;
- odds_side_b = b;
- odds_side_c = fixFloat((c+1) * (inner_odds_first+1) - 1);
- break;
- }
- return eventSolutions({ ...betInfo, odds_side_a, odds_side_b, odds_side_c }, true);
- }
- /**
- * 结合第一关亏损计算第二关新利润
- */
- const calcTotalProfitWithFixedFirst = (betInfo1, betInfo2, inner_base, inner_rebate) => {
- const {
- cross_type: crossType1,
- inner_index: inner_index_1,
- gold_side_a: goldA1,
- gold_side_b: goldB1,
- gold_side_c: goldC1,
- odds_side_a: oddsA1,
- odds_side_b: oddsB1,
- odds_side_c: oddsC1,
- rebate_side_a: rebateA1,
- rebate_side_b: rebateB1,
- rebate_side_c: rebateC1,
- } = betInfo1;
- let loss_out_1 = 0, inner_ref_value = 0, inner_odds_1 = 0;
- switch (inner_index_1) {
- case 0:
- loss_out_1 = goldB1 * (1 - rebateB1) + goldC1 * (1 - rebateC1);
- inner_ref_value = goldA1;
- inner_odds_1 = oddsA1;
- break;
- case 1:
- loss_out_1 = goldA1 * (1 - rebateA1) + goldC1 * (1 - rebateC1);
- inner_ref_value = goldB1;
- inner_odds_1 = oddsB1;
- break;
- case 2:
- const { loss_proportion_a: lpA1, loss_proportion_b: lpB1 } = lossProportion(betInfo1);
- loss_out_1 = goldA1 * lpA1 + goldB1 * lpB1;
- inner_ref_value = goldC1;
- inner_odds_1 = oddsC1;
- break;
- }
- if (inner_base && inner_base != inner_ref_value) {
- Logs.out('inner_base is not equal to inner_ref_value', inner_base, inner_ref_value);
- throw new Error('内盘基准额度和内盘索引额度不一致');
- }
- const profitInfo = calcSecondProfit({ ...betInfo2, inner_base, inner_odds_first: inner_odds_1, inner_rebate });
- // profitInfo.win_side_a = fixFloat(profitInfo.win_side_a - loss_out_1);
- // profitInfo.win_side_b = fixFloat(profitInfo.win_side_b - loss_out_1);
- // profitInfo.win_side_c = fixFloat(profitInfo.win_side_c - loss_out_1);
- profitInfo.win_average = fixFloat(profitInfo.win_average - loss_out_1);
- return profitInfo;
- }
- module.exports = { calcTotalProfit, calcTotalProfitWithFixedFirst };
|