Esempio n. 1
0
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 해당 코인 보유금액이 있으면 매도 없으면 매수
            var candles = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount);   // 캔들 조회
            //var currCandle = candles.First();                                                 // 현재 캔들
            //var currPrice = currCandle.Close;                                                 // 현재가
            var ticker    = ApiData.getTicker(coinName).FirstOrDefault();
            var currPrice = (ticker != null) ? ticker.Value <double>("trade_price") : 0D;        // 현재가

            //var orderChance = GetOrderChance(ApiData, coinName, currPrice);                   // 주문 가능 정보
            var orderChance    = GetBalance(ApiData, coinName, currPrice, StartKRW);
            var krwBalance     = orderChance.KRWBalance;                                        // 보유 현금
            var coinBalance    = orderChance.CoinBalance;                                       // 코인 보유금
            var coinVol        = orderChance.CoinVol;                                           // 보유 코인 수량
            var avgPrice       = orderChance.AvgBuyPrice;                                       // 매수 평단가
            var coinBuyBalance = avgPrice * coinVol;                                            // 코인 매수금
            var minTradeKRW    = Settings.Default.minTradeKRW;                                  // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            var prevCandle    = candles[1];                                                     // 직전 캔들
            var lastCandle    = candles.Last();                                                 // 마지막 캔들
            var prevPrice     = prevCandle.Low;                                                 // 직전저가
            var highPrice     = candles.GetRange(1, candles.Count - 1).Max(x => x.High);        // 최고가
            var changePrice   = (currPrice - prevPrice);                                        // 변동가
            var changeRate    = (changePrice / currPrice) * 100;                                // 변동율
            var candlesChange = (currPrice - highPrice);                                        // 캔들 변동가
            var candlesRate   = (candlesChange / highPrice) * 100;                              // 캔들 변동율
            var profit        = coinBalance - coinBuyBalance;                                   // 수익
            //var tradeProfitRate = (avgPrice == 0) ? 0D : (profit / avgPrice) * 100;           // 수익율
            var targetProfit = coinBuyBalance + (coinBuyBalance * ((triggerRate / candleCount) + (feeRate * 2)) / 100);
            //var targetPrice = avgPrice + targetProfit;
            //var target = coinBuyPrice

            var result  = null as JObject;
            var args    = new object[] { coinName, currPrice, prevPrice, highPrice, changeRate, candlesRate, avgPrice, targetProfit };
            var buyTs   = DateTime.Now - LastBuyDate;
            var sellTs  = DateTime.Now - LastSellDate;
            var minutes = sellTs.TotalMinutes;

            WriteCurrent("{0} : 현재가 {1:N0}, 직전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 등락폭 {5:F6}, 평단가 {6:N0}, 목표가 {7:N0}", args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            try
            {
                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
                }
                else if (krwBalance > minTradeKRW &&
                         sellTs.TotalSeconds >= CandleType.Minute * 60 / 2 &&
                         candlesRate <= -(triggerRate + (feeRate * 2)) &&
                         changeRate >= (feeRate * 2))
                {
                    // BUY
                    // 보유현금이 최소 거래금액 보다 많음
                    // 수익율 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
                    var total = ToOrderPrice(krwBalance);
                    result      = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### {0} BUY : 금액 {1:N0}, 수량 {2:F6}", coinName, total, total / currPrice);
                }
                else if (coinBalance > minTradeKRW &&
                         (buyTs.TotalSeconds >= CandleType.Minute * 60 * candleCount / 2 ||
                          targetProfit <= coinBalance))
                {
                    // SELL
                    // 코인평가금 최소 거래금액 보다 많음
                    // 분봉 하나의 시간이 지나면 시장가에 전체 매도
                    // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
                    var vol = coinVol;
                    vol          = Math.Truncate(vol * 100000) / 100000;
                    result       = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }

            if (result != null)
            {
                var uuid       = result.Value <string>("uuid");
                var coinProfit = GetOrderResult(ApiData, uuid, coinName, currPrice, StartKRW);

                //WriteLog("#### RESULT : {0}", result.ToString());

                //var coinProfit = GetBalance(ApiData, coinName, StartKRW);

                //txtProfitPrice.Text = coinProfit.KrwProfit.ToString("N0");
                //txtProfitRate.Text = coinProfit.KrwProfitRate.ToString("F6");
                var profits = new object[] { coinName, StartKRW, coinProfit.TotalBalance, coinProfit.KrwProfit, coinProfit.KrwProfitRate };
                WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}", profits);
            }
        }
