/* -------------------------------------------------------------------------- */
/* 자바스크립트 전역변수 */
/* -------------------------------------------------------------------------- */
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("");
}
});
}