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

            if (result != null)
            {
                Debug.WriteLine("#### RESULT : {0}", result.ToString());
                WriteLog("#### RESULT : {0}", result.ToString());
            }
        }
        private OrderChance GetOrderChance(string coinName)
        {
            var result      = new OrderChance();
            var orderChance = ApiData.getOrdersChance(CoinName);
            var ask         = orderChance["ask_account"];
            var bid         = orderChance["bid_account"];

            result.KRWBalance = bid.Value <double>("balance");  // 보유 현금
            result.CoinVol    = ask.Value <double>("balance");  // 보유 코인 수량
        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);                              // 매도 목표가

            var buyTs  = DateTime.Now - LastBuyDate;
            var sellTs = DateTime.Now - LastSellDate;
            var result = null as JObject;
            var args   = new object[] {
                coinName,
                currPrice,
                prevPrice,
                highPrice,
                downRate,
                upRate,
                avgPrice,
                targetSellPrice,
                DateTime.Now;
            };
Beispiel #4
0
        private OrderChance GetOrderChance(string coinName)
        {
            var orderChance = ApiData.getOrdersChance(CoinName);
            var ask         = orderChance["ask_account"];
            var bid         = orderChance["bid_account"];
            var result      = new OrderChance()
            {
                KRWBalance  = bid.Value <double>("balance"),            // 보유 현금
                CoinVol     = ask.Value <double>("balance"),            // 보유 코인 수량
                AvgBuyPrice = ask.Value <double>("avg_buy_price"),      // 매수 평단가
            };

            return(result);
        }
        private OrderChance GetOrderChance(ApiData apiData, string coinName, double currentPrice, bool setText = true)
        {
            var orderChance = apiData.getOrdersChance(coinName);
            var ask         = orderChance["ask_account"];
            var bid         = orderChance["bid_account"];
            var result      = new OrderChance(currentPrice)
            {
                KRWBalance  = bid.Value <double>("balance"),            // 보유 현금
                CoinVol     = ask.Value <double>("balance"),            // 보유 코인 수량
                AvgBuyPrice = ask.Value <double>("avg_buy_price"),      // 매수 평단가
            };

            txtKRW.Text         = result.KRWBalance.ToString("N0");
            txtCoinBalance.Text = result.CoinBalance.ToString("N0");

            return(result);
        }
        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);                              // 매도 목표가

            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,
                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}";
            WriteCurrent(format, 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);
                }
                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");

                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   = 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") ?? 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;                                      // 최소 거래 금액

            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,
                buyCountDownSec,
            };

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

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

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

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

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

            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);
            }
        }
Beispiel #8
0
        private void PointHalfStrategy()
        {
            // 3분봉 2개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            // 등낙률 0.5% 이상이 되면 오르면 팔고/ 내리면 산다.
            // 거래시 보유 현금, 보유 코인의 절반을 거래하되 거래 금액이 만원 미만인 경우 전체 금액으로 거래한다.

            var KRWAccount = ApiData.getAsset().Where(x => "KRW".Equals(x.Value <string>("currency"))).First();
            var krwBalance = KRWAccount.Value <double>("balance");                                           // 보유 현금

            txtKRW.Text = krwBalance.ToString("N0");
            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 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;
                    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);
            }

            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 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 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;                // 매도 목표가

        //    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}";

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

        //    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 (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));

        //        }
        //        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");

        //            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");

        //                    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 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;             // 매도 목표가

            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 unitPrice   = GetOrderUnitPrice(currPrice);
                    var total       = Math.Truncate(coinBalance / unitPrice) * unitPrice;
                    var avgBuyPrice = currPrice;
                    orderResult = React.executeDeal(true, false, coinName, 0, 0, total);
                    IsWaiting   = true;
                    LastBuyDate = DateTime.Now;
                    WriteLog("#### BUY : {0}", orderResult.ToString(Formatting.Indented));

                    var buyUUID        = orderResult.Value <string>("uuid");
                    var buyOrderResult = await GetBuyMarketResultAsync(buyUUID);

                    // SELL
                    if (buyOrderResult != null)
                    {
                        var buyPrice        = buyOrderResult.Value <double>("price");
                        var targetBuyPrice  = GetOrderUnitPrice(buyPrice + Math.Max((buyPrice * 0.5 / 100) + (fee * 2), 10));
                        var buyVol          = buyOrderResult.Value <double>("executed_volume");
                        var sellOrderResult = await GetSellOrderResultAsync(coinName, targetBuyPrice, buyVol);

                        IsWaiting   = false;
                        LastBuyDate = DateTime.Now;
                        WriteLog("#### SELL : {0}", sellOrderResult.ToString(Formatting.Indented));
                    }
                }
                //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);
            }
        }
