하아찡

[C#/WPF] Upbit 프로젝트 Chart - 4(캔들) 본문

C#/코인프로그램 - 코드

[C#/WPF] Upbit 프로젝트 Chart - 4(캔들)

하아찡 2023. 12. 2. 00:39

코인정보를 받아 생성을할땐

CreateChart함수를 호출하고

그다음부터는 ShowChart를 사용해서 움직임을 처리함.

 

캔들데이터변수는 크게 2가지로 존재합니다.

모든 캔들 정보를 가지고있는 TickData

UI쪽에 출력을 담당하는 PrintTick이 존재하는데

서로는 다른 구조를 가지고 있기때문에 데이터를 TickData에서 PrintTick으로 보내줄때 변경하는 작업이 필요합니다.

 

CreateChart

/// <summary>
/// ShowChart함수 최소한 기능 첫 로드 차트만 생성해줌
/// 23.11.11 -완
/// </summary>
private void CreateChart()
{
    PrintTick = new ObservableCollection<Structs.Tick>();

    if (Market != "")
    {
        //초기 차트 생성
        TickData = pfnc.Candles(TickType, MinTickTime, this.Market, "", 200);
        TickData.Reverse();

        int cnt = Math.Min(PrintCursor + PrintCandle, TickData.Count);

        for (int i = PrintCursor, j = 0; i < cnt; i++, j++)
        {

            Structs.Tick tick = GetTick(i, j);
            PrintTick.Add(tick);
        }
        CandleMaxMin();
        GetCandleTime();
        GetCoinPrice();
    }

}

 

TickData = pfnc.Candles(TickType, MinTickTime, this.Market, "", 200);
TickData.Reverse();

위 코드는 CreateChart함수에서 중요 코드.

UpbitAPI를 사용해 해당 코인 틱 데이터를 불러와야함.(최대 200개까지 호출가능)

해당 틱 데이터를 불러오는 함수는

PublicFunctions에 있는 Candles 함수를 호출하여 파리미터값을 잘 맞춰 보내주면 데이터를 받아 올 수 있습니다.

받아온 데이터를 역으로 돌리는 이유는 데이터를 받아오는 과정을 살펴봐야하는데

최근데이터에서(배열 0) ~ 나중데이터(배열 199) 이런식으로 받아오는데 필자는 데이터처리를

나중데이터에서 최근데이터 순으로 저장하길원해서 첫 데이터는 역순으로 정렬을 해줌.

그래서 나중에 이전데이터를 더 받아왔을때 0번에 계속 Insert를 사용해주면 자동으로 데이터를 정렬함.

 

해당 PublicFunctions 코드는 아래 주소를 참고해주세요

https://thesh.tistory.com/55

 

C# Upbit프로젝트 PublicFunctions(Upbit관련함수)

해당 코드는 UpbitAPI에서 제공하여 어떠한 Key코드없이 사용이 가능 기능 입니다. PublicFunctions.cs using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Diagnostics; using System.Net.Http; using

thesh.tistory.com

 

 

 

CandleMaxMin

/// <summary>
/// 캔들에 등록된 데이터를 가져와 Max갑과 Min값을 구해줌.
/// 23.11.14 -완
/// </summary>
private async void CandleMaxMin()
{
    MaxPrice = 0;
    MinPrice = 999999999;
    MaxVolume = 0;

    
    
    foreach (Structs.Tick tick in PrintTick)
    {
        if (MinPrice > tick.LP)
            MinPrice = tick.LP;
        if (MaxPrice < tick.HP)
            MaxPrice = tick.HP;
        if (MaxVolume < tick.Volume)
            MaxVolume = tick.Volume;
    }

    string len = MaxPrice.ToString().Split(".")[0];
    FLen = len.Length > 1 ? (len.Length > 2 ? "F0" : "F4") : "F8";

    PrintChartYAxis[0].CursorValue = MaxPrice.ToString(FLen);
    PrintChartYAxis[1].CursorValue = MinPrice.ToString(FLen);
    ChartTickHeight = GridHeight / (MaxPrice - MinPrice);
    VolumeTickHeight = GridVolumeHeight / MaxVolume;

    PrintChartYAxis[3].CursorValue = PrintTick[PrintTick.Count - 1].TP.ToString();
    PrintChartYAxis[3].Y = (GridHeight * ((MaxPrice - PrintTick[PrintTick.Count - 1].TP) / (MaxPrice - MinPrice))) + GapYAxis;
}

차트 최대값과 최소값을 계산해주는부분

해당 부분에서 VolumeTickHeight와 ChartTickHeight값을 구해야 UI쪽에서 캔들의 높이값을 설정해 줄 수 있음.

FLen 소수점 출력 개수를 변경하기위해 사용함.

Upbit는 소수점 최대 8자리까지 필요하기때문에 8번째 까지만 출력하게하고, 소수점 개수가 적어졌을경우 점점 줄어들고 필요없어진 경우엔 F0로 소수점을 출력하지 않게 변경한다.

 

이 함수는 연산량이 많아 다르게 처리할 방법을 찾아봐야겠습니다.

 

 

ShowChart

/// <summary>
/// 차트를 갱신시켜줌.
/// 23.11.14 -완
/// </summary>
private void ShowChart()
{
    int newLeft = (int)((FirstXPos - move) / 10);
    if (newLeft == 0)
        return;

    int AddCnt = 0;
    bool MoveType = false;
    FirstXPos = move;

    //이전 데이터를 받아옴.
    if ((PrintCursor + newLeft) < 0)
    {
        //갱신
        DateTime FirstTime = Convert.ToDateTime(TickData[0].utc);
        List<Candle> item = new List<Candle>();
        item = pfnc.Candles(TickType, MinTickTime, this.Market, FirstTime.ToString("yyyy-MM-dd'T'HH:mm:ss"), 200);
        for (int i = 0; i < item.Count; i++)
        {
            TickData.Insert(0, item[i]);
        }

        //더 갱신한 코인데이터 개수만큼 더해줌
        KeepPrintCursor += item.Count;
        PrintCursor += item.Count;
    }



    if (newLeft > 0)
    {
        PrintCursor = Math.Min(PrintCursor + newLeft, TickData.Count - LeaveCnt);
        MoveType = true;
    }
    else if (newLeft < 0)
    {
        PrintCursor = Math.Max(PrintCursor + newLeft, 0);
        MoveType = false;
    }
    AddCnt = Math.Abs(PrintCursor - KeepPrintCursor);
    KeepPrintCursor = PrintCursor;

    for (int i = 0; i < AddCnt; i++)
    {
        if (MoveType == true && PrintTick.Count > 1)
            PrintTick.RemoveAt(0);

        if (MoveType == false && PrintCandle - AddCnt < PrintTick.Count)
            PrintTick.RemoveAt(PrintTick.Count - 1);
    }

    //기존데이터 X축 땡겨주고 밀어줌
    if (AddCnt != 0)
    {
        foreach (Structs.Tick tick in PrintTick)
        {
            if (MoveType == true)
                tick.PreNumber(Math.Abs(AddCnt));
            else
                tick.NextNumber(Math.Abs(AddCnt));
        }
    }

    //데이터 등록
    for (int i = 0; i < AddCnt; i++)
    {
        //정해진 개수를 넘어가면 추가하지않음.
        int index = PrintCursor + PrintTick.Count + i;
        if (MoveType && TickData.Count <= index)
        {
            break;
        }

        Candle c;
        Structs.Tick tick = new Structs.Tick();
        if (MoveType == true)
        {
            c = TickData[PrintCursor + PrintTick.Count];
            tick.X = PrintTick.Count;
        }
        else
        {
            c = TickData[PrintCursor + (AddCnt - (i + 1))];
            tick.X = AddCnt - (i + 1);
        }
        tick.Y = 0;
        tick.HP = c.hp;
        tick.LP = c.lp;
        tick.TP = c.tp;
        tick.OP = c.op;
        tick.UTC = c.utc;
        tick.KST = c.kst;
        tick.Timestamp = c.timestamp;
        tick.Volume = c.candle_acc_trade_volume;

        tick.Colors = SetCandleColor(c.op, c.tp);

        if (MoveType == true)
        {
            PrintTick.Add(tick);
        }
        else
        {
            PrintTick.Insert(0, tick);
        }
    }

    CandleMaxMin();
    GetCandleTime();
    GetCoinPrice();
}

차트를 다시 리로드할때 사용함.

확대, 축소, 양사이드 드래그를 사용 할 때 사용하게됨.

 

MoveType값은 좌측움직임인지 우측 움직임인지 체크하기위한 변수

True일 경우 마우스를 좌측에서 우측으로 당기는 행동.

False일 경우 마우스를 우측에서 좌측으로 당기는 행동.

 

그래서 True일 경우엔 앞쪽 데이터를 삭제시키고 뒤에 데이터를 불러오는 방식이고

False일 경우엔 뒤쪽 데이터를 삭제시키고 앞쪽 데이터를 불러오는 방식입니다.

그래서 False일 경우엔 더이상 불러올 데이터가 없을 경우에 이전 데이터를 불러와서 데이터를 추가해줍니다.

 

 

이전 데이터 불러오기

//이전 데이터를 받아옴.
if ((PrintCursor + newLeft) < 0)
{
    //갱신
    DateTime FirstTime = Convert.ToDateTime(TickData[0].utc);
    List<Candle> item = new List<Candle>();
    item = pfnc.Candles(TickType, MinTickTime, this.Market, FirstTime.ToString("yyyy-MM-dd'T'HH:mm:ss"), 200);
    for (int i = 0; i < item.Count; i++)
    {
        TickData.Insert(0, item[i]);
    }

    //더 갱신한 코인데이터 개수만큼 더해줌
    KeepPrintCursor += item.Count;
    PrintCursor += item.Count;
}

ShowChart함수쪽 보면 왼쪽으로 움직일때 현재 로드된 데이터가 0보다 작아졌을경우 이전데이터가 없음으로 이전데이터를 불러온후 Insert를사용해서 0번째 인덱스에 계속 데이터를 넣어주면 자동으로 역순으로 정렬이됨.

해당부분이 CreateChart쪽에서 언급했던 코드입니다.

이전 데이터를 불러오기위해 기준 시간을 전달해줘야하는데 항상 TickData[0]번째는 가장 나중 데이터이기때문에 해당시간을 기준으로 캔들을 호출하게되면 이전데이터들을 정상적으로 호출 할 수 있습니다.

 

반응형