하아찡

[C#/WPF/수정] Upbit프로젝트 Balance 본문

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

[C#/WPF/수정] Upbit프로젝트 Balance

하아찡 2023. 12. 5. 00:54

디자인은 변경될 가능성이 매우매우매우매우 높음...

 

 

Balance.xaml

<UserControl x:Class="Upbit.Views.Balance"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             xmlns:conv="clr-namespace:Upbit.Converter"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:custom="clr-namespace:TextBoxPlaceHolder;assembly=TextBoxPlaceHolder"
             Width="180"
             prism:ViewModelLocator.AutoWireViewModel="True">
    <UserControl.Resources>
        <conv:IsVisibilityConverter x:Key="isVisiblityConverter"/>
        <conv:TwoMultipleConverter x:Key="TwoMultipleConverter"/>
        <conv:FloorNumber x:Key="FloorNumber"/>
        <conv:SubConverter x:Key="SubConverter"/>
        <conv:PlusConverter x:Key="PlusConverter"/>
        <Style x:Key="ListviewHeader" TargetType="{x:Type GridViewColumnHeader}">
            <Setter Property="Foreground" Value="{Binding MyColors.ColorText}"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Background" Value="{Binding MyColors.ColorBack}"/>
        </Style>

    </UserControl.Resources>
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <StackPanel Orientation="Vertical">
                <TextBlock Text="내 보유자산" FontWeight="Bold" FontSize="14"/>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="1*"/>
                        <RowDefinition Height="5"/>
                        <RowDefinition Height="1*"/>
                        <RowDefinition Height="5"/>
                    </Grid.RowDefinitions>

                    <StackPanel Grid.Row="0" Orientation="Vertical"  Margin="0,0,5,0">
                        <TextBlock Text="보유KRW"/>
                        <TextBlock Text="{Binding MyAccount.Balance, Converter={StaticResource FloorNumber}}" FontSize="16" FontWeight="Bold" Margin="0,0,0,5"/>
                        <Grid Grid.Row="0">
                            <TextBlock Text="총매수" HorizontalAlignment="Left"/>
                            <TextBlock Text="{Binding MyAccount.AvgBuyPrice, Converter={StaticResource FloorNumber}}" HorizontalAlignment="Right" />
                        </Grid>
                        <Grid Grid.Row="1">
                            <TextBlock Text="총평가" HorizontalAlignment="Left"/>
                            <TextBlock Text="{Binding MyAccount.NowPrice, Converter={StaticResource FloorNumber}}" HorizontalAlignment="Right" />
                        </Grid>
                    </StackPanel>

                    <StackPanel Grid.Row="1" Margin="2" Background="Gray"/>
                    <StackPanel Grid.Row="3" Margin="2" Background="Gray"/>

                    <StackPanel Grid.Row="2" Orientation="Vertical" Margin="0,0,5,0">
                        <TextBlock Text="총 보유자산"/>
                        <TextBlock FontSize="16" FontWeight="Bold"  Margin="0,0,0,5">
                            <TextBlock.Text>
                                <MultiBinding Converter="{StaticResource PlusConverter}">
                                    <Binding Path="MyAccount.NowPrice" />
                                    <Binding Path="MyAccount.Balance" />
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                        <Grid Grid.Row="0">
                            <TextBlock Text="평가손익" HorizontalAlignment="Left"/>
                            <TextBlock HorizontalAlignment="Right" Foreground="{Binding MyAccount.Color}" >
                                <TextBlock.Text>
                                    <MultiBinding Converter="{StaticResource SubConverter}">
                                        <Binding Path="MyAccount.NowPrice" />
                                        <Binding Path="MyAccount.AvgBuyPrice" />
                                    </MultiBinding>
                                </TextBlock.Text>
                            </TextBlock>
                        </Grid>
                        <Grid Grid.Row="1">
                            <TextBlock Text="수익률"  HorizontalAlignment="Left"/>
                            <TextBlock Text="{Binding MyAccount.Rate, StringFormat={}{0:0.##}%}" Foreground="{Binding MyAccount.Color}" HorizontalAlignment="Right" />
                        </Grid>
                    </StackPanel>
                </Grid>
            </StackPanel>
        </Grid>
        
        <Grid Grid.Row="1"  >
            <!-- 인증 안됐을때 텍스트로 출력 -->
            <TextBlock Panel.ZIndex="1" Foreground="{Binding MyColors.ColorText}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Lang.LNotAccess}" Visibility="{Binding NoneAccessView}"  />
            <ListView  ItemsSource="{Binding Accounts}" Foreground="{Binding MyColors.ColorText}" 
                       ScrollViewer.VerticalScrollBarVisibility="Disabled" BorderThickness="0"
                       x:Name="MyListView">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseDoubleClick">
                        <i:InvokeCommandAction Command="{Binding CommandSendCoin}" CommandParameter="{Binding ElementName=MyListView, Path=SelectedItem}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                        <Setter Property="BorderThickness" Value="0"/>
                        <Setter Property="Margin" Value="0"/>
                        <Setter Property="Padding" Value="0"/>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="0,10,0,0">
                            <StackPanel Orientation="Vertical">
                                <Grid >
                                    <StackPanel Orientation="Horizontal">
                                        <TextBlock Text="{Binding CoinName}" FontWeight="Bold" FontSize="14" />
                                        <TextBlock Text="{Binding Currency,StringFormat={}({0})}"  FontWeight="Bold" FontSize="14" />
                                    </StackPanel>
                                </Grid>
                                <Grid>
                                    <TextBlock Text="수익률" />
                                    <TextBlock Foreground="{Binding Color}" Text="{Binding Rate,StringFormat={}{0:0.##}%}" HorizontalAlignment="Right"/>
                                </Grid>
                                <Grid>
                                    <TextBlock Text="평가손익" />
                                    <TextBlock Foreground="{Binding Color}" HorizontalAlignment="Right">
                                        <TextBlock.Text>
                                            <MultiBinding Converter="{StaticResource SubConverter}">
                                                <Binding Path="TotalPrice" />
                                                <Binding Path="Cost" />
                                            </MultiBinding>
                                        </TextBlock.Text>
                                    </TextBlock>
                                </Grid>
                                
                                <Grid>
                                    <TextBlock Text="평단가" />
                                    <TextBlock Text="{Binding AvgBuyPrice, Converter={StaticResource FloorNumber}}" HorizontalAlignment="Right"/>
                                </Grid>
                                <Grid>
                                    <TextBlock Text="보유수량" />
                                    <TextBlock Text="{Binding TotalBalance}" HorizontalAlignment="Right"/>
                                </Grid>

                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>


    </Grid>
</UserControl>

 

 

BalanceViewModel.cs

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Threading;
using Upbit.UpbitFunctions;
using System.Windows;
using Upbit.UpbitFunctions.Interface;
using Language;
using System.Windows.Media;
using PublicColor;
using Prism.Events;
using Upbit.Event;
using Upbit.Functions.Interface;
using Upbit.Functions;
using System.Windows.Controls.Primitives;
using System.Linq;
using Language.Lang;
using static Upbit.Structs;
using System.Diagnostics;

namespace Upbit.ViewModels
{
    public class BalanceViewModel : BindableBase,IAccess, ILanguage, IColors, IWebSocketTicker, IMyTrade
    {
        #region Lang
        private Language.Lang.Balance lang;
        public Language.Lang.Balance Lang
        {
            get { if (lang is null) lang = new Language.Lang.Balance(); return lang; }
            set { SetProperty(ref lang, value); }
        }
        public virtual void SetLanguage()
        {
            Lang.UpdateLang();
            SetCoinName();
        }
        
        #endregion

        #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;
        }

        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 Prism
        private IEventAggregator _ea;
        #endregion Prism

        #region Models

        private Structs.Accounts myaccount;
        public Structs.Accounts MyAccount
        {
            get { return myaccount; }
            set { SetProperty(ref myaccount, value); }
        }

        private Visibility noneaccessview;
        public Visibility NoneAccessView
        {
            get { return noneaccessview; }
            set { SetProperty(ref noneaccessview, value); }
        }

        public bool IsBalance { get; private set; } 

        private UpbitFunctions.Accounts FAccount = new UpbitFunctions.Accounts();

        private List<Structs.Accounts> accounts;
        public List<Structs.Accounts> Accounts
        {
            get { return accounts; }
            set { 
                SetProperty(ref accounts, value);
            }
        }



        private List<Structs.MarketCodes> Markets;
        private PublicFunctions pf = new PublicFunctions();
        #endregion Models

        /// <summary>
        /// 코인 선택 이벤트
        /// </summary>
        private DelegateCommand<object> _sendcoin;
        public DelegateCommand<object> CommandSendCoin =>
            _sendcoin ?? (_sendcoin = new DelegateCommand<object>(ExecuteCommandSendCoin));

        void ExecuteCommandSendCoin(object obj)
        {
            if (obj is null) return;
            Structs.Accounts marketCodes = (Structs.Accounts)obj;

            _ea.GetEvent<MessagaeAccountEvent>().Publish(marketCodes);

        }


        public void TradeEvent(Structs.Trade tr)
        {
            throw new NotImplementedException();
        }

        public void OrderEvent(Structs.Orderbook ob)
        {
            throw new NotImplementedException();
        }

        public void TickerEvent(Structs.Ticker tick)
        {
            if(Accounts is not null)
            {
                if(Accounts.Count > 0)
                {
                    for(int i = 0; i < Accounts.Count; i++)
                    {
                        if(tick.cd == Accounts[i].Market)
                        {
                            Accounts[i].NowPrice = tick.tp;
                            Accounts[i].TotalPrice = tick.tp;
                            Accounts[i].Rate = tick.tp;
                            Accounts[i].Color = SetTextColor(Accounts[i].NowPrice , Convert.ToDouble(Accounts[i].AvgBuyPrice));

                            if(MyAccount is not null)
                            {
                                //내 지갑
                                SetMyAccount();
                            }
                            
                            break;
                        }
                    }
                }
            }
        }

        private Brush SetTextColor(double now, double avg)
        {
            Brush val = Brushes.Transparent;

            if (now >= avg)
                val = MyColors.ColorBid;
            else
                val = MyColors.ColorAsk;


            return val;
        }

        /// <summary>
        /// key값을 전달받았을때 이벤트 
        /// </summary>
        /// <param name="b"></param>
        public void D_AccessEvent(bool b)
        {
            IsBalance = Access.UpbitInstance.GetAccess();
            if (IsBalance)
            {
                NoneAccessView = Visibility.Collapsed;
                Accounts = Task.Run(() => FAccount.AccountsAsync()).Result;
                PullOutMyAccount(ref accounts);
                SetCoinName();
            }
            else
            {
                Accounts = new List<Structs.Accounts>();
                NoneAccessView = Visibility.Visible;
            }
        }

        private void SetCoinName()
        {
            if (Accounts is null)
                return;
            for(int i = 0; i < Accounts.Count; i++) 
            {
                foreach(var v in Markets)
                {
                    if(v.Market == Accounts[i].Market)
                    {
                        string str = "";
                        if (Language.Language.Lang.LangType == "kr")
                            str = v.Korean_name;
                        else
                            str = v.English_name;
                        Accounts[i].CoinName = str;
                        break;  
                    }
                }
            }
        }
        private double GetMyTotalPrice()
        {
            double value = 0;
            foreach(var v in Accounts)
            {
                value += v.TotalPrice;
            }

            return value;
        }
        private double GetMyTotalCost()
        {
            double value = 0;
            foreach (var v in Accounts)
            {
                value += v.Cost;
            }

            return value;
        }
        private void PullOutMyAccount(ref List<Structs.Accounts> list)
        {
            if (Accounts is null)
                return;
            try
            {
                int index = -1;
                for (int i = 0; i < list.Count; i++)
                {
                    if ("KRW" == list[i].Currency)
                    {
                        MyAccount = list[i];
                        index = i;
                    }
                    else
                    {
                        if (list[i].NowPrice == 0)
                        {
                            Structs.Orderbook now = pf.OrderBook(list[i].Market);

                            list[i].NowPrice = now.obu[0].bp;
                            list[i].TotalPrice = now.obu[0].bp;
                            list[i].Rate = now.obu[0].bp;
                            list[i].Color = SetTextColor(list[i].NowPrice, Convert.ToDouble(list[i].AvgBuyPrice));
                        }
                    }


                }
                if (index != -1)
                    list.RemoveAt(index);
            }
            catch(Exception e) 
            {
                Debug.WriteLine("PullOut");
            }


            SetMyAccount();

        }

        /// <summary>
        /// 내 KRW잔고 갱신
        /// </summary>
        private void SetMyAccount()
        {
            MyAccount.NowPrice = GetMyTotalPrice();
            MyAccount.AvgBuyPrice = GetMyTotalCost().ToString();
            MyAccount.Cost = GetMyTotalCost();
            MyAccount.Rate = MyAccount.NowPrice;
            MyAccount.Color = SetTextColor(MyAccount.NowPrice, Convert.ToDouble(MyAccount.AvgBuyPrice));
        }
        #region Account Timer Functions

        
        /// <summary>
        /// 잔고 갱신
        /// </summary>
        public void RefreshAccount()
        {
            List<Structs.Accounts> list = Task.Run(() => FAccount.AccountsAsync()).Result;
            PullOutMyAccount(ref list);
            
            for (int j = 0; j < list.Count; j++)
            {
                
                for (int i = 0; i < Accounts.Count; i++)
                {
                    if (Accounts[i].Market == list[j].Market)
                    {
                        list[j].CoinName = Accounts[i].CoinName;
                        list[j].NowPrice = Accounts[i].NowPrice;
                        list[j].TotalPrice = Accounts[i].NowPrice;
                        list[j].Color = SetTextColor(Accounts[i].NowPrice, Convert.ToDouble(Accounts[i].AvgBuyPrice));
                        

                        break;
                    }
                }
            }
            Accounts = list;
            SetCoinName();
        }

        /// <summary>
        /// 코인 주문 성공했을때 현재 잔고 갱긴을위해사용
        /// </summary>
        /// <param name="b"></param>
        public void RefreshAccount(bool b)
        {
            if (b)
            {
                RefreshAccount();
            }
        }



        public virtual void MyTradeEvent(Structs.MyTrade mytrade)
        {

            //원화는 그냥 불러와서 계산하는게 정확할거같아서 불러다씀
            RefreshAccount();
        }




        #endregion Account Timer Functions


        public BalanceViewModel(IEventAggregator ea)
        {
            _ea = ea;

            EventUpdateColor();
            SetLanguage();

            Accounts = new List<Structs.Accounts>();

            _ea.GetEvent<MessageBoolEvent>().Subscribe(RefreshAccount);

            //Key값 이벤트 등록
            Access.CheckedAccess += (D_AccessEvent);
            PublicFunctions Upbitpf = new PublicFunctions();
            Markets = Task.Run(() => Upbitpf.MarketsAsync()).Result;
            //색변경 이벤트 등록
            PublicColor.Colors.ColorUpdate += (EventUpdateColor);

            //언어 변경시 이벤트 등록
            Language.Language.LangChangeEvent += (SetLanguage);

            //실시간 틱데이터를 받아오기위해 이벤트등록
            WebSocketTicker.TickerEvent += new WebSocketTicker.TickEventHandler(TickerEvent);

            MyTradeClientWebSocket.MyTradeEvent += new MyTradeClientWebSocket.MyTradeEventHandler(MyTradeEvent);

            IsBalance = Access.UpbitInstance.GetAccess();
            if (IsBalance)
            {
                NoneAccessView = Visibility.Collapsed;
                Accounts = Task.Run(() => FAccount.AccountsAsync()).Result;
                PullOutMyAccount(ref accounts);
                SetCoinName();

            }
            else
            {
                Accounts = new List<Structs.Accounts>();
                NoneAccessView = Visibility.Visible;
            }
        }

    }
}

전체 코드중 이전과 다르게 추가된점은

PullOutMyAccount 함수쪽인데 이전엔 현재가를 받아오지 못한상태에서 웹소켓에서 데이터가 들어올때가지 표시가 안되는 버그가 있었는데 그 문제를 OrderBook함수를 호출해서 현재 호가를 불러와 일부 계산을 시켜서 버그 해결.

 

 

PullOutMyAccount함수

/// <summary>
/// 내 잔고를 뺌과 동시에 내 지갑에있는 코인 현재가가없을경우 현재가를 출력해줌
/// 23.12.04 -완
/// </summary>
/// <param name="list"></param>
private void PullOutMyAccount(ref List<Structs.Accounts> list)
{
    if (Accounts is null)
        return;
    try
    {
        int index = -1;
        for (int i = 0; i < list.Count; i++)
        {
            if ("KRW" == list[i].Currency)
            {
                MyAccount = list[i];
                index = i;
            }
            else
            {
                if (list[i].NowPrice == 0)
                {
                    Structs.Orderbook now = pf.OrderBook(list[i].Market);

                    list[i].NowPrice = now.obu[0].bp;
                    list[i].TotalPrice = now.obu[0].bp;
                    list[i].Rate = now.obu[0].bp;
                    list[i].Color = SetTextColor(list[i].NowPrice, Convert.ToDouble(list[i].AvgBuyPrice));
                }
            }


        }
        if (index != -1)
            list.RemoveAt(index);
    }
    catch(Exception e) 
    {
        Debug.WriteLine("PullOut");
    }


    SetMyAccount();

}

 

반응형