|
@@ -1,459 +0,0 @@
|
|
|
-import path from "node:path";
|
|
|
|
|
-import { fileURLToPath } from "node:url";
|
|
|
|
|
-
|
|
|
|
|
-import { getData } from "../libs/cache.js";
|
|
|
|
|
-import Logs from "../libs/logs.js";
|
|
|
|
|
-
|
|
|
|
|
-import eventSolutions from '../triangle/eventSolutions.js';
|
|
|
|
|
-
|
|
|
|
|
-import { getOrderBook, placeOrder as polymarketPlaceOrder } from "../../polymarket/libs/polymarketClient.js";
|
|
|
|
|
-import { getLineInfo, placeOrder as pinnaclePlaceOrder } from "../../polymarket/libs/pinnacleClient.js";
|
|
|
|
|
-
|
|
|
|
|
-const __filename = fileURLToPath(import.meta.url);
|
|
|
|
|
-const __dirname = path.dirname(__filename);
|
|
|
|
|
-
|
|
|
|
|
-const polymarketMarketsCacheFile = path.join(__dirname, "../../polymarket/cache/polymarketMarketsCache.json");
|
|
|
|
|
-const pinnacleGamesCacheFile = path.join(__dirname, "../../pinnacle/cache/pinnacleGamesCache.json");
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 最小盈利率
|
|
|
|
|
- */
|
|
|
|
|
-const MIN_PROFIT_RATE = -1;
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 精确浮点数字
|
|
|
|
|
- * @param {number} number
|
|
|
|
|
- * @param {number} x
|
|
|
|
|
- * @returns {number}
|
|
|
|
|
- */
|
|
|
|
|
-const fixFloat = (number, x=3) => {
|
|
|
|
|
- return parseFloat(number.toFixed(x));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 依次执行任务
|
|
|
|
|
- */
|
|
|
|
|
-const runSequentially = async (tasks) => {
|
|
|
|
|
- return new Promise(async (resolve, reject) => {
|
|
|
|
|
- const results = [];
|
|
|
|
|
- let isError = false;
|
|
|
|
|
- for (const task of tasks) {
|
|
|
|
|
- // task 必须是一个「返回 Promise 的函数」
|
|
|
|
|
- const res = await task().catch(err => {
|
|
|
|
|
- err.results = results;
|
|
|
|
|
- isError = true;
|
|
|
|
|
- reject(err);
|
|
|
|
|
- });
|
|
|
|
|
- if (isError) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- else if (res) {
|
|
|
|
|
- results.push(res);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (isError) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- resolve(results);
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 计算符合比例的最大和最小数组
|
|
|
|
|
- */
|
|
|
|
|
-const findMaxMinGroup = (ratios, minVals, maxVals) => {
|
|
|
|
|
- const n = ratios.length;
|
|
|
|
|
-
|
|
|
|
|
- // 1. 计算比例总和 & 归一化比例
|
|
|
|
|
- const totalRatio = ratios.reduce((a, b) => a + b, 0);
|
|
|
|
|
- const proportions = ratios.map(r => r / totalRatio);
|
|
|
|
|
-
|
|
|
|
|
- // 2. 计算每个位置允许的最大/最小倍数
|
|
|
|
|
- let maxPossibleScale = Infinity;
|
|
|
|
|
- let minPossibleScale = 0; // 如果允许0,通常从0开始
|
|
|
|
|
-
|
|
|
|
|
- for (let i = 0; i < n; i++) {
|
|
|
|
|
- // 上限约束
|
|
|
|
|
- if (proportions[i] > 0) {
|
|
|
|
|
- maxPossibleScale = Math.min(maxPossibleScale, maxVals[i] / proportions[i]);
|
|
|
|
|
- }
|
|
|
|
|
- // 下限约束(如果 minVals[i] > 0 才有意义)
|
|
|
|
|
- if (proportions[i] > 0 && minVals[i] > 0) {
|
|
|
|
|
- minPossibleScale = Math.max(minPossibleScale, minVals[i] / proportions[i]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 3. 最终取值
|
|
|
|
|
- const maxGroup = proportions.map(p => p * maxPossibleScale);
|
|
|
|
|
- const minGroup = proportions.map(p => p * Math.max(minPossibleScale, 0));
|
|
|
|
|
-
|
|
|
|
|
- return {
|
|
|
|
|
- maxGroup: maxGroup.map(v => fixFloat(v)), // 可控制精度
|
|
|
|
|
- minGroup: minGroup.map(v => fixFloat(v)),
|
|
|
|
|
- proportions: proportions.map(v => fixFloat(v)),
|
|
|
|
|
- scaleForMax: fixFloat(maxPossibleScale),
|
|
|
|
|
- scaleForMin: fixFloat(minPossibleScale)
|
|
|
|
|
- };
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 解析盘口信息
|
|
|
|
|
- */
|
|
|
|
|
-const parseRatio = (ratioString) => {
|
|
|
|
|
- if (!ratioString) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- return parseFloat(`${ratioString[0]}.${ratioString.slice(1)}`);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const parseIor = (ior) => {
|
|
|
|
|
- const iorMatch = ior.match(/ior_(m|r|ou|wm|ot)([ao])?([hcn])?_?(\d+)?/);
|
|
|
|
|
- if (!iorMatch) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const [, type, action, side, ratio] = iorMatch;
|
|
|
|
|
- return { type, action, side, ratio };
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getPolymarketIorInfo = async (ior, id) => {
|
|
|
|
|
- const cacheData = await getData(polymarketMarketsCacheFile);
|
|
|
|
|
- const marketsData = cacheData[id]?.marketsData;
|
|
|
|
|
- if (!marketsData) {
|
|
|
|
|
- Logs.outDev('polymarket markets data not found', id);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const iorOptions = parseIor(ior);
|
|
|
|
|
- if (!iorOptions) {
|
|
|
|
|
- Logs.outDev('polymarket ior options not found', ior);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { type, action, side, ratio } = iorOptions;
|
|
|
|
|
-
|
|
|
|
|
- let marketTypeData, outcomesSide;
|
|
|
|
|
-
|
|
|
|
|
- if (type === 'm' && !ratio) {
|
|
|
|
|
- const sideKey = side === 'h' ? 'Home' : side === 'c' ? 'Away' : 'Draw';
|
|
|
|
|
- const sideAction = action === 'o' ? 'No' : 'Yes';
|
|
|
|
|
- marketTypeData = marketsData.moneyline[sideKey];
|
|
|
|
|
- outcomesSide = sideAction;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'r') {
|
|
|
|
|
- const sideKey = side === 'h' ? 'Home' : side === 'c' ? 'Away' : '';
|
|
|
|
|
- let ratioDirection = 1;
|
|
|
|
|
- if (side === 'c' && action === 'a' || side === 'h' && !action) {
|
|
|
|
|
- ratioDirection = -1;
|
|
|
|
|
- }
|
|
|
|
|
- const ratioValue = parseRatio(ratio) * ratioDirection;
|
|
|
|
|
- const ratioKey = ratioValue > 0 ? `+${ratioValue}` : `${ratioValue}`;
|
|
|
|
|
- marketTypeData = marketsData.spreads?.[ratioKey];
|
|
|
|
|
- outcomesSide = sideKey;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'ou') {
|
|
|
|
|
- const sideKey = side === 'c' ? 'Over' : side === 'h' ? 'Under' : '';
|
|
|
|
|
- const ratioKey = parseRatio(ratio);
|
|
|
|
|
- marketTypeData = marketsData.totals[ratioKey];
|
|
|
|
|
- outcomesSide = sideKey;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const result = marketTypeData?.outcomes?.[outcomesSide];
|
|
|
|
|
-
|
|
|
|
|
- if (!result) {
|
|
|
|
|
- Logs.outDev('polymarket market type data not found', { ior, id, type, action, side, ratio, marketTypeData, outcomesSide });
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- return result;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getPinnacleIorInfo = async (ior, id) => {
|
|
|
|
|
- const cacheData = await getData(pinnacleGamesCacheFile);
|
|
|
|
|
- const gamesData = cacheData[id];
|
|
|
|
|
- if (!gamesData) {
|
|
|
|
|
- Logs.outDev('pinnacle games data not found', id);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const iorOptions = parseIor(ior);
|
|
|
|
|
- if (!iorOptions) {
|
|
|
|
|
- Logs.outDev('pinnacle ior options not found', ior);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { type, action, side, ratio } = iorOptions;
|
|
|
|
|
-
|
|
|
|
|
- const { leagueId, id: eventId, home: homeTeamName, away: awayTeamName, periods={}, specials={}} = gamesData;
|
|
|
|
|
-
|
|
|
|
|
- const straightData = periods.straight ?? {};
|
|
|
|
|
- const { lineId: straightLineId, moneyline, spreads, totals } = straightData;
|
|
|
|
|
- const { winningMargin, exactTotalGoals } = specials;
|
|
|
|
|
-
|
|
|
|
|
- if (type === 'm' && moneyline && !action && !ratio) {
|
|
|
|
|
- const sideKey = side === 'h' ? 'home' : side === 'c' ? 'away' : 'draw';
|
|
|
|
|
- const team = side === 'h' ? 'TEAM1' : side === 'c' ? 'TEAM2' : 'DRAW';
|
|
|
|
|
- const odds = moneyline[sideKey];
|
|
|
|
|
- return { leagueId, eventId, betType: 'MONEYLINE', team, lineId: straightLineId, odds };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'r' && spreads) {
|
|
|
|
|
- let ratioDirection = 1;
|
|
|
|
|
- if (side === 'c' && action === 'a' || side === 'h' && !action) {
|
|
|
|
|
- ratioDirection = -1;
|
|
|
|
|
- }
|
|
|
|
|
- const ratioKey = parseRatio(ratio) * ratioDirection;
|
|
|
|
|
- const itemSpread = spreads.find(spread => spread.hdp == ratioKey);
|
|
|
|
|
- if (!itemSpread) {
|
|
|
|
|
- Logs.outDev('pinnacle item spread not found', id, type, action, side, ratio);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { altLineId=null, home, away } = itemSpread;
|
|
|
|
|
- const odds = side === 'h' ? home : away;
|
|
|
|
|
- const team = side === 'h' ? 'TEAM1' : 'TEAM2';
|
|
|
|
|
- const handicap = ratioKey * (side === 'h' ? 1 : -1);
|
|
|
|
|
- return { leagueId, eventId, handicap, betType: 'SPREAD', team, lineId: straightLineId, altLineId, odds };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'ou' && totals) {
|
|
|
|
|
- const ratioKey = parseRatio(ratio);
|
|
|
|
|
- const itemTotal = totals.find(total => total.points == ratioKey);
|
|
|
|
|
- if (!itemTotal) {
|
|
|
|
|
- Logs.outDev('pinnacle item total not found', id, type, action, side, ratio);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { altLineId=null, over, under } = itemTotal;
|
|
|
|
|
- const odds = side === 'c' ? over : under;
|
|
|
|
|
- const sideKey = side === 'c' ? 'OVER' : 'UNDER';
|
|
|
|
|
- return { leagueId, eventId, handicap: ratioKey, betType: 'TOTAL_POINTS', side: sideKey, lineId: straightLineId, altLineId, odds };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'wm' && winningMargin) {
|
|
|
|
|
- const ratioKey = parseRatio(ratio);
|
|
|
|
|
- const { id: specialId } = winningMargin;
|
|
|
|
|
- const wmName = side === 'h' ? `${homeTeamName} By ${ratioKey}` : side === 'c' ? `${awayTeamName} By ${ratioKey}` : '';
|
|
|
|
|
- const wmItem = winningMargin.contestants.find(contestant => contestant.name == wmName);
|
|
|
|
|
- if (!wmItem) {
|
|
|
|
|
- Logs.outDev('pinnacle item winning margin not found', id, type, action, side, ratio);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { id: contestantId, lineId, price } = wmItem;
|
|
|
|
|
- return { leagueId, eventId, specialId, contestantId, lineId, odds: price };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else if (type === 'ot' && exactTotalGoals) {
|
|
|
|
|
- const ratioKey = parseRatio(ratio);
|
|
|
|
|
- const { id: specialId } = exactTotalGoals;
|
|
|
|
|
- const otItem = exactTotalGoals.contestants.find(contestant => contestant.name == ratioKey);
|
|
|
|
|
- if (!otItem) {
|
|
|
|
|
- Logs.outDev('pinnacle item exact total goals not found', id, type, action, side, ratio);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const { id: contestantId, lineId, price } = otItem;
|
|
|
|
|
- return { leagueId, eventId, specialId, contestantId, lineId, odds: price };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else {
|
|
|
|
|
- Logs.outDev('pinnacle ior type not found', ior, id);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 获取平台盘口id信息
|
|
|
|
|
- */
|
|
|
|
|
-export const getPlatformIorInfo = async (ior, platform, id) => {
|
|
|
|
|
- const getInfo = {
|
|
|
|
|
- polymarket() {
|
|
|
|
|
- return getPolymarketIorInfo(ior, id);
|
|
|
|
|
- },
|
|
|
|
|
- pinnacle() {
|
|
|
|
|
- return getPinnacleIorInfo(ior, id);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- Logs.outDev('getPlatformIorInfo', { ior, platform, id });
|
|
|
|
|
- return getInfo[platform]?.();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 获取polymarket盘口详细信息
|
|
|
|
|
- */
|
|
|
|
|
-const getPolymarketIorDetailInfo = async (info) => {
|
|
|
|
|
- const { id } = info;
|
|
|
|
|
- return getOrderBook(id);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 获取pinnacle盘口详细信息
|
|
|
|
|
- */
|
|
|
|
|
-const getPinnacleIorDetailInfo = async (info, channel) => {
|
|
|
|
|
- return getLineInfo(info, channel);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 获取平台盘口详细信息
|
|
|
|
|
- */
|
|
|
|
|
-export const getPlatformIorsDetailInfo = async (ior, platform, id, channel) => {
|
|
|
|
|
- const info = await getPlatformIorInfo(ior, platform, id);
|
|
|
|
|
- if (!info) {
|
|
|
|
|
- return Promise.reject(new Error('platform ior info not found', { cause: 400 }));
|
|
|
|
|
- }
|
|
|
|
|
- const getInfo = {
|
|
|
|
|
- polymarket() {
|
|
|
|
|
- return getPolymarketIorDetailInfo(info);
|
|
|
|
|
- },
|
|
|
|
|
- pinnacle() {
|
|
|
|
|
- return getPinnacleIorDetailInfo(info, channel);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return getInfo[platform]?.();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 平台盘口下注
|
|
|
|
|
- */
|
|
|
|
|
-export const placePlatformOrder = async (ior, platform, id, stake=0, channel) => {
|
|
|
|
|
- const iorInfo = await getPlatformIorsDetailInfo(ior, platform, id, channel);
|
|
|
|
|
- const betInfo = { ...iorInfo, stakeSize: stake };
|
|
|
|
|
- const placeOrder = {
|
|
|
|
|
- polymarket() {
|
|
|
|
|
- return polymarketPlaceOrder(betInfo);
|
|
|
|
|
- },
|
|
|
|
|
- pinnacle() {
|
|
|
|
|
- return pinnaclePlaceOrder(betInfo, channel);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return placeOrder[platform]?.();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 根据最新赔率获取策略
|
|
|
|
|
- */
|
|
|
|
|
-export const getSolutionByLatestIors = (iorsInfo, cross_type, retry=false) => {
|
|
|
|
|
- const askIndex = +retry;
|
|
|
|
|
- const iorsValues = iorsInfo.map(item => {
|
|
|
|
|
- if (item.asks) {
|
|
|
|
|
- const bestAsk = [...item.asks].sort((a, b) => a.price - b.price)[askIndex];
|
|
|
|
|
- const value = fixFloat(1 / bestAsk.price, 3);
|
|
|
|
|
- const maxStake = fixFloat(bestAsk.size * bestAsk.price);
|
|
|
|
|
- const minStake = fixFloat(item.min_order_size * bestAsk.price);
|
|
|
|
|
- return { value, maxStake, minStake, bestPrice: bestAsk.price };
|
|
|
|
|
- }
|
|
|
|
|
- else if (item.info) {
|
|
|
|
|
- const value = item.info.price;
|
|
|
|
|
- const maxStake = Math.floor(item.info.maxRiskStake);
|
|
|
|
|
- const minStake = Math.ceil(item.info.minRiskStake*10);
|
|
|
|
|
- return { value, maxStake, minStake };
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const nullIndex = iorsValues.findIndex(item => item.value == null);
|
|
|
|
|
-
|
|
|
|
|
- if (nullIndex >= 0) {
|
|
|
|
|
- return { error: `IORS_NULL_VALUE_AT_INDEX_${nullIndex}_RETRY_${askIndex}`, data: iorsInfo };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const baseIndex = iorsValues.reduce((minIdx, cur, idx) => cur.value < iorsValues[minIdx].value ? idx : minIdx, 0);
|
|
|
|
|
-
|
|
|
|
|
- if (iorsValues.length === 2) {
|
|
|
|
|
- iorsValues.push({ value: 1, maxStake: 0, minStake: 0 });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const betInfo = {
|
|
|
|
|
- cross_type,
|
|
|
|
|
- base_index: baseIndex,
|
|
|
|
|
- base_stake: 10000,
|
|
|
|
|
- odds_side_a: fixFloat(iorsValues[0].value - 1),
|
|
|
|
|
- odds_side_b: fixFloat(iorsValues[1].value - 1),
|
|
|
|
|
- odds_side_c: fixFloat(iorsValues[2].value - 1),
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const sol = eventSolutions(betInfo, true);
|
|
|
|
|
- const { win_average, win_profit_rate, gold_side_a, gold_side_b, gold_side_c } = sol;
|
|
|
|
|
-
|
|
|
|
|
- if (win_profit_rate < MIN_PROFIT_RATE) {
|
|
|
|
|
- Logs.outDev('win_profit_rate is less than profit rate limit', sol, iorsValues, iorsInfo, cross_type);
|
|
|
|
|
- return { error: `WIN_PROFIT_RATE_LESS_THAN_MIN_PROFIT_RATE_RETRY_${askIndex}`, data: { sol, iorsValues, iorsInfo } };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const goldRatios = [gold_side_a, gold_side_b];
|
|
|
|
|
- if (gold_side_c) {
|
|
|
|
|
- goldRatios.push(gold_side_c);
|
|
|
|
|
- }
|
|
|
|
|
- const minVals = iorsValues.map(item => item.minStake);
|
|
|
|
|
- const maxVals = iorsValues.map(item => item.maxStake);
|
|
|
|
|
-
|
|
|
|
|
- const stakeLimit = findMaxMinGroup(goldRatios, minVals, maxVals);
|
|
|
|
|
- const { scaleForMax, scaleForMin } = stakeLimit;
|
|
|
|
|
-
|
|
|
|
|
- if (scaleForMax < scaleForMin) {
|
|
|
|
|
- Logs.outDev('scaleForMax is less than scaleForMin');
|
|
|
|
|
- if (!retry) {
|
|
|
|
|
- return getSolutionByLatestIors(iorsInfo, cross_type, true);
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- return { error: `NO_ENOUGH_STAKE_SIZE_TO_BET_RETRY_${askIndex}`, data: { sol, iorsValues, iorsInfo } };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const winLimit = {
|
|
|
|
|
- max: fixFloat(win_average * scaleForMax / (gold_side_a + gold_side_b + gold_side_c)),
|
|
|
|
|
- min: fixFloat(win_average * scaleForMin / (gold_side_a + gold_side_b + gold_side_c)),
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return { sol, iors: iorsValues, stakeLimit, winLimit };
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-export const getSoulutionBetResult = async ({ iors, iorsInfo, stakeLimit, stake=0 }) => {
|
|
|
|
|
- const maxStake = stakeLimit.maxGroup.reduce((acc, curr) => acc + curr, 0);
|
|
|
|
|
- const minStake = stakeLimit.minGroup.reduce((acc, curr) => acc + curr, 0);
|
|
|
|
|
- let betStakeGroup = [];
|
|
|
|
|
- if (stake > maxStake || stake < 0) {
|
|
|
|
|
- stake = maxStake;
|
|
|
|
|
- betStakeGroup = stakeLimit.maxGroup;
|
|
|
|
|
- }
|
|
|
|
|
- else if (stake < minStake) {
|
|
|
|
|
- stake = minStake;
|
|
|
|
|
- betStakeGroup = stakeLimit.minGroup;
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- betStakeGroup = stakeLimit.proportions.map(p => p * stake);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const betInfo = iorsInfo.map((item, index) => {
|
|
|
|
|
- if (item.asks) {
|
|
|
|
|
- const bestPrice = +iors[index].bestPrice;
|
|
|
|
|
- const stakeSize = fixFloat(betStakeGroup[index] / bestPrice, 0); // 必须保证买单金额小数不超过2位
|
|
|
|
|
- return { ...item, stakeSize, bestPrice, betIndex: index, platform: 'polymarket' }
|
|
|
|
|
- }
|
|
|
|
|
- else if (item.info) {
|
|
|
|
|
- const stakeSize = fixFloat(betStakeGroup[index], 0);
|
|
|
|
|
- return { ...item, stakeSize, betIndex: index, platform: 'pinnacle' }
|
|
|
|
|
- }
|
|
|
|
|
- }).sort((a, b) => {
|
|
|
|
|
- if (a.platform === 'polymarket' && b.platform === 'pinnacle') {
|
|
|
|
|
- return -1;
|
|
|
|
|
- }
|
|
|
|
|
- else if (a.platform === 'pinnacle' && b.platform === 'polymarket') {
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // return { betInfo };
|
|
|
|
|
- return runSequentially(betInfo.map(item => async() => {
|
|
|
|
|
- if (item.asks) {
|
|
|
|
|
- const result = await polymarketPlaceOrder(item);
|
|
|
|
|
- return [result, item.betIndex]
|
|
|
|
|
- }
|
|
|
|
|
- else if (item.info) {
|
|
|
|
|
- const result = await pinnaclePlaceOrder(item);
|
|
|
|
|
- return [result, item.betIndex]
|
|
|
|
|
- }
|
|
|
|
|
- })).then(results => {
|
|
|
|
|
- return results.sort((a, b) => a[1] - b[1]).map(item => item[0]);
|
|
|
|
|
- }).catch(error => {
|
|
|
|
|
- Logs.out('get soulution bet result error', error.message);
|
|
|
|
|
- Logs.errDev(error);
|
|
|
|
|
- return Promise.reject(error);
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|