◆ HTML, Javascript, Plotly 등을 이용하여 아래 그림처럼 나만의 비트코인 차트를 만들어 보자

 

 

* 차트 실행해보기 → Bitcoin Chart (sparrow-lee.github.io)

 

Bitcoin Chart

캔들 단위 1분 3분 5분 10분 15분 30분 1시간 4시간 업데이트 간격 없음 1초 3초 10초 30초 1분 5분 30분 1시간

sparrow-lee.github.io

 

onLoad 초기화 함수에서는

  • 업비트에서 전체 마켓 코인 리스트를 받아와서 전역변수로 가지고 있고, (필요한 것만 재가공해서) Select태그 내부에 옵션 태그를 동적으로 추가시켜서 코인 리스트를 선택 가능하게 한다.
  • 그리고, 이전에 저장했던 셋팅값들을 localStorage에서 읽어와서, 캔들 시간, 선택 코인들을 셋팅한 다음
  • 차트를 그리게 한다.
            console.log('> init')
            await getAllCoinInfo();
            console.log('getAllCoinInfo done');
            setSelectTags()
            console.log('set select list');

            // 저장된 캔들 시간 유닛 읽어오기
            gSelectedUnitIndex = getData("candleUnit");
            if (gSelectedUnitIndex == undefined) {
                console.log('gSelectedUnitIndex = undefined');
                gSelectedUnitIndex = 7;   // 디폴드 4시간봉
                setData("candleUnit", 7);
            }
            let selectUnitElement = document.getElementById("selectUnit");
            selectUnitElement.selectedIndex = gSelectedUnitIndex;
            gSelectedUnitValue = selectUnitElement.options[selectUnitElement.selectedIndex].value;

            console.log("selectUnitElement.options[e.selectedIndex] : " + gSelectedUnitValue);

            // 유저가 선택했던 코인 리스트 읽어오기
            let savedValue = getData("markets");
            console.log(`getData market=${savedValue}`);
            if (savedValue && savedValue.length > 3) {
                let markets = savedValue.split(',');
                
                gSelectedCoins = [];              
                markets.forEach(market => {
                    if (market != 'None') {
                        let info = gAllCoinsInfo.KRWDict[market];
                        gSelectedCoins.push({
                            market,
                            name : info.name
                        })
                    }
                })
            } else {
                console.log('no saved data form markets')
            }
            let idx = 0;
            gSelectedCoins.forEach(coin => {
                setSelectMarket(idx, gAllCoinsInfo.KRWDict[coin.market].index + 1)

                idx += 1;
            })
            console.log(JSON.stringify(gSelectedCoins));
            console.log('> init done')

 

 

 

setInterval 을 이용하여 주기적으로 업데이트 되도록 함.

        // 업데이트 간격 변경시
        function updateIntervalOnChange(index, unit, text) {
            console.log(`> updateIntervalOnChange > index=${index}, unit value=${unit}, text=${text}`);

            if (gIntervalId) {
                clearInterval(gIntervalId);
                gInterval = 0;
            }
            if (unit == 'None') {
                console.log('update interval = 0');
                gInterval = 0;
                return;
            }
            gInterval = gUpdateInterval[unit];
            startInterval();
        }

        // 반복 시작
        function startInterval() {
            console.log("> startInterval");
            if (gInterval && gInterval > 0) {
                gIntervalId = setInterval(intervalHandler, gInterval);
            }
        }

        // 반복 처리 함수. 전체 코인 시세를 가져와서 다시 그리기
        async function intervalHandler() {
            console.log("> intervalHandler");
            await redrawAll();
        }

 

 

→ setTimeout 은 한번만 수행되는 것이고, setInterval은 주기적으로 계속 반복을 하게 된다. 멈춰야 할 경우는 clearInterval을 호출하면 된다. setInterval 호출시 반환되는 intervalId를 저장해두었다가 clearInterval할 때 같이 넘겨주면 된다.

코인을 변경하였을 때 처리.

localStorage에 선택사항을 저장하고 차트를 그려준다. 6개의 차트가 있고 id가 각각 0~5번까지 있다.

None을 선택하면, div 를 hidden 처리한다.

 

 

        async function selectOnChange(selId, value, text) {
            console.log(`> selectOnChange${selId} : ${value}, ${text}`);
            gSelectedCoins[selId] = {
                market: value,
                name: text,
            };

            let chartDivElement = document.getElementById(`chart${selId}`);
            if (value == 'None') {
                chartDivElement.hidden = 'hidden';
            } else {
                chartDivElement.hidden = undefined;
            }

            let markets = [];
            gSelectedCoins.forEach(coin => {
                if (coin.market != 'None') markets.push(coin.market);
            })

            console.log(markets.join(','));
            setData('markets', markets.join(','));

            if (value != 'None') {
                let res = await getBitPrice({count: 100, market: value, unit: gSelectedUnitValue, name: text});
                drawChart(res.trace, res.info, `chart${selId}`);
            }
        }

 

 

 