Esempio n. 2
0
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = OrderRate;
            var triggerRate = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles          = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount * 2); // 캔들 조회 (2배로 여유롭게)
            var currPrice        = candles.First().Close;                                                     // 현재가
            var prevPrice        = candles[1].Close;                                                          // 직전종가
            var highPrice        = candles.GetRange(1, candleCount - 1).Max(x => x.High);                     // 최고가
            var downPrice        = highPrice * (triggerRate + (feeRate * 2)) / 100;                           // 하락가
            var triggerDownPrice = highPrice - downPrice;                                                     // 매수 하락 촉발가
            var downRate         = Math.Min(0D, (currPrice - highPrice) * 100 / highPrice);                   // 하락율
            var upPrice          = prevPrice * ((triggerRate / candleCount) + (feeRate * 2)) / 100;           // 반등가
            var triggerUpPrice   = prevPrice + upPrice;                                                       // 매수 반등 촉발가
            var upRate           = Math.Max(0D, (currPrice - prevPrice) * 100 / prevPrice);                   // 반등 상승율
            var downUpRate       = upRate == 0 ? 0D : (-downRate / upRate);                                   // 반등율

            // 보유현금 및 주문 정보
            var orderChance    = ApiData.getOrdersChance(coinName);                                 // 주문 가능 정보
            var bid            = orderChance["bid_account"];
            var ask            = orderChance["ask_account"];
            var krwBalance     = bid.Value <double>("balance");                                     // 보유 현금
            var coinVol        = ask.Value <double>("balance");                                     // 보유 코인 수량
            var avgPrice       = ask.Value <double>("avg_buy_price");                               // 매수 평단가
            var buyLock        = bid.Value <double?>("locked") ?? 0D;                               // 매수 대기 금액
            var sellLock       = ask.Value <double?>("locked") ?? 0D;                               // 매도 대기 금액
            var coinBuyBalance = avgPrice * coinVol;                                                // 코인 매수금
            var coinBalance    = currPrice * coinVol;                                               // 코인 평가금
            var totalBalance   = krwBalance + coinBalance;                                          // 현재 자산
            var minTradeKRW    = Settings.Default.minTradeKRW;                                      // 최소 거래 금액

            avgPrice = avgPrice < minTradeKRW ? currPrice : avgPrice;

            // 거래 쿨다운 타임
            var buyTs           = (DateTime.Now - LastSellDate).TotalSeconds;                       // 마지막 매도 이후 경과 시간 (초)
            var buyCoolDownSec  = CandleType.Minute * 60;                                           // 매수 쿨다운 시간 (초)
            var buyCountDownSec = buyCoolDownSec - buyTs;                                           // 매수 쿨다운 시간 (초)

            var sellTs           = (DateTime.Now - LastBuyDate).TotalSeconds;                       // 마지막 매수 이후 경과 시간 (초)
            var sellCoolDownSec  = (CandleType.Minute + (1 / candleCount)) * 60;                    // 매도 쿨다운 시간 (초)
            var sellCountDownSec = sellCoolDownSec - sellTs;                                        // 매도 까지 남은 시간 (초)

            var targetRate      = ((triggerRate / (candleCount - 1)) + (feeRate * 2)) / 100;        // 목표 수익율
            var targetSellPrice = avgPrice + (avgPrice * targetRate);                               // 매도 목표가

            var args = new object[] {
                DateTime.Now,
                coinName,
                currPrice,
                prevPrice,
                highPrice,
                downRate,
                upRate,
                avgPrice,
                StartKRW,
                totalBalance,
                triggerDownPrice,
                triggerUpPrice,
                targetSellPrice,
                sellCountDownSec,
                buyCoolDownSec,
            };

            log.Debug(JsonConvert.SerializeObject(candles));
            log.Debug(JsonConvert.SerializeObject(orderChance));

            string format = "[{0:T}] {1} : 현재가 {2:N0}, 직전가 {3:N0}, 최고가 {4:N0}, 하락율 {5:F6}, 반등율 {6:F6}, 평단가 {7:N0}";

            format += (sellCountDownSec > 0) ? ", 매수 까지 남은 시간(초) {13:N0}" : "";
            format += (buyCoolDownSec > 0) ? ", 매도 까지 남은 시간(초) {14:N0}" : "";
            format += "\r\n[{0:T}] {1} : 시작자산 {8:N0}, 현재자산 {9:N0} 매수 하락 촉발가 {10:N0}, 매수 반등 촉발가 {11:N0}, 매도 목표가 {12:N0}";
            WriteCurrent(format, args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            txtKRWBalance.Text  = krwBalance.ToString("N0");
            txtCoinBalance.Text = coinBalance.ToString("N0");
            txtBalance.Text     = totalBalance.ToString("N0");
            txtStartKRW.Text    = StartKRW.ToString("N0");

            try
            {
                var orderResult = null as JObject;
                var order       = null as JObject;
                var uuid        = "";
                var side        = "";
                var state       = "";

                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가(금액 부족) : 보유현금 {0}, 코인보유금 {1}, 매도 대기 금액 {2}, 매수 대기 금액 {3}, 최소 거래 금액 {4}", krwBalance, coinBalance, buyLock, sellLock, minTradeKRW);
                    return;
                }

                if (sellCountDownSec <= 0 && buyCountDownSec <= 0)
                {
                    // 거래 쿨타임 일때
                    WriteLog("#### 거래 불가(쿨타임) : 매수 까지 남은 시간(초) {0}, 매수 까지 남은 시간(초) {1}, 마지막 매수 {2:T}, 마지막 매도 {3:T}", buyCountDownSec, sellCountDownSec, LastSellDate, LastBuyDate);
                    return;
                }

                if (IsWaiting)
                {
                    // 거래 대기 중일때
                    uuid  = LastOrderResult.Value <string>("uuid");
                    side  = LastOrderResult.Value <string>("side");
                    state = LastOrderResult.Value <string>("state");
                    WriteLog("#### 거래 불가(거래 대기중) : side {0}, state {1}, uuid {2}", side, state, uuid);
                    return;
                }

                if (krwBalance > minTradeKRW &&                                                                     // 보유 현금이 최소거래 금액 보다 클때
                    currPrice <= Math.Truncate(triggerDownPrice) && currPrice >= Math.Truncate(triggerUpPrice) &&   // 현재가가 촉발 금액 사이에서 반등하고
                    downUpRate <= candleCount * 2)                                                                  // 반등율이 캔들갯수의 2배수가 넘지 않아야 함
                {
                    // BUY
                    var total       = ToOrderPrice(krwBalance);
                    var avgBuyPrice = currPrice;
                    orderResult = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### BUY : {0}", orderResult.ToString(Formatting.Indented));
                }
                else if (coinBalance > minTradeKRW &&                                                               // 코인 보유금이 최소거래 금액 보다 클때
                         currPrice >= Math.Truncate(targetSellPrice))                                               // 현재가가 목표 매수 금액보다 클때
                {
                    // SELL
                    var vol = coinVol;
                    vol          = Math.Truncate(vol * 100000) / 100000;
                    orderResult  = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### SELL : {0}", orderResult.ToString(Formatting.Indented));
                }

                if (orderResult != null)
                {
                    IsWaiting       = true;
                    LastOrderResult = orderResult;
                    uuid            = orderResult.Value <string>("uuid");
                    side            = orderResult.Value <string>("side");
                    state           = orderResult.Value <string>("state");

                    System.Threading.Thread.Sleep(1500);
                    order = ApiData.checkOrder(uuid);

                    if (order != null)
                    {
                        IsWaiting = false;
                        WriteLog("#### TRADE RESULT : {0}", order.ToString(Formatting.Indented));

                        var orderState  = order.Value <string>("state");
                        var orderPrice  = order.Value <double?>("price") ?? 0D;
                        var orderVolume = order.Value <double?>("volume") ?? 0D;
                        var tradeCount  = order.Value <double>("trade_count");
                        var trades      = order["trades"] as JArray;
                        var tradePrice  = trades[0].Value <double?>("price") ?? 0D;
                        var tradeVolume = trades[0].Value <double?>("volume") ?? 0D;
                        var tradeFunds  = trades[0].Value <double?>("funds") ?? 0D;

                        if ("done".Equals(orderState))
                        {
                            if ("bid".Equals(side))
                            {
                                // BUY
                                WriteLog("#### {0} BUY : 매수금 {1:N0} : 매수 평단가 {2:N0} : 수량 {3:F6}", coinName, tradeFunds, tradePrice, tradeVolume);
                            }
                            else if ("ask".Equals(side))
                            {
                                // SELL
                                WriteLog("#### {0} SELL : 매도금 {1:N0} : 매도 평단가 {2:N0} : 수량 {3:F6}", coinName, tradeFunds, tradePrice, tradeVolume);
                            }

                            // 수익
                            var balance = GetBalance(coinName);
                            krwBalance   = balance.KRWBalance;
                            coinBalance  = currPrice * balance.CoinVol;
                            totalBalance = krwBalance + coinBalance;
                            var profit     = totalBalance - StartKRW;
                            var profitRate = (StartKRW == 0) ? 0D : profit / StartKRW * 100;

                            txtKRWBalance.Text  = krwBalance.ToString("N0");
                            txtCoinBalance.Text = coinBalance.ToString("N0");
                            txtBalance.Text     = totalBalance.ToString("N0");
                            txtProfitPrice.Text = profit.ToString("N0");
                            txtProfitRate.Text  = profitRate.ToString("F4");

                            args = new object[] { coinName, StartKRW, totalBalance, profit, profitRate, krwBalance, coinBalance };
                            WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}, 보유현금 {5:N0}, 코인 평가 {6:N0}", args);
                        }
                    }
                    else
                    {
                        WriteLog("#### 거래 결과를 가져올수 없습니다.");
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }
        }
