/*
******************************************************************************************************************
UKION TRIANGULAR ARBITRAGE ver 1.01 ALPHA (Binance only)

author: Igor Ustijanoski

Instructions:
------------

1. 	Install NODE.JS
2. 	Import CCXT JavaScript library
3. 	Set INVESTMENT_AMOUNT_DOLLARS parameter - initial investment in USDT
		(minimum investment in Binance SPOT wallet: 150 USDT)
		(Due to exchange limitation, recommended value in application is 100)
4. 	Set MIN_PROFIT_DOLLARS - minimum profit for triangular pair
		(recommended value is 1 or 1.5)
5. 	Set BROKERAGE_PER_TRANSACTION_PERCENT - fees are taken from exchange
		(however there are some dast which always stay in wallet so recommended value for additional
		 fee is between 0.01 and 0.04 (must check first time if there are profit or lose)
6. 	Add apiKey and secret
7. 	Comment out the line: exchange.set_sandbox_mode(true); // using binance testnet
8. 	Watch the console for any errors. If errors increase, please check wallet to transfer stuck funds to USDT
9. 	Watch console for any errors. If error increase please check wallet to transfer stucked funds to
	USDT in SPOT wallet
10. Report error to: [email protected]

	GOOD LUCK with TRADING!!!

!!! Please be aware that this is not finansial advice. There can be lose. So please watch closly !!!
******************************************************************************************************************
*/

// binance
// api c2TmoYy8eZCbeKIK71vJtPcvjKPqEzjh8UUZVkl506JikkGVojIQYXfSej2dfNnr
// secret tZGQSh5r4lYJ400oJl0Rjux6TL8nJSsTljsvSHJlzshv6N7cDvv0nkRouQnZLuUe

// binance testnet
// V9gGChVv5aRW8oFDApiOgTJmXPXfrSBkvdEQZJZ0Wwn3hjVAS0nYCokaxstu7SlJ
// 8rAOB4OQcVuq73XpRgSnEg5bIjNhJKCYofdEIcCCYEuoOZqzP83OfP6PfQlZIN7J

// Import the ccxt library


/*
const CONFIG = require('../../config/config');
const logger = require('./Loggers');
const Util = require('./Util');
const Binance = require('node-binance-api');
const binance = new Binance().options(Object.assign({
    APIKEY: CONFIG.KEYS.API,
    APISECRET: CONFIG.KEYS.SECRET,
    test: !CONFIG.EXECUTION.ENABLED,
    log: (...args) => logger.binance.info(args.length > 1 ? args : args[0]),
    verbose: true
}, CONFIG.BINANCE_OPTIONS));

*/
const ccxt = require('ccxt');
const CONFIG = require('./config/config');
const colors = require('colors/safe');
colors.enable();
let continue1 = false;


if (process.argv[2] && process.argv[3] && process.argv[4]) {
	continue1 = true;
}
if (!continue1) {
	throw new Error("All 3 parameters must be filled: \nAPP.js_param1:exchangeName_param2:fee(-1 for default)_param3:investment\n");
}
// true za test server, false za staging server
let testingServer = false;
let exchangeName = process.argv[2];
if (process.argv[2] === 'test') {
	exchangeName = 'binance';
	testingServer = true;

} 

let apikey, secret, password,uid;
let timeInForce = 'Normal';
let waitingSecondsBetweenTrades = 2;
let imatPari = false;
let fees = process.argv[3];

if (testingServer) {

	// testing server Binance

	apikey = 'V9gGChVv5aRW8oFDApiOgTJmXPXfrSBkvdEQZJZ0Wwn3hjVAS0nYCokaxstu7SlJ';
	secret = '8rAOB4OQcVuq73XpRgSnEg5bIjNhJKCYofdEIcCCYEuoOZqzP83OfP6PfQlZIN7J';
	waitingSecondsBetweenTrades = 0;
	imatPari = true;
	

	// testing server - Kucoin
	//apikey = 'b54bcf4d-1bca-4e8e-9a24-22ff2c3d462c';
	//secret = '';

} else {

	// staging servers
	
	
	// crypto.com -0.0750%

	

	if (exchangeName === 'poloniex') {
		// o.2 fee, ne najde parovi, nekolku pati pusteno
		apikey = 'GZS2NK59-1GTS4YJV-HEKQNR3T-U9BF8FJB';
		secret = 'cb6441b08a6a852404377505cd711ef915d39046c4ba2490e14a82583df9b00fad5c523389a6198b8cf1bdb08ec6728f8c6c4e70f2f4fe1a591fd9b749505174';
	}	
	
	if (exchangeName === 'bitmex') {
		// bitmex - 0.025% fee
		apikey = 'pba5eGL7PkB8H7jFGrBilL5a';
		secret = 'BtbNgqpgqoTPMsAFDt2A6XWo8dyRZgnIAFtY2-x9KXq0kI56';
	}
	
	if (exchangeName === 'mexc') {
		// mexc - naogja tek tuk parovi, slab profit, so 0% fee e probano, denski 0.05 usd profit
		apikey = 'mx0vglDbQjBY9iHe3J';
		secret = '931c1bcdb92c427e98c901a92aebf176';
		waitingSecondsBetweenTrades = 1.5;
		imatPari = true;
	}
	if (exchangeName === 'bybit') {
		// bybit - Buy Limit Order is forbidden to trade, so market order ne naogja parovi!
		// 0.1% fee spored oficijalniot sajt
		apikey = 'AUFsjrOQUznt5IfrcI';
		secret = '5TbKzzvOJV5gZQ8PmXGeUPpWLI7eW87eBAV4';
	}
	if (exchangeName === 'gate') {
		// Gate, treba da se proveri ubavo, so 0.2 fee nema parovi
		apikey = '63090e1b65e0bd540c2091782dfb8d10';
		secret = 'ad609a563ffddcac9026bd478b16911ab3d7418c8f68d71308b121b75e4a1c60';
		
	}
	if (exchangeName === 'binance') {
		// binance, ne najde nieden par dosega so 0.075 fee
		apikey = 'c2TmoYy8eZCbeKIK71vJtPcvjKPqEzjh8UUZVkl506JikkGVojIQYXfSej2dfNnr';
		secret = 'tZGQSh5r4lYJ400oJl0Rjux6TL8nJSsTljsvSHJlzshv6N7cDvv0nkRouQnZLuUe';
		waitingSecondsBetweenTrades = 0.5;
	}
	if (exchangeName === 'woo') {
		// woo - nesho ima problem so fetchOrder, treba da se analizira kodot i da se zameni samo so woo so druga funkcija
		apikey = 'ysSRS/ePjPWDNwlM4bYLZQ==';
		secret = 'DVTWR2QHWBVXUXZL2LVIMEQTZE5R';
	}
	if (exchangeName === 'okx') {
		// OKX, 0.08% fee, slabo parovi
		apikey = '71956a5c-879f-4dc0-8197-84c4ac10971f';
		secret = 'BB7259C0BD56949D1E828E9C51E24CB9';
		password = '&yEDYxC7!f$m+KU';
	}
	if (exchangeName === 'bitmart') {
		//bitmart - naogja parovi so 0 fee nekojpat
		uid = '1234567890';
		apikey = '09f509e9de7b3ae3766d5c3211c596954824084b';
		secret = '4df6acc79c2d64861663965aa3028cfa713d6aa387c619e3bb47841c0e817516';
		
	}
		if (exchangeName === 'kucoin') {
		// fees 0.1 - 0.3
		apikey = '656a248edae7240001a955f9';
		secret = 'ed405c5d-e4a2-436f-906f-4261e5116ba7';
		password = '123456789';
	}

}


