const { ToadScheduler, SimpleIntervalJob, AsyncTask } = require('toad-scheduler'); const { settings, transferProfit, saveProfit, getAllClosedOrders, getAllOpenOrders, getOpenOrder, monitorOrderComplete, calculateAdaptiveTargets, sleep, placeSellOrder, placeBuyOrder, evaluateMarketConditions, getAvailableUSDBalance, getHistoricalData, readJsonFile, writeJsonFile, settingsFile, getCurrencyInfo, openTradesFile, closedTradesFile, setTradingCurrency, setCurrencyInfo, manageStaleOrders, shuffle, syncSettings } = require("./utils") async function monitorAndPlace(currencyData, usdAllocatedBalance, currency) { let CURRENCY_INFO = currencyData; // const settings = readJsonFile(settingsFile); // console.log(tradingCurrency + 'USD', CURRENCY_INFO) let openTrades = readJsonFile(openTradesFile); if (!settings) return {openTrades, removedIds: []}; let closedTrades = await getAllClosedOrders(); // console.log("closedTrades", JSON.stringify(closedTrades, null, 4)) // return const pair = currency.tradingCurrency + 'USD'; let candleQuant = 16; const marketCondition = await evaluateMarketConditions(pair); const averageLow = (await getHistoricalData(pair, 15)).slice(-candleQuant).reduce((acc, candle) => acc + parseFloat(candle[3]), 0) / candleQuant; const availableUSD = usdAllocatedBalance; let removedIds = []; if(availableUSD <= 0){ console.log("Insufficient / error reaching funds") return {openTrades, removedIds: []} } const balance = availableUSD * settings.maximumOrderSize; let oTrades = await getAllOpenOrders(false); for(let x = 0; x < Object.keys(oTrades).length; x++){ let id = Object.keys(oTrades)[x]; let sellid = null; let trade = oTrades[id]; //make sure we are only working with orders for this pair if(!(trade.descr.pair === pair || trade.descr.pair === currency?.compPair)){ continue; } if(trade.descr.type === 'sell'){ //find original buying order sellid = id; console.log("id ", id); let buyingOrder = Object.entries(openTrades).find(itemArr => itemArr[1].sellid === id) if(!buyingOrder){ console.log("Unable to find local trade") continue; } id = buyingOrder[1].id; console.log(buyingOrder) openTrades[id].sellid = sellid; } let localTrade = openTrades[id]; if(!localTrade){ openTrades[id] = { ...openTrades[id], id: id, userref: trade.userref, pair: pair, starttimestamp: new Date().toISOString(), type: trade.descr.type, status: trade.status, ordertype: trade.descr.ordertype, volume: trade.vol, } console.log(typeof trade.descr.price) if(trade.descr.type === 'sell'){ openTrades[id].sellprice = Number(trade.descr.price).toFixed(CURRENCY_INFO.pair_decimals); }else { openTrades[id].price = Number(trade.descr.price).toFixed(CURRENCY_INFO.pair_decimals); } // writeJsonFile(openTradesFile, openTrades); } } let allIds = Object.keys(openTrades); for(let x = 0; x < allIds.length; x++){ let id = allIds[x]; console.log(id); let trade = oTrades[id]; let localTrade = openTrades[id]; let closedTrade = closedTrades[id]; // console.log("trade", localTrade) if(!(localTrade.pair === pair || localTrade.pair === currency?.compPair)){ continue; } await calculateAdaptiveTargets(pair, settings.profitTargetPercent, settings.stopLossPercentage, riskRewardRatio = 10) if(localTrade.type === 'buy'){ if(['pending', 'open', 'closed'].includes(closedTrade?.status) || ['pending', 'open', 'closed'].includes(localTrade.status)){ if(closedTrade?.status === 'closed' || localTrade.status === 'closed'){ console.log('Buy order completed:', localTrade); const sellPrice = parseFloat(localTrade.price) * (1 + settings.profitTargetPercent); const stopLoss = parseFloat(localTrade.price) * (1 - settings.stopLossPercentage); const sellOrder = await placeSellOrder(pair, localTrade.volume, sellPrice, stopLoss, localTrade.userref, localTrade.id, localTrade); if(sellOrder){ openTrades[localTrade.id] = sellOrder; console.log('Sell order placed:', sellOrder); } // writeJsonFile(openTradesFile, openTrades); } }else { console.log("Buy order expired or cancelled, clearing from local list") //order expired or cancelled, whipe from list delete openTrades[id]; removedIds.push(id) // writeJsonFile(openTradesFile, openTrades); } }else{ localTrade = openTrades[id]; //originalBuyTrade trade = oTrades[localTrade.sellid]; //openselltrade closedTrade = closedTrades[localTrade.sellid]; //potentially closed sell trade if(['pending', 'open', 'closed'].includes(closedTrade?.status) || ['pending', 'open', 'closed'].includes(trade.status)){ if(closedTrade?.status === 'closed' || trade.status === 'closed'){ let sellOrder = {...openTrades[localTrade.id]}; console.log('Sell order completed:', sellOrder); delete openTrades[sellOrder.id]; removedIds.push(sellOrder.id) // writeJsonFile(openTradesFile, openTrades); const sellPrice = parseFloat(localTrade.sellprice) const closedTrades = readJsonFile(closedTradesFile); const buyTotal = parseFloat(localTrade.price) * localTrade.volume; const sellTotal = sellPrice * localTrade.volume; const transactionFees = (settings.transactionFeePercent) * (buyTotal + sellTotal); const profit = sellTotal - buyTotal - transactionFees; console.log('Sell order Profit:', profit); if (settings.profitWallet && settings.profitSavePercent) { const profitSaveAmount = profit * (settings.profitSavePercent); console.log('Saving Profit:', profitSaveAmount); await saveProfit(profitSaveAmount, profit); } closedTrades[sellOrder.id] = { ...sellOrder, sellPrice, volume: localTrade.volume, profit, timestamp: new Date().toISOString(), }; writeJsonFile(closedTradesFile, closedTrades); //transfer profits and write to closedTrades.json } }else { //order expired or cancelled, whipe from list console.log("Sell order expired or cancelled, clearing from local list") console.log("trade", trade) console.log("localTrade", localTrade) console.log("closedTrade", closedTrade) console.log("closedTrades", JSON.stringify(closedTrades, null, 4)) delete openTrades[id]; removedIds.push(id) // writeJsonFile(openTradesFile, openTrades); } } } oTrades = await getAllOpenOrders(false); let openTradeIds = Object.keys(oTrades); let priceRangeExclusion = settings.priceRangeExclusion; let excludeBasedOnPrevOrder = []; for(let x = 0; x < openTradeIds.length; x++){ let id = openTradeIds[x]; let trade = oTrades[id]; if(!(trade.descr.pair === pair || trade.descr.pair === currency?.compPair)){ continue; } let price = parseFloat(trade.descr.price); let diffPer = Math.abs(price - averageLow) / averageLow; // console.log(`DIff Percentage ${diffPer} ${price} ${averageLow}`) if(diffPer < priceRangeExclusion){ excludeBasedOnPrevOrder.push(id); } if(excludeBasedOnPrevOrder.length >= settings.priceRangeExclusionMaxOpen){ break; } } if(excludeBasedOnPrevOrder.length >= settings.priceRangeExclusionMaxOpen){ console.log('Excluding based on open orders: ', excludeBasedOnPrevOrder.join(", ")) return {openTrades, removedIds} } console.log(` Attempting to place buy order for ${balance} USD worth of ${currency.tradingCurrency} at ${averageLow} USD = ${balance / averageLow} ${currency.tradingCurrency}`); if (marketCondition) { const buyOrder = await placeBuyOrder(pair, balance / averageLow, averageLow); if (buyOrder) { console.log('Buy order placed:', buyOrder); openTrades[buyOrder.id] = {...buyOrder}; // writeJsonFile(openTradesFile, openTrades); } } return {openTrades, removedIds} } async function cycleCurrencies(){ let builtCurriencies = []; let settings = syncSettings(); for(let x = 0; x < settings.tradingCurrencies.length; x++){ let currency = settings.tradingCurrencies[x]; let CURRENCY_INFO = await getCurrencyInfo(currency.tradingCurrency); const availableUSD = await getAvailableUSDBalance(currency.allocation) builtCurriencies.push({CURRENCY_INFO, availableUSD, currency}); } shuffle(builtCurriencies); let trades = readJsonFile(openTradesFile); for(let x = 0; x < builtCurriencies.length; x++){ console.log("*********************************************") console.log("*********************************************") console.log("***************TRADING***********************") console.log("*********************************************") console.log(`*****************${builtCurriencies[x].currency.tradingCurrency}*************************`) console.log("*********************************************") console.log("*********************************************") console.log("*********************************************") setTradingCurrency(builtCurriencies[x].currency.tradingCurrency); setCurrencyInfo(builtCurriencies[x].CURRENCY_INFO); let {openTrades, removedIds} = await monitorAndPlace(builtCurriencies[x].CURRENCY_INFO, builtCurriencies[x].availableUSD, builtCurriencies[x].currency); trades = {...trades, ...openTrades}; for(let i = 0; i < removedIds.length; i++){ delete trades[removedIds[i]] } writeJsonFile(openTradesFile, trades); } await manageStaleOrders(); } const scheduler = new ToadScheduler(); const task = new AsyncTask('monitor and place orders', cycleCurrencies, (err) => { console.error('Error in scheduled task:', err); }); cycleCurrencies(); const job = new SimpleIntervalJob({ [settings.tradingIntervalType]: settings.tradingInterval }, task); scheduler.addSimpleIntervalJob(job); console.log('Trading bot is running.');