flyzto 3 тижнів тому
батько
коміт
a38271ce32

+ 219 - 56
server/models/GamesPs.js

@@ -69,6 +69,160 @@ const fixFloat = (number, x=2) => {
   return parseFloat(number.toFixed(x));
 }
 
+/**
+ * 盘口排序
+ */
+const IOR_ORDER = {
+  'm': 0,
+  '0': 1,
+  '-': 2,
+  '+': 3,
+  'ou': 4,
+  'wm': 5,
+  'ot': 6,
+}
+
+/**
+ * 获取盘口前缀
+ * @param {string} ior
+ * @returns {string}
+ */
+const getIorPrefix = (ior) => {
+  const iorParts = ior.split('_');
+  if (iorParts.length === 1) {
+    return iorParts[0][0];
+  }
+  return iorParts[0];
+}
+
+
+/**
+ * 解析盘口比例
+ * @param {string} ratioString
+ * @returns {number}
+ */
+const parseRatio = (ratioString) => {
+  return parseFloat(`${ratioString[0]}.${ratioString.slice(1)}`);
+}
+
+/**
+ * 解析盘口键
+ * @param {string} iorKey
+ * @returns {object}
+ */
+const parseIorKey = (iorKey) => {
+  const iorKeyMatch = iorKey.match(/^ior_(r|ou|m|wm|ot|os)(a?)(h|c|n)?(_([\d-]+))?$/);
+  if (!iorKeyMatch) {
+    console.log('no iorKeyMatch', iorKey);
+    return null;
+  }
+  const [, type, accept, side, , ratioString] = iorKeyMatch;
+  let ratio = 0;
+  if (type === 'ot' || type === 'os') {
+    ratio = ratioString;
+  }
+  else if (ratioString) {
+    ratio = parseRatio(ratioString) * (accept ? 1 : -1);
+  }
+  return { type, side, ratio };
+}
+
+/**
+ * 格式化盘口数据
+ * @param {object} events
+ * @returns {array}
+ */
+const formatEvents = (events) => {
+  const eventsMap = {};
+  Object.keys(events).forEach(key => {
+    const { type, side, ratio } = parseIorKey(key) ?? {};
+    if (!type) {
+      return;
+    }
+    let ratioKey, index;
+    if (type === 'r') {
+      if (side === 'h') {
+        ratioKey = ratio;
+        index = 0;
+      }
+      else if (side === 'c') {
+        ratioKey = -ratio;
+        index = 1;
+      }
+    }
+    else if (type === 'm') {
+      ratioKey = 'm';
+      if (side == 'h') {
+        index = 0;
+      }
+      else if (side == 'c') {
+        index = 2;
+      }
+      else {
+        index = 1;
+      }
+    }
+    else if (type === 'wm') {
+      ratioKey = `wm_${Math.abs(ratio)}`;
+      if (side === 'h') {
+        index = 0;
+      }
+      else if (side === 'c') {
+        index = 1;
+      }
+    }
+    else if (type === 'ou') {
+      ratioKey = `ou_${Math.abs(ratio)}`;
+      if (side === 'c') {
+        index = 0;
+      }
+      else if (side === 'h') {
+        index = 1;
+      }
+    }
+    else if (type === 'ot') {
+      ratioKey = type;
+    }
+    if (typeof (ratioKey) == 'number') {
+      if (ratioKey > 0) {
+        ratioKey = `+${ratioKey}`;
+      }
+      else {
+        ratioKey = `${ratioKey}`;
+      }
+    }
+
+    if (!ratioKey) {
+      return;
+    }
+
+    const value = events[key]?.v ?? 0;
+    const origin = events[key]?.r;
+    const source = events[key]?.s;
+
+    if (!eventsMap[ratioKey]) {
+      eventsMap[ratioKey] = new Array();
+    }
+    if (ratioKey == 'ot') {
+      eventsMap[ratioKey].push({ key, value, origin, source });
+    }
+    else {
+      eventsMap[ratioKey][index] = { key, value, origin, source };
+    }
+  });
+
+  return Object.keys(eventsMap).sort((a, b) => {
+    const aPrefix = getIorPrefix(a);
+    const bPrefix = getIorPrefix(b);
+    if (aPrefix !== bPrefix) {
+      return IOR_ORDER[aPrefix] - IOR_ORDER[bPrefix];
+    }
+    return a.localeCompare(b);
+  }).map(key => {
+    return [key, ...eventsMap[key]];
+  });
+}
+
 /**
  * 获取市场类型
  */
