totalProfitCalc.js 8.1 KB

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