|
|
@@ -1,7 +1,50 @@
|
|
|
+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 };
|
|
|
+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;
|
|
|
@@ -132,39 +175,11 @@ const HandicapCalc = function (data) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 计算输赢比例
|
|
|
- * 与其他方法中的输赢逻辑相反
|
|
|
- * 正数为输
|
|
|
- * 负数为赢
|
|
|
+ * 根据预期盈利计算下注金额
|
|
|
*/
|
|
|
-const CROSS_TYPE_MAP = { w: -1, l: 1, a: 1, h: 0.5, d: 0, r: 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 calcExternalHandicap = (data) => {
|
|
|
+const calcGoldsWithTarget = (data) => {
|
|
|
const {
|
|
|
- gold_side_inner: g,
|
|
|
+ inner_base: g,
|
|
|
odds_side_a: a,
|
|
|
odds_side_b: b,
|
|
|
odds_side_c: c,
|
|
|
@@ -185,67 +200,78 @@ const calcExternalHandicap = (data) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const calcGoldsWithWinTarget = (data) => {
|
|
|
- const { gold_side_inner, win_target, sol1, sol2 } = data;
|
|
|
+/**
|
|
|
+ * 根据预期盈利计算金额和内盘盈亏
|
|
|
+ */
|
|
|
+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,
|
|
|
- } = calcExternalHandicap({ ...sol1, gold_side_inner, win_target });
|
|
|
+ } = 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 + goldC1;
|
|
|
- win_inner_1 = gold_side_inner * (oddsA1 + 1);
|
|
|
+ loss_out_1 = goldB1 * (1 - rebateB1) + goldC1 * (1 - rebateC1);
|
|
|
+ win_inner_1 = inner_base * (oddsA1 + 1);
|
|
|
break;
|
|
|
case 1:
|
|
|
- loss_out_1 = goldA1 + goldC1;
|
|
|
- win_inner_1 = gold_side_inner * (oddsB1 + 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 = gold_side_inner * (oddsC1 + 1)
|
|
|
+ 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,
|
|
|
- } = calcExternalHandicap({ ...sol2, gold_side_inner, win_target, loss_out: loss_out_1 });
|
|
|
+ } = 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 = gold_side_inner +goldB2 + goldC2 + loss_out_1;
|
|
|
+ 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 = gold_side_inner + goldA2 + goldC2 + loss_out_1;
|
|
|
+ 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 = gold_side_inner + goldA2 * lpA2 + goldB2 * lpB2 + loss_out_1;
|
|
|
+ loss_out_2 = inner_base + goldA2 * lpA2 + goldB2 * lpB2 + loss_out_1;
|
|
|
win_inner_2 = win_inner_1 * (oddsC2 + 1);
|
|
|
break;
|
|
|
}
|
|
|
@@ -261,39 +287,51 @@ const calcGoldsWithWinTarget = (data) => {
|
|
|
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: gold_side_inner,
|
|
|
+ inner_base,
|
|
|
+ inner_rebate,
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-const calcTotalProfit = (sol1, sol2, gold_side_inner, rebate_side_inner) => {
|
|
|
- const rebateInner = gold_side_inner * rebate_side_inner / 100;
|
|
|
+/**
|
|
|
+ * 根据单关盈亏计算综合利润
|
|
|
+ */
|
|
|
+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 = calcGoldsWithWinTarget({ gold_side_inner, win_target: winTarget1, sol1, sol2 })?.win_inner;
|
|
|
- const win2 = calcGoldsWithWinTarget({ gold_side_inner, win_target: winTarget2, sol1, sol2 })?.win_inner;
|
|
|
+ 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);
|
|
|
@@ -302,7 +340,7 @@ const calcTotalProfit = (sol1, sol2, gold_side_inner, rebate_side_inner) => {
|
|
|
|
|
|
for (let i = start; i > end; i--) {
|
|
|
const win_target = i;
|
|
|
- const goldsInfo = calcGoldsWithWinTarget({ gold_side_inner, win_target, sol1, sol2 });
|
|
|
+ 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);
|
|
|
@@ -313,7 +351,102 @@ const calcTotalProfit = (sol1, sol2, gold_side_inner, rebate_side_inner) => {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
return result.at(-1);
|
|
|
}
|
|
|
|
|
|
-module.exports = calcTotalProfit;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * ----------------------------
|
|
|
+ * 计算第一关锁定之后的新利润
|
|
|
+ * 第一关过关后,重新计算第二关金额
|
|
|
+ * 结合第一关亏损计算第二关新利润
|
|
|
+ * ----------------------------
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 计算第二关利润
|
|
|
+ */
|
|
|
+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) {
|
|
|
+ throw new Error('inner_base is not equal to inner_ref_value');
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 };
|