totalProfitCalc.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. const fixFloat = (number, x = 2) => {
  2. return parseFloat(number.toFixed(x));
  3. }
  4. const HandicapCalc = function (data) {
  5. const { i, g, a, b, c, A, B, C, w, l } = data;
  6. const t = w + l;
  7. const k1 = a * (1 + A);
  8. const k2 = 1 - A;
  9. const k3 = b * (1 + B);
  10. const k4 = 1 - B;
  11. const k5 = c * (1 + C);
  12. const k6 = 1 - C;
  13. const calcTemplate = (handlers) => {
  14. if (i > 2 || i < 0) {
  15. return {};
  16. }
  17. if (i === 2) {
  18. const z = g;
  19. const m = t + k6 * z;
  20. const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
  21. const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
  22. return { x, y, z };
  23. };
  24. return handlers[i]?.() ?? {};
  25. };
  26. return {
  27. la_wh_wa() {
  28. return calcTemplate([
  29. () => {
  30. const x = g;
  31. const m = t + k2 * x;
  32. const z = m / (2 * k5 + k6);
  33. const y = (k5 + k6) * m / (k3 * k5 + k3 * k6 / 2);
  34. return { x, y, z };
  35. },
  36. () => {
  37. const y = g;
  38. const z = ((k1 + k2) * t + (k2 * k4 - k1 * k3 / 2) * y) / (k1 * k5 - k2 * k6);
  39. const x = ((k5 + k6) * t + (k4 * k5 - k3 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
  40. return { x, y, z };
  41. }
  42. ]);
  43. },
  44. la_dr_wa() {
  45. return calcTemplate([
  46. () => {
  47. const x = g;
  48. const m = t + k2 * x;
  49. const z = m / k5;
  50. const y = (k5 + k6) * m / (k3 * k5);
  51. return { x, y, z };
  52. },
  53. () => {
  54. const y = g;
  55. const z = ((k1 + k2) * t + k2 * k4 * y) / (k1 * k5 - k2 * k6);
  56. const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6);
  57. return { x, y, z };
  58. }
  59. ]);
  60. },
  61. la_lh_wa() {
  62. return calcTemplate([
  63. () => {
  64. const x = g;
  65. const m = t + k2 * x;
  66. const z = (2 * k3 + k4) * m / (2 * k3 * k5 - k4 * k6);
  67. const y = (k5 + k6) * m / (k3 * k5 - k4 * k6 / 2);
  68. return { x, y, z };
  69. },
  70. () => {
  71. const y = g;
  72. const z = ((k1 + k2) * t + (k2 * k4 + k1 * k4 / 2) * y) / (k1 * k5 - k2 * k6);
  73. const x = ((k5 + k6) * t + (k4 * k5 + k4 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
  74. return { x, y, z };
  75. }
  76. ]);
  77. },
  78. lh_dr_wa() {
  79. return calcTemplate([
  80. () => {
  81. const x = g;
  82. const z = (t + k2 * x / 2) / k5;
  83. const y = ((k5 + k6) * t + (k2 * k5 + k2 * k4 / 2) * x) / (k3 * k5);
  84. return { x, y, z };
  85. },
  86. () => {
  87. const y = g;
  88. const z = ((2 * k1 + k2) * t + k2 * k4 * y) / (2 * k1 * k5 - k2 * k6);
  89. const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6 / 2);
  90. return { x, y, z };
  91. }
  92. ]);
  93. },
  94. lh_lh_wa() {
  95. return calcTemplate([
  96. () => {
  97. const x = g;
  98. const z = ((2 * k3 + k4) * t + (k3 + k4) * k2 * x) / (2 * k3 * k5 - k4 * k6);
  99. const y = ((k5 + k6) * t + (k5 + k6 / 2) * k2 * x) / (k3 * k5 - k4 * k6 / 2);
  100. return { x, y, z };
  101. },
  102. () => {
  103. const y = g;
  104. const z = ((2 * k1 + k2) * t + (k1 + k2) * k4 * y) / (2 * k1 * k5 - k2 * k6);
  105. const x = ((k5 + k6) * t + (k5 + k6 / 2) * k4 * y) / (k1 * k5 - k2 * k6 / 2);
  106. return { x, y, z };
  107. }
  108. ]);
  109. },
  110. la_la_wa() {
  111. return calcTemplate([
  112. () => {
  113. const x = g;
  114. const m = t + k2 * x;
  115. const z = (k3 + k4) * m / (k3 * k5 - k4 * k6);
  116. const y = (k5 + k6) * m / (k3 * k5 - k4 * k6);
  117. return { x, y, z };
  118. },
  119. () => {
  120. const y = g;
  121. const m = t + k2 * y;
  122. const z = (k1 + k2) * m / (k1 * k5 - k2 * k6);
  123. const x = (k5 + k6) * m / (k1 * k5 - k2 * k6);
  124. return { x, y, z };
  125. }
  126. ]);
  127. }
  128. }
  129. }
  130. /**
  131. * 计算输赢比例
  132. * 与其他方法中的输赢逻辑相反
  133. * 正数为输
  134. * 负数为赢
  135. */
  136. const CROSS_TYPE_MAP = { w: -1, l: 1, a: 1, h: 0.5, d: 0, r: 0 };
  137. const lossProportion = (sol) => {
  138. const { cross_type, odds_side_a, odds_side_b, rebate_side_a, rebate_side_b } = sol;
  139. const typeList = cross_type.split('_').map(part => {
  140. return part.split('').map(key => CROSS_TYPE_MAP[key]);
  141. }).map(([a, b])=> a * b);
  142. let loss_proportion_a = 0, loss_proportion_b = 0;
  143. if (typeList[0] >= 0) {
  144. loss_proportion_a = typeList[0] * (1 - rebate_side_a);
  145. }
  146. else {
  147. loss_proportion_a = typeList[0] * odds_side_a * (1 + rebate_side_a);
  148. }
  149. if (typeList[1] >= 0) {
  150. loss_proportion_b = typeList[1] * (1 - rebate_side_b);
  151. }
  152. else {
  153. loss_proportion_b = typeList[1] * odds_side_b * (1 + rebate_side_b);
  154. }
  155. return { loss_proportion_a, loss_proportion_b };
  156. }
  157. const calcExternalHandicap = (data) => {
  158. const {
  159. gold_side_inner: g,
  160. odds_side_a: a,
  161. odds_side_b: b,
  162. odds_side_c: c,
  163. rebate_side_a: A,
  164. rebate_side_b: B,
  165. rebate_side_c: C,
  166. inner_index: i,
  167. cross_type: t,
  168. win_target: w,
  169. loss_out: l = 0,
  170. } = data;
  171. const calc = new HandicapCalc({ i, g, a, b, c, A, B, C, w, l });
  172. const { x, y, z } = calc?.[t]() ?? {};
  173. return {
  174. gold_side_a: fixFloat(x),
  175. gold_side_b: fixFloat(y),
  176. gold_side_c: fixFloat(z),
  177. }
  178. }
  179. const calcGoldsWithWinTarget = (data) => {
  180. const { gold_side_inner, win_target, sol1, sol2 } = data;
  181. const {
  182. odds_side_a: oddsA1,
  183. odds_side_b: oddsB1,
  184. odds_side_c: oddsC1,
  185. inner_index: inner_index_1,
  186. } = sol1;
  187. const {
  188. gold_side_a: goldA1,
  189. gold_side_b: goldB1,
  190. gold_side_c: goldC1,
  191. } = calcExternalHandicap({ ...sol1, gold_side_inner, win_target });
  192. let loss_out_1 = 0, win_inner_1 = 0;
  193. switch (inner_index_1) {
  194. case 0:
  195. loss_out_1 = goldB1 + goldC1;
  196. win_inner_1 = gold_side_inner * (oddsA1 + 1);
  197. break;
  198. case 1:
  199. loss_out_1 = goldA1 + goldC1;
  200. win_inner_1 = gold_side_inner * (oddsB1 + 1);
  201. break;
  202. case 2:
  203. const { loss_proportion_a: lpA1, loss_proportion_b: lpB1 } = lossProportion(sol1);
  204. loss_out_1 = goldA1 * lpA1 + goldB1 * lpB1 ;
  205. win_inner_1 = gold_side_inner * (oddsC1 + 1)
  206. break;
  207. }
  208. const {
  209. odds_side_a: oddsA2,
  210. odds_side_b: oddsB2,
  211. odds_side_c: oddsC2,
  212. inner_index: inner_index_2,
  213. } = sol2;
  214. const {
  215. gold_side_a: goldA2,
  216. gold_side_b: goldB2,
  217. gold_side_c: goldC2,
  218. } = calcExternalHandicap({ ...sol2, gold_side_inner, win_target, loss_out: loss_out_1 });
  219. let loss_out_2 = 0, win_inner_2 = 0, inner_base_key;
  220. switch (inner_index_2) {
  221. case 0:
  222. inner_base_key = 'goldA2';
  223. loss_out_2 = gold_side_inner +goldB2 + goldC2 + loss_out_1;
  224. win_inner_2 = win_inner_1 * (oddsA2 + 1);
  225. break;
  226. case 1:
  227. inner_base_key = 'goldB2';
  228. loss_out_2 = gold_side_inner + goldA2 + goldC2 + loss_out_1;
  229. win_inner_2 = win_inner_1 * (oddsB2 + 1);
  230. break;
  231. case 2:
  232. const { loss_proportion_a: lpA2, loss_proportion_b: lpB2 } = lossProportion(sol2);
  233. inner_base_key = 'goldC2';
  234. loss_out_2 = gold_side_inner + goldA2 * lpA2 + goldB2 * lpB2 + loss_out_1;
  235. win_inner_2 = win_inner_1 * (oddsC2 + 1);
  236. break;
  237. }
  238. const win_inner = fixFloat(win_inner_2 - loss_out_2);
  239. const goldsInfo = { goldA1, goldB1, goldC1, goldA2, goldB2, goldC2 }
  240. if (goldsInfo[inner_base_key]) {
  241. goldsInfo[inner_base_key] = win_inner_1;
  242. }
  243. const result = {
  244. bet_info: [
  245. {
  246. gold_side_a: goldsInfo.goldA1,
  247. gold_side_b: goldsInfo.goldB1,
  248. gold_side_c: goldsInfo.goldC1,
  249. odds_side_a: oddsA1,
  250. odds_side_b: oddsB1,
  251. odds_side_c: oddsC1,
  252. inner_index: inner_index_1,
  253. },
  254. {
  255. gold_side_a: goldsInfo.goldA2,
  256. gold_side_b: goldsInfo.goldB2,
  257. gold_side_c: goldsInfo.goldC2,
  258. odds_side_a: oddsA2,
  259. odds_side_b: oddsB2,
  260. odds_side_c: oddsC2,
  261. inner_index: inner_index_2,
  262. }
  263. ],
  264. win_inner,
  265. inner_base: gold_side_inner,
  266. }
  267. return result;
  268. }
  269. const calcTotalProfit = (sol1, sol2, gold_side_inner, rebate_side_inner) => {
  270. const rebateInner = gold_side_inner * rebate_side_inner / 100;
  271. const winTarget1 = fixFloat(sol1.win_average - rebateInner);
  272. const winTarget2 = fixFloat(sol2.win_average - rebateInner);
  273. const winTarget = fixFloat(Math.min(winTarget1, winTarget2));
  274. const win1 = calcGoldsWithWinTarget({ gold_side_inner, win_target: winTarget1, sol1, sol2 })?.win_inner;
  275. const win2 = calcGoldsWithWinTarget({ gold_side_inner, win_target: winTarget2, sol1, sol2 })?.win_inner;
  276. const win_inner = fixFloat(Math.max(win1, win2), 2);
  277. const start = Math.max(winTarget, win_inner);
  278. const end = Math.min(winTarget, win_inner);
  279. const result = [];
  280. for (let i = start; i > end; i--) {
  281. const win_target = i;
  282. const goldsInfo = calcGoldsWithWinTarget({ gold_side_inner, win_target, sol1, sol2 });
  283. const { win_inner, ...goldsRest } = goldsInfo;
  284. const win_diff = Math.abs(fixFloat(win_target - goldsInfo.win_inner));
  285. const lastResult = result.at(-1);
  286. if (!lastResult?.win_diff || win_diff < lastResult.win_diff) {
  287. result.push({ win_target: fixFloat(win_target + rebateInner), win_diff, win_inner: fixFloat(win_inner + rebateInner), ...goldsRest });
  288. }
  289. else {
  290. break;
  291. }
  292. }
  293. return result.at(-1);
  294. }
  295. module.exports = calcTotalProfit;