|
@@ -5,15 +5,20 @@ const SETTING = {
|
|
|
innerDefaultAmount: 10000,
|
|
innerDefaultAmount: 10000,
|
|
|
minProfitAmount: 0,
|
|
minProfitAmount: 0,
|
|
|
innerRebateRatio: 0,
|
|
innerRebateRatio: 0,
|
|
|
|
|
+ obRebateRatio: 0,
|
|
|
|
|
+ hgRebateRatio: 0,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 筛选最优赔率
|
|
* 筛选最优赔率
|
|
|
*/
|
|
*/
|
|
|
-function getOptimalSelections(data, combos) {
|
|
|
|
|
|
|
+function getOptimalSelections(odds, rules) {
|
|
|
const results = [];
|
|
const results = [];
|
|
|
|
|
+ const { obRebateRatio, hgRebateRatio } = SETTING;
|
|
|
|
|
+ const obRebate = 1 + obRebateRatio / 100;
|
|
|
|
|
+ const hgRebate = 1 + hgRebateRatio / 100;
|
|
|
|
|
|
|
|
- combos.forEach((rule, index) => {
|
|
|
|
|
|
|
+ rules.forEach((rule, index) => {
|
|
|
let validOptions = [];
|
|
let validOptions = [];
|
|
|
|
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
for (let i = 0; i < 3; i++) {
|
|
@@ -22,7 +27,7 @@ function getOptimalSelections(data, combos) {
|
|
|
let isValid = true;
|
|
let isValid = true;
|
|
|
for (let j = 0; j < 3; j++) {
|
|
for (let j = 0; j < 3; j++) {
|
|
|
const key = rule[j];
|
|
const key = rule[j];
|
|
|
- const item = data[key];
|
|
|
|
|
|
|
+ const item = odds[key];
|
|
|
|
|
|
|
|
if (!item) {
|
|
if (!item) {
|
|
|
isValid = false;
|
|
isValid = false;
|
|
@@ -46,7 +51,7 @@ function getOptimalSelections(data, combos) {
|
|
|
isValid = false;
|
|
isValid = false;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- const best = candidates.reduce((a, b) => item[a] > item[b] ? a : b);
|
|
|
|
|
|
|
+ const best = candidates.reduce((a, b) => item[a]*obRebate > item[b]*hgRebate ? a : b);
|
|
|
selection.push({
|
|
selection.push({
|
|
|
k: key,
|
|
k: key,
|
|
|
p: best,
|
|
p: best,
|
|
@@ -92,13 +97,13 @@ const triangleProfitCalc = (goldsInfo, oddsOption) => {
|
|
|
const {
|
|
const {
|
|
|
gold_side_a: x,
|
|
gold_side_a: x,
|
|
|
gold_side_b: y,
|
|
gold_side_b: y,
|
|
|
- gold_side_m: z,
|
|
|
|
|
|
|
+ gold_side_c: z,
|
|
|
odds_side_a: a,
|
|
odds_side_a: a,
|
|
|
odds_side_b: b,
|
|
odds_side_b: b,
|
|
|
- odds_side_m: c
|
|
|
|
|
|
|
+ odds_side_c: c
|
|
|
} = goldsInfo;
|
|
} = goldsInfo;
|
|
|
|
|
|
|
|
- const { crossType, innerIndex } = oddsOption;
|
|
|
|
|
|
|
+ const { crossType, innerIndex, rebateA: A = 0, rebateB: B = 0, rebateC: C = 0 } = oddsOption;
|
|
|
/**
|
|
/**
|
|
|
* crossType:
|
|
* crossType:
|
|
|
* la: 全输
|
|
* la: 全输
|
|
@@ -120,67 +125,67 @@ const triangleProfitCalc = (goldsInfo, oddsOption) => {
|
|
|
inner_rebate = z * innerRebateRatio;
|
|
inner_rebate = z * innerRebateRatio;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let win_side_a = 0, win_side_b = 0, win_side_m = 0;
|
|
|
|
|
- win_side_a = a * x - y - z;
|
|
|
|
|
- win_side_b = b * y - x - z;
|
|
|
|
|
|
|
+ let win_side_a = 0, win_side_b = 0, win_side_c = 0;
|
|
|
|
|
+ win_side_a = a*x - y - z + a*A*x + B*y + C*z;
|
|
|
|
|
+ win_side_b = b*y - x - z + b*B*y + A*x + C*z;
|
|
|
|
|
|
|
|
switch (crossType) {
|
|
switch (crossType) {
|
|
|
case 'la_wh_wa': // 全输 半赢 全赢
|
|
case 'la_wh_wa': // 全输 半赢 全赢
|
|
|
- win_side_m = c*z - x + b*y/2;
|
|
|
|
|
|
|
+ win_side_c = c*z - x + b*y/2 + c*C*z + A*x + b*B*y/2;
|
|
|
break;
|
|
break;
|
|
|
case 'la_dr_wa': // 全输 和局 全赢
|
|
case 'la_dr_wa': // 全输 和局 全赢
|
|
|
- win_side_m = c*z - x;
|
|
|
|
|
|
|
+ win_side_c = c*z - x + c*C*z + A*x;
|
|
|
break;
|
|
break;
|
|
|
case 'la_lh_wa': // 全输 半输 全赢
|
|
case 'la_lh_wa': // 全输 半输 全赢
|
|
|
- win_side_m = c*z - x - y/2;
|
|
|
|
|
|
|
+ win_side_c = c*z - x - y/2 + c*C*z + A*x + B*y/2;
|
|
|
break;
|
|
break;
|
|
|
case 'lh_dr_wa': // 半输 和局 全赢
|
|
case 'lh_dr_wa': // 半输 和局 全赢
|
|
|
- win_side_m = c*z - x/2;
|
|
|
|
|
|
|
+ win_side_c = c*z - x/2 + c*C*z + A*x/2;
|
|
|
break;
|
|
break;
|
|
|
case 'lh_lh_wa': // 半输 半输 全赢
|
|
case 'lh_lh_wa': // 半输 半输 全赢
|
|
|
- win_side_m = c*z - x/2 - y/2;
|
|
|
|
|
|
|
+ win_side_c = c*z - x/2 - y/2 + c*C*z + A*x/2 + B*y/2;
|
|
|
break;
|
|
break;
|
|
|
case 'la_la_wa': // 全输 全输 全赢
|
|
case 'la_la_wa': // 全输 全输 全赢
|
|
|
- win_side_m = c*z - x - y;
|
|
|
|
|
|
|
+ win_side_c = c*z - x - y + c*C*z + A*x + B*y;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
win_side_a = fixFloat(win_side_a + inner_rebate);
|
|
win_side_a = fixFloat(win_side_a + inner_rebate);
|
|
|
win_side_b = fixFloat(win_side_b + inner_rebate);
|
|
win_side_b = fixFloat(win_side_b + inner_rebate);
|
|
|
- win_side_m = fixFloat(win_side_m + inner_rebate);
|
|
|
|
|
- const win_average = fixFloat((win_side_a + win_side_b + win_side_m) / 3);
|
|
|
|
|
|
|
+ win_side_c = fixFloat(win_side_c + inner_rebate);
|
|
|
|
|
+ const win_average = fixFloat((win_side_a + win_side_b + win_side_c) / 3);
|
|
|
|
|
|
|
|
- return { win_side_a, win_side_b, win_side_m, win_average }
|
|
|
|
|
|
|
+ return { win_side_a, win_side_b, win_side_c, win_average }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const triangleGoldCalc = (oddsInfo, oddsOption) => {
|
|
const triangleGoldCalc = (oddsInfo, oddsOption) => {
|
|
|
const { innerDefaultAmount } = SETTING;
|
|
const { innerDefaultAmount } = SETTING;
|
|
|
- const { odds_side_a: a, odds_side_b: b, odds_side_m: c } = oddsInfo;
|
|
|
|
|
|
|
+ const { odds_side_a: a, odds_side_b: b, odds_side_c: c } = oddsInfo;
|
|
|
if (!a || !b || !c) {
|
|
if (!a || !b || !c) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const { crossType, innerIndex } = oddsOption;
|
|
|
|
|
|
|
+ const { crossType, innerIndex, rebateA: A = 0, rebateB: B = 0, rebateC: C = 0 } = oddsOption;
|
|
|
let x = innerDefaultAmount;
|
|
let x = innerDefaultAmount;
|
|
|
- let y = (a + 1) * x / (b + 1);
|
|
|
|
|
|
|
+ let y = (a + a*A + 1 - A) * x / (b + b*B + 1 - B);
|
|
|
let z;
|
|
let z;
|
|
|
switch (crossType) {
|
|
switch (crossType) {
|
|
|
case 'la_wh_wa': // 全输 半赢 全赢
|
|
case 'la_wh_wa': // 全输 半赢 全赢
|
|
|
- z = b * y / 2 / (c + 1);
|
|
|
|
|
|
|
+ z = (b + b*B) * y / 2 / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
case 'la_dr_wa': // 全输 和局 全赢
|
|
case 'la_dr_wa': // 全输 和局 全赢
|
|
|
- z = b * y / (c + 1);
|
|
|
|
|
|
|
+ z = (b + b*B) * y / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
case 'la_lh_wa': // 全输 半输 全赢
|
|
case 'la_lh_wa': // 全输 半输 全赢
|
|
|
- z = (2 * b + 1) * y / 2 / (c + 1);
|
|
|
|
|
|
|
+ z = (2*b + 2*b*B + 1 - B) * y / 2 / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
case 'lh_dr_wa': // 半输 和局 全赢
|
|
case 'lh_dr_wa': // 半输 和局 全赢
|
|
|
- z = (b * y - x / 2) / (c + 1);
|
|
|
|
|
|
|
+ z = ((b + b*B) * y - (1 - A) * x / 2) / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
case 'lh_lh_wa': // 半输 半输 全赢
|
|
case 'lh_lh_wa': // 半输 半输 全赢
|
|
|
- z = (b * y + y / 2 - x / 2) / (c + 1);
|
|
|
|
|
|
|
+ z = ((2*b + 2*b*B + 1 - B) * y / 2 + (1 - A) * x / 2) / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
case 'la_la_wa': // 全输 全输 全赢
|
|
case 'la_la_wa': // 全输 全输 全赢
|
|
|
- z = (b + 1) * y / (c + 1);
|
|
|
|
|
|
|
+ z = (b + b*B + 1 - B) * y / (c + c*C + 1 - C);
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|
|
|
z = 0;
|
|
z = 0;
|
|
@@ -202,10 +207,10 @@ const triangleGoldCalc = (oddsInfo, oddsOption) => {
|
|
|
return {
|
|
return {
|
|
|
gold_side_a: x,
|
|
gold_side_a: x,
|
|
|
gold_side_b: fixFloat(y),
|
|
gold_side_b: fixFloat(y),
|
|
|
- gold_side_m: fixFloat(z),
|
|
|
|
|
|
|
+ gold_side_c: fixFloat(z),
|
|
|
odds_side_a: a,
|
|
odds_side_a: a,
|
|
|
odds_side_b: b,
|
|
odds_side_b: b,
|
|
|
- odds_side_m: c,
|
|
|
|
|
|
|
+ odds_side_c: c,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -218,12 +223,12 @@ const eventSolutions = (oddsInfo, oddsOption) => {
|
|
|
}
|
|
}
|
|
|
const profitInfo = triangleProfitCalc(goldsInfo, oddsOption);
|
|
const profitInfo = triangleProfitCalc(goldsInfo, oddsOption);
|
|
|
|
|
|
|
|
- const { odds_side_a, odds_side_b, odds_side_m } = goldsInfo;
|
|
|
|
|
|
|
+ const { odds_side_a, odds_side_b, odds_side_c } = goldsInfo;
|
|
|
const { win_average } = profitInfo;
|
|
const { win_average } = profitInfo;
|
|
|
return {
|
|
return {
|
|
|
odds_side_a,
|
|
odds_side_a,
|
|
|
odds_side_b,
|
|
odds_side_b,
|
|
|
- odds_side_m,
|
|
|
|
|
|
|
+ odds_side_c,
|
|
|
win_average,
|
|
win_average,
|
|
|
cross_type: oddsOption.crossType,
|
|
cross_type: oddsOption.crossType,
|
|
|
inner_index: oddsOption.innerIndex,
|
|
inner_index: oddsOption.innerIndex,
|
|
@@ -231,6 +236,35 @@ const eventSolutions = (oddsInfo, oddsOption) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 将数组中的指定索引的元素移动到最前面
|
|
|
|
|
+*/
|
|
|
|
|
+const moveToFront = (arr, index) => {
|
|
|
|
|
+ if (index < 0 || index >= arr.length) return arr; // index 越界处理
|
|
|
|
|
+ const item = arr.splice(index, 1)[0]; // 删除该项并获取
|
|
|
|
|
+ arr.unshift(item); // 插入到最前面
|
|
|
|
|
+ return arr;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 添加返佣
|
|
|
|
|
+ */
|
|
|
|
|
+const attachRebate = (ior) => {
|
|
|
|
|
+ const { innerRebateRatio, obRebateRatio, hgRebateRatio } = SETTING;
|
|
|
|
|
+ const { p } = ior;
|
|
|
|
|
+ let rebate = 0;
|
|
|
|
|
+ if (p == 'ps') {
|
|
|
|
|
+ rebate = innerRebateRatio;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (p == 'ob') {
|
|
|
|
|
+ rebate = obRebateRatio;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (p == 'hg') {
|
|
|
|
|
+ rebate = hgRebateRatio;
|
|
|
|
|
+ }
|
|
|
|
|
+ return { ...ior, r: rebate };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const eventsCombination = (passableEvents, setting) => {
|
|
const eventsCombination = (passableEvents, setting) => {
|
|
|
|
|
|
|
|
Object.keys(setting).forEach(key => {
|
|
Object.keys(setting).forEach(key => {
|
|
@@ -240,42 +274,60 @@ const eventsCombination = (passableEvents, setting) => {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- const solutions = [];
|
|
|
|
|
|
|
+ const solutions = {};
|
|
|
passableEvents.forEach(events => {
|
|
passableEvents.forEach(events => {
|
|
|
const { odds, info } = events;
|
|
const { odds, info } = events;
|
|
|
- Object.keys(IOR_KEYS_MAP).forEach(group => {
|
|
|
|
|
- const rules = IOR_KEYS_MAP[group];
|
|
|
|
|
|
|
+ Object.keys(IOR_KEYS_MAP).forEach(iorGroup => {
|
|
|
|
|
+ const rules = IOR_KEYS_MAP[iorGroup];
|
|
|
const optimalSelections = getOptimalSelections(odds, rules);
|
|
const optimalSelections = getOptimalSelections(odds, rules);
|
|
|
|
|
+
|
|
|
optimalSelections.forEach(selection => {
|
|
optimalSelections.forEach(selection => {
|
|
|
const { rule, iors, index } = selection;
|
|
const { rule, iors, index } = selection;
|
|
|
const [, , , crossType] = rule;
|
|
const [, , , crossType] = rule;
|
|
|
-
|
|
|
|
|
- const oddsSideA = iors[0];
|
|
|
|
|
- const oddsSideB = iors[1];
|
|
|
|
|
- const oddsSideM = iors[2];
|
|
|
|
|
|
|
+ const oddsSideA = attachRebate(iors[0]);
|
|
|
|
|
+ const oddsSideB = attachRebate(iors[1]);
|
|
|
|
|
+ const oddsSideC = attachRebate(iors[2]);
|
|
|
const innerIndex = iors.findIndex(item => item.p == 'ps');
|
|
const innerIndex = iors.findIndex(item => item.p == 'ps');
|
|
|
- if (!oddsSideA || !oddsSideB || !oddsSideM) {
|
|
|
|
|
|
|
+ if (!oddsSideA || !oddsSideB || !oddsSideC) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- const cpr = [ oddsSideA, oddsSideB, oddsSideM ];
|
|
|
|
|
|
|
+ const cpr = [ oddsSideA, oddsSideB, oddsSideC ];
|
|
|
const oddsInfo = {
|
|
const oddsInfo = {
|
|
|
odds_side_a: fixFloat(oddsSideA.v - 1),
|
|
odds_side_a: fixFloat(oddsSideA.v - 1),
|
|
|
odds_side_b: fixFloat(oddsSideB.v - 1),
|
|
odds_side_b: fixFloat(oddsSideB.v - 1),
|
|
|
- odds_side_m: fixFloat(oddsSideM.v - 1)
|
|
|
|
|
|
|
+ odds_side_c: fixFloat(oddsSideC.v - 1),
|
|
|
|
|
+ };
|
|
|
|
|
+ const oddsOption = {
|
|
|
|
|
+ crossType, innerIndex,
|
|
|
|
|
+ rebateA: parseFloat((oddsSideA.r / 100).toFixed(4)),
|
|
|
|
|
+ rebateB: parseFloat((oddsSideB.r / 100).toFixed(4)),
|
|
|
|
|
+ rebateC: parseFloat((oddsSideC.r / 100).toFixed(4)),
|
|
|
};
|
|
};
|
|
|
- const oddsOption = { crossType, innerIndex };
|
|
|
|
|
const sol = eventSolutions(oddsInfo, oddsOption);
|
|
const sol = eventSolutions(oddsInfo, oddsOption);
|
|
|
if (sol?.win_average > SETTING.minProfitAmount) {
|
|
if (sol?.win_average > SETTING.minProfitAmount) {
|
|
|
const id = info.id;
|
|
const id = info.id;
|
|
|
- const keys = cpr.map(item => `${item.k}`).join('_');
|
|
|
|
|
|
|
+ const sortedCpr = moveToFront([...cpr], innerIndex);
|
|
|
|
|
+ const crpGroup = `${id}_${sortedCpr[0].k}`;
|
|
|
|
|
+ const keys = sortedCpr.map(item => `${item.k}`).join('_');
|
|
|
const sid = `${id}_${keys}`;
|
|
const sid = `${id}_${keys}`;
|
|
|
const timestamp = Date.now();
|
|
const timestamp = Date.now();
|
|
|
- solutions.push({sid, sol, cpr, info, rule: `${group}:${index}`, timestamp});
|
|
|
|
|
|
|
+ if (!solutions[crpGroup]) {
|
|
|
|
|
+ solutions[crpGroup] = [];
|
|
|
|
|
+ }
|
|
|
|
|
+ solutions[crpGroup].push({sid, sol, cpr, info, rule: `${iorGroup}:${index}`, timestamp});
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
- return solutions;
|
|
|
|
|
|
|
+ return Object.values(solutions).map(item => {
|
|
|
|
|
+ return item.sort((a, b) => {
|
|
|
|
|
+ return b.sol.win_average - a.sol.win_average;
|
|
|
|
|
+ })
|
|
|
|
|
+ .slice(0, 2);
|
|
|
|
|
+ })
|
|
|
|
|
+ .sort((a, b) => {
|
|
|
|
|
+ return b[0].sol.win_average - a[0].sol.win_average;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
module.exports = { eventsCombination };
|
|
module.exports = { eventsCombination };
|