하아찡

[C#/WPF] Upbit 프로젝트 CoinOrder - 2 본문

카테고리 없음

[C#/WPF] Upbit 프로젝트 CoinOrder - 2

하아찡 2023. 11. 30. 21:20

 

CoinOrderViewModel.cs

using Language;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using PublicColor;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using Upbit.Event;
using Upbit.UpbitFunctions;

namespace Upbit.ViewModels
{
    public class CoinOrderViewModel : BindableBase,ILanguage, IColors
    {
        #region Models
        private bool BBidAskType = true; //true => BId / False = Ask
        /// <summary>
        /// 매수 매도 확인을 위한 컬러 변경
        /// </summary>
        private Brush colorgrid;
        public Brush GridColor
        {
            get { return colorgrid; }
            set { SetProperty(ref colorgrid, value); }
        }


        //주문버튼 색상변경용
        private Brush btnborder;
        public Brush BtnBorder
        {
            get { return btnborder; }
            set { SetProperty(ref btnborder, value); }
        }

        private Brush btnback;
        public Brush BtnBack
        {
            get { return btnback; }
            set { SetProperty(ref btnback, value); }
        }

        //주문버튼 텍스트
        private string btntext;
        public string BtnText
        {
            get { return btntext; }
            set { SetProperty(ref btntext, value); }
        }

        //false = 지정가 / true = 시장가
        private bool ismarket;
        public bool IsMarket
        {
            get { return ismarket; }
            set { SetProperty(ref ismarket, value); }
        }

        private bool marketbuy;
        public bool MarketBuy
        {
            get { return marketbuy; }
            set {
                bool v;
                if (value)
                {
                    v = value && BBidAskType;
                }
                else
                {
                    v = !value;
                }
                SetProperty(ref marketbuy, v); 
            
            }
        }

        private bool marketsell;
        public bool MarketSell
        {
            get { return marketsell; }
            set {
                bool v;
                if (value)
                {
                    v = value && (!BBidAskType);
                }
                else
                {
                    v = !value;
                }
                SetProperty(ref marketsell, v); 
            }
        }

        //구매 화폐
        private string currency;
        public string Currency
        {
            get { return currency; }
            set { SetProperty(ref currency, value); }
        }

        //구매 코인
        private string ordercoin;
        public string OrderCoin
        {
            get { return ordercoin; }
            set { SetProperty(ref ordercoin, value); }
        }

        //구매 수량
        private double quantity;
        public double Quantity
        {
            get { return GetDoubleValue(quantity); }
            set { SetProperty(ref quantity, value);}
        }

        //구매가격
        private double price;
        public double Price
        {
            get { return GetDoubleValue(price); }
            set { SetProperty(ref price, value);}
        }

        //최종 내가 내야할 금액
        private double totalprice;
        public double TotalPrice
        {
            get { return GetDoubleValue(totalprice); }
            set {
                SetProperty(ref totalprice, value);}
        }

        //텍스트박스 실수처리를 입력하기위해 스트링으로 받고 실수로 변환
        private string strprice;
        public string StrPrice
        {
            get { return strprice; }
            set {
                string str = GetNumberic(value);
                if (CheckedDot(str))
                {
                    SetProperty(ref strprice, str);
                    Price = str != "" ? double.Parse(str) : 0;
                    if (IsPrice)
                    {
                        IsPrice = false;
                    }
                    else
                    {
                        ChangePrice();
                    }
                }
            }
        }

        //텍스트박스 실수처리를 입력하기위해 스트링으로 받고 실수로 변환
        private string strquantity;
        public string StrQuantity
        {
            get { return strquantity; }
            set {
                string str = GetNumberic(value);
                if (CheckedDot(str))
                {
                    SetProperty(ref strquantity, str);
                    Quantity = str != "" ? double.Parse(str) : 0;
                    if (IsQuantity)
                    {
                        IsQuantity = false;
                    }
                    else
                    {
                        ChangeQuantity();
                    }
                }
            }
        }

        //텍스트박스 실수처리를 입력하기위해 스트링으로 받고 실수로 변환
        private string strtotalprice;
        public string StrTotalPrice
        {
            get { return strtotalprice; }
            set {
                string str = GetNumberic(value);
                if (CheckedDot(str))
                {
                    SetProperty(ref strtotalprice, str);
                    TotalPrice = str != "" ? double.Parse(str) : 0;
                    if (IsTotalPrice)
                    {
                        IsTotalPrice = false;
                    }
                    else
                    {
                        ChangeTotalPrice();
                    }
                }
                
                 }
        }


        private bool orderview;
        public bool OrderView
        {
            get { return orderview; }
            set { SetProperty(ref orderview, value); }
        }
        private bool orderlistview;
        public bool OrderListView
        {
            get { return orderlistview; }
            set { SetProperty(ref orderlistview, value); }
        }

        //해당코인이 주문이 가능한 상태인지
        private bool activeorder;
        public bool ActiveOrder
        {
            get { return activeorder; }
            set { SetProperty(ref activeorder, value); }
        }

        private string activeordercurrency;
        public string AcitiveOrderCurrency
        {
            get { return activeordercurrency; }
            set { SetProperty(ref activeordercurrency, value); }
        }
        //주문가능금액
        private double activeorderprice;
        public double ActiveOrderPrice
        {
            get { return activeorderprice; }
            set { SetProperty(ref activeorderprice, value); }
        }

        private Structs.Chance chance;
        public Structs.Chance Chance
        {
            get { return chance; }
            set { SetProperty(ref chance, value); }
        }

        //주문리스트 저장
        private List<Structs.OrderList> orderlist;
        public List<Structs.OrderList> OrderList
        {
            get { return orderlist; }
            set { SetProperty(ref orderlist, value); }
        }

        //언어변경됐을때 주문리스트 갱신 기본값은 체결대기
        private string KeepOrderListType = "wait";

        private bool IsPrice = false;
        private bool IsTotalPrice = false;
        private bool IsQuantity = false;
        private double OrderMin = 0;
        private double Fee = 0;
        private string SelectCoin = "";
        Order Orderfnc = new Order();

        #endregion Models

        #region Colors
        /// <summary>
        /// 색상 업데이트 이벤트
        /// </summary>
        public void EventUpdateColor()
        {
            MyColors.ColorText = PublicColor.Colors.Colorinstance.ColorText;
            MyColors.ColorBack = PublicColor.Colors.Colorinstance.ColorBack;
            MyColors.ColorBid = PublicColor.Colors.Colorinstance.ColorBid;
            MyColors.ColorAsk = PublicColor.Colors.Colorinstance.ColorAsk;
            MyColors.ColorBidBack = PublicColor.Colors.Colorinstance.ColorBidBack;
            MyColors.ColorAskBack = PublicColor.Colors.Colorinstance.ColorAskBack;

            SetGridColor();
        }

        private PublicColor.Colors mycolors;
        public PublicColor.Colors MyColors
        {
            get
            {
                if (mycolors is null) mycolors = new PublicColor.Colors();
                return mycolors;
            }
            set { SetProperty(ref mycolors, value); }
        }
        #endregion Colors

        #region Lang

        private Language.Lang.PublicCoin lang;
        public Language.Lang.PublicCoin Lang
        {
            get { if (lang is null) lang = new Language.Lang.PublicCoin(); return lang; }
            set { SetProperty(ref lang, value); }
        }
        public virtual void SetLanguage()
        {
            string saveprice = OrderCoin;
            Lang.UpdateLang();
            if(KeepOrderListType != "" && Currency != "" && OrderCoin != "")
            {
                string SelectCoin = Currency + "-" + OrderCoin;
                OrderList = Task.Run(() => Orderfnc.OrderListAsync(SelectCoin, KeepOrderListType)).Result;
            }
            if (Lang.LPrice == saveprice)
            {
                OrderCoin = Lang.LPrice;
                Currency = Lang.LCurrency;
            }
        }

        #endregion Lang

        #region Prism
        private IEventAggregator _ea;
        #endregion Prism

        #region Event
        /// <summary>
        /// 매수 매도 버튼
        /// </summary>
        private DelegateCommand<string> _bidasktype;
        public DelegateCommand<string> CommandBidAskType =>
            _bidasktype ?? (_bidasktype = new DelegateCommand<string>(ExecuteBidAskType));

        void ExecuteBidAskType(string parameter)
        {
            if(parameter == "T")
            {
                BBidAskType = true;
                OrderListView = false;
                OrderView = true;
                GetChance(Currency + "-" + OrderCoin);
            }
            else if (parameter == "F")
            {
                BBidAskType = false;
                OrderView = true;
                OrderListView = false;
                GetChance(Currency + "-" + OrderCoin);
            }
            else
            {
                OrderListView = true;
                OrderView = false;
            }
            MarketBuy = MarketBuy;
            MarketSell = MarketSell;
            //현재 매수 매도타입을 필요한곳에 전달
            _ea.GetEvent<MessageBidAskCheckEvent>().Publish(BBidAskType);
            SetGridColor();
            

        }

        /// <summary>
        /// 지정가인지 시장가 주문인지 선택하는 이벤트
        /// </summary>
        private DelegateCommand<string> commandismarket;
        public DelegateCommand<string> CommandIsMarket =>
            commandismarket ?? (commandismarket = new DelegateCommand<string>(ExecuteCommandIsMarket));

        void ExecuteCommandIsMarket(string p)
        {
            if (p == "l")
            {
                IsMarket = false;//지정가
                MarketBuy = false;
                MarketSell = false;
            }
            else
            {
                IsMarket = true;//시장가
                MarketBuy = true;
                MarketSell = true;
            }
        }

        private DelegateCommand<object> commandcoinorder;
        public DelegateCommand<object> CommandCoinOrder =>
            commandcoinorder ?? (commandcoinorder = new DelegateCommand<object>(ExecuteCommandCoinOrder));

        void ExecuteCommandCoinOrder(object parameter)
        {
            
            bool IsOrder = CoinOrder();

            if (IsOrder)
            {
                //주문성공
                _ea.GetEvent<MessageBoolEvent>().Publish(true);
            }
            else
            {
                //주문 실패
            }
        }

        public bool CoinOrder()
        {

            Language.Lang.Message msg = new Language.Lang.Message();
            if (Quantity == 0 || Price == 0)
                return false;
            if ((Price * Quantity) < OrderMin)
            {
                MessageBox.Show(msg.LErrorOrderMinPrice + " -> " + OrderMin.ToString());
                return false;
            }
            if ((BBidAskType == false && Quantity > ActiveOrderPrice) || 
                (BBidAskType && (TotalPrice * (1 + Fee)) > ActiveOrderPrice))
            {
                MessageBox.Show(msg.LErrorOrderOverflowQuantity);
                return false;
            }

            string ordertype = BBidAskType ? "bid" : "ask";
            string markettype = IsMarket ? (BBidAskType ? "price" : "market") : "limit";

            var IsOrder = Task.Run(() => Orderfnc.Orders(Currency + "-" + OrderCoin,
                ordertype,
                Quantity,
                Price,
                markettype,
                Price * Quantity
                )).Result;

            return IsOrder;
        }

        private DelegateCommand<string> selectedorderlist;
        public DelegateCommand<string> CommandSelectedOrderList =>
            selectedorderlist ?? (selectedorderlist = new DelegateCommand<string>(ExecuteSelectedOrderList));

        void ExecuteSelectedOrderList(string ordertype)
        {
            
            if (ordertype == "" || ordertype is null)
                return;

            GetOrderList(SelectCoin, ordertype);
            

            KeepOrderListType = ordertype;
        }

        private DelegateCommand<string> ordercancel;
        public DelegateCommand<string> CommandOrderCancel =>
            ordercancel ?? (ordercancel = new DelegateCommand<string>(ExecuteCommandOrderCancel));

        void ExecuteCommandOrderCancel(string uuid)
        {
            Structs.OrderCancel a = Task.Run(() => Orderfnc.OrderCancelAsync(uuid)).Result;
            GetOrderList(SelectCoin, KeepOrderListType);
        }
        #endregion Event

        #region Functions
        private double GetDoubleValue(double val)
        {
            int places = 8;
            double value = Math.Floor(val * Math.Pow(10, places)) / Math.Pow(10, places); ;


            return value;

        }
        private string GetNumberic(string str)
        {
            string val = "";
            
            string pattern = @"[^0-9.]";
            val = Regex.Replace(str, pattern, "");

            return val;
        }

        private bool CheckedDot(string str)
        {
            bool b = false;
            int count = str.Split(".").Length - 1;
            b = count >= 2 ? false : true;

            return b;
        }
        
        /// <summary>
        /// 가격 변경이 일어났을때 작동함수.
        /// </summary>
        private void ChangePrice()
        {
            if (Price == 0 || Quantity == 0)
                return;
            IsTotalPrice = true;
            TotalPrice = Price * Quantity;
            StrTotalPrice = TotalPrice.ToString("F8");
        }

        /// <summary>
        /// 수량 변경
        /// </summary>
        private void ChangeTotalPrice()
        {
            if (TotalPrice == 0 || Price == 0)
                return;
            IsQuantity = true;
            Quantity = TotalPrice / Price;
            StrQuantity = Quantity.ToString("F8");
        }

        /// <summary>
        /// 수량 변경
        /// </summary>
        private void ChangeQuantity()
        {
            if (Quantity == 0 || Price == 0)
                return;
            IsTotalPrice = true;
            TotalPrice = Price * Quantity;
            StrTotalPrice = TotalPrice.ToString("F8");
        }

        /// <summary>
        /// 매수 매도 그리드 컬러 변경해주는 함수
        /// </summary>
        private void SetGridColor()
        {
            if (BBidAskType)
            {
                GridColor = MyColors.ColorBidBack;
                BtnBorder = MyColors.ColorBid;
                BtnText = Lang.LCoinBuy;
            }
            else
            {
                GridColor = MyColors.ColorAskBack;
                BtnBorder = MyColors.ColorAsk;
                BtnText = Lang.LCoinSell;
            }
            if (OrderListView)
            {
                GridColor = MyColors.ColorDefaultBack;
                GetOrderList(SelectCoin, KeepOrderListType);
            }
            BtnBack = GridColor;

        }
        private void GetOrderList(string market, string listtype)
        {
            if (Currency != "" && OrderCoin != "")
            {
                
                OrderList = Task.Run(() => Orderfnc.OrderListAsync(market,listtype)).Result;
            }
        }
        private void GetMarket(Structs.MarketCodes market)
        {
            SelectCoin = market.Market;
            string[] str = market.Market.Split("-");
            Currency = str[0];
            OrderCoin = str[1];
            GetChance(market.Market);

            GetOrderList(SelectCoin, KeepOrderListType);
        }

        /// <summary>
        /// 호가창에서 선택된 가격으로 변경
        /// </summary>
        /// <param name="mp"></param>
        private void GetPrice(Structs.MessageCoinPirce mp)
        {
            Price = mp.Price;
            StrPrice = Price.ToString();
        }

        private void GetChance(string market)
        {
            Order order = new Order();
            Chance = Task.Run(() => order.OrderChanceAsync(market)).Result;
            if (BBidAskType)
            {
                ActiveOrderPrice = chance.bid_account_balance;
                AcitiveOrderCurrency = Currency;
                OrderMin = chance.market_bid_min_total;
                Fee = chance.bid_fee;
            }
            else
            {
                ActiveOrderPrice = chance.ask_account_balance;
                AcitiveOrderCurrency = OrderCoin;
                OrderMin = chance.market_ask_min_total;
                Fee = chance.ask_fee;
            }
            //해당 코인이 주문 가능한 상태일경우
            GetActiveOrder(Chance.state);
        }
        private void GetActiveOrder(string type)
        {
            if (type == "active")
                ActiveOrder = true;
            else
                ActiveOrder = false;
        }
        #endregion Functions

        #region Interface

        #endregion Interface
        public CoinOrderViewModel(IEventAggregator ea)
        {

            _ea = ea;
            _ea.GetEvent<MessageCoinNameEvent>().Subscribe(GetMarket);
            _ea.GetEvent<MessageCoinPriceEvent>().Subscribe(GetPrice);

            OrderListView = false;
            OrderView = true;

            MarketBuy = false;
            MarketSell = false;

            OrderCoin = Lang.LPrice;
            Currency = Lang.LCurrency;
            //언어 변경됐을경우 이벤트호출
            Language.Language.LangChangeEvent += (SetLanguage);
            SetLanguage();

            //색변경 이벤트 등록
            PublicColor.Colors.ColorUpdate += (EventUpdateColor);

            //그리드 배경색
            SetGridColor();
        }
    }
}

 

외부에서 CoinName을 받음

private void GetMarket(Structs.MarketCodes market)
{
    SelectCoin = market.Market;
    string[] str = market.Market.Split("-");
    Currency = str[0];
    OrderCoin = str[1];
    GetChance(market.Market);

    GetOrderList(SelectCoin, KeepOrderListType);
}

현재는 CoinList에서 더블클릭으로 발생하는 이벤트로 마켓정보를 전달받아 해당 마켓 거래를 할 수 있게 마켓정보를 저장하는 함수이다.

 

Prism을 사용해서 

_ea.GetEvent<MessageCoinNameEvent>().Subscribe(GetMarket);

위와 같이 구독을하면 이벤트호출시 해당 함수를 호출해줌

 

 

매수 매도 주문내역

/// <summary>
/// 매수 매도 버튼
/// </summary>
private DelegateCommand<string> _bidasktype;
public DelegateCommand<string> CommandBidAskType =>
    _bidasktype ?? (_bidasktype = new DelegateCommand<string>(ExecuteBidAskType));

void ExecuteBidAskType(string parameter)
{
    if(parameter == "T")
    {
        BBidAskType = true;
        OrderListView = false;
        OrderView = true;
        GetChance(Currency + "-" + OrderCoin);
    }
    else if (parameter == "F")
    {
        BBidAskType = false;
        OrderView = true;
        OrderListView = false;
        GetChance(Currency + "-" + OrderCoin);
    }
    else
    {
        OrderListView = true;
        OrderView = false;
    }
    MarketBuy = MarketBuy;
    MarketSell = MarketSell;
    //현재 매수 매도타입을 필요한곳에 전달
    _ea.GetEvent<MessageBidAskCheckEvent>().Publish(BBidAskType);
    SetGridColor();
    

}

MakretBuy와 MarketSell은 UI쪽에서 시장가로 변경됐을때 Textbox활성화를 끄기위해 사용.

GetChance는 아래 설명할텐데 내가 현재 가지고 있는 코인 혹은 구매가능 수량을 체크하기위해 미리 확인을함.

SetGridColor는 같은 Grid에서 변화만 주다보니 색상변경도 주는게 좋을거같아서 추가했음.

 

 

그리드 색상변경

private void SetGridColor()
{
    if (BBidAskType)
    {
        GridColor = MyColors.ColorBidBack;
        BtnBorder = MyColors.ColorBid;
        BtnText = Lang.LCoinBuy;
    }
    else
    {
        GridColor = MyColors.ColorAskBack;
        BtnBorder = MyColors.ColorAsk;
        BtnText = Lang.LCoinSell;
    }
    if (OrderListView)
    {
        GridColor = MyColors.ColorDefaultBack;
        GetOrderList(SelectCoin, KeepOrderListType);
    }
    BtnBack = GridColor;

}

PublicColor에서 색상값을 받아와서 Grid색상을 변경시켜주는 이벤트입니다.

 

 

주문하기

public bool CoinOrder()
{

    Language.Lang.Message msg = new Language.Lang.Message();
    if (Quantity == 0 || Price == 0)
        return false;
    if ((Price * Quantity) < OrderMin)
    {
        MessageBox.Show(msg.LErrorOrderMinPrice + " -> " + OrderMin.ToString());
        return false;
    }
    if ((BBidAskType == false && Quantity > ActiveOrderPrice) || 
        (BBidAskType && (TotalPrice * (1 + Fee)) > ActiveOrderPrice))
    {
        MessageBox.Show(msg.LErrorOrderOverflowQuantity);
        return false;
    }

    string ordertype = BBidAskType ? "bid" : "ask";
    string markettype = IsMarket ? (BBidAskType ? "price" : "market") : "limit";

    var IsOrder = Task.Run(() => Orderfnc.Orders(Currency + "-" + OrderCoin,
        ordertype,
        Quantity,
        Price,
        markettype,
        Price * Quantity
        )).Result;

    return IsOrder;
}

각종 수량에 맞게 입력되지않으면 구매가 불가능하게 처리해두었음.

Upbit기준으로 5000원 미만 단위 거래는 불가능함.

Upbit에서 주문하는 함수는 이 다음글에서 따로 Order로 설명드리겠습니다.

 

 

코인 거래에 필요한 데이터

private void GetChance(string market)
{
    Order order = new Order();
    Chance = Task.Run(() => order.OrderChanceAsync(market)).Result;
    if (BBidAskType)
    {
        ActiveOrderPrice = chance.bid_account_balance;
        AcitiveOrderCurrency = Currency;
        OrderMin = chance.market_bid_min_total;
        Fee = chance.bid_fee;
    }
    else
    {
        ActiveOrderPrice = chance.ask_account_balance;
        AcitiveOrderCurrency = OrderCoin;
        OrderMin = chance.market_ask_min_total;
        Fee = chance.ask_fee;
    }
    //해당 코인이 주문 가능한 상태일경우
    GetActiveOrder(Chance.state);
}

해당 OrderChanceAsync함수도 Order를 설명할때 같이 올려드리겠습니다.

매수 / 매도에 따라 수수료, 구매 최소수량, 매도 최소수량 등등 데이터를 받아올 수 있음.

 

 

매수 / 매도 버튼 비활성화

private void GetActiveOrder(string type)
{
    if (type == "active")
        ActiveOrder = true;
    else
        ActiveOrder = false;
}

위 GetChance에서 데이터를 받아와 현재 보는 코인이 거래 가능상태인지를 체크해 UI쪽에서 매수 매도버튼 활성화 유무를 변경해주는 함수.

 

 

텍스트박스에 소수점입력시 처리해주는 함수

private string GetNumberic(string str)
{
    string val = "";
    
    string pattern = @"[^0-9.]";
    val = Regex.Replace(str, pattern, "");

    return val;
}

private bool CheckedDot(string str)
{
    bool b = false;
    int count = str.Split(".").Length - 1;
    b = count >= 2 ? false : true;

    return b;
}
반응형