6개의 select와 6개의 div (차트영역)

 

    <select name="selectMarket0" id="selectMarket0" class="form-select form-select-sm select_coin" onchange="selectOnChange(0, this.value, this.options[this.selectedIndex].text)"></select>
    <select name="selectMarket1" id="selectMarket1" class="form-select form-select-sm select_coin" onchange="selectOnChange(1, this.value, this.options[this.selectedIndex].text)"></select>
    <select name="selectMarket2" id="selectMarket2" class="form-select form-select-sm select_coin" onchange="selectOnChange(2, this.value, this.options[this.selectedIndex].text)"></select>
    <select name="selectMarket3" id="selectMarket3" class="form-select form-select-sm select_coin" onchange="selectOnChange(3, this.value, this.options[this.selectedIndex].text)"></select>
    <select name="selectMarket4" id="selectMarket4" class="form-select form-select-sm select_coin" onchange="selectOnChange(4, this.value, this.options[this.selectedIndex].text)"></select>
    <select name="selectMarket5" id="selectMarket5" class="form-select form-select-sm select_coin" onchange="selectOnChange(5, this.value, this.options[this.selectedIndex].text)"></select>
    <br/><br/>
    <div id="chart0" style="width:100%;height:200px;float: both" hidden></div>
    <div id="chart1" style="width:100%;height:200px;float: both" hidden></div>
    <div id="chart2" style="width:100%;height:200px;float: both" hidden></div>
    <div id="chart3" style="width:100%;height:200px;float: both" hidden></div>
    <div id="chart4" style="width:100%;height:200px;float: both" hidden></div>
    <div id="chart5" style="width:100%;height:200px;float: both" hidden></div>

 

 

캔들 단위와 업데이트 간격 select 태그

 

    <table style="width:50%; min-width:300px"><tr><td>
    <div style="float: left; font-size: 15px; margin:4px"> 캔들 단위 </div>
    <select name="selectUnit" id="selectUnit" class="form-select form-select-sm" style="width:20%; min-width:100px; vertical-align:middle" onchange="selectUnitOnChange(this.selectedIndex, this.value, this.options[this.selectedIndex].text)">
        <option id="0" value="1">1분</option>
        <option id="1" value="3">3분</option>
        <option id="2" value="5">5분</option>
        <option id="3" value="10">10분</option>
        <option id="4" value="15">15분</option>
        <option id="5" value="30">30분</option>
        <option id="6" value="60">1시간</option>
        <option id="7" value="240">4시간</option>
    </select>
    </td><td>
    <div style="float: left; font-size: 15px; margin:4px"> 업데이트 간격 </div>
    <select name="updateInterval" id="updateInterval" class="form-select form-select-sm" style="width:20%; min-width:100px; vertical-align:middle" onchange="updateIntervalOnChange(this.selectedIndex, this.value, this.options[this.selectedIndex].text)">
        <option id="0" value="None">없음</option>
        <option id="1" value="1s">1초</option>
        <option id="2" value="3s">3초</option>
        <option id="3" value="10s">10초</option>
        <option id="4" value="30s">30초</option>
        <option id="5" value="1m">1분</option>
        <option id="6" value="5m">5분</option>
        <option id="7" value="30m">30분</option>
        <option id="8" value="1h">1시간</option>
    </select>
    </td></tr></table>

 

 

 

전체 코인정보를 업비트로부터 읽어온다. 별도의 개발자 등록이나 키값이 필요하진 않다. 개인 거래이력까지 가져올려면 키를 받아오긴 해야된다.

전체 정보를 읽어와서 접근하기 쉬운형태로 재가공해서 전역변수로 가지고 있게한다.

 

 

        // 전체 코인 메타 정보 가져오기
        async function getAllCoinInfo() {
            let url = `https://api.upbit.com/v1/market/all`;
            const response = await fetch(url);
            const coinInfos = await response.json();

            // 코인 검색시 JSON 형태가 쉬울때가 많아서 리스트와 함께 두가지 형태로 저장한다
            gAllCoinsInfo = {
                "KRW" : [],
                "KRWDict" : {},
            };

            let KRW = [], head = [];
            let KRWDict = {};

            coinInfos.forEach(info => {
                let ids = info.market.split("-");

                if (ids[0] == 'KRW') {
                    let name = info.korean_name;
                    let en_name = info.english_name;

                    // 비트코인과 이더리움은 정렬에서 제외시키고 맨앞에 둔다.
                    if (ids[1] == 'BTC' || ids[1] == 'ETH') {
                        head.push({
                            market : info.market, name, en_name,
                        })
                    } else {
                        KRW.push({
                            market : info.market, name, en_name,
                        })
                    }
                    
                }
            })
            KRW = KRW.sort((a, b) => a.name > b.name ? 1 : -1);

            gAllCoinsInfo.KRW = head.concat(KRW);

            for(let i=0; i<gAllCoinsInfo.KRW.length; i += 1) {
                let coin = gAllCoinsInfo.KRW[i];
                gAllCoinsInfo.KRWDict[coin.market] = {
                    index: i,
                    market: coin.market,
                    name: coin.name,
                    en_name: coin.en_name,
                }
            }
            console.log(gAllCoinsInfo)
        }

 

 

아무래도 코인 market id (예. "KRW-BTC") 로 검색을 하는 경우가 많아 JSON 형태로 데이터(gAllCoinsInfo.KRWDict)를 별도로 가지고 있게 했다. select 옵션에 넣을 때는 list형태가 편하므로 list(gAllCoinsInfo.KRW) 로도 가지고 있게했다.

특정 코인, 캔들 간격으로 시세 가져오기. 코인이 바뀔 수 있게 parameter로 넘기게 구현

        // 특정 코인 캔들 정보 리스트 가져오기
        async function getBitPrice({count, market, unit, name}) {
            let url = `https://api.upbit.com/v1/candles/minutes/${unit}?market=${market}&count=${count}`;
            const response = await fetch(url);
            const prices = await response.json();

 

 

코인이 동시에 6개에, 캔들 간격, 업데이트 간격 등이 들어가게 되어서 코드가 제법 복잡해졌다.

서버 없이도 나름 쓸만한 로컬 동작가능한 차트 뷰어가 만들어 진 것 같다.

 

 

소스 코드는 아래를 참고하면 된다.

 

exam-codes-public/javascript/bitcoin-chart.html at main · sparrow-lee/exam-codes-public (github.com)

+ Recent posts