@@ -317,21 +471,27 @@ const syncBaseEvents = ({ mk, games, outrights }) => {
         if (isRb && (ior.startsWith('ior_r') || ior.startsWith('ior_m') || ior.startsWith('ior_wm')) && subsidyRbWmAmount) {
           const sourceOdds = events[ior].v;
           events[ior].v = fixFloat(sourceOdds * (1 + subsidyRbWmAmount), 3);
-          events[ior].s = sourceOdds
+          events[ior].s = sourceOdds;
         }
         // 滚球进球数补水
         // API采集特殊盘口用
         else if (isRb && ior.startsWith('ior_ot') && subsidyRbOtAmount) {
           const sourceOdds = events[ior].v;
           events[ior].v = fixFloat(sourceOdds * (1 + subsidyRbOtAmount), 3);
-          events[ior].s = sourceOdds
+          events[ior].s = sourceOdds;
         }
         // 赛前进球数补水
         // API采集特殊盘口用
         else if (!isRb && isSubsidy && ior.startsWith('ior_ot') && subsidyAmount) {
           const sourceOdds = events[ior].v;
           events[ior].v = fixFloat(sourceOdds * (1 + subsidyAmount), 3);
-          events[ior].s = sourceOdds
+          events[ior].s = sourceOdds;
+        }
+        else if (ior.startsWith('ior_r') || ior.startsWith('ior_ou')) {
+          events[ior].q = events[ior]?.v < innerOuMinValue ? 0 : 1;
+          // const sourceOdds = events[ior].v;
+          // events[ior].v = 0;
+          // events[ior].s = sourceOdds;
         }
       });
       baseGame.originId = originId;