const exchange = new ccxt[exchangeName](
{

'maxCapacity': 2000,

// sendbox binance server parametar
'enableRateLimit': true,
//'rateLimit': 100,
'uid': uid,
'apiKey': apikey,
'secret': secret,
'password': password,
 'options': {
        'defaultType': 'spot',
        'adjustForTimeDifference': true
		}
});

// sandbox binance server
if (testingServer) {
	// testing server
	exchange.set_sandbox_mode(true); // using binance testnet
}

// CONFIG VARIABLES
let INVESTMENT_AMOUNT_DOLLARS = parseFloat(process.argv[4]);
let MIN_PROFIT_DOLLARS = 0.0001; // expected profit
let BROKERAGE_PER_TRANSACTION_PERCENT = fees; 

console.log('exchange:',colors.blue(exchangeName),'fee:',colors.yellow(BROKERAGE_PER_TRANSACTION_PERCENT), 'investment-USDT:', colors.yellow(INVESTMENT_AMOUNT_DOLLARS), 'min-profit:', colors.yellow(MIN_PROFIT_DOLLARS),'timeInForce:', colors.blue(timeInForce));
let BAD_TOKEN_MAX_LOSE = MIN_PROFIT_DOLLARS*2; // Maximum lose for Loosing -> BAD_TOKEN
let MASTER_CURRENCY = 'USDT';
let ORDER_TYPE = 'limit';


// APP VARIABLES
let PROFIT = 0;
let BALANCESTART;
let PRETHODEN_PROFIT = 0;
let TOTALPROFIT =0;
let balance, balance1,balanceBTC;
let count = 0;
let badTokensInfo = [];
let startTime = new Date().getTime();
let nowTime;


// Apprication start
main();

async function main() {
	let _symbols, markets;
	// clear screen
	//console.clear();

	//const markets = await exchange.fetchMarketData(undefined, 'future');
	//let markets = await exchange.loadMarkets();
	//let markets = await exchange.fetchMarkets();
	//console.log(markets);

	//exchange.verbose =true;
	//let 

	//let _symbols = await filterOnlyValidTokens_2(markets);
	if (exchangeName === 'mexc') {
		_symbols = await getAllMexcTokens(); //MEXC exchange
	} else if (exchangeName === 'woo') {
		markets = await exchange.fetchMarkets();
		_symbols = await market_symbols(markets);
	} else if (exchangeName === 'bitmart') {
		_symbols = await getAllBitMartTokens(); // bitmart exchange - fee = 0 tokens	
	} else if (exchangeName === 'bitmex') {
		_symbols = await getAllBitMexTokens(); // bitmex
	} else {
		_symbols = await filterOnlyValidTokens();
	}
	
	
	console.log(_symbols);

	balance = await getAllBalancesInUSDT(exchange);
	console.log('CURRENT BALANCE ALL:',balance);
	BALANCESTART = await getBalance('USDT');
	console.log('CURRENT BALANCE USDT: ',BALANCESTART);

	PROFIT = BALANCESTART;
	PRETHODEN_PROFIT = PROFIT;


	console.log(colors.blue('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'));
	//console.log(_symbols);
	console.log(`No. of market symbols: ${_symbols.length}`);

	let wx_combinations_usdt = await get_crypto_combinations(_symbols, MASTER_CURRENCY);
	console.log(`No. of triangular combinations: ${wx_combinations_usdt.length*2}`);
//	console.table(wx_combinations_usdt);
	console.log(colors.blue('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'));

//	throw new Error("STOP Execution!");

	let recursion, feeCheck;
	console.time();
	while(true) {

		for (var combination, _pj_c = 0, _pj_a = wx_combinations_usdt, _pj_b = _pj_a.length; _pj_c < _pj_b; _pj_c += 1) {
			combination = _pj_a[_pj_c];
//if (!testingServer) {
			base = combination["base"];
			intermediate = combination["intermediate"];
			ticker = combination["ticker"];
//} else {		
//			base = 'USDT';
//			intermediate = 'BTC';
//			ticker = 'WTC';
//}

			s1 =  `${intermediate}/${base}`;
			s2 =  `${ticker}/${intermediate}`;
			s3 =  `${ticker}/${base}`;


				try {
					recursion = true;
					feeCheck = await findRealFee('BUY_BUY_SELL', s1,s2, s3);
					if (feeCheck === true) {

						while (recursion === true) {
							recursion = await perform_triangular_arbitrage(s1, s2, s3, "BUY_BUY_SELL", INVESTMENT_AMOUNT_DOLLARS, BROKERAGE_PER_TRANSACTION_PERCENT, MIN_PROFIT_DOLLARS);
						}
					}
					//setTimeout(() => {}, "1000");

					recursion = true;
					feeCheck = await findRealFee('BUY_SELL_SELL', s3,s2,s1);
					if (feeCheck === true) {
						while (recursion === true) {

							recursion = await perform_triangular_arbitrage(s3,s2,s1,"BUY_SELL_SELL",INVESTMENT_AMOUNT_DOLLARS, BROKERAGE_PER_TRANSACTION_PERCENT, MIN_PROFIT_DOLLARS);

						}
					}
 					//setTimeout(() => {}, "1000");

				} catch (e) {
					throw e;
				}

	  }

		//balance1 = PROFIT; //await getBalance('USDT');
		PROFIT = await getBalance('USDT');
		TOTALPROFIT = PROFIT - BALANCESTART;
		nowTime = new Date().getTime();
		let time =  Math.floor(((nowTime-startTime) % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))+'h '+ Math.floor(((nowTime-startTime) % (1000 * 60 * 60)) / (1000 * 60)) +'m '+Math.floor(((nowTime-startTime) % (1000 * 60)) / 1000) + 's';
		//PROFIT = balance1;
		console.log(colors.blue('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'));
		console.log('START USDT balance:',BALANCESTART,'NEW USDT Balance:', PROFIT,'TOTAL PROFIT:', TOTALPROFIT, 'Execution Time:', time);
		console.log(colors.blue('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'));

		count = 0;
	} // while end
}

async function checkIfTokenIsBad(badTokens, token1, token2, token3) {
	// Loop through the array of tokens
	for (let token of badTokens) {
		// If any of the tokens match any of the strings, return true
		if (token === token1 || token === token2 || token === token3) {
			return true;
		}
	}
	// If none of the tokens match any of the strings, return false
	return false;
}