Beispiel #10
0
        private void PointHalfStrategy()
        {
            // 1분봉 3개를 기준으로 이전 시가 에서 현재 금액 등낙률을 가져온다.
            // 등낙률 0.5% 이상이 되면 오르면 팔고/ 내리면 산다.
            // 거래시 보유 현금, 보유 코인의 절반을 거래하되 거래 금액이 만원 미만인 경우 전체 금액으로 거래한다.
            var idx        = Convert.ToInt32(txtMaxRatio.Text);
            var candleType = BotSetting.CandleTypes[idx];
            var candles    = ApiData.getCandle <List <Candle> >(CoinName, candleType, 3);
            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 ratio  = (currPrice - prevPrice) / currPrice * 100;     // 등락폭 (%)
            var result = null as JObject;                               // 거래 결과

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

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

            //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 candleCount = CandleCount;
            var feeRate     = FeeRate;
            var tradeRate   = TradeRate;
            var triggerRate = TriggerRate;

            // 캔들 갯수 많큼 캔들 가져오기
            var candles   = ApiData.getCandle <List <Candle> >(coinName, candleType, candleCount);  // 캔들 조회
            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 askLock     = ask.Value <double>("locked");

            //var avgPrice = ask.Value<double>("avg_buy_price");                                    // 매수 평단가
            //var coinBalance = orderChance.CoinBalance;                                          // 코인 보유금
            //var coinBuyBalance = avgPrice * coinVol;                                              // 코인 매수금
            //var minTradeKRW = Settings.Default.minTradeKRW;                                     // 최소 거래 금액

            WriteLog("krwBalance {0:N0}, coinVol {1:F6}, avgPrice {2:N0}, bidLock {3:N0}, askLock {4:N0}", krwBalance, coinVol, avgPrice, bidLock, askLock);

            //// 분봉 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);
            //}
        }
        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 triggerDownPrice = highPrice - (highPrice * (triggerRate + (feeRate * 2)) / 100);                     // 매수 하락 촉발가
            var downRate         = (currPrice - highPrice) / highPrice * 100;                                         // 하락율
            var triggerUpPrice   = prevPrice + (prevPrice * ((triggerRate / candleCount + 1) + (feeRate * 2)) / 100); // 매수 반등 촉발가
            var upRate           = (currPrice - prevPrice) / prevPrice * 100;                                         // 반등 상승율
            var downUpRate       = upRate == 0 ? 0D : (-downRate / upRate);                                           // 반등율

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

            WriteLog("currPrice {0:N0}, triggerDownPrice {1:N0}, triggerUpPrice {2:N0}, highPrice {3:N0}, prevPrice {4:N0}, downUpRate {5:N0}", currPrice, triggerDownPrice, triggerUpPrice, highPrice, prevPrice, 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 / 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);
            //}
        }
        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 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 bidLock        = bid.Value <double>("locked");                                      // 매수 대기 금액
            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 * 60 * candleCount / 3 * 2);                  // 매도 쿨다운 시간 (초)
            var sellCountDownSec = sellCoolDownSec - sellTs;                                        // 매도 까지 남은 시간 (초)

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

            var result = null as JObject;
            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");

            var buySates  = null as string;
            var sellSates = null as string;

            try
            {
                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
                    return;
                }

                if (coinBalance <= minTradeKRW && krwBalance <= minTradeKRW)
                {
                    // 보유현금과 보유 코인이 최소 거래금액 보다 적으면 거래 없음
                    WriteLog("#### 거래 불가 : 보유현금 {0}, 코인보유금 {1}, 최소 거래 금액 {2},", krwBalance, coinBalance, minTradeKRW);
                    return;
                }

                if (krwBalance > minTradeKRW &&                                                                   // 보유 현금이 최소거래 금액 보다 크고
                    buySec <= 0D && /* */                                                                         // 매도 이후 분봉의 절반이상이 지나고
                    currPrice <= Math.Truncate(triggerDownPrice) && currPrice >= Math.Truncate(triggerUpPrice) && // 현재가가 촉발 금액 사이에서 반등하고
                    downUpRate <= candleCount * 2)                                                                // 반등율이 캔들갯수의 2배수가 넘지 않아야 함
                {
                    // BUY
                    var total       = ToOrderPrice(krwBalance);
                    var avgBuyPrice = currPrice;
                    result   = React.executeDeal(true, false, coinName, 0, 0, total);
                    buySates = result.Value <string>("state");

                    if ("cancel".Equals(buySates))
                    {
                        System.Threading.Thread.Sleep(1000);
                        result   = React.executeDeal(true, false, coinName, 0, 0, total);
                        buySates = result.Value <string>("state");
                    }
                }
                else if (coinBalance > minTradeKRW &&                                                               // 코인 보유금이 최소거래 금액 보다 크고
                         (sellCountDownSec <= 0D ||                                                                 // 매도 이후 분봉의 3분2 이상이 지나거나
                          currPrice >= Math.Truncate(targetSellPrice)))                                             // 현재가가 평단가 보다 (수익율/캔들수-1) + 수료율2배 이상일때 전체 매도
                {
                    // SELL
                    // 현재가가 평단가 보다 (수익율/캔들수 + 수료율2배) 이상일때 전체 매도
                    var vol = coinVol;
                    vol       = Math.Truncate(vol * 100000) / 100000;
                    result    = React.executeDeal(false, false, coinName, vol, 0, 0);
                    sellSates = result.Value <string>("state");
                }

                if (result != null)
                {
                    WriteLog("{0} RESULT : {1}", coinName, JsonConvert.SerializeObject(result));

                    var uuid = result.Value <string>("uuid");
                    var side = result.Value <string>("side");

                    if (result != null && !"cancel".Equals(buySates))
                    {
                        WriteLog("#### START GetOrderResultAsync : {0}", uuid);
                        //result = GetOrderResultAsync(uuid);
                        result = GetOrderResult(uuid);
                        WriteLog("#### FINISH GetOrderResultAsync : {0}", uuid);

                        if (result == null)
                        {
                            WriteLog("#### 거래 결과를 가져올수 없습니다.");
                            return;
                        }

                        var state       = result.Value <string>("state");
                        var price       = result.Value <double?>("price") ?? 0D;
                        var volume      = result.Value <double?>("volume") ?? 0D;
                        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;
                        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);
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.StackTrace);
            }
        }