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 / candleMinute * candleCount) + (feeRate * 2)) / 100; // 목표 수익율 var targetSellPrice = avgPrice * (1 + targetRate); // 매도 목표가 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; System.Threading.Thread.Sleep(500); 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; System.Threading.Thread.Sleep(500); 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 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 tradeResult = 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; result = React.executeDeal(true, false, coinName, 0, 0, total); LastBuyDate = DateTime.Now; WriteLog("#### BUY : {0}", result.ToString(Formatting.Indented)); } else if (coinBalance > minTradeKRW && // 코인 보유금이 최소거래 금액 보다 클때 currPrice >= Math.Truncate(targetSellPrice)) // 현재가가 목표 매수 금액보다 클때 { // SELL var vol = coinVol; vol = Math.Truncate(vol * 100000) / 100000; result = React.executeDeal(false, false, coinName, vol, 0, 0); LastSellDate = DateTime.Now; WriteLog("#### SELL : {0}", result.ToString(Formatting.Indented)); } if (result != null) { IsWaiting = true; LastOrderResult = result; uuid = result.Value <string>("uuid"); side = result.Value <string>("side"); state = result.Value <string>("state"); System.Threading.Thread.Sleep(1500); var order = ApiData.checkOrder(uuid); if (order != null) { IsWaiting = false; WriteLog("#### ORDER RESULT : {0}", order.ToString(Formatting.Indented)); //result = GetOrderResultAsync(uuid); //result = GetOrderResult(uuid); //WriteLog("#### FINISH GetOrderResultAsync : {0}", uuid); } 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); } }