async function market_symbols(markets) {
  var _pj_a = [],
      _pj_b = markets;

  for (var _pj_c = 0, _pj_d = _pj_b.length; _pj_c < _pj_d; _pj_c += 1) {
    var market = _pj_b[_pj_c];

    _pj_a.push(market["symbol"]);
  }

  return _pj_a;
}


// Create all triangular arbitrage pairs of available symbols
async function get_crypto_combinations(_symbols, base) {
  var combination, combinations, sym1_token1, sym1_token2, sym2_token1, sym2_token2, sym3_token1, sym3_token2;
  combinations = [];

  for (let i = 0; i < _symbols.length; i++) {
	let [sym1_token1, sym1_token2] = _symbols[i].split('/');

    if (sym1_token2 === base) { //&&  sym1_token1 === 'BTC') {

	  for (let j = 0; j < _symbols.length; j++) {
		let [sym2_token1, sym2_token2] = _symbols[j].split('/');

        if (sym1_token1 === sym2_token2  && sym2_token1 !== 'IOTX' && sym2_token1 !== 'COS') {
			for (let k = 0; k < _symbols.length; k++) {
				let [sym3_token1, sym3_token2] = _symbols[k].split('/');

				if (sym2_token1 === sym3_token1 && sym3_token2 === sym1_token2) {
				  combination = {
					"base": sym1_token2,
					"intermediate": sym1_token1,
					"ticker": sym2_token1
				  };
				  combinations.push(combination);
				}
			}
        }
      }
    }
  }

  return combinations;
}

async function filterOnlyValidTokens() {

	let tickers = await exchange.fetchTickers();
	//console.log(tickers);
	// Initialize an empty array to store the symbols
	let result1 = [];
	// Loop through the entries of the tickers object
		for (let [symbol, ticker] of Object.entries(tickers)) {
			// Check if the ticker has ask, bid or close values
			if (ticker.ask !== undefined) {
				if (ticker.bid !== undefined) {
					//if (ticker.close !== undefined) {
					//	if (ticker.symbol.split("/")[0] !== 'BUSD') {
					//		if (ticker.symbol.split("/")[1] !== 'BUSD') {
								result1.push(symbol);
					//		}
					//	}
					//}
				}
			}
		}
	return result1;
}

async function filterOnlyValidTokens_2(markets) {

	let symbols = [];
	// Loop through the list of markets and append the symbol name to the array
	for (let market of markets) {
		symbols.push(market['symbol']);
	}
	// Return the array of symbols
	return symbols;

}

// Define an async function that takes an exchange as input and returns an object with all balances in USDT
async function getAllBalancesInUSDT(exchange) {

	let brojacUSDT = 0;
	try {
		// Fetch the balance from the exchange
		let balance = await exchange.fetchBalance({type: 'spot'});
		//console.log(balance);
		// Fetch the prices from the exchange
		let prices = await exchange.fetchTickers(); // all exchanges
//		let prices = await exchange.fetchMarketData(); // woo exchange
		//console.log(prices);
		// Initialize an empty object to store the balances in USDT
		let balancesInUSDT = {};
		// Loop through the coins in the balance object
		for (let coin in balance.free) {
			// Get the amount of the coin
			let amount = balance.free[coin];
			// If the amount is positive, convert it to USDT
			if (amount > 0) {
				// Check if the coin is USDT itself
				if (coin === 'USDT') {
					// Set the balance in USDT to the amount
					balancesInUSDT[coin] = amount;
					brojacUSDT = amount;
				} else {
					// Try to find the price of the coin in USDT
					let symbol = coin + '/USDT';
					let price = prices[symbol] ? prices[symbol].last : undefined;
					// If the price is defined, multiply it by the amount and set the balance in USDT
					if (price) {
/*
// sell all tokens to USDT
						try {
							console.log(coin,amount);
							await sellToken(symbol,await getBalance(coin));
						} catch (e) {
							//throw e;
						}
*/
						//balancesInUSDT[coin] = amount * price;
						brojacUSDT = brojacUSDT + (amount * price);
					}
				}
			}
		}
		// Return the balances in USDT object
//		throw new Error("STOP EXECUTION!!!");
		return brojacUSDT;
	} catch (e) {
		throw e;
	}
}
// x=1 BuyBuySell, x=2 BuySellSell

async function fetchLimitPrice(symbol,askbid) {
	
	// askbid for fetchTicker - ask/bid, for fetchOrderBook - asks/bids
	let price;
	const ticker = await exchange.fetchOrderBook(symbol,1); //await exchange.fetchTicker(symbol);
	

	price = ticker[askbid][0][0]; //ticker[askbid];

	if (price === undefined) { return 0;}
	return price;

}


// Get market price of symbol
async function fetch_current_ticker_price(ticker) {
	// use the await keyword to fetch the ticker details
	let current_ticker_details = await exchange.fetchTicker(ticker);
	// use the ternary operator to assign the ticker price or null
	let ticker_price = current_ticker_details ? current_ticker_details["close"] : undefined; // "last" bese "close", probvam so last dali e ok.
	//console.log(ticker_price);
	return ticker_price;
}

// use the Math object instead of the math module for mathematical operations
function check_if_float_zero(value) {
	return Math.abs(value - 0.0) <= 1e-3;
};

// round number on X decimals
function roundNumber(number, X) {
// Declare a constant variable to store the result
const ret = Math.round(number * Math.pow (10, X)) / Math.pow (10, X);
// Return the result
return ret;
}

function checkPriceOk(price) {
	return price === undefined ? 0 : price;
	
}

