268 lines
12 KiB
JavaScript
268 lines
12 KiB
JavaScript
|
|
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.');
|
|
|