Esempio n. 3
0
        private void MinuteCandleByTicks()
        {
            // 1분봉 3개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            // 등낙률 0.5% 이상이 되면 오르면 팔고/ 내리면 산다.
            // 거래시 보유 현금, 보유 코인의 절반을 거래하되 거래 금액이 만원 미만인 경우 전체 금액으로 거래한다.
            var algIdx      = cmbAlgorithm.SelectedIndex;
            var idx         = (algIdx > 0) ? algIdx : Convert.ToInt32(txtMinute.Text);
            var candleType  = BotSetting.CandleTypes[idx];
            var candles     = ApiData.getCandle <List <Candle> >(CoinName, candleType, 3);
            var prevCandle  = candles.Last();           // 시가 봉
            var currCandle  = candles.First();          // 현재가 봉
            var currPrice   = currCandle.Close;
            var prevPrice   = prevCandle.Close;
            var orderChance = GetOrderChance(ApiData, CoinName, currPrice);
            //var ask = orderChance["ask_account"];
            //var bid = orderChance["bid_account"];
            var krwBalance = orderChance.KRWBalance;                            // 보유 현금
            var coinVol    = orderChance.CoinVol;                               // 보유 코인 수량
            var avgPrice   = orderChance.AvgBuyPrice;                           // 매수 평단가
            var coinPrice  = orderChance.CoinBalance;                           // 보유 코인 금액

            //var upRatio = (currPrice - avgPrice) / currPrice * 100;     // 평단 대비 상승폭 (%)
            //var downRatio = (currPrice - prevPrice) / currPrice * 100;  // 현재가 대비 하락폭 (%)
            var ratio     = (currPrice - prevPrice) / currPrice * 100;      // 등락폭 (%)
            var tradeRate = Convert.ToDouble(txtTradeRate.Text) / 100D;     // 거래 비중 (%)
            var result    = null as JObject;                                // 거래 결과

            //txtKRW.Text = krwBalance.ToString("N0");
            //txtCoinBalance.Text = coinPrice.ToString("N0");

            Debug.WriteLine("이전가: {0:N0}, 현재가 {1:N0}, ratio : {2:F4}%", prevPrice, currPrice, ratio);
            WriteLog("이전가: {0:N0}, 현재가 {1:N0}, ratio : {2:F4}%", prevPrice, currPrice, ratio);

            try
            {
                if (avgPrice + (2 * (avgPrice * TriggerRatio / 100)) <= currPrice && coinPrice >= 5000)
                {
                    // 현재가가 평단가보다 등락폭 2배 이상 올랐을때 전량 매도
                    var vol = coinVol;
                    vol = Math.Truncate(vol * 100000) / 100000;
                    Debug.WriteLine("#### {2} ALL SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    WriteLog("#### {2} ALL SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    result       = React.executeDeal(false, false, CoinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                }
                else if (ratio >= TriggerRatio && coinPrice >= 5000 && ((DateTime.Now - LastSellDate).TotalMinutes >= idx || ratio >= TriggerRatio * 1.5))
                {
                    // 올랐을때 코인 금액 절반 팔기
                    var vol = (coinPrice * tradeRate) > 5000 ? coinVol * tradeRate : coinVol;
                    vol = Math.Truncate(vol * 100000) / 100000;
                    Debug.WriteLine("#### {2} SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    WriteLog("#### {2} SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    result       = React.executeDeal(false, false, CoinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                }
                else if (ratio <= -(TriggerRatio * 1.2) && krwBalance > 5000 && (DateTime.Now - LastBuyDate).TotalMinutes >= idx)
                {
                    // 1.2배 내렸을때 보유 현금 절반으로 코인 사기
                    var total = (krwBalance * tradeRate) > 5000 ? krwBalance * tradeRate : krwBalance;
                    total = Math.Truncate(total * 1000) / 1000;
                    Debug.WriteLine("#### {2} BUY : 금액 {0:N0}, 수량 {1:F6}", total, total / currPrice, CoinName);
                    WriteLog("#### {2} BUY : 금액 {0:N0}, 수량 {1:F6}", total, total / currPrice, CoinName);
                    result      = React.executeDeal(true, false, CoinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                WriteLog(ex.StackTrace);
            }

            orderChance = GetOrderChance(ApiData, CoinName, currPrice);

            if (result != null)
            {
                Debug.WriteLine("#### RESULT : {0}", result.ToString());
                WriteLog("#### RESULT : {0}", result.ToString());
            }
        }
Esempio n. 4
0
        private async Task MinuteCandleByTicksAsync()
        {
            // 설정값
            var coinName     = Coin.Ticker;
            var candleType   = CandleType.Name;
            var candleMinute = CandleType.Minute;
            var candleCount  = CandleCount;
            var feeRate      = FeeRate;
            var tradeRate    = OrderRate;
            var triggerRate  = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles          = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount * 2); // 캔들 조회 (2배로 여유롭게)
            var currPrice        = candles.First().Close;                                                     // 현재가
            var prevPrice        = candles[1].Low;                                                            // 직전저가
            var highPrice        = candles.GetRange(1, candleCount - 1).Max(x => x.High);                     // 최고가
            var downPrice        = highPrice * (triggerRate + (feeRate * (candleCount - 1))) / 100;           // 하락가
            var triggerDownPrice = highPrice - downPrice;                                                     // 매수 하락 촉발가
            var downRate         = Math.Min(0D, (currPrice - highPrice) * 100 / highPrice);                   // 하락율
            var upPrice          = prevPrice * (feeRate * (candleCount - 1)) / 100;                           // 반등가
            var triggerUpPrice   = prevPrice + upPrice;                                                       // 매수 반등 촉발가
            var upRate           = (currPrice - prevPrice) * 100 / prevPrice;                                 // 반등 상승율
            var downUpRate       = upRate <= 0 && downRate <= 0 ? 0D : (-downRate / upRate);                  // 반등율

            // 보유현금 및 주문 정보
            var orderChance    = ApiData.getOrdersChance(coinName);                                 // 주문 가능 정보
            var bid            = orderChance["bid_account"];
            var ask            = orderChance["ask_account"];
            var krwBalance     = bid.Value <double>("balance");                                     // 보유 현금
            var coinVol        = ask.Value <double?>("balance") ?? 0D;                              // 보유 코인 수량
            var avgPrice       = ask.Value <double?>("avg_buy_price") ?? 0D;                        // 매수 평단가
            var buyLock        = bid.Value <double?>("locked") ?? 0D;                               // 매수 대기 금액
            var sellLock       = ask.Value <double?>("locked") ?? 0D;                               // 매도 대기 금액
            var coinBuyBalance = avgPrice * coinVol;                                                // 코인 매수금
            var coinBalance    = currPrice * coinVol;                                               // 코인 평가금
            var totalBalance   = krwBalance + coinBalance;                                          // 현재 자산
            var minTradeKRW    = Settings.Default.minTradeKRW;                                      // 최소 거래 금액
            var fee            = currPrice * feeRate / 100;                                         // 거래 수수료
            var sellFee        = coinBalance * feeRate / 100;                                       // 매도 총 수수료
            var buyFee         = totalBalance * feeRate / 100;                                      // 매수 총 수수료
            var buyUnitPrice   = GetOrderUnitPrice(krwBalance);                                     // 매수 주문 가격 단위
            var sellUnitPrice  = GetOrderUnitPrice(coinBalance);                                    // 매도 주문 가격 단위
            //var orderUintPrice = GetOrderUnitPrice(currPrice);                                      // 캔들 가격 단위

            // 거래 쿨다운 타임
            var buyTs                = (DateTime.Now - LastSellDate).TotalSeconds;                     // 마지막 매도 이후 경과 시간 (초)
            var buyCoolDownSec       = CandleType.Minute * 60 * (candleCount - 2) / (candleCount - 1); // 매수 쿨다운 시간 (초)
            var buyRemainCoolDownSec = buyCoolDownSec - buyTs;                                         // 매수 쿨다운 시간 (초)

            var sellTs                = (DateTime.Now - LastBuyDate).TotalSeconds;                     // 마지막 매수 이후 경과 시간 (초)
            var sellCoolDownSec       = (CandleType.Minute + (1 / candleCount)) * 60;                  // 매도 쿨다운 시간 (초)
            var sellRemainCoolDownSec = sellCoolDownSec - sellTs;                                      // 매도 까지 남은 시간 (초)
            var targetRate            = ((triggerRate / 2) + (feeRate * 2)) / 100;                     // 목표 수익율
            var targetSellPrice       = Math.Round(avgPrice * (1 + targetRate) / 10) * 10;             // 매도 목표가

            targetSellPrice = avgPrice + Math.Max((avgPrice * 0.5 / 100) + (fee * 2), 10);

            var now   = DateTime.Now;
            var line1 = $"[{now:T}] {coinName}";

            line1 += $" : 현재/직전/최고 {currPrice:N0}/{prevPrice:N0}/{highPrice:N0}";
            line1 += $", 하락율/반등율 {downRate:F2}/{upRate:F2}, 평단가 {avgPrice:N0}";
            line1 += (sellRemainCoolDownSec > 0) ? $", 매수쿨 {sellRemainCoolDownSec:N0}(초)" : "";
            line1 += (buyRemainCoolDownSec > 0) ? $", 매도쿨 {buyRemainCoolDownSec:N0}(초)" : "";
            var line2 = $"[{now:T}] {coinName}";

            line2 += $" : 시작자산 {StartKRW:N0}, 현재자산 {totalBalance:N0}";
            line2 += $", 매수촉발가(하락/반등) {triggerDownPrice:N0} / {triggerUpPrice:N0}, 매도 목표가 {targetSellPrice:N0}, 매수 수수료 {totalBalance * feeRate / 100:F2}";

            WriteCurrent("{0}\r\n{1}", line1, line2);
            //WriteLog("orderChance {0}", orderChance.ToString(Formatting.Indented));

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            if (InvokeRequired)
            {
                BeginInvoke(new Action(() =>
                {
                    txtKRWBalance.Text  = krwBalance.ToString("N0");
                    txtCoinBalance.Text = coinBalance.ToString("N0");
                    txtBalance.Text     = totalBalance.ToString("N0");
                    txtStartKRW.Text    = StartKRW.ToString("N0");
                }));
            }
            else
            {
                txtKRWBalance.Text  = krwBalance.ToString("N0");
                txtCoinBalance.Text = coinBalance.ToString("N0");
                txtBalance.Text     = totalBalance.ToString("N0");
                txtStartKRW.Text    = StartKRW.ToString("N0");
            }

            try
            {
                var orderResult = null as JObject;
                var order       = null as JObject;
                var uuid        = "";
                var side        = "";
                var state       = "";

                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가(금액 부족) : 보유현금 {0}, 코인보유금 {1}, 매도 대기 금액 {2}, 매수 대기 금액 {3}, 최소 거래 금액 {4}", krwBalance, coinBalance, buyLock, sellLock, minTradeKRW);
                    return;
                }

                if (IsWaiting)
                {
                    // 거래 대기 중일때
                    uuid  = LastOrderResult.Value <string>("uuid");
                    side  = LastOrderResult.Value <string>("side");
                    state = LastOrderResult.Value <string>("state");
                    WriteLog("#### 거래 불가(거래 대기중) : side {0}, state {1}, uuid {2}", side, state, uuid);
                    return;
                }

                if (krwBalance > minTradeKRW &&                                                                     // 보유 현금이 최소거래 금액 보다 클때
                    buyRemainCoolDownSec <= 0 &&                                                                    // 매도 유예시간이 지났을때
                    currPrice <= Math.Truncate(triggerDownPrice) && currPrice >= Math.Truncate(triggerUpPrice) &&   // 현재가가 촉발 금액 사이에서 반등하고
                    downUpRate <= candleCount * 2)                                                                  // 반등율이 캔들갯수의 2배수가 넘지 않아야 함
                {
                    // BUY
                    var total       = ToOrderPrice(krwBalance);
                    var avgBuyPrice = currPrice;
                    orderResult = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### BUY : {0}", orderResult.ToString(Formatting.Indented));
                    var buyUUID    = orderResult.Value <string>("uuid");
                    var orderCheck = await GetBuyMarketResultAsync(buyUUID);
                }
                else if (coinBalance > minTradeKRW &&                                                               // 코인 보유금이 최소거래 금액 보다 클때
                         (sellRemainCoolDownSec <= 0 || currPrice >= Math.Truncate(targetSellPrice)))               // 매수 유예시간이 지나거나 현재가가 목표 매수 금액보다 클때
                {
                    // SELL
                    var vol = coinVol;
                    vol          = Math.Truncate(vol * 100000) / 100000;
                    orderResult  = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### SELL : {0}", orderResult.ToString(Formatting.Indented));
                }

                if (orderResult != null)
                {
                    IsWaiting       = true;
                    LastOrderResult = orderResult;
                    uuid            = orderResult.Value <string>("uuid");
                    side            = orderResult.Value <string>("side");
                    state           = orderResult.Value <string>("state");

                    await Task.Delay(1500);

                    order = ApiData.checkOrder(uuid);

                    if (order != null)
                    {
                        IsWaiting = false;
                        WriteLog("#### CHECK ORDER RESULT : {0}", order.ToString(Formatting.Indented));

                        var orderState  = order.Value <string>("state");
                        var orderPrice  = order.Value <double?>("price") ?? 0D;
                        var orderVolume = order.Value <double?>("volume") ?? 0D;
                        var tradeCount  = order.Value <double>("trade_count");
                        var trades      = order["trades"] as JArray;
                        var tradePrice  = 0D;
                        var tradeVolume = 0D;
                        var tradeFunds  = 0D;

                        if (trades != null && trades.Count > 0)
                        {
                            tradePrice  = trades[0].Value <double?>("price") ?? 0D;
                            tradeVolume = trades[0].Value <double?>("volume") ?? 0D;
                            tradeFunds  = trades[0].Value <double?>("funds") ?? 0D;
                        }

                        if ("done".Equals(orderState))
                        {
                            if ("bid".Equals(side))
                            {
                                // BUY
                                WriteLog("#### {0} BUY : 매수금 {1:N0} : 매수 평단가 {2:N0} : 수량 {3:F6}", coinName, tradeFunds, tradePrice, tradeVolume);
                            }
                            else if ("ask".Equals(side))
                            {
                                // SELL
                                WriteLog("#### {0} SELL : 매도금 {1:N0} : 매도 평단가 {2:N0} : 수량 {3:F6}", coinName, tradeFunds, tradePrice, tradeVolume);
                            }

                            // 수익
                            var balance = GetBalance(coinName);
                            krwBalance   = balance.KRWBalance;
                            coinBalance  = currPrice * balance.CoinVol;
                            totalBalance = krwBalance + coinBalance;
                            var profit     = totalBalance - StartKRW;
                            var profitRate = (StartKRW == 0) ? 0D : profit / StartKRW * 100;

                            if (InvokeRequired)
                            {
                                BeginInvoke(new Action(() =>
                                {
                                    txtKRWBalance.Text  = krwBalance.ToString("N0");
                                    txtCoinBalance.Text = coinBalance.ToString("N0");
                                    txtBalance.Text     = totalBalance.ToString("N0");
                                    txtProfitPrice.Text = profit.ToString("N0");
                                    txtProfitRate.Text  = profitRate.ToString("F4");
                                }));
                            }
                            else
                            {
                                txtKRWBalance.Text  = krwBalance.ToString("N0");
                                txtCoinBalance.Text = coinBalance.ToString("N0");
                                txtBalance.Text     = totalBalance.ToString("N0");
                                txtProfitPrice.Text = profit.ToString("N0");
                                txtProfitRate.Text  = profitRate.ToString("F4");
                            }

                            var args = new object[] { coinName, StartKRW, totalBalance, profit, profitRate, krwBalance, coinBalance };
                            WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}, 보유현금 {5:N0}, 코인 평가 {6:N0}", args);
                        }
                    }
                    else
                    {
                        WriteLog("#### 거래 결과를 가져올수 없습니다.");
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }
        }
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 해당 코인 보유금액이 있으면 매도 없으면 매수
            var candles     = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount); // 캔들 조회
            var currCandle  = candles.First();                                                       // 현재 캔들
            var currPrice   = currCandle.Close;                                                      // 현재가
            var orderChance = GetOrderChance(ApiData, coinName, currPrice);                          // 주문 가능 정보
            var coinPrice   = orderChance.CoinBalance;                                               // 코인 보유금
            var krwBalance  = orderChance.KRWBalance;                                                // 보유 현금
            var coinVol     = orderChance.CoinVol;                                                   // 보유 코인 수량
            var avgPrice    = orderChance.AvgBuyPrice;                                               // 매수 평단가
            var minTradeKRW = Settings.Default.minTradeKRW;                                          // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            var prevCandle      = candles[1];                                               // 직전 캔들
            var lastCandle      = candles.Last();                                           // 마지막 캔들
            var prevPrice       = prevCandle.Close;                                         // 직전종가
            var startPrice      = lastCandle.Open;                                          // 마지막 캔들 시작가
            var change          = (currPrice - prevPrice);                                  // 변동가
            var currentRate     = (change / currPrice) * 100;                               // 등락율
            var downChange      = (startPrice - currPrice);                                 // 상승전 변동가
            var downRate        = (downChange / startPrice) * 100;                          // 상승전 하락율
            var profit          = (currPrice - avgPrice);                                   // 수익
            var tradeProfitRate = (profit / avgPrice) * 100;                                // 수익율
            var result          = null as JObject;
            var args            = new object[] { coinName, currPrice, prevPrice, startPrice, currentRate, downRate };

            WriteLog("{0} : 현재가 {1:N0}, 직전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 하락폭 {5:F6}", args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinPrice < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            try
            {
                if (coinPrice <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinPrice, minTradeKRW);
                }
                else if (krwBalance > minTradeKRW &&
                         downRate >= triggerRate + (feeRate * 2) &&
                         currentRate >= (feeRate * 2))
                {
                    // BUY
                    // 보유현금이 최소 거래금액 보다 많음
                    // 수익율 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
                    var total = Math.Truncate(krwBalance * 1000) / 1000;
                    result      = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### {0} BUY : 금액 {1:N0}, 수량 {2:F6}", coinName, total, total / currPrice);
                }
                else if (coinPrice > minTradeKRW &&
                         (tradeProfitRate <= -(triggerRate - (feeRate * 2)) || tradeProfitRate >= triggerRate + (feeRate * 2)))
                {
                    // SELL
                    // 코인평가금 최소 거래금액 보다 많음
                    // 현재가가 평단가 보다 (수익율 - 수료율 * 2) 이하일때 전체 매도
                    // 현재가가 평단가 보다 (수익율 + 수료율 * 2) 이상일때 전체 매도
                    var vol = coinVol;
                    vol          = Math.Truncate(vol * 100000) / 100000;
                    result       = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }

            if (result != null)
            {
                var chance        = GetOrderChance(ApiData, coinName, currPrice);
                var totalKrw      = chance.KRWBalance + chance.CoinBalance;
                var krwProfit     = totalKrw - StartKRW;
                var krwProfitRate = krwProfit / StartKRW * 100;
                var profits       = new object[] { coinName, StartKRW, totalKrw, krwProfit, krwProfitRate };
                WriteLog("#### RESULT : {0}", result.ToString());
                WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}", profits);
            }
        }
Esempio n. 6
0
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles   = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount * 2); // 캔들 조회 (2배로 여유롭게)
            var currPrice = candles.First().Close;                                                     // 현재가

            // 보유현금 및 주문 정보
            var orderChance    = ApiData.getOrdersChance(coinName);                                 // 주문 가능 정보
            var bid            = orderChance["bid_account"];
            var ask            = orderChance["ask_account"];
            var krwBalance     = bid.Value <double>("balance");                                     // 보유 현금
            var coinVol        = ask.Value <double>("balance");                                     // 보유 코인 수량
            var avgPrice       = ask.Value <double>("avg_buy_price");                               // 매수 평단가
            var bidLock        = bid.Value <double>("locked");                                      // 매수 대기 금액
            var coinBuyBalance = avgPrice * coinVol;                                                // 코인 매수금
            var coinBalance    = currPrice * coinVol;                                               // 코인 평가금
            var minTradeKRW    = Settings.Default.minTradeKRW;                                      // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            //var prevCandle = candles[1];                                                          // 직전 캔들
            //var lastCandle = candles.Last();                                                      // 마지막 캔들
            var prevPrice        = candles[1].Close;                                                // 직전종가
            var highPrice        = candles.GetRange(1, candleCount - 1).Max(x => x.High);           // 최고가
            var downPrice        = highPrice * (triggerRate + (feeRate * 2)) / 100;                 // 하락가
            var triggerDownPrice = highPrice - downPrice;                                           // 매수 하락 촉발가
            var downRate         = Math.Min(0D, (currPrice - highPrice) * 100 / highPrice);         // 하락율
            var upPrice          = prevPrice * ((triggerRate / candleCount) + (feeRate * 2)) / 100; // 반등가
            var triggerUpPrice   = prevPrice + upPrice;                                             // 매수 반등 촉발가
            var upRate           = Math.Max(0D, (currPrice - prevPrice) * 100 / prevPrice);         // 반등 상승율
            var downUpRate       = upRate == 0 ? 0D : (-downRate / upRate);                         // 반등율
            var targetRate       = ((triggerRate / (candleCount - 1)) + (feeRate * 2)) / 100;       // 목표 수익율
            var targetSellPrice  = avgPrice + (avgPrice * targetRate);                              // 매도 목표가

            // 현재가가 평단가 보다 (수익율/2 + 수료율2배) 이상일때 전체 매도


            WriteLog("currPrice {0:N0}, prevPrice {1:N0}, highPrice {2:N0}", currPrice, prevPrice, highPrice);
            WriteLog("currPrice {0:N0}, downRate {1:F6}, upRate {2:F6}, downUpRate {3:F6}", currPrice, downRate, upRate, downUpRate);

            //var changePrice = (currPrice - prevPrice);                                              // 변동가
            //var changeRate = (changePrice / currPrice) * 100;                                       // 변동율
            //var candlesChange = (currPrice - highPrice);                                            // 캔들 변동가
            //var candlesRate = (candlesChange / highPrice) * 100;                                    // 캔들 변동율
            //var profit = coinBalance - coinBuyBalance;                                              // 수익
            //var tradeProfitRate = (avgPrice == 0) ? 0D : (profit / avgPrice) * 100;               // 수익율
            //var targetProfit = coinBuyBalance + (coinBuyBalance * ((triggerRate / candleCount) + (feeRate * 2)) / 100);
            //var targetPrice = avgPrice + targetProfit;
            //var target = coinBuyPrice

            //var result = null as JObject;
            //var args = new object[] { coinName, currPrice, prevPrice, highPrice, changeRate, candlesRate, avgPrice, targetProfit };
            var buyTs  = DateTime.Now - LastBuyDate;
            var sellTs = DateTime.Now - LastSellDate;

            //var minutes = sellTs.TotalMinutes;

            //WriteCurrent("{0} : 현재가 {1:N0}, 직전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 등락폭 {5:F6}, 평단가 {6:N0}, 목표가 {7:N0}", args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            try
            {
                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
                }
                else if (krwBalance > minTradeKRW &&                                                                // 보유 현금이 최소거래 금액 보다 크고
                         sellTs.TotalSeconds >= CandleType.Minute * 60 / 2 &&                                       // 매도 이후 분봉의 절반이상이 지나고
                         currPrice <= triggerDownPrice && currPrice >= triggerUpPrice &&                            // 현재가가 촉발 금액 사이에서 반등하고
                         downUpRate <= candleCount * 2)                                                             // 반등율이 캔들갯수의 2배수가 넘지 않아야 함
                {
                    // BUY
                    var total       = ToOrderPrice(krwBalance);
                    var avgBuyPrice = currPrice;
                    //result = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### {0} BUY : 매수 평단가 {1:N0}: 매수금 {2:N0}: 수량 {3:F6}", coinName, avgBuyPrice, total, total / avgBuyPrice);
                }
                else if (coinBalance > minTradeKRW &&                                                               // 코인 보유금이 최소거래 금액 보다 크고
                         (buyTs.TotalSeconds >= CandleType.Minute * 60 * candleCount / 3 * 2 ||                     // 매도 이후 분봉의 3분2 이상이 지나고
                          currPrice >= targetSellPrice))                                                            // 현재가가 평단가 보다 (수익율/캔들수-1 + 수료율2배) 이상일때 전체 매도
                {
                    // SELL
                    // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
                    var vol = coinVol;
                    vol = Math.Truncate(vol * 100000) / 100000;
                    //result = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }

            //if (result != null)
            //{
            //    var uuid = result.Value<string>("uuid");
            //    var coinProfit = GetOrderResult(ApiData, uuid, coinName, currPrice, StartKRW);

            //    //WriteLog("#### RESULT : {0}", result.ToString());

            //    //var coinProfit = GetBalance(ApiData, coinName, StartKRW);

            //    //txtProfitPrice.Text = coinProfit.KrwProfit.ToString("N0");
            //    //txtProfitRate.Text = coinProfit.KrwProfitRate.ToString("F6");
            //    var profits = new object[] { coinName, StartKRW, coinProfit.TotalBalance, coinProfit.KrwProfit, coinProfit.KrwProfitRate };
            //    WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}", profits);
            //}
        }
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles   = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount * 2); // 캔들 조회 (2배로 여유롭게)
            var currPrice = candles.First().Close;                                                     // 현재가

            // 보유현금 및 주문 정보
            var orderChance    = ApiData.getOrdersChance(coinName);                                 // 주문 가능 정보
            var bid            = orderChance["bid_account"];
            var ask            = orderChance["ask_account"];
            var krwBalance     = bid.Value <double>("balance");                                     // 보유 현금
            var coinVol        = ask.Value <double>("balance");                                     // 보유 코인 수량
            var avgPrice       = ask.Value <double>("avg_buy_price");                               // 매수 평단가
            var bidLock        = bid.Value <double>("locked");                                      // 매수 대기 금액
            var coinBuyBalance = avgPrice * coinVol;                                                // 코인 매수금
            var coinBalance    = currPrice * coinVol;                                               // 코인 평가금
            var balance        = krwBalance + coinBalance;                                          // 현재 자산
            var minTradeKRW    = Settings.Default.minTradeKRW;                                      // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            //var prevCandle = candles[1];                                                          // 직전 캔들
            //var lastCandle = candles.Last();                                                      // 마지막 캔들
            var prevPrice        = candles[1].Close;                                                // 직전종가
            var highPrice        = candles.GetRange(1, candleCount - 1).Max(x => x.High);           // 최고가
            var downPrice        = highPrice * (triggerRate + (feeRate * 2)) / 100;                 // 하락가
            var triggerDownPrice = highPrice - downPrice;                                           // 매수 하락 촉발가
            var downRate         = Math.Min(0D, (currPrice - highPrice) * 100 / highPrice);         // 하락율
            var upPrice          = prevPrice * ((triggerRate / candleCount) + (feeRate * 2)) / 100; // 반등가
            var triggerUpPrice   = prevPrice + upPrice;                                             // 매수 반등 촉발가
            var upRate           = Math.Max(0D, (currPrice - prevPrice) * 100 / prevPrice);         // 반등 상승율
            var downUpRate       = upRate == 0 ? 0D : (-downRate / upRate);                         // 반등율
            var targetRate       = ((triggerRate / (candleCount - 1)) + (feeRate * 2)) / 100;       // 목표 수익율
            var targetSellPrice  = avgPrice + (avgPrice * targetRate);                              // 매도 목표가

            var buyTs  = DateTime.Now - LastBuyDate;
            var sellTs = DateTime.Now - LastSellDate;
            var result = null as JObject;
            var args   = new object[] {
                DateTime.Now,
                coinName,
                currPrice,
                prevPrice,
                highPrice,
                downRate,
                upRate,
                avgPrice,
                StartKRW,
                balance,
                targetSellPrice,
                triggerDownPrice,
                triggerUpPrice,
                targetSellPrice,
            };

            string format = "[{0:T}] {1} : 현재가 {2:N0}, 직전가 {3:N0}, 최고가 {4:N0}, 하락율 {5:F6}, 반등율 {6:F6}, 평단가 {7:N0}";

            format += "\r\n[{0:T}] {1} : 시작자산 {8:N0}, 현재자산 {9:N0} 매수 하락 촉발가 {10:N0}, 매수 반등 촉발가 {11:N0}, 매도 목표가 {12:N0}";
            WriteCurrent(format, args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            txtKRWBalance.Text  = krwBalance.ToString("N0");
            txtCoinBalance.Text = coinBalance.ToString("N0");
            txtBalance.Text     = balance.ToString("N0");
            txtKRWBalance.Text  = krwBalance.ToString("N0");
            txtKRWBalance.Text  = krwBalance.ToString("N0");
            txtStartKRW.Text    = StartKRW.ToString("N0");

            try
            {
                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
                }
                else if (krwBalance > minTradeKRW &&                                                                // 보유 현금이 최소거래 금액 보다 크고
                         sellTs.TotalSeconds >= CandleType.Minute * 60 / 2 &&                                       // 매도 이후 분봉의 절반이상이 지나고
                         currPrice <= triggerDownPrice && currPrice >= triggerUpPrice &&                            // 현재가가 촉발 금액 사이에서 반등하고
                         downUpRate <= candleCount * 2)                                                             // 반등율이 캔들갯수의 2배수가 넘지 않아야 함
                {
                    // BUY
                    var total       = ToOrderPrice(krwBalance);
                    var avgBuyPrice = currPrice;
                    result = React.executeDeal(true, false, coinName, 0, 0, total);
                }
                else if (coinBalance > minTradeKRW &&                                                               // 코인 보유금이 최소거래 금액 보다 크고
                         (buyTs.TotalSeconds >= CandleType.Minute * 60 * candleCount / 3 * 2 ||                     // 매도 이후 분봉의 3분2 이상이 지나고
                          currPrice >= targetSellPrice))                                                            // 현재가가 평단가 보다 (수익율/캔들수-1) + 수료율2배 이상일때 전체 매도
                {
                    // SELL
                    // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
                    var vol = coinVol;
                    vol    = Math.Truncate(vol * 100000) / 100000;
                    result = React.executeDeal(false, false, coinName, vol, 0, 0);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }

            if (result != null)
            {
                var uuid = result.Value <string>("uuid");
                var side = result.Value <string>("side");

                WriteLog("#### START GetOrderResultAsync : {0}", uuid);
                result = GetOrderResultAsync(uuid, coinName).GetAwaiter().GetResult();
                WriteLog("#### FINISH GetOrderResultAsync : {0}", uuid);

                var state       = result.Value <double>("state");
                var price       = result.Value <double>("price");
                var volume      = result.Value <double>("volume");
                var tradeCount  = result.Value <double>("trade_count");
                var trades      = result["trades"] as JArray;
                var tradePrice  = trades[0].Value <double>("price");
                var tradeVolume = trades[0].Value <double>("volume");
                var tradeFunds  = trades[0].Value <double>("funds");

                args = new object[] { uuid, side, state, price, volume, tradeCount, tradePrice, tradeVolume, tradeFunds };
                WriteLog("#### uuid {0}, side {1}, state {2}, price {3:N0}, volume {4:F6}, tradeCount {5:N0}, tradePrice {6:N0}, tradeVolume {7:N0}, tradeFunds {8:N0}", args);

                if ("bid".Equals(side))
                {
                    // BUY
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### {0} BUY : 매수금 {1:N0} : 매수 평단가 {1:N0} : 수량 {3:F6}", coinName, price * volume, price, volume);
                }
                else if ("ask".Equals(side))
                {
                    // SELL
                    LastSellDate = DateTime.Now;
                    WriteLog("#### {0} SELL : 매도금 {1:N0} : 매도 평단가 {1:N0} : 수량 {3:F6}", coinName, price * volume, price, volume);
                }

                // 수익
                var balance = GetBalance(coinName);
                krwBalance  = balance.KRWBalance;
                coinBalance = currPrice * balance.CoinVol;
                var totalBalance = krwBalance + coinBalance;
                var profit       = totalBalance - StartKRW;
                var profitRate   = (StartKRW == 0) ? 0D : profit / StartKRW * 100;

                txtKRWBalance.Text  = krwBalance.ToString("N0");
                txtCoinBalance.Text = coinBalance.ToString("N0");
                txtBalance.Text     = totalBalance.ToString("N0");
                txtProfitPrice.Text = profit.ToString("N0");
                txtProfitRate.Text  = profitRate.ToString("F4");

                args = new object[] { coinName, StartKRW, totalBalance, profit, profitRate, krwBalance, coinBalance };
                WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}, 보유현금 {5:N0}, 코인 평가 {6:N0}", args);
            }
        }
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 해당 코인 보유금액이 있으면 매도 없으면 매수
            var candles    = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount); // 캔들 조회
            var currCandle = candles.First();                                                       // 현재 캔들
            var currPrice  = currCandle.Close;                                                      // 현재가

            System.Threading.Thread.Sleep(1000);

            var orderChance = GetOrderChance(ApiData, coinName, currPrice);                     // 주문 가능 정보
            var coinPrice   = orderChance.CoinBalance;                                          // 코인 보유금
            var krwBalance  = orderChance.KRWBalance;                                           // 보유 현금
            var coinVol     = orderChance.CoinVol;                                              // 보유 코인 수량
            var avgPrice    = orderChance.AvgBuyPrice;                                          // 매수 평단가
            var minTradeKRW = Settings.Default.minTradeKRW;                                     // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            var prevCandle = candles[1];                                              // 직전 캔들
            var lastCandle = candles.Last();                                          // 마지막 캔들
            var prevPrice  = prevCandle.Close;                                        // 직전종가

            prevPrice = prevCandle.Low;                                               // 직전 저가 대입
            var startPrice = lastCandle.Open;                                         // 마지막 캔들 시작가
            var highPrice  = candles.GetRange(1, candles.Count - 1).Max(x => x.High); // 최고가

            startPrice = Math.Max(startPrice, highPrice);                             // 캔들 최고가 대입
            var change          = (currPrice - prevPrice);                            // 변동가
            var currentRate     = (change / currPrice) * 100;                         // 등락율
            var candlesChange   = (currPrice - startPrice);                           // 캔들 변동가
            var candlesRate     = (candlesChange / startPrice) * 100;                 // 캔들 등락율
            var profit          = (avgPrice == 0) ? 0D : (currPrice - avgPrice);      // 수익
            var tradeProfitRate = (avgPrice == 0) ? 0D : (profit / avgPrice) * 100;   // 수익율
            var result          = null as JObject;
            var args            = new object[] { coinName, currPrice, prevPrice, startPrice, currentRate, candlesRate, avgPrice, tradeProfitRate };
            var buyTs           = DateTime.Now - LastBuyDate;
            var sellTs          = DateTime.Now - LastSellDate;
            var minutes         = sellTs.TotalMinutes;

            WriteLog("{0} : 현재가 {1:N0}, 직전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 등락폭 {5:F6}, 평단가 {6:N0}, 수익율 {7:F6}", args);

            if (StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinPrice < minTradeKRW)
            {
                // 거래 시작 금액
                StartKRW = krwBalance;
            }

            try
            {
                if (coinPrice <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinPrice, minTradeKRW);
                }
                else if (krwBalance > minTradeKRW &&
                         sellTs.TotalMinutes >= CandleType.Minute &&
                         candlesRate <= -(triggerRate + (feeRate * 2)) &&
                         currentRate >= (feeRate * 2))
                {
                    // BUY
                    // 보유현금이 최소 거래금액 보다 많음
                    // 수익율 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
                    var total = ToOrderPrice(krwBalance);
                    result      = React.executeDeal(true, false, coinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### {0} BUY : 금액 {1:N0}, 수량 {2:F6}", coinName, total, total / currPrice);
                }
                else if (coinPrice > minTradeKRW &&
                         (buyTs.TotalSeconds >= CandleType.Minute * 60 ||
                          tradeProfitRate >= (triggerRate / candleCount) + (feeRate * 2)))
                {
                    // SELL
                    // 코인평가금 최소 거래금액 보다 많음
                    // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
                    var vol = coinVol;
                    vol          = Math.Truncate(vol * 100000) / 100000;
                    result       = React.executeDeal(false, false, coinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                    WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }

            if (result != null)
            {
                WriteLog("#### RESULT : {0}", result.ToString());
            }

            var coinProfit = new Profit(ApiData, coinName, StartKRW);
            var profits    = new object[] { coinName, StartKRW, coinProfit.TotalKrw, coinProfit.KrwProfit, coinProfit.KrwProfitRate };

            txtProfitPrice.Text = coinProfit.KrwProfit.ToString("N0");
            txtProfitRate.Text  = coinProfit.KrwProfitRate.ToString("F6");
            WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}", profits);
        }
        private void Timer_Tick(object sender, EventArgs e)
        {
            var KRWAccount = ApiData.getAsset().Where(x => "KRW".Equals(x.Value <string>("currency"))).First();

            Thread.Sleep(100);
            var krwBalance = KRWAccount.Value <double>("balance");                                           // 보유 현금
            var candles    = ApiData.getCandle <List <Candle> >(CoinName, CandleType, CandleCount);

            Thread.Sleep(100);
            var prevCandle = candles[1];
            var currCandle = candles[0];
            var currPrice  = currCandle.Close;
            var prevPrice  = prevCandle.Close;

            var orderChance = ApiData.getOrdersChance(CoinName);

            Thread.Sleep(100);
            var ask      = orderChance["ask_account"];
            var coinVol  = ask.Value <double>("balance");                                                  // 보유 코인 수량
            var avgPrice = (coinVol * currPrice < 5000) ? currPrice : ask.Value <double>("avg_buy_price"); // 매수 평단가

            var coinPrice = avgPrice * coinVol;                                                            // 보유 코인 금액
            var upRatio   = (currPrice - avgPrice) / currPrice * 100;                                      // 평단 대비 상승폭 (%)
            var downRatio = (currPrice - prevPrice) / currPrice * 100;                                     // 현재가 대비 하락폭 (%)
            var result    = null as JObject;                                                               // 거래 결과

            Debug.WriteLine("upRatio : {0}, downRatio {1}", upRatio, downRatio);
            WriteLog("upRatio : {0}, downRatio {1}", upRatio, downRatio);

            try
            {
                if (upRatio >= TriggerRatio && coinPrice > 5000)
                {
                    // 올랐을때 코인 금액 절반 팔기
                    var vol = (coinPrice / 2) > 5000 ? coinVol / 2 : coinVol;
                    Debug.WriteLine("#### {2} SELL : 금액 {0}, 수량 {1}", vol * currPrice, vol, CoinName);
                    WriteLog("#### {2} SELL : 금액 {0}, 수량 {1}", vol * currPrice, vol, CoinName);
                    Thread.Sleep(100);
                    result = React.executeDeal(false, false, CoinName, vol, 0, 0);
                }
                else if (downRatio <= -(TriggerRatio) && krwBalance > 5000)
                {
                    // 내렸을때 보유 현금 절반으로 코인 사기
                    var total = (krwBalance / 2) > 5000 ? krwBalance / 2 : krwBalance;
                    Debug.WriteLine("#### {2} BUY : 금액 {0}, 수량 {1}", total, total / currPrice, CoinName);
                    WriteLog("#### {2} BUY : 금액 {0}, 수량 {1}", total, total / currPrice, CoinName);
                    Thread.Sleep(100);
                    result = React.executeDeal(true, false, CoinName, 0, 0, Math.Truncate(total / 10) * 10);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

            if (result != null)
            {
                Debug.WriteLine("#### RESULT : {0}", result.ToString());
                WriteLog("#### RESULT : {0}", result.ToString());
            }
        }
        private void PointHalfStrategy()
        {
            // 1분봉 3개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            // 등낙률 0.5% 이상이 되면 오르면 팔고/ 내리면 산다.
            // 거래시 보유 현금, 보유 코인의 절반을 거래하되 거래 금액이 만원 미만인 경우 전체 금액으로 거래한다.

            var candles    = ApiData.getCandle <List <Candle> >(CoinName, CandleType, CandleCount);
            var prevCandle = candles[2];        // 시가 봉
            var currCandle = candles[0];        // 현재가 봉
            var currPrice  = currCandle.Close;
            var prevPrice  = prevCandle.Close;

            var orderChance = ApiData.getOrdersChance(CoinName);
            var ask         = orderChance["ask_account"];
            var bid         = orderChance["bid_account"];
            var krwBalance  = bid.Value <double>("balance");                                                  // 보유 현금
            var coinVol     = ask.Value <double>("balance");                                                  // 보유 코인 수량
            var avgPrice    = (coinVol * currPrice < 5000) ? currPrice : ask.Value <double>("avg_buy_price"); // 매수 평단가
            var coinPrice   = currPrice * coinVol;                                                            // 보유 코인 금액

            var upRatio   = (currPrice - avgPrice) / currPrice * 100;                                         // 평단 대비 상승폭 (%)
            var downRatio = (currPrice - prevPrice) / currPrice * 100;                                        // 현재가 대비 하락폭 (%)
            var result    = null as JObject;                                                                  // 거래 결과

            txtKRW.Text         = krwBalance.ToString("N0");
            txtCoinBalance.Text = coinPrice.ToString("N6");

            Debug.WriteLine("upRatio : {0}, downRatio {1}", upRatio, downRatio);
            WriteLog("upRatio : {0}, downRatio {1}", upRatio, downRatio);

            //try
            //{
            //    if (upRatio >= TriggerRatio && coinPrice > 5000)
            //    {
            //        // 올랐을때 코인 금액 절반 팔기
            //        var vol = (coinPrice / 2) > 5000 ? coinVol / 2 : coinVol;
            //        vol = Math.Truncate(vol * 100000) / 100000;
            //        Debug.WriteLine("#### {2} SELL : 금액 {0}, 수량 {1}", vol * currPrice, vol, CoinName);
            //        WriteLog("#### {2} SELL : 금액 {0}, 수량 {1}", vol * currPrice, vol, CoinName);
            //        result = React.executeDeal(false, false, CoinName, vol, 0, 0);
            //    }
            //    else if (downRatio <= -(TriggerRatio) && krwBalance > 5000)
            //    {
            //        // 내렸을때 보유 현금 절반으로 코인 사기
            //        var total = (krwBalance / 2) > 5000 ? krwBalance / 2 : krwBalance;
            //        total = Math.Truncate(total * 1000) / 1000;
            //        Debug.WriteLine("#### {2} BUY : 금액 {0}, 수량 {1}", total, total / currPrice, CoinName);
            //        WriteLog("#### {2} BUY : 금액 {0}, 수량 {1}", total, total / currPrice, CoinName);
            //        result = React.executeDeal(true, false, CoinName, 0, 0, total);
            //    }
            //}
            //catch (Exception ex)
            //{
            //    Debug.WriteLine(ex);
            //    WriteLog(ex.StackTrace);
            //}

            orderChance = ApiData.getOrdersChance(CoinName);
            ask         = orderChance["ask_account"];
            bid         = orderChance["bid_account"];
            krwBalance  = bid.Value <double>("balance");                                                // 보유 현금
            coinVol     = ask.Value <double>("balance");                                                // 보유 코인 수량
            coinPrice   = currPrice * coinVol;                                                          // 보유 코인 금액

            txtKRW.Text         = krwBalance.ToString("N0");
            txtCoinBalance.Text = coinPrice.ToString("N6");

            //if (result != null)
            //{
            //    Debug.WriteLine("#### RESULT : {0}", result.ToString());
            //    WriteLog("#### RESULT : {0}", result.ToString());
            //}
        }
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var ticks       = CandleCount;
            var triggerRate = Rate;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var profitRate  = ProfitRate;

            // 해당 코인 보유금액이 있으면 매도 없으면 매수
            var candles     = ApiData.getCandle <List <Candle> >(coinName, candleType, CandleCount); // 캔들 조회
            var currCandle  = candles.First();                                                       // 현재 캔들
            var currPrice   = currCandle.Close;                                                      // 현재가
            var orderChance = GetOrderChance(ApiData, coinName, currPrice);                          // 주문 가능 정보
            var coinPrice   = orderChance.CoinBalance * currPrice;                                   // 코인 보유금
            var krwBalance  = orderChance.KRWBalance;                                                // 보유 현금
            var coinVol     = orderChance.CoinVol;                                                   // 보유 코인 수량
            var avgPrice    = orderChance.AvgBuyPrice;                                               // 매수 평단가
            var minTradeKRW = Settings.Default.minTradeKRW;                                          // 최소 거래 금액

            // 분봉 N개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            var prevCandle      = candles[1];                                               // 직전 캔들
            var lastCandle      = candles.Last();                                           // 마지막 캔들
            var prevPrice       = prevCandle.Close;                                         // 직전종가
            var startPrice      = lastCandle.Open;                                          // 마지막 캔들 시작가
            var change          = (currPrice - prevPrice);                                  // 변동가
            var currentRate     = (change / currPrice) * 100;                               // 등락율
            var downChange      = (startPrice - currPrice);                                 // 상승전 변동가
            var downRate        = (downChange / startPrice) * 100;                          // 상승전 하락율
            var profit          = (currPrice - avgPrice);                                   // 수익
            var tradeProfitRate = (profit / avgPrice) * 100;                                // 수익율
            var result          = null as JObject;
            var args            = new object[] { coinName, currPrice, prevPrice, startPrice, currentRate, downRate };

            WriteLog("{0} : 현재가 {1:N0}, 이전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 하락폭 {5:F6}", args);

            if (coinPrice <= minTradeKRW && krwBalance <= minTradeKRW)
            {
                // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinPrice, minTradeKRW);
            }
            else if (krwBalance > minTradeKRW &&
                     downRate >= profitRate + (feeRate * 2) &&
                     currentRate >= (feeRate * 2))
            {
                // BUY
                // 보유현금이 최소 거래금액 보다 많음
                // 수익율 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
                var total = Math.Truncate(krwBalance * 1000) / 1000;
                result      = React.executeDeal(true, false, coinName, 0, 0, total);
                LastBuyDate = DateTime.Now;
                WriteLog("#### {0} BUY : 금액 {1:N0}, 수량 {2:F6}", coinName, total, total / currPrice);
            }
            else if (krwBalance > minTradeKRW &&
                     (tradeProfitRate <= profitRate - (feeRate * 2) || tradeProfitRate >= profitRate + (feeRate * 2)))
            {
                // SELL
                // 코인평가금 최소 거래금액 보다 많음
                // 현재가가 평단가 보다 (수익율 - 수료율 * 2) 이하일때 전체 매도
                // 현재가가 평단가 보다 (수익율 + 수료율 * 2) 이상일때 전체 매도
                var vol = coinVol;
                vol          = Math.Truncate(vol * 100000) / 100000;
                result       = React.executeDeal(false, false, coinName, vol, 0, 0);
                LastSellDate = DateTime.Now;
                WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
            }



            // 분봉 N개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            // 촉발 등낙률 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
            // 매수 이후 (1% - 수수료율) 이상 떨어지면 전체 손절
            // 매수 이후 (1% + 수수료율) 이상 오르면 전체 익절
            var prevCandle  = candles[1];                                                   // 직전 캔들
            var lastCandle  = candles.Last();                                               // 마지막 캔들
            var prevPrice   = prevCandle.Close;                                             // 직전종가
            var startPrice  = lastCandle.Open;                                              // 마지막 캔들 시작가
            var change      = (currPrice - prevPrice);                                      // 변동가
            var currentRate = (change / currPrice) * 100;                                   // 등락율
            var downChange  = (startPrice - currPrice);                                     // 상승전 변동가
            var downRate    = (downChange / startPrice) * 100;                              // 상승전 하락율


            //var algIdx = cmbAlgorithm.SelectedIndex;
            //var idx = (algIdx > 0) ? algIdx : Convert.ToInt32(txtMinute.Text);
            //var candleType = BotSetting.CandleTypes[idx];
            var orderChance = GetOrderChance(ApiData, CoinName, currPrice);
            //var ask = orderChance["ask_account"];
            //var bid = orderChance["bid_account"];
            var krwBalance = orderChance.KRWBalance;                            // 보유 현금
            var coinVol    = orderChance.CoinVol;                               // 보유 코인 수량
            var avgPrice   = orderChance.AvgBuyPrice;                           // 매수 평단가
            var coinPrice  = orderChance.CoinBalance;                           // 보유 코인 금액

            //var upRatio = (currPrice - avgPrice) / currPrice * 100;     // 평단 대비 상승폭 (%)
            //var downRatio = (currPrice - prevPrice) / currPrice * 100;  // 현재가 대비 하락폭 (%)
            var ratio     = (currPrice - prevPrice) / currPrice * 100;      // 등락폭 (%)
            var tradeRate = Convert.ToDouble(txtTradeRate.Text) / 100D;     // 거래 비중 (%)
            var result    = null as JObject;                                // 거래 결과

            //txtKRW.Text = krwBalance.ToString("N0");
            //txtCoinBalance.Text = coinPrice.ToString("N0");

            Debug.WriteLine("이전가: {0:N0}, 현재가 {1:N0}, ratio : {2:F4}%", prevPrice, currPrice, ratio);
            WriteLog("이전가: {0:N0}, 현재가 {1:N0}, ratio : {2:F4}%", prevPrice, currPrice, ratio);

            try
            {
                if (avgPrice + (2 * (avgPrice * Rate / 100)) <= currPrice && coinPrice >= 5000)
                {
                    // 현재가가 평단가보다 등락폭 2배 이상 올랐을때 전량 매도
                    var vol = coinVol;
                    vol = Math.Truncate(vol * 100000) / 100000;
                    Debug.WriteLine("#### {2} ALL SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    WriteLog("#### {2} ALL SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    result       = React.executeDeal(false, false, CoinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                }
                else if (ratio >= Rate && coinPrice >= 5000 && ((DateTime.Now - LastSellDate).TotalMinutes >= idx || ratio >= Rate * 1.5))
                {
                    // 올랐을때 코인 금액 절반 팔기
                    var vol = (coinPrice * tradeRate) > 5000 ? coinVol * tradeRate : coinVol;
                    vol = Math.Truncate(vol * 100000) / 100000;
                    Debug.WriteLine("#### {2} SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    WriteLog("#### {2} SELL : 금액 {0:N0}, 수량 {1:F6}", vol * currPrice, vol, CoinName);
                    result       = React.executeDeal(false, false, CoinName, vol, 0, 0);
                    LastSellDate = DateTime.Now;
                }
                else if (ratio <= -(Rate * 1.2) && krwBalance > 5000 && (DateTime.Now - LastBuyDate).TotalMinutes >= idx)
                {
                    // 1.2배 내렸을때 보유 현금 절반으로 코인 사기
                    var total = (krwBalance * tradeRate) > 5000 ? krwBalance * tradeRate : krwBalance;
                    total = Math.Truncate(total * 1000) / 1000;
                    Debug.WriteLine("#### {2} BUY : 금액 {0:N0}, 수량 {1:F6}", total, total / currPrice, CoinName);
                    WriteLog("#### {2} BUY : 금액 {0:N0}, 수량 {1:F6}", total, total / currPrice, CoinName);
                    result      = React.executeDeal(true, false, CoinName, 0, 0, total);
                    LastBuyDate = DateTime.Now;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                WriteLog(ex.StackTrace);
            }

            orderChance = GetOrderChance(ApiData, CoinName, currPrice);

            if (result != null)
            {
                Debug.WriteLine("#### RESULT : {0}", result.ToString());
                WriteLog("#### RESULT : {0}", result.ToString());
            }
        }
        private void MinuteCandleByTicks()
        {
            // 설정값
            var coinName    = Coin.Ticker;
            var candleType  = CandleType.Name;
            var candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles   = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount * 2); // 캔들 조회 (2배로 여유롭게)
            var currPrice = candles.First().Close;                                                     // 현재가

            // 보유현금 및 주문 정보
            var orderChance    = ApiData.getOrdersChance(coinName);                                 // 주문 가능 정보
            var bid            = orderChance["bid_account"];
            var ask            = orderChance["ask_account"];
            var krwBalance     = bid.Value <double>("balance");                                     // 보유 현금
            var coinVol        = ask.Value <double>("balance");                                     // 보유 코인 수량
            var avgPrice       = ask.Value <double>("avg_buy_price");                               // 매수 평단가
            var bidLock        = bid.Value <double>("locked");                                      // 매수 대기 금액
            var coinBuyBalance = avgPrice * coinVol;                                                // 코인 매수금
            var coinBalance    = currPrice * coinVol;                                               // 코인 평가금
            var minTradeKRW    = Settings.Default.minTradeKRW;                                      // 최소 거래 금액

            // 분봉 N개를 기준으로 직전 시가 에서 현재 금액 등낙률을 가져온다.
            //var prevCandle = candles[1];                                                          // 직전 캔들
            //var lastCandle = candles.Last();                                                      // 마지막 캔들
            var prevPrice        = candles[1].Low;                                                  // 직전저가
            var highPrice        = candles.GetRange(1, candleCount - 1).Max(x => x.High);           // 최고가
            var triggerDownPrice = highPrice - (highPrice * (triggerRate + (feeRate * 2)) / 100);   // 매수 하락 촉발가
            var triggerUpPrice   = prevPrice + (prevPrice * (feeRate * 2) / 100);                   // 매수 반등 촉발가

            //var targetDownPrice = prevPrice + (prevPrice * (feeRate * 2) / 100);                    // 매수 목표가

            WriteLog("currPrice {0:N0}, triggerDownPrice {1:N0}, triggerUpPrice {2:N0}, highPrice {3:N0}, prevPrice {4:N0}", currPrice, triggerDownPrice, triggerUpPrice, highPrice, prevPrice);

            if (currPrice <= triggerDownPrice && currPrice >= triggerUpPrice)
            {
                WriteLog("#### 매수 : currPrice {0:N0}", currPrice);
            }

            //var changePrice = (currPrice - prevPrice);                                              // 변동가
            //var changeRate = (changePrice / currPrice) * 100;                                       // 변동율
            //var candlesChange = (currPrice - highPrice);                                            // 캔들 변동가
            //var candlesRate = (candlesChange / highPrice) * 100;                                    // 캔들 변동율
            //var profit = coinBalance - coinBuyBalance;                                              // 수익
            //var tradeProfitRate = (avgPrice == 0) ? 0D : (profit / avgPrice) * 100;               // 수익율
            //var targetProfit = coinBuyBalance + (coinBuyBalance * ((triggerRate / candleCount) + (feeRate * 2)) / 100);
            //var targetPrice = avgPrice + targetProfit;
            //var target = coinBuyPrice

            //var result = null as JObject;
            //var args = new object[] { coinName, currPrice, prevPrice, highPrice, changeRate, candlesRate, avgPrice, targetProfit };
            //var buyTs = DateTime.Now - LastBuyDate;
            //var sellTs = DateTime.Now - LastSellDate;
            //var minutes = sellTs.TotalMinutes;

            //WriteCurrent("{0} : 현재가 {1:N0}, 직전가 {2:N0}, 시작가 {3:N0}, 직전등락폭 {4:F6}, 등락폭 {5:F6}, 평단가 {6:N0}, 목표가 {7:N0}", args);

            //if(StartKRW < minTradeKRW && krwBalance > minTradeKRW && coinBalance < minTradeKRW)
            //{
            //    // 거래 시작 금액
            //    StartKRW = krwBalance;
            //}

            //try
            //{
            //    if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
            //    {
            //        // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
            //        WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
            //    }
            //    else if (krwBalance > minTradeKRW
            //        && sellTs.TotalSeconds >= CandleType.Minute * 60 / 2
            //        && candlesRate <= -(triggerRate + (feeRate * 2))
            //        && changeRate >= (feeRate * 2))
            //    {
            //        // BUY
            //        // 보유현금이 최소 거래금액 보다 많음
            //        // 수익율 초과하여 떨어지다 수수료율 2배 이상 상승 했을때 거래비율 만큼 산다.
            //        var total = ToOrderPrice(krwBalance);
            //        result = React.executeDeal(true, false, coinName, 0, 0, total);
            //        LastBuyDate = DateTime.Now;
            //        WriteLog("#### {0} BUY : 금액 {1:N0}, 수량 {2:F6}", coinName, total, total / currPrice);
            //    }
            //    else if (coinBalance > minTradeKRW
            //        && (buyTs.TotalSeconds >= CandleType.Minute * 60 * candleCount / 2
            //        || targetProfit <= coinBalance ))
            //    {
            //        // SELL
            //        // 코인평가금 최소 거래금액 보다 많음
            //        // 분봉 하나의 시간이 지나면 시장가에 전체 매도
            //        // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
            //        var vol = coinVol;
            //        vol = Math.Truncate(vol * 100000) / 100000;
            //        result = React.executeDeal(false, false, coinName, vol, 0, 0);
            //        LastSellDate = DateTime.Now;
            //        WriteLog("#### {0} SELL : 금액 {1:N0}, 수량 {2:F6}", coinName, vol * currPrice, vol);
            //    }
            //}
            //catch (Exception ex)
            //{
            //    WriteLog(ex.StackTrace);
            //}

            //if (result != null)
            //{
            //    var uuid = result.Value<string>("uuid");
            //    var coinProfit = GetOrderResult(ApiData, uuid, coinName, currPrice, StartKRW);

            //    //WriteLog("#### RESULT : {0}", result.ToString());

            //    //var coinProfit = GetBalance(ApiData, coinName, StartKRW);

            //    //txtProfitPrice.Text = coinProfit.KrwProfit.ToString("N0");
            //    //txtProfitRate.Text = coinProfit.KrwProfitRate.ToString("F6");
            //    var profits = new object[] { coinName, StartKRW, coinProfit.TotalBalance, coinProfit.KrwProfit, coinProfit.KrwProfitRate };
            //    WriteLog("#### {0} 수익 : 거래시작금액 {1:N0}, 현재평가 금액 {2:N0}, 수익금액 {3:N0}, 수익율 {4:F6}", profits);
            //}
        }