async function calculateTriangularTrade(type, symbol1, symbol2, symbol3, orderBook1, orderBook2, orderBook3, initialamount, fee) {

	let _finalPrice, _limit_qty, _limit_prices, _amountCheck,_limitCheck,_fee1;
	let orderPrice1, orderPrice2, orderPrice3, limitPrice1, limitPrice2, limitPrice3;

	_finalPrice = 0;
	_limit_qty = {};
	_limit_prices = {};

	// BUY_BUY_SELL PREDICTION
	if (type === 'bbs') {

		// buy base - with input quote -> ex: BTC/USDT, BTC=base, USDT=quote
		_amountCheck = initialamount;
		orderPrice1 = checkPriceOk(orderBook1['asks'][0][0]);
		//orderPrice1 = (orderBook1['asks'][0][0]+ orderBook1['bids'][0][0])/2;
		orderAmount1 = orderBook1['asks'][0][1];


		_limitCheck = orderAmount1*orderPrice1;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder1 = _amountCheck/orderPrice1;
			_fee1 = fee === '-1' ? await getOrderFee(symbol1, ORDER_TYPE, 'buy', limitOrder1, orderPrice1) : fee/100;
			//console.log(_fee1);
			limitOrder1 = limitOrder1 * (1-_fee1);

			limitOrder1 = await setSymbolPrecision(symbol1,limitOrder1);

			limitPrice1 = orderPrice1;
			//console.log('\tbuy',symbol1, limitOrder1, limitPrice1);


		} else {
			return [0,0,0];
		}

		//buy base - with input quote
		_amountCheck = limitOrder1;
		orderPrice2 = checkPriceOk(orderBook2['asks'][0][0]);
		//orderPrice2 = (orderBook2['asks'][0][0]+ orderBook2['bids'][0][0])/2;
		orderAmount2 = orderBook2['asks'][0][1];

		_limitCheck = orderAmount2*orderPrice2;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder2 = _amountCheck/orderPrice2;
			_fee1 = fee === '-1' ? await getOrderFee(symbol2, ORDER_TYPE, 'buy', limitOrder2, orderPrice2) : fee/100;
			limitOrder2 = limitOrder2 * (1-_fee1);
			limitOrder2 = await setSymbolPrecision(symbol2,limitOrder2);
			limitPrice2 = orderPrice2;
			//console.log('\tbuy',symbol2, limitOrder2, limitPrice2);

		} else {
			return [0,0,0];
		}

		//sell base - wuth input value base
		_amountCheck = limitOrder2;
		orderPrice3 = checkPriceOk(orderBook3['bids'][0][0]);
		//orderPrice3 = (orderBook3['asks'][0][0]+ orderBook3['bids'][0][0])/2;
		orderAmount3 = orderBook3['bids'][0][1];

		_limitCheck = orderAmount3;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder3 = _amountCheck;
			limitOrder3 = await setSymbolPrecision(symbol3,limitOrder3)
			limitPrice3 = orderPrice3;
			//console.log('\tsell',symbol3, limitOrder3, limitPrice3);

			_fee1 = fee === '-1' ? await getOrderFee(symbol3, ORDER_TYPE, 'sell', limitOrder3, orderPrice3) : fee/100;
			_finalPrice = roundNumber(limitOrder3*limitPrice3 * (1-_fee1),5);

		} else {
			return [0,0,0];
		}
	}

	// BUY_SELL_SELL PREDICTION
	if (type === 'bss') {

		// buy base - with input quote -> ex: BTC/USDT, BTC=base, USDT=quote
		_amountCheck = initialamount;
		orderPrice1 = checkPriceOk(orderBook1['asks'][0][0]);
//		orderPrice1 = (orderBook1['asks'][0][0]+ orderBook1['bids'][0][0])/2;
		orderAmount1 = orderBook1['asks'][0][1];

		_limitCheck = orderAmount1*orderPrice1;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder1 = _amountCheck/orderPrice1;

			_fee1 = fee === '-1' ? await getOrderFee(symbol1, ORDER_TYPE, 'buy', limitOrder1, orderPrice1) : fee/100;
			limitOrder1 = limitOrder1 * (1-_fee1);
			limitOrder1 = await setSymbolPrecision(symbol1,limitOrder1);
			limitPrice1 = orderPrice1;
		//	console.log('\tbuy',symbol1, limitOrder1, limitPrice1);


		} else {
			return [0,0,0];
		}

		//sell base - with input base
		_amountCheck = limitOrder1;
		orderPrice2 = checkPriceOk(orderBook2['bids'][0][0]);
//		orderPrice2 = (orderBook2['bids'][0][0]+ orderBook2['bids'][0][0])/2;
		orderAmount2 = orderBook2['bids'][0][1];

		_limitCheck = orderAmount2;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder2 = _amountCheck;
//			console.log('limitOrder2_1',limitOrder2)
			limitOrder2 = await setSymbolPrecision(symbol2,limitOrder2);
	//		console.log('limitOrder2_2',limitOrder2)
			limitPrice2 = orderPrice2;
	//		console.log('\tsell',symbol2, limitOrder2, limitPrice2);

		} else {
			return [0,0,0];
		}

		//sell base - wuth input base
		_fee1 = fee === '-1' ? await getOrderFee(symbol2, ORDER_TYPE, 'sell', limitOrder2, orderPrice2) : fee/100;
		_amountCheck = limitOrder2*limitPrice2*(1-_fee1);
		orderPrice3 = checkPriceOk(orderBook3['bids'][0][0]);
//		orderPrice3 = (orderBook3['bids'][0][0]+ orderBook3['bids'][0][0])/2;
		orderAmount3 = orderBook3['bids'][0][1];

		_limitCheck = orderAmount3;

		if (_limitCheck-_amountCheck >= 0) {
			limitOrder3 = _amountCheck;
//			console.log('limitOrder3_1',limitOrder3)
			limitOrder3 = await setSymbolPrecision(symbol3,limitOrder3)
//			console.log('limitOrder3_2',limitOrder3)
			limitPrice3 = orderPrice3;
//			console.log('\tsell',symbol3, limitOrder3, limitPrice3);

			_fee1 = fee === '-1' ? await getOrderFee(symbol3, ORDER_TYPE, 'sell', limitOrder3, orderPrice3): fee/100;
			_finalPrice = roundNumber(limitOrder3*limitPrice3 * (1-_fee1),5);

		} else {
			return [0,0,0];
		}
	}


		_limit_qty = {
			[symbol1]: limitOrder1,
			[symbol2]: limitOrder2,
			[symbol3]: limitOrder3
		};

		_limit_prices = {
			[symbol1]: limitPrice1,
			[symbol2]: limitPrice2,
			[symbol3]: limitPrice3
		};
		return [_finalPrice, _limit_qty, _limit_prices]


}

async function getOrderFee(symbol, type, side, amount, price) {
	let fee;
	try {
		fee = await exchange.calculateFee(symbol, type, side, amount, price);
		//console.log('rate',fee.rate, 'cost',fee.cost);
		//console.log(fee);
		return fee.rate;

	} catch (e) {
		throw e;
	}
}


// find all current prices for 1 triangular combination for buy_buy_sell
async function check_buy_buy_sell(scrip1, scrip2, scrip3, initial_investment) {
	let final_price, scrip_qty,  scrip_prices, orderBook;

	final_price = 0;
	scrip_prices = {};
	scrip_qty = {};

	orderBook = await Promise.all([exchange.fetchOrderBook(scrip1), exchange.fetchOrderBook(scrip2), exchange.fetchOrderBook(scrip3)]);
	[final_price,scrip_qty, scrip_prices] = await calculateTriangularTrade('bbs', scrip1, scrip2, scrip3, orderBook[0], orderBook[1], orderBook[2], initial_investment, BROKERAGE_PER_TRANSACTION_PERCENT);

	return [final_price,scrip_qty, scrip_prices];
}

// find all current prices for 1 triangular combination for buy_sell_sell
async function check_buy_sell_sell(scrip1, scrip2, scrip3, initial_investment) {
	let final_price, scrip_qty,  scrip_prices, orderBook;

	final_price = 0;
	scrip_prices = {};
	scrip_qty = {};
	
	// original bellow, for polonex argument 2 is removed
	//orderBook = await Promise.all([exchange.fetchOrderBook(scrip1,1), exchange.fetchOrderBook(scrip2,1), exchange.fetchOrderBook(scrip3,1 )]);
	orderBook = await Promise.all([exchange.fetchOrderBook(scrip1), exchange.fetchOrderBook(scrip2), exchange.fetchOrderBook(scrip3)]);
	[final_price,scrip_qty, scrip_prices] = await calculateTriangularTrade('bss', scrip1, scrip2, scrip3, orderBook[0], orderBook[1], orderBook[2], initial_investment, BROKERAGE_PER_TRANSACTION_PERCENT);

	return [final_price,scrip_qty, scrip_prices];
}

// check profit of 1 triangular combination
async function check_profit_loss(total_price_after_sell, initial_investment, transaction_brokerage, min_profit) {
  var apprx_brokerage, min_profitable_price, profit_loss;
  apprx_brokerage = 0; //transaction_brokerage * initial_investment / 100 * 3;
  min_profitable_price = initial_investment + apprx_brokerage + min_profit;

  profit_loss = Math.round((total_price_after_sell - min_profitable_price) * 1e7) / 1e7;
  //profit_loss = total_price_after_sell - min_profitable_price;

  return profit_loss;
}



// TUTORIAL
/* RequestTimeout
This exception is raised when the connection with the exchange fails or data is not fully received in a specified amount of time. This is controlled by the timeout option. When a RequestTimeout is raised, the user doesn't know the outcome of a request (whether it was accepted by the exchange server or not).

Thus it's advised to handle this type of exception in the following manner:

for fetching requests it is safe to retry the call
for a request to cancelOrder() a user is required to retry the same call the second time. A subsequent retry to cancelOrder() will return one of the following possible results:
a request is completed successfully, meaning the order has been properly canceled now
an OrderNotFound exception is raised, which means the order was either already canceled on the first attempt or has been executed (filled and closed) in the meantime between the two attempts.
if a request to createOrder() fails with a RequestTimeout the user should:
call fetchOrders(), fetchOpenOrders(), fetchClosedOrders() to check if the request to place the order has succeeded and the order is now open
if the order is not 'open' the user should fetchBalance() to check if the balance has changed since the order was created on the first run and then was filled and closed by the time of the second check. */

async function fetchOrderInternal(orderID, symbol, count) {
	let order;
	if (!count) {count = 1;};
	try {
		 order = await exchange.fetchOrder(orderID, symbol);
		 //waiting(1)
	} catch (e) {
		console.log('fetch',e.message);
		//waiting(1);
		if (count < 9) { 
			await fetchOrderInternal(orderID, symbol,count+1);
		} else {
			return [true, 'closed'];
		}
	}
	return [true, order.status];
	
}

async function cancelOrderInternal(orderID, symbol,count) {
	let order;
	try {
		 order = await exchange.cancelOrder(orderID, symbol);
		 waiting(1);
	} catch (e) {
		console.log('cancel', e.message);
		//waiting(1);
		if (count < 2) { 
			await cancelOrderInternal(orderID, symbol,2);
		} else {
			return [false,'canceled'];
		}
	}
	return [false,order.status];
	
}
//limit buy order
async function place_buy_order_limit(scrip, quantity,price,x) {

	let  order,qty, price1, orderID, order_c, orderTime;
	let  check = false;
	let  z = 1;
	let buffer = '>>>>>>';

		try {
	        while(!check) {

				price1 = await fetchLimitPrice(scrip,'asks');
				if (x === 1) {
					qty = quantity;
				} else {
					qty = await getBalance(scrip.split('/')[1])/price1;
					qty = await setSymbolPrecision(scrip,qty);
				}

				if (z>1) {buffer = colors.blue(buffer);}
				console.log ('\t',buffer,z,'. try - Creating',ORDER_TYPE,'BUY order: ', scrip,  qty , price1);
				order = await exchange.createOrder (scrip, ORDER_TYPE, 'buy', qty ,price1);
				orderID = order.id;
				orderTime = order.timestamp;
				[check,order.status] = await fetchOrderInternal(orderID, scrip);

				console.log('1',colors.yellow(orderID),order.status);
   				while (order.status === 'open') {
					waiting(0.5);
					try {
						
						[check,order.status] = await fetchOrderInternal(orderID, scrip);
						console.log('1.1.',colors.yellow(orderID),order.status);					
						
						if ((Date.now() - orderTime) > 3000) {
						
							if (order.status === 'open') {
								[check,order.status] = await cancelOrderInternal(orderID, scrip);
								console.log('1.2.',colors.yellow(orderID),order.status);
					        	break;		
							} 
						}
					} catch (e) {

						console.log('ovde buy', e);
						//check = true;
						//console.log('-closed');
						//break;
						
					}
				} 
				
				if (order.status === 'canceled') { check = false; console.log('2',order.status);}
				if (order.status === 'closed' || order.status === 'filled' || order.status === undefined) {
					waiting(waitingSecondsBetweenTrades);
					check = true; console.log('3',order.status); break;
				}
				
				z++;
			}

		} catch (e) {
			throw e;

		}

		console.log('\t>>> BUY order created!')

}

//limit sell order
async function place_sell_order_limit(scrip, quantity,price,x) {

	let  order,qty, orderID, price1, order_c, orderTime;
	let  check = false;
	let  z = 1;
	let buffer = '>>>>>>';

		try {
			while(!check) {
				price1 = await fetchLimitPrice(scrip,'bids');
				qty = Math.min(await getBalance(scrip.split('/')[0]),quantity);
				qty =  await setSymbolPrecision(scrip,qty);

				if (z>1) {buffer = colors.blue(buffer);}
				console.log ('\t',buffer,z,'. try - Creating ',ORDER_TYPE,' SELL order: ', scrip, qty, price1);
				order = await exchange.createOrder (scrip, ORDER_TYPE, 'sell', qty ,price1);
				orderID = order.id;
				orderTime = order.timestamp;
					[check,order.status] = await fetchOrderInternal(orderID, scrip);
							
				console.log('1',colors.yellow(orderID),order.status);
				
   				while (order.status === 'open') {
					waiting(0.5);
					try {
					
						[check,order.status] = await fetchOrderInternal(orderID, scrip);
						console.log('1.1.',colors.yellow(orderID),order.status);
						
						if ((Date.now() - orderTime) > 3000) {

							if (order.status === 'open') {
								[check,order.status] = await cancelOrderInternal(orderID, scrip);
								console.log('1.2.',colors.yellow(orderID),order.status);
								break;
							}	
													
						}
					} catch (e) {

						console.log('ovde buy', e);
						//check = true;
						//console.log('-closed');
						//break;
						
					}
				} 
				if (order.status === 'canceled') { check = false; console.log('2',order.status);}
				if (order.status === 'closed' || order.status === 'filled' || order.status === undefined) {
					waiting(waitingSecondsBetweenTrades);
					check = true; console.log('3',order.status); break;
				}
				z++;
			}

		} catch (e) {
				throw e;
		}

		console.log('\t>>> SELL order created!')

}

