/* -------------------------------------------------------------------------- */ /* 자바스크립트 전역변수 */ /* -------------------------------------------------------------------------- */ const exchangeOption = (function() { let _usdtToWon = null; let _fee = null; let _minAmt = 1; // 최소 거래갯수 return { getMinAmt: function() { return _minAmt; }, getUsdtToWon: function() { return _usdtToWon; }, setUsdtToWon: function(newRate) { _usdtToWon = newRate; }, getFee: function(params) { return params?.isPercentage ? _fee * 0.01 : _fee; }, setFee: function(newFee) { _fee = newFee; }, }; })(); const apiConfig = (function() { let _server = 'https://img.gaia-global.co.kr'; // API 서버 // let _server_202310171650 = 'http://221.145.168.238:8784'; // let _server_202310101110 = 'http://112.160.98.20:8784'; let _successSign = 'true'; // API서버에 AJAX 통신 반환값이 이것일 때 성공이라고 보장. let _hogaLength = 10 + 2; // 호가(ask, bid) 표시 개수 let _subjectObj = { pairName: 'sow_usdt', unitPrice: null, }; return { getServer: function() { return _server; }, getSuccessSign: function() { return _successSign; }, getHogaLength: function() { return _hogaLength; }, getSubjectObj: function() { return _subjectObj; }, setSubjectObj: function(newUnitPrice) { _subjectObj = { pairName: 'sow_usdt', unitPrice: newUnitPrice, }; } }; })(); /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* DOCUMENT.READY */ /* -------------------------------------------------------------------------- */ $(function () { refreshHogaList(); refreshExchangeSummary(); setInterval(() => { refreshHogaList(); refreshExchangeSummary(); }, 1000); //인풋 - 이벤트바인딩 $('#BuyAmt, #SellAmt').on('keydown, keyup', function (event) { const keyCode = event.keyCode; if (!(keyCode >= 48 && keyCode <= 57)) return; //숫자입력이 아니라면, 여기서 멈춤 let amt = parseFloatInput(event.target.value); event.target.value = amt.toLocaleString('ko-KR'); const total_USDT = $(event.target).closest('tbody').find('.total_USDT'); const total_WON = $(event.target).closest('tbody').find('.total_WON'); const orderAmt = $(event.target).closest('tbody').find('.order_amt'); $(total_USDT).val((amt * apiConfig.getSubjectObj().unitPrice).toFixed(4)); $(total_WON).text((amt * apiConfig.getSubjectObj().unitPrice * exchangeOption.getUsdtToWon()).toFixed(4)); $(orderAmt).val((amt * apiConfig.getSubjectObj().unitPrice).toFixed(4)); }); }); //doc.ready /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* 일반 함수 */ /* -------------------------------------------------------------------------- */ function refreshHogaList() { API_ASKBID_PRICE({pairName: apiConfig?.getSubjectObj().pairName}, result => { const arrBid = (result?.data?.bids)?.slice(0, apiConfig?.getHogaLength()); // 필요한만큼 적절히 배열을 자름 const totalBidAmt = arrBid.reduce((a,b) => a[1] + b[1]); //총 수량 const arrAsk = ((result?.data?.asks)?.slice(0, apiConfig?.getHogaLength())).reverse(); // 뒤집고, 필요한만큼 적절히 배열을 자름 const totalAskAmt = arrAsk.reduce((a,b) => a[1] + b[1]); //총 수량 const mergedArr = [...arrAsk, ...arrBid]; const minObj = mergedArr.reduce((min, current) => { const currentValue = parseFloat(current[1]); const minValue = parseFloat(min[1]); return currentValue < minValue ? current : min; }, mergedArr[0]); const min = minObj[1]; const maxObj = mergedArr.reduce((max, current) => { const currentValue = parseFloat(current[1]); const maxValue = parseFloat(max[1]); return currentValue > maxValue ? current : max; }, mergedArr[0]); const max = maxObj[1]; let tag_bid = ''; let tag_ask = ''; const currentDisplay = getCookie('cookie_lang') == 'kr' ? "현재가" : "Current"; //매수·매도 호가리스트 하·상단에 현재가격 노출(박경호, 2023-10-23 15:41) tag_bid += `
${currentDisplay}
${!isEmpty(apiConfig.getSubjectObj().unitPrice) ? apiConfig.getSubjectObj().unitPrice : "불러오는중"}
`; //호가 (bidding) arrBid?.forEach(item => { // 버전1: bidding price 뭉치에서 해당 item이 차지하는 비율 // const width = (item[1] / totalBothAmt) * 100; // 버전2: 전체(ask+bid)에서 해당 item이 차지하는 비율 const width = (((item[1] - min) / (max - min)) * 100).toFixed(4); tag_bid += `
${item[1]}

${parseFloat(item[0]).toFixed(4)}

${!isEmpty(item[2]) ? `${item[2]}%` : ''}
`; }); //호가 (asking) arrAsk?.forEach(item => { // 버전1: asking price 뭉치에서 해당 item이 차지하는 비율 // const width = (item[1] / totalBothAmt) * 100; // 버전2: 전체(ask+bid)에서 해당 item이 차지하는 비율 const width = (((item[1] - min) / (max - min)) * 100).toFixed(4); tag_ask += `
${!isEmpty(item[2]) ? `${item[2]}%` : ''}

${parseFloat(item[0]).toFixed(4)}

${item[1]}
`; }); //매수·매도 호가리스트 하·상단에 현재가격 노출(박경호, 2023-10-23 15:41) tag_ask += `
${!isEmpty(apiConfig.getSubjectObj().unitPrice) ? apiConfig.getSubjectObj().unitPrice : "불러오는중"}
${currentDisplay}
`; $('#BID_LIST').html(tag_bid); $('#ASK_LIST').html(tag_ask); }); } function refreshExchangeSummary() { API_EXCHANGE_FEE(res => { const res_fee = JSON.parse(res); const fee = parseFloat(res_fee?.value?.fee); exchangeOption.setFee(fee); API_USDT_KRW_RATIO({pairName: 'sow_usdt'}, (res)=>{ exchangeOption.setUsdtToWon(res[0].basePrice); API_CURRENCT_PRICE({pairName: apiConfig?.getSubjectObj().pairName}, result => { const data = result.data[0]; /* { "symbol": "sow_usdt", "ticker": { "high": 1.5906, // 24시간 동안 최고가 "vol": 102162.7043, // 24시간 동안의 거래량 "low": 1.5684, // 24시간 동안 최저가 "change": 1.02, // 24시간 전이랑 몇 % 변했는가. "turnover": 161449.2607, //LBANK에서 보내준 값.. 모름.. "latest": 1.5848 // 현재 실시간 가격 }, "timestamp": 1696571722335 } */ if (!isEmpty(data.ticker.latest)) { const isUp = data.ticker.change > 0; const rootNode = $('#DisplayCurrentPrice_USDT').closest('.price_wrap'); const prevClosingPrice = data.ticker.latest / (1 + (data.ticker.change / 100)); //전일 종가 const changeAmt_USDT = (data.ticker.latest - prevClosingPrice).toFixed(4); //변화량 //전일 종가 대비 색상 처리 (+:빨, =:검, -:파) if (data.ticker.change > 0) { $(rootNode).removeClass('equal'); $(rootNode).removeClass('minus'); } else if (data.ticker.change < 0) { $(rootNode).removeClass('equal'); $(rootNode).addClass('minus'); } else { $(rootNode).removeClass('minus'); $(rootNode).addClass('equal'); } const tag = `USDT(${isUp ? '+' + data.ticker.change : data.ticker.change}% ${isUp ? '▴' : data.ticker.change < 0 ? '▾' : ''}${changeAmt_USDT})`; $('#DisplayCurrentPrice_USDT').html((data.ticker.latest).toFixed(4) + ' ' + tag); apiConfig.setSubjectObj((data.ticker.latest).toFixed(4)); } if (!isEmpty(data.ticker.latest_krw)) { //API에서 latest_krw 안주는데 준다고 하면.. 뭐 쓰지.. 상상의 분기 $('#DisplayCurrentPrice_KRW').html(data.ticker.latest_krw + ' ' + 'KRW'); } else { // 이리로 분기탐 $('#DisplayCurrentPrice_KRW').html(parseFloat((data.ticker.latest * exchangeOption.getUsdtToWon()).toFixed(4)) + ' ' + 'KRW'); $('#UnitPrice_Purchase_USDT').val(parseFloat((data.ticker.latest).toFixed(4))); $('#UnitPrice_Sell_USDT').val(parseFloat((data.ticker.latest).toFixed(4))); const buyAmt = $('#BuyAmt').val(); $('#TotalOrderAmt_Purchase_USDT').val((parseFloat(data.ticker.latest * parseFloatInput(buyAmt)).toFixed(4))); $('#TotalOrderAmt_Purchase_WON').text((parseFloat(data.ticker.latest * parseFloatInput(buyAmt) * exchangeOption.getUsdtToWon())).toFixed(4)); $('#BuyAmt_USDT').val((parseFloat(((data.ticker.latest * parseFloatInput(buyAmt)) + ((data.ticker.latest * parseFloatInput(buyAmt)) * exchangeOption.getFee({isPercentage: true}))).toFixed(4)))); //수수료를 더함 const sellAmt = $('#SellAmt').val(); $('#TotalOrderAmt_Sell_USDT').val(parseFloat((data.ticker.latest * parseFloatInput(sellAmt))).toFixed(4)); $('#TotalOrderAmt_Sell_WON').text((parseFloat(data.ticker.latest * parseFloatInput(sellAmt) * exchangeOption.getUsdtToWon())).toFixed(4)); $('#SellAmt_USDT').val((parseFloat(((data.ticker.latest * parseFloatInput(sellAmt)) - ((data.ticker.latest * parseFloatInput(sellAmt)) * exchangeOption.getFee({isPercentage: true}))).toFixed(4)))); $('.DisplayCurrentPrice').text(parseFloat((data.ticker.latest).toFixed(4))); refreshKRWElements(); } }); }); }); } //원화를 환율에 맞추어 새로고침 function refreshKRWElements() { const unitPricePurchaseUSDT = $('#UnitPrice_Purchase_USDT').val(); const unitPriceSellUSDT = $('#UnitPrice_Sell_USDT').val(); $('#UnitPrice_Purchase_WON').text((parseFloat(unitPricePurchaseUSDT * exchangeOption.getUsdtToWon())).toFixed(4)); $('#UnitPrice_Sell_WON').text((parseFloat(unitPriceSellUSDT * exchangeOption.getUsdtToWon())).toFixed(4)); $('.currentPriceNodes').text((parseFloat(unitPriceSellUSDT * exchangeOption.getUsdtToWon())).toFixed(4)); } function handleClickOrderConfrimBtn(orderType, params) { if (isEmpty(orderType)) { console.error('주문구분(orderType)이 입력되지 않았습니다.'); // 다국어처리 안해도되는 부분 return; } //매도는 2024년에 오픈한다는 정책으로 인해 임시 메시지 후 리턴(박경호, 2023-11-13 11:46) : S if (orderType == 2) { AlertInfo(params.err_msg_prepare); return; } //매도는 2024년에 오픈한다는 정책으로 인해 임시 메시지 후 리턴(박경호, 2023-11-13 11:46) : E if (orderType == 'required_login') { AlertError(params.err_msg_login); return; } if (orderType == 1 && !(0 < parseFloatInput($('#BuyAmt').val()))) { AlertError(params.err_msg, () => { $('#BuyAmt').focus(); }); return; } if (orderType == 2 && !(0 < parseFloatInput($('#SellAmt').val()))) { AlertError(params.err_msg, () => { $('#SellAmt').focus(); }); return; } let order_type = ''; let color = ''; switch (orderType) { case 1: order_type = 'BUY'; color = '#cf4f4f'; $('#ModalExchConfirm .unit_price').text(($('#UnitPrice_Purchase_USDT').val()).toLocaleString('ko-KR')); $('#ModalExchConfirm .order_cnt_sow').text(($('#BuyAmt').val()).toLocaleString('ko-KR')); $('#ModalExchConfirm .order_cnt_usdt').text(($('#BuyAmt_USDT').val()).toLocaleString('ko-KR')); break; case 2: order_type = 'SELL'; color = '#4681c9'; $('#ModalExchConfirm .unit_price').text(($('#UnitPrice_Sell_USDT').val()).toLocaleString('ko-KR')); $('#ModalExchConfirm .order_cnt_sow').text(($('#SellAmt').val()).toLocaleString('ko-KR')); $('#ModalExchConfirm .order_cnt_usdt').text(($('#SellAmt_USDT').val()).toLocaleString('ko-KR')); break; } $('#ModalExchConfirm').css('display', 'flex'); $('#ModalExchConfirm').addClass('active'); $('#ModalExchConfirm').attr('data-color', color); $('#ModalExchTitle').text(params.order_type_display); $('#ModalExchTitle').css('color', color); $('#ModalExchTitle').css('border-bottom', `2px solid ${color}`); $('#ModalExchViewAgree').css('color', color); $('#ModalExchBtnPrimary').css('background-color', color); $('#order_type').val(order_type); //모바일인 경우 $('#ModalExchBtnPrimary_M').css('background-color', color); $('#ModalExchBtnPrimary_M').text(params.order_type_display + params.btn_confirm); } function handleClickDispatchBtn(btn, actionType) { if (isEmpty(btn)) return; if (isEmpty(actionType)) return; const times = 1; // 플마 버튼 1회 클릭시 증감단위 const input = $(btn).closest('.num_ctrl').find('input'); const inputVal = $(input).val().replace(/,/g, ''); const inputTotalUSDT = $(btn).closest('tbody').find('.total_USDT'); const inputTotalWon = $(btn).closest('tbody').find('.total_WON'); const order_amt = $(btn).closest('tbody').find('.order_amt'); const order_type = $(btn).attr('data-order-type'); let afterVal = ''; let afterFeeOderAmt = null; switch (actionType) { case '+': afterVal = parseFloat(inputVal) + times; break; case '-': afterVal = parseFloat(inputVal) - times; break; } if (afterVal < exchangeOption.getMinAmt()) { afterVal = exchangeOption.getMinAmt(); } //수수료를 적용함 switch (order_type) { case 'BUY': afterFeeOderAmt = (afterVal * apiConfig.getSubjectObj().unitPrice) + (afterVal * apiConfig.getSubjectObj().unitPrice * exchangeOption.getFee({isPercentage: true})); break; case 'SELL': afterFeeOderAmt = (afterVal * apiConfig.getSubjectObj().unitPrice) - (afterVal * apiConfig.getSubjectObj().unitPrice * exchangeOption.getFee({isPercentage: true})); break; } $(input).val(afterVal.toLocaleString('ko-KR')); $(inputTotalUSDT).val((parseFloat(afterVal * apiConfig.getSubjectObj().unitPrice)).toFixed(4)); $(inputTotalWon).text((parseFloat(afterVal * apiConfig.getSubjectObj().unitPrice * exchangeOption.getUsdtToWon())).toFixed(4)); $(order_amt).val(parseFloat(afterFeeOderAmt).toFixed(4)); } //시간 버튼 클릭시 ifame src 변경 + 클릭한 버튼 하단에 밑줄처리 function handleClickChangeTimeOption(btn, params) { if (isEmpty(params)) { console.error('차트 iframe을 UPDATE하기 위한 params가 입력되지 않았습니다.'); return; }; $(btn).closest('.top_btn').find('.btn').removeClass('active'); $(btn).addClass('active'); $('#IframeChart').attr('src', `${apiConfig?.getServer()}/chart?${jsonToQs(params)}`); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* API 사용 함수 */ /* -------------------------------------------------------------------------- */ //호가 API function API_ASKBID_PRICE(params, callback) { if (isEmpty(params)) { console.error('호가 API를 사용하기 위한 params가 입력되지 않았습니다.'); return; }; $.ajax({ type: 'GET', url: apiConfig?.getServer() + '/get/current/depth?' + jsonToQs(params), success: function(res) { if (res?.result == apiConfig?.getSuccessSign()) { if (!isEmpty(callback) && typeof callback == 'function') { callback(res); } } }, error: function(XMLHttpRequest, textStatus, errorThrown) { console.log(""); } }); } //현재 가격 API (1초마다 UPDATE 必) function API_CURRENCT_PRICE(params, callback) { if (isEmpty(params)) { console.error('현재 가격 API를 사용하기 위한 params가 입력되지 않았습니다.'); return; }; $.ajax({ type: 'GET', url: apiConfig?.getServer() + '/get/current/price?' + jsonToQs(params), success: function(res) { if (res?.result == apiConfig?.getSuccessSign()) { if (!isEmpty(callback) && typeof callback == 'function') { callback(res); } } }, error: function(XMLHttpRequest, textStatus, errorThrown) { console.log(""); } }); } //환율 조회 API function API_USDT_KRW_RATIO(params, callback) { if (isEmpty(params)) { console.error('환율 조회 API를 사용하기 위한 params가 입력되지 않았습니다.'); return; }; $.ajax({ type: 'GET', // url: "https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD", // 두나무 url: "https://img.gaia-global.co.kr/get/current/usd", success: function(res) { if (!isEmpty(res)) { if (!isEmpty(callback) && typeof callback == 'function') { callback(res); } } }, error: function(XMLHttpRequest, textStatus, errorThrown) { console.log(""); } }); } //수수료 조회 API function API_EXCHANGE_FEE(callback) { $.ajax({ type: 'GET', url: "/api/spowars/v1/exchange_fee.php", success: function(res) { if (!isEmpty(res)) { if (!isEmpty(callback) && typeof callback == 'function') { callback(res); } } }, error: function(XMLHttpRequest, textStatus, errorThrown) { console.log(""); } }); }