@@ -387,48 +547,49 @@ const syncBaseEvents = ({ mk, games, outrights }) => {
 
   if (games?.length) {
     const gamesList = baseList[marketType]?.map(game => {
-      const { evtime, events, sptime, special, ...gameInfo } = game;
+      const { evtime, events, ...gameInfo } = game;
       const expireTimeEv = nowTime - expireTimeEvents;
-      const expireTimeSP = nowTime - expireTimeSpecial;
-      let odds = {};
+      // const expireTimeSP = nowTime - expireTimeSpecial;
+      const odds = {};
       if (evtime > expireTimeEv) {
-        odds = { ...odds, ...events };
-      }
-      if (sptime > expireTimeSP) {
-        odds = { ...odds, ...special };
+        Object.assign(odds, events);
       }
-      const matches = PS_IOR_KEYS.map(([label, ...keys]) => {
-        let match = keys.map(key => {
-          let value = odds[key]?.v ?? 0;
-          if (key.startsWith('ior_ou') && value < innerOuMinValue) {
-            value = 0;
-          }
-          return {
-            key, value,
-            origin: odds[key]?.r,
-            source: odds[key]?.s,
-          }
-        });
-        if (label == 'jqs' || label == 'ou') {
-          match = match.filter(item => item.value !== 0);
-        }
-        return {
-          label,
-          match
-        };
-      }).filter(item => {
-        if (item.label == 'jqs' || item.label == 'ou') {
-          return item.match.length;
-        }
-        else {
-          return item.match.every(entry => {
-            // if (entry.key == '-' || entry.key.startsWith('ior_ou')) {
-            //   return true;
-            // }
-            return entry.value !== 0;
-          });
-        }
-      });
+      // if (sptime > expireTimeSP) {
+      //   odds = { ...odds, ...special };
+      // }
+      const matches = formatEvents(odds);
+      // const matches = PS_IOR_KEYS.map(([label, ...keys]) => {
+      //   let match = keys.map(key => {
+      //     let value = odds[key]?.v ?? 0;
+      //     if (key.startsWith('ior_ou') && value < innerOuMinValue) {
+      //       value = 0;
+      //     }
+      //     return {
+      //       key, value,
+      //       origin: odds[key]?.r,
+      //       source: odds[key]?.s,
+      //     }
+      //   });
+      //   if (label == 'jqs' || label == 'ou') {
+      //     match = match.filter(item => item.value !== 0);
+      //   }
+      //   return {
+      //     label,
+      //     match
+      //   };
+      // }).filter(item => {
+      //   if (item.label == 'jqs' || item.label == 'ou') {
+      //     return item.match.length;
+      //   }
+      //   else {
+      //     return item.match.every(entry => {
+      //       // if (entry.key == '-' || entry.key.startsWith('ior_ou')) {
+      //       //   return true;
+      //       // }
+      //       return entry.value !== 0;
+      //     });
+      //   }
+      // });
 
       game.matches = matches; // matches 也记录下来
 
@@ -436,6 +597,8 @@ const syncBaseEvents = ({ mk, games, outrights }) => {
       return { ...gameInfo, matches, uptime };
     });
 
+    // console.log('gamesList', gamesList);
+
     // if (gamesList.filter(item => item.uptime > 0).length) {
     //   // if (mk == 2) {
     //   //   Logs.out('syncBaseEvents', JSON.stringify({ mk, gamesList }, null, 2));
@@ -452,21 +615,21 @@ const syncBaseEvents = ({ mk, games, outrights }) => {
     let update = 0;
     const relatedMap = new Map(relatedGames.map(item => [item.eventId, item]));
 
-    gamesList?.forEach(game => {
-      const { eventId, matches, uptime, stage, retime, score, wm } = game;
+    baseList[marketType]?.forEach(game => {
+      const { eventId, events, evtime, stage, retime, score, wm } = game;
       const relatedGame = relatedMap.get(eventId);
       if (relatedGame) {
-        const events = {};
-        matches.forEach(({ label, match }) => {
-          match.forEach(({ key, value, origin, source }) => {
-            events[key] = {
-              v: value,
-              r: origin,
-              s: source
-            };
-          });
-        });
-        relatedGame.evtime = uptime;
+        // const events = {};
+        // matches.forEach(({ label, match }) => {
+        //   match.forEach(({ key, value, origin, source }) => {
+        //     events[key] = {
+        //       v: value,
+        //       r: origin,
+        //       s: source
+        //     };
+        //   });
+        // });
+        relatedGame.evtime = evtime;
         relatedGame.events = events;
         relatedGame.stage = stage;
         relatedGame.retime = retime;
@@ -1278,7 +1441,7 @@ setInterval(() => {
 const getTotalProfit = async (sol1, sol2, inner_base, inner_rebate) => {
   const { innerDefaultAmount, innerRebateRatio } = getSetting();
   inner_base = inner_base ? +inner_base : innerDefaultAmount;
-  inner_rebate = inner_rebate ? +inner_rebate : fixFloat(innerRebateRatio / 100, 3);
+  // inner_rebate = inner_rebate ? +inner_rebate : fixFloat(innerRebateRatio / 100, 3);
   const profit = calcTotalProfit(sol1, sol2, inner_base, inner_rebate);
   return profit;
 }

+ 0 - 2
server/routes/pstery.js

@@ -200,7 +200,6 @@ router.post('/calc_total_profit', (req, res) => {
     inner_base = data.inner_base;
     inner_rebate = data.inner_rebate;
   }
-  // const [sid1, sid2, inner_base, inner_rebate] = req.body;
   Games.getTotalProfitWithSid(sid1, sid2, inner_base, inner_rebate)
   .then(totalProfit => {
     res.sendSuccess(totalProfit);
@@ -214,7 +213,6 @@ router.post('/calc_total_profit', (req, res) => {
  * 计算自定义综合利润方案
  */
 router.post('/calc_custom_total_profit', (req, res) => {
-  // const [bet_info_1, bet_info_2, fixed, inner_base, inner_rebate] = req.body;
   const data = req.body;
   let bet_info_1, bet_info_2, fixed, inner_base, inner_rebate;
   if (Array.isArray(data)) {

+ 15 - 17
server/triangle/eventSolutions.js

@@ -16,8 +16,6 @@ const fixFloat = (number, x=2) => {
 const triangleProfitCalc = (betInfo) => {
   const {
     cross_type,
-    inner_index,
-    inner_rebate,
     gold_side_a: x,
     gold_side_b: y,
     gold_side_c: z,
@@ -42,16 +40,16 @@ const triangleProfitCalc = (betInfo) => {
    *  la_wh_wa, la_dr_wa, la_lh_wa, lh_dr_wa, lh_lh_wa, la_la_wa
    */
 
-  let inner_rebate_value = 0;
-  if (inner_index == 0) {
-    inner_rebate_value = x * inner_rebate;
-  }
-  else if (inner_index == 1) {
-    inner_rebate_value = y * inner_rebate;
-  }
-  else if (inner_index == 2) {
-    inner_rebate_value = z * inner_rebate;
-  }
+  // let inner_rebate_value = 0;
+  // if (inner_index == 0) {
+  //   inner_rebate_value = x * inner_rebate;
+  // }
+  // else if (inner_index == 1) {
+  //   inner_rebate_value = y * inner_rebate;
+  // }
+  // else if (inner_index == 2) {
+  //   inner_rebate_value = z * inner_rebate;
+  // }
 
   const k1 = TA == 1 ? a + A : a * (1 + A);
   const k2 = 1 - A;
@@ -90,9 +88,9 @@ const triangleProfitCalc = (betInfo) => {
       win_side_c = 0;
   }
 
-  win_side_a = fixFloat(win_side_a + inner_rebate_value);
-  win_side_b = fixFloat(win_side_b + inner_rebate_value);
-  win_side_c = fixFloat(win_side_c + inner_rebate_value);
+  win_side_a = fixFloat(win_side_a);
+  win_side_b = fixFloat(win_side_b);
+  win_side_c = fixFloat(win_side_c);
   let win_average = 0;
   if (cross_type == 'la_wa_rv') {
     win_side_c = 0;
@@ -192,7 +190,7 @@ const eventSolutions = (betInfo, showGolds=false) => {
   const { win_side_a, win_side_b, win_side_c, win_average } = profitInfo;
 
   const {
-    cross_type, inner_index, inner_rebate, inner_base,
+    cross_type, inner_index, inner_base,
     odds_side_a, odds_side_b, odds_side_c,
     rebate_side_a, rebate_side_b, rebate_side_c,
     rebate_type_side_a, rebate_type_side_b, rebate_type_side_c,
@@ -206,7 +204,7 @@ const eventSolutions = (betInfo, showGolds=false) => {
     rebate_side_a, rebate_side_b, rebate_side_c,
     rebate_type_side_a, rebate_type_side_b, rebate_type_side_c,
     win_average, win_average_rate, win_profit_rate, cross_type,
-    inner_index, inner_base, inner_rebate,
+    inner_index, inner_base,
   }
 
   if (showGolds) {

+ 114 - 64
server/triangle/totalProfitCalc.js

@@ -12,56 +12,82 @@ const fixFloat = (number, x = 2) => {
 }
 
 /**
- * 计算输赢比例
- * 与其他方法中的输赢逻辑相反
- * 正数为输
- * 负数为赢
+ * 获取内盘位置
  */
-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,
-    rebate_type_side_a, rebate_type_side_b,
-  } = sol;
-  const typeList = cross_type.split('_').map(part => {
+const getBetSide = (bet_index) => {
+  switch (bet_index) {
+    case 0:
+      return 'side_a';
+    case 1:
+      return 'side_b';
+    case 2:
+      return 'side_c';
+  }
+  return undefined;
+}
+
+/**
+ * 盘口输赢映射
+ */
+const CROSS_TYPE_MAP = { w: 1, l: -1, a: 1, h: 0.5, d: 0, r: 0, v: 0 };
+const getCrossTypeList = (cross_type) => {
+  return 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 {
-    if (rebate_type_side_a == 1) {
-      loss_proportion_a = typeList[0] * (odds_side_a + 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);
+/**
+ * 计算输赢比例
+ */
+const getWinProportion = (sol, bet_index) => {
+  const typeList = getCrossTypeList(sol.cross_type);
+  const betSide = getBetSide(bet_index);
+  let win_proportion = 0;
+  if (typeList[bet_index] <= 0) {
+    // 输或平
+    win_proportion = typeList[bet_index] * (1 - sol[`rebate_${betSide}`]);
   }
   else {
-    if (rebate_type_side_b == 1) {
-      loss_proportion_b = typeList[1] * (odds_side_b + rebate_side_b);
+    // 赢
+    if (sol[`rebate_type_${betSide}`] == 1) {
+      // 按本金返水
+      win_proportion = typeList[bet_index] * (sol[`odds_${betSide}`] + sol[`rebate_${betSide}`]);
     }
     else {
-      loss_proportion_b = typeList[1] * odds_side_b * (1 + rebate_side_b);
+      // 按结算返水
+      win_proportion = typeList[bet_index] * sol[`odds_${betSide}`] * (1 + sol[`rebate_${betSide}`]);
     }
   }
+  return win_proportion;
+}
 
+/**
+ * 计算盘口输的比例
+ */
+const lossProportion = (sol) => {
+  const loss_proportion_a = getWinProportion(sol, 0) * -1;
+  const loss_proportion_b = getWinProportion(sol, 1) * -1;
   return { loss_proportion_a, loss_proportion_b };
 }
 
+/**
+ * 外盘特殊盘赢时的内盘剩余额度
+ */
+const getInnerWinGoldsWithSpecialWin = (sol) => {
+  const { inner_base, inner_index } = sol;
+  const win_proportion = getWinProportion(sol, inner_index);
+  const inner_win_golds = win_proportion * inner_base;
+  return fixFloat(inner_base + inner_win_golds);
+}
+
 /**
  * 不同组合的金额计算
  */
 const HandicapCalc = function (data) {
-  const { i, g, a, b, c, A, B, C, TA, TB, TC, w, l } = data;
+  // console.log('HandicapCalc', data);
+  const { i, g, r, a, b, c, A, B, C, TA, TB, TC, w, l } = data;
   const t = w + l;
+  const k0 = 1 - r;
   const k1 = TA == 1 ? a + A : a * (1 + A);
   const k2 = 1 - A;
   const k3 = TB == 1 ? b + B : b * (1 + B);
@@ -76,6 +102,7 @@ const HandicapCalc = function (data) {
     if (i === 2) {
       const z = g;
       const m = t + k6 * z;
+      // const m = t + k0 * z;
       const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
       const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
       return { x, y, z };
@@ -89,6 +116,7 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const m = t + k2 * x;
+          // const m = t + k0 * x;
           const z = m / (2 * k5 + k6);
           const y = (k5 + k6) * m / (k3 * k5 + k3 * k6 / 2);
           return { x, y, z };
@@ -97,6 +125,8 @@ const HandicapCalc = function (data) {
           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);
+          // const z = ((k1 + k2) * t + (k0 * k2 - k1 * k3 / 2) * y) / (k1 * k5 - k2 * k6);
+          // const x = ((k5 + k6) * t + (k0 * k5 - k3 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
           return { x, y, z };
         }
       ]);
@@ -106,6 +136,7 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const m = t + k2 * x;
+          // const m = t + k0 * x;
           const z = m / k5;
           const y = (k5 + k6) * m / (k3 * k5);
           return { x, y, z };
@@ -114,6 +145,8 @@ const HandicapCalc = function (data) {
           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);
+          // const z = ((k1 + k2) * t + k0 * k2 * y) / (k1 * k5 - k2 * k6);
+          // const x = ((k5 + k6) * t + k0 * k5 * y) / (k1 * k5 - k2 * k6);
           return { x, y, z };
         }
       ]);
@@ -123,6 +156,7 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const m = t + k2 * x;
+          // const m = t + k0 * 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 };
@@ -131,6 +165,8 @@ const HandicapCalc = function (data) {
           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);
+          // const z = ((k1 + k2) * t + (k0 * k2 + k1 * k4 / 2) * y) / (k1 * k5 - k2 * k6);
+          // const x = ((k5 + k6) * t + (k0 * k5 + k4 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
           return { x, y, z };
         }
       ]);
@@ -140,13 +176,16 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const z = (t + k2 * x / 2) / k5;
-          const y = ((k5 + k6) * t + (k2 * k5 + k2 * k4 / 2) * x) / (k3 * k5);
+          const y = ((k5 + k6) * t + (k5 + k6 / 2) * k2 * x) / (k3 * k5);
+          // const y = ((k5 + k6) * t + (k0 * k5 + k2 * k6 / 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);
+          // const z = ((2 * k1 + k2) * t + k0 * k2 * y) / (2 * k1 * k5 - k2 * k6);
+          // const x = ((k5 + k6) * t + k0 * k5 * y) / (k1 * k5 - k2 * k6 / 2);
           return { x, y, z };
         }
       ]);
@@ -157,12 +196,16 @@ const HandicapCalc = function (data) {
           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);
+          // const z = ((2 * k3 + k4) * t + (k0 * k4 + k2 * k3) * x) / (2 * k3 * k5 - k4 * k6);
+          // const y = ((k5 + k6) * t + (k0 * k5 + k2 * k6 / 2) * 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);
+          // const z = ((2 * k1 + k2) * t + (k0 * k2 + k1 * k4) * y) / (2 * k1 * k5 - k2 * k6);
+          // const x = ((k5 + k6) * t + (k0 * k5 + k4 * k6 / 2) * y) / (k1 * k5 - k2 * k6 / 2);
           return { x, y, z };
         }
       ]);
@@ -172,6 +215,7 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const m = t + k2 * x;
+          // const m = t + k0 * x;
           const z = (k3 + k4) * m / (k3 * k5 - k4 * k6);
           const y = (k5 + k6) * m / (k3 * k5 - k4 * k6);
           return { x, y, z };
@@ -179,6 +223,7 @@ const HandicapCalc = function (data) {
         () => {
           const y = g;
           const m = t + k4 * y;
+          // const m = t + k0 * y;
           const z = (k1 + k2) * m / (k1 * k5 - k2 * k6);
           const x = (k5 + k6) * m / (k1 * k5 - k2 * k6);
           return { x, y, z };
@@ -190,12 +235,14 @@ const HandicapCalc = function (data) {
         () => {
           const x = g;
           const y = (k2 * x + t) / k3;
+          // const y = (k0 * x + t) / k3;
           const z = 0;
           return { x, y, z };
         },
         () => {
           const y = g;
           const x = (k4 * y + t) / k1;
+          // const x = (k0 * y + t) / k1;
           const z = 0;
           return { x, y, z };
         }
@@ -204,20 +251,14 @@ const HandicapCalc = function (data) {
   }
 }
 
-/**
- * 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_index: i,
     inner_base: g,
+    inner_rebate: r,
     odds_side_a: a,
     odds_side_b: b,
     odds_side_c: c,
@@ -227,12 +268,11 @@ const calcGoldsWithTarget = (data) => {
     rebate_type_side_a: TA,
     rebate_type_side_b: TB,
     rebate_type_side_c: TC,
-    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, TA, TB, TC, w, l });
+  const calc = new HandicapCalc({ i, g, r, a, b, c, A, B, C, TA, TB, TC, w, l });
   const { x, y, z } = calc?.[t]() ?? {};
   return {
     gold_side_a: fixFloat(x),
@@ -259,30 +299,36 @@ const calcWinResultWithTarget = (data) => {
     rebate_type_side_c: rebateTypeC1,
     inner_index: inner_index_1,
   } = sol1;
+
+  const innerSide1 = getBetSide(inner_index_1);
+  const innerOdds1 = sol1[`odds_${innerSide1}`];
+  const innerRebateTotal = inner_rebate ?? sol1[`rebate_${innerSide1}`];
+  const innerRebateActual = fixFloat(innerRebateTotal / (innerOdds1 + 2), 3);
+
+  const firstSolData = { ...sol1, inner_base, inner_rebate: innerRebateTotal, win_target };
+  firstSolData[`rebate_${innerSide1}`] = innerRebateActual;
+
   const {
     gold_side_a: goldA1,
     gold_side_b: goldB1,
     gold_side_c: goldC1,
-  } = calcGoldsWithTarget({ ...sol1, inner_base, win_target });
+  } = calcGoldsWithTarget(firstSolData);
 
-  let loss_out_1 = 0, win_inner_1 = 0;
+  let loss_out_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);
+  loss_out_1 = fixFloat(loss_out_1);
 
   const {
     cross_type: crossType2,
@@ -297,39 +343,43 @@ const calcWinResultWithTarget = (data) => {
     rebate_type_side_c: rebateTypeC2,
     inner_index: inner_index_2,
   } = sol2;
+
+  const innerSide2 = getBetSide(inner_index_2);
+  const innerOdds2 = (sol2[`odds_${innerSide2}`] + 1) * (innerOdds1 + 1) - 1;
+  const secondSolData = { ...sol2, inner_base, inner_rebate: innerRebateTotal, win_target, loss_out: loss_out_1 };
+  secondSolData[`odds_${innerSide2}`] = innerOdds2;
+  secondSolData[`rebate_${innerSide2}`] = innerRebateActual;
+
   const {
     gold_side_a: goldA2,
     gold_side_b: goldB2,
     gold_side_c: goldC2,
-  } = calcGoldsWithTarget({ ...sol2, inner_base, win_target, loss_out: loss_out_1 });
+  } = calcGoldsWithTarget(secondSolData);
 
-  let loss_out_2 = 0, win_inner_2 = 0, inner_base_key;
+  const win_inner_2 = inner_base * (innerOdds2 + 1 + innerRebateTotal);
+  let loss_out_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);
+      loss_out_2 = goldB2 * (1 - rebateB2) + goldC2 * (1 - rebateC2) + loss_out_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);
+      loss_out_2 = goldA2 * (1 - rebateA2) + goldC2 * (1 - rebateC2) + loss_out_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);
+      loss_out_2 = goldA2 * lpA2 + goldB2 * lpB2 + loss_out_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;
-  }
+  // if (goldsInfo[inner_base_key]) {
+  //   goldsInfo[inner_base_key] = win_inner_1;
+  // }
 
   const result = {
     bet_info: [
@@ -378,9 +428,9 @@ const calcWinResultWithTarget = (data) => {
  * 根据单关盈亏计算综合利润
  */
 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 rebateInner = inner_base * inner_rebate;
+  const winTarget1 = fixFloat(sol1.win_average);
+  const winTarget2 = fixFloat(sol2.win_average);
   const winTarget = fixFloat(Math.min(winTarget1, winTarget2));
 
   const win1 = calcWinResultWithTarget({ inner_base, inner_rebate, win_target: winTarget1, sol1, sol2 })?.win_inner;
@@ -400,7 +450,7 @@ const calcTotalProfit = (sol1, sol2, inner_base, inner_rebate) => {
     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 });
+      result.push({ win_target: fixFloat(win_target), win_diff, win_inner: fixFloat(win_inner), ...goldsRest });
     }
     else {
       break;

+ 7 - 6
server/triangle/trangleCalc.js

@@ -39,6 +39,7 @@ const getOptimalSelections = (odds, rules) => {
             v: item.ps.v,
             r: item.ps.r,
             s: item.ps.s,
+            q: item.ps.q,
             o: item
           }]);
         }
@@ -109,12 +110,12 @@ const getPlatformKey = (cpr) => {
  * 添加返佣
  */
 const attachRebate = (ior) => {
-  const { obRebateRatio, obRebateType, hgRebateRatio, hgRebateLower, hgRebateType, pcRebateRatio, pcRebateType } = getSetting();
+  const { innerRebateRatio, obRebateRatio, obRebateType, hgRebateRatio, hgRebateLower, hgRebateType, pcRebateRatio, pcRebateType } = getSetting();
   const { p, k } = ior;
   let rebate = 0, rebateType = 0;
   if (p == 'ps') {
-    rebate = 0;
-    rebateType = -1;
+    rebate = innerRebateRatio;
+    rebateType = 1;
   }
   else if (p == 'pc') {
     rebate = pcRebateRatio;
@@ -224,8 +225,8 @@ const getPassableEvents = (relations, eventsLogsMap) => {
 /**
  * 盘口组合计算
  */
-const eventsCombination = (passableEvents, innerBase, innerRebate) => {
-  const { innerDefaultAmount, innerRebateRatio } = getSetting();
+const eventsCombination = (passableEvents, innerBase) => {
+  const { innerDefaultAmount } = getSetting();
   const solutions = [];
   passableEvents.forEach(events => {
     const { odds, info } = events;
@@ -247,7 +248,7 @@ const eventsCombination = (passableEvents, innerBase, innerRebate) => {
           cross_type: crossType,
           inner_index: innerIndex,
           inner_base: innerBase ?? innerDefaultAmount,
-          inner_rebate: innerRebate ?? fixFloat(innerRebateRatio / 100, 3),
+          // inner_rebate: innerRebate ?? 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),