async function qtyToPrecision(scrip, quantity) {
			return Math.round((quantity) * 1e8) / 1e8;
}

async function quantity_To_Precision(scrip, quantity) {
	try {
		let formatedqty = await exchange.amountToPrecision (scrip, quantity);
		return formatedqty;
	} catch (e) {
		//throw e;
		return 0;
	}
}


async function place_order(type,scrip,qty1,price,x) {
	let order;
		try {
				if (type === 'BUY') {

						order = await place_buy_order_limit(scrip, qty1,price,x);

				} else {

					if (type === 'SELL') {

							order = await place_sell_order_limit(scrip, qty1, price,x);


					}
				}
		} catch (e) {
			throw e;
		}
}

async function place_trade_orders(type, scrip1, scrip2, scrip3, initial_amount, scrip_prices, scrip_qty) {
  var order;

  let problem = 0;
  console.log(colors.blue('************************************************************************************************************'));

  try {
	  if (type === "BUY_BUY_SELL") {

		try {
			order = await place_order('BUY',scrip1, scrip_qty[scrip1],scrip_prices[scrip1],1);
		} catch (e) {problem = 1; throw e;}


			try {
				order =await place_order('BUY',scrip2,scrip_qty[scrip2],scrip_prices[scrip2],2);
			} catch (e) {problem = 2; throw e;}

				try {
					order =await place_order('SELL',scrip3,scrip_qty[scrip3],scrip_prices[scrip3],3);
				} catch (e) {problem = 3; throw e;}
		} else {
			if (type === "BUY_SELL_SELL") {

				try {
					order = await place_order('BUY',scrip1,scrip_qty[scrip1],scrip_prices[scrip1],1);
				} catch (e) {problem = 4; throw e;}

					try {
						order = await place_order('SELL',scrip2,scrip_qty[scrip2],scrip_prices[scrip2],2);
					} catch (e) {problem = 5; throw e;}

						try {
							order = await place_order('SELL',scrip3,scrip_qty[scrip3],scrip_prices[scrip3],3);
						} catch (e) {problem = 6; throw e;}
			}

		}
		 console.log(colors.blue('************************************************************************************************************'));
		//return true;
	} catch (e) {
		 console.log('Problem: ', problem, e.message);
		 //return 0;
		throw e;
	}
}

async function perform_triangular_arbitrage(scrip1, scrip2, scrip3, arbitrage_type, initial_investment, transaction_brokerage, min_profit) {
	var final_price, profit_loss, scrip_prices, scrip_qty;
	let bal, balBTC, balBNB, balETH;
	let trueCheck = false;
	let checkNear = false;
	let textcolor = [];
	final_price = 0.0;
	profit_loss = 0;


	if (arbitrage_type === "BUY_BUY_SELL") {
		[final_price, scrip_qty, scrip_prices] = await check_buy_buy_sell(scrip1, scrip2, scrip3, initial_investment);
	} else {
		if (arbitrage_type === "BUY_SELL_SELL") {
			[final_price, scrip_qty, scrip_prices] = await check_buy_sell_sell(scrip1, scrip2, scrip3, initial_investment);
		}
	}

		profit_loss = await check_profit_loss((final_price), initial_investment, transaction_brokerage, min_profit);



			textcolor[1] = arbitrage_type+' '+scrip1+' '+scrip2+' '+scrip3;
			textcolor[4] = 'Final Price: '+final_price;
			textcolor[5] = 'P/L Market: '+profit_loss;


		if (final_price >=initial_investment*0.995) {
			textcolor[4] = colors.brightBlue.bold('Final Price:',final_price);
		}
		if (final_price >=initial_investment*0.9985) {
			textcolor[4] = colors.yellow.bold('Final Price:',final_price);
		}
		if (final_price >=initial_investment*0.999) {
			textcolor[4] = colors.magenta.bold('Final Price:',final_price);
			checkNear = false; // dali da povtori proverka na ovoj triangl pak, true e DA
		}
		if (profit_loss > 0) {
			textcolor[5] = colors.green.bold('P/L Market:',profit_loss);
			textcolor[4] = colors.green.bold('Final Price:',final_price);
			checkNear = false;
		}

		count = count+1;

		console.log(count,exchangeName,':',roundNumber(PROFIT-BALANCESTART,4),'\t',textcolor[1],textcolor[5],textcolor[4]);
		if (checkNear) {count--; return true;}

		if ( profit_loss > 0) { // && (final_price-final_price_AB) <= initial_investment/100*1/3) {

			console.log(colors.green('>>>EXECUTE TRANSACTIONS:'),arbitrage_type, scrip1,scrip2,scrip3, 'P/L:', profit_loss);
			count = count -1;

			try {	//

				if (testingServer) {
					// test server trading
					checker = await place_trade_orders(arbitrage_type, scrip1, scrip2, scrip3, initial_investment, scrip_prices, scrip_qty);
				} else {
					// staging	server real trading (uncomment for real trade)
					if (imatPari) {
						checker = await place_trade_orders(arbitrage_type, scrip1, scrip2, scrip3, initial_investment, scrip_prices, scrip_qty);
					}
				}

				//setTimeout(() => {}, "1000");


				bal = await getBalance('USDT')// so trading
				//balBTC = await getBalance('BTC');
				//balBNB = await getBalance('BNB');
				//balETH = await getBalance('ETH');



				if (testingServer) {
					PROFIT = (bal); // calculation for real trading on test server
				}else {
					// calculation for real trading on staging server (uncomment for real data, comment calculation below)
					if (imatPari) {
						PROFIT = (bal);
					} else {
						// calculation for testing without trading
						PROFIT = PROFIT + (final_price-initial_investment); //bez trading presmetka
					}
				}


				if (PROFIT >= PRETHODEN_PROFIT) {
					// ako najde triangular t.e. ako vleze tuka -> povtorno se izvrsuva ovaa funkcija so isti vrednosti
					PRETHODEN_PROFIT = PROFIT;
					trueCheck = true;

				} else {

					//console.log('');
					//await addBadToken(arbitrage_type, scrip1, scrip2,scrip3,PROFIT, PRETHODEN_PROFIT);
					//console.log(badTokensInfo);
					console.log(colors.red('>>> Profit pomal od prethodna triangular kombinacija, baram druga triangular kombinacija!'));
					console.log('');
					PRETHODEN_PROFIT = PROFIT;
					trueCheck = false;

				}

				let balanceUSD;

				if (trueCheck) {

					console.log('USDT :', bal, 'BTC:',balBTC, 'ETH:',balBTC, 'BNB:',balBNB);
				} else {
					console.log('USDT :', bal, 'BTC:',balBTC, 'ETH:',balBTC, 'BNB:',balBNB);
					//throw new Error("NO PROFIT! STOP Execution!");
				}

				console.log('');
/*
				if (bal <= INVESTMENT_AMOUNT_DOLLARS) {

					throw new Error("LOOSER!!! STOP Execution!");

				}
*/
				return trueCheck;


			} catch(error) {
				console.log('new order problem:', error);
				throw error;
			}

		}

	return false;
}



async function getBalance(token) {
    let balanceToken;
	try {
		let balanceAll = await exchange.fetchBalance();
		balanceToken = balanceAll.free[token];
		if (balanceToken === undefined) { balanceToken = 0; }
		return balanceToken;
	} catch (e) {
		throw e;
	}
}

// KUPI PRODAJ TOKEN samo vo USDT, USDTamount = za kolku USDT kupuvame base token
async function buyToken(symbol,USDTamount) {
	let order;
	try {

		order = await exchange.createOrder (symbol, 'market', 'buy', null, null, {'quoteOrderQty': USDTamount});

		while (order.status !== "closed") {
			order = await exchange.fetchOrder(order.id, order.symbol);
		}
		if (order.status === "closed") {
			console.log('Buy ',symbol.split('/')[0],' for ', USDTamount, symbol.split('/')[1]);
			console.log(colors.blue('Transaction closed'));
		}
		return true;
	} catch (e) {
		throw e;
	}
}

// KUPI PRODAJ TOKEN za site, amount = kolicina na kupuvanje za base token
async function buyTokenAll(symbol,amount) {
	let order;
	try {

		order = await exchange.createOrder (symbol, 'market', 'buy', amount);

		while (order.status !== "closed") {
			order = await exchange.fetchOrder(order.id, order.symbol);
		}
		if (order.status === "closed") {
			console.log('Buy ',amount,symbol.split('/')[0]);
			//console.log('Transaction closed');
		}
		return true;
	} catch (e) {
		throw e;
	}
}

// PRODAJ TOKEN - ZA TEST, amount = kolicina na prodavaje na base token
async function sellToken(ticker,amount) {
	let price,order;

	try {
		price = await exchange.fetchTicker(ticker);
		//formattedAmount = await exchange.amountToPrecision(ticker, qty);
		order = await exchange.createOrder(ticker, 'market', 'sell', amount);

		while (order.status !== "closed") {
			try {
				order = await exchange.fetchOrder(order.id, order.symbol);
				if (order.status === "expired") {break;};
			} catch (e) {
				throw e;
			}
		}
		console.log('================> PRODADENO:',amount,ticker.split('/')[0],'na',ticker.split('/')[1],'!');

	} catch (e) {
		console.log('INFO:',ticker,amount, e.message);
		throw e;
	} finally {}
}

async function addBadToken(arbitrage_type, symbol1, symbol2,symbol3, profit, prethoden_profit) {
	 let badCombination; //,realfee;
	 //realfee = 100 - ((profit-prethoden_profit+initial_investment)/initial_investment*100);
	// console.log(realfee);

	 if ((profit - prethoden_profit) < (-BAD_TOKEN_MAX_LOSE)) {
		badCombination = {
			"arbitrage_type": arbitrage_type,
			"symbol1": symbol1,
			"symbol2": symbol2,
			"symbol3": symbol3

		};
		badTokensInfo.push(badCombination);
		console.log('--> Added to bad tokens list!!!');
	 }
 }

// Define a function that takes four input parameters
async function findRealFee(arbitrage_type, symbol1, symbol2, symbol3) {

	// Loop through the array below
	for (let item of badTokensInfo) {
		// Check if the input parameters match the item in all four properties
		if (
		arbitrage_type === item.arbitrage_type &&
		symbol1 === item.symbol1 &&
		symbol2 === item.symbol2 &&
		symbol3 === item.symbol3
		) {
			// Return the realfee value of the item
			//console.log('realfee',item.realfee);
			console.log('--> not real fee data!!! Go to next combination.');
			return false;

		}
	}

	// If no match is found, return 0
	return true;
}

async function consolePrint() {
	process.stdout.write('.');
}

async function marketNotional_ (symbol) {

	let minNotional = await exchange.market(symbol);
//	console.log (symbol, minNotional['limits']['amount']['min']);
	return minNotional['limits']['amount']['min'];
}

async function marketPrecision_ (symbol) {

	let precision1 = await exchange.market(symbol);
	return precision1['precision']['amount'];
}


function countDecimals (number) {

	if (Math.floor (number) === number) {
		// Return 0 as the number of decimals
		return 0;
	}
	// Convert the number to a string in scientific notation
	let parts = number.toExponential ().split ('e');
	// Return the absolute value of the exponent as the number of decimals
	//console.log('countdecimals',number, 'return', Math.abs (parts[1]));
	return Math.abs (parts[1]);
/*
	console.log('countdecimals',number);
	// Check if the number is an integer, meaning it has no decimals

	// Convert the number to a string and split it by the decimal point
	let parts = number.toString ().split (".");
	// Return the length of the second part, which is the number of decimals
	return parts[1].length;
*/
}


// Define a function that returns the dust limit for the coin
function getDustLimit(coin) {
	// Get the market symbol for the coin and USDT pair
	let symbol = coin + '/USDT';

	// Get the market information for the symbol from the markets dictionary
	let market = markets[symbol];

	// Get the dust limit from the market information
	let dustLimit = market['limits']['amount']['min'];

	// Return the dust limit
	return dustLimit;
}

// Define a function that returns the number of decimal places for the coin
async function getDecimals(symbol) {


	// Get the market information for the symbol from the markets dictionary
	let market = await exchange.market(symbol);

	// Get the number of decimal places from the market information
	let decimals = market['precision']['amount'];

	// Return the number of decimal places
	return decimals;

}

async function setSymbolPrecision(symbol, qty) {
	let precision1, precision2;
	let qtyNumberPrecision;
	let market = await exchange.market(symbol);

	precision1 = market['precision']['amount'];
//console.log('precision',precision1 )
	if (Number.isInteger(precision1)) {
		precision2 = precision1;
	} else {
		precision2 = countDecimals(precision1);
	}

	let factor = Math.pow(10, precision2);
	if (factor !== 0) {
		qtyNumberPrecision = Math.trunc(qty*factor)/factor;
	} else {
		qtyNumberPrecision = Math.trunc(qty);
	}
	return qtyNumberPrecision;


}

function waiting(sec) {
	let time1 = Date.now();
	while (true) {
		if ((Date.now() - time1) > sec*1000) {
			break;
		}
	}
}

async function getAllMexcTokens() {
	let niza = ['USDC/USDT', 'XWG/USDC','XWG/USDT','HOP/USDC','HOP/USDT','USDD/USDC','USDD/USDT','PAXG/USDC','PAXG/USDT','CGPT/USDC','CGPT/USDT','ENS/USDC','ENS/USDT','WBTC/USDC','WBTC/USDT','STETH/USDC','STETH/USDT','AZERO/USDC','AZERO/USDT','EUL/USDC','EUL/USDT','MX/USDC','MX/USDT','GAL/USDC','GAL/USDT','FITFI/USDC','FITFI/USDT','RAY/USDC','RAY/USDT','*****/USDC','*****/USDT','CEL/USDC','CEL/USDT'];

	return niza;
}

async function getAllBitMartTokens() {
	
	let niza = ['1INCH/USDT','AAVE/USDT','ACH/USDT','ADA/USDC','ADA/USDT','AGB/USDT','AGI/USDT','AGIX/USDT','AIDOGE/USDT','ALGO/USDC','ALGO/USDT','AMP/USDT','ANKR/UST','APE/USDC','APE/USDT','APT/USDT','ARB/USDT','ARKM/USDT','ARPA/USDT','ASTR/USDT','ATOM/BTC','ATOM/USDT','AUDIO/USDT','AVAX/USDT','AXS/USDT','BAT/USD','BCH/BTC','BCH/USDT','BEN/USDT','BLUR/USDT','BNB/BTC','BNB/USDT','BOB/USDT','BONE/USDT','BSV/USDT','BTC/DAI','BTC/TUSD','BTC/USDC','BTC/USDT','BTT/UDT','CAKE/USDT','CANDY/USDT','CAPO/USDT','CETUS/USDT','CFX/USDT','CGPT/USDT','CHZ/USDT','COMBO/USDT','CRO/USDT','CRV/USDT','CVX/USDT','CYBER/USDT','DAH/BTC','DASH/USDT','DOGE/USDC','DOGE/USDT','DOT/USDT','DYDX/USDT','DZOO/USDT','EDU/USDT','EGLD/USDT','ELF/USDT','ENJ/USDT','EOS/USDC','EOS/USDT','ETC/SDT','ETH/BTC','ETH/DAI','ETH/TUSD','ETH/USDC','ETH/USDT','EURT/USDT','FERC/USDT','FET/USDT','FIL/USDT','FLOKI/DOGE','FLOKI/USDT','FLOW/USDT','FLR/USD','FTM/USDT','FTT/USDT','FUN/USDT','FXS/USDC','GALA/USDC','GALA/USDT','GLMR/USDT','GMX/USDT','GRT/USDT','HBAR/USDT','HNT/USDT','HOOK/USDT','HT/USDT','CP/USDT','ID/USDT','IMX/USDT','IRON/USDT','JOE/USDT','KAS/USDT','KEY/USDT','KLAY/USDT','KNC/USDT','LADYS/USDT','LBR/USDT','LDO/USDC','LDO/USDT','LINK/TC','LINK/ETH','LINK/USDT','LMWR/USDT','LQTY/USDT','LRC/USDT','LTC/BTC','LTC/ETH','LTC/USDC','LTC/USDT','LUNC/USDT','MAGIC/USDT','MANA/USDT','MATIC/BT','MATIC/USDC','MATIC/USDT','MAV/USDT','MDT/USDT','MEME/USDT','MEMEBRC/USDT','MINA/USDT','MKR/BTC','MNT/USDT','MONG/USDT','MYRIA/USDT','NEAR/USDT','NE/ETH','NEO/USDT','NTRN/USDT','OBI/USDT','OKB/USDT','OP/USDC','OP/USDT','ORDI/USDT','OSMO/USDT','PAW/USDT','PENDLE/USDT','PEPE/USDT','PEPEBRC/USDT','PIABRC/USDT','PLAY/USDT','PVFYBO/USDT','QNT/USDT','RFD/USDT','RPL/USDT','RUNE/USDT','SAMO/USDT','SAND/USDC','SAND/USDT','SATS/USDT','SEI/USDT','SHIB/USD','SHIB/USDT','SHIBAI/USDT','SHRAP/USDT','SNX/USDT','SOL/USDC','SOL/USDT','SPONGE/USDT','SSWP/USDT','STPT/USDT','STX/USDT','SUI/USDT','SYS/USDT','THET/USDT','TIA/USDT','TON/USDT','TRB/USDT','TRX/BTC','TRX/ETH','TRX/USDC','TRX/USDT','TWT/USDT','UNI/USDT','VELA/USDT','VELO/USDT','VET/BTC','VET/ETH','VT/USDT','VMPX/USDT','WBTC/USDT','WEMIX/USDT','WLD/USDT','WOJAK/USDT','WSB/USDT','XAUT/USDT','XLM/BTC','XLM/ETH','XLM/USDC','XLM/USDT','XMR/BTC','XMR/UDT','XRD/USDT','XRP/USDT','XTZ/USDT','YFI/USDT','ZEC/USDT','ZIL/BTC','ZZZ/USDT','$LINA/USDT','$PRIME/USDT','$TRAC/USDT','$TURBO/USDT'];
	
	return niza;
}

async function getAllBitMexTokens() {
	
	let niza = ['BTC/USDT','BMEX/USDT','SOL/USDT','ETH/BTC','ETH/USDT','TRX/USDT','UNI/USDT','LINK/USDT','APE/USDT','AXS/USDT','MATIC/USDT'];
	
	return niza;
}


 

NodeJS Online Compiler

Write, Run & Share NodeJS code online using OneCompiler's NodeJS online compiler for free. It's one of the robust, feature-rich online compilers for NodeJS language,running on the latest LTS version NodeJS 16.14.2. Getting started with the OneCompiler's NodeJS editor is easy and fast. The editor shows sample boilerplate code when you choose language as NodeJS and start coding. You can provide the dependencies in package.json.

About NodeJS

Node.js is a free and open-source server environment. Node.js is very popular in recent times and a large number of companies like Microsoft, Paypal, Uber, Yahoo, General Electric and many others are using Node.js.

Key features

  • Built on Google chrome's javascript engine V8 and is pretty fast.
  • Node.js was developed by Ryan Dahl in 2009.
  • Server-side platform for building fast and scalable applications.
  • Node.js is Asynchronous, event-driven and works on single-thread model thus eliminating the dis-advantages of multi-thread model.
  • Supports various platforms like Windows, Linux, MacOS etc.
  • Provides rich library of java script modules which simplifies the development efforts.
  • Released under MIT license.

Express Framework

Express is one of the most popular web application framework in the NodeJS echosystem.

  • Pretty fast
  • Minimalist
  • Unopinionated
  • Very flexible

Syntax help

Examples

Using Moment

let moment = require('moment');

console.log(moment().format('MMMM Do YYYY, h:mm:ss a'));

Using Lodash

const _ = require("lodash");

let colors = ['blue', 'green', 'yellow', 'red'];

let firstElement = _.first(colors);
let lastElement = _.last(colors);

console.log(`First element: ${firstElement}`);
console.log(`Last element: ${lastElement}`);

Libraries supported

Following are the libraries supported by OneCompiler's NodeJS compiler.

  • lodash
  • moment
  • underscore
  • uuid
  • ejs
  • md5
  • url