using DevExpress.XtraTreeList.Nodes; using Microsoft.CodeAnalysis.CSharp.Scripting; using Million; using Million.Data; using Million.Data.Upbit; using Million.Data.Upbit.Order; using Million.DbManagement; using Million.IndicatorConditionSetup; using Million.Manager; using Million.SaveData; using Million_Buyer.SubForm; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static Million.CommonProperty; namespace Million_Buyer { public partial class MainForm : Form { bool RealTimeMode = true; private SavedResultData _saveResultData; //결과 데이터 private List resultDataList; //구매 데이터 private List buyList; bool SetIndicator = false; bool ThreadFlag = false; private Thread thread = null; private Thread orderThread = null; private List accounts = null; private PaymentType payment = PaymentType.KRW; private int cycleCount = 0; private double cycleBuyMoney = 0; private int buyCycleCount = 0; private bool buyFlag = true; public MainForm() { InitializeComponent(); Init(); } public void Init() { // data setting DbServiceFunctions.GetMarketTable_FromDB("Upbit", "KRW"); // Result Data DB에 생성 Million.DbManagement.DBConnection.QueryUpdateExcuter (CommonProperty.DbConnectionResultData, DbServiceFunctions.CreateSavedAnalysisDataTableQuery()); Million.DbManagement.DBConnection.QueryUpdateExcuter (CommonProperty.DbConnectionResultData, DbServiceFunctions.CreateSavedResultDataTableQuery()); Million.DbManagement.DBConnection.QueryUpdateExcuter (CommonProperty.DbConnectionResultData, DbServiceFunctions.CreateRealTimeIndicatorTableQuery()); _saveResultData = new SavedResultData { BuySetupList = new List(), SellSetupList = new List() }; resultDataList = new List(); buyList = new List(); // 현재 연결되어 있는 KEY를 이용하여 확인 cb_UserList.Items.Add("이장화"); cb_UserList.Items.Add("김영호"); cb_UserList.Items.Add("이찬균"); //cb_UserList.SelectedIndex = 0; } /// /// 실시간 모드일 경우 /// /// /// private void rd_Realtime_CheckedChanged(object sender, EventArgs e) { RealTimeMode = true; SetReadOnly(true); } /// /// Buyer 모드일 경우 /// /// /// private void rd_buyer_CheckedChanged(object sender, EventArgs e) { RealTimeMode = false; SetReadOnly(false); } /// /// Read Only Set(모드 변경) /// /// private void SetReadOnly(bool flag) { tb_CycleCount.ReadOnly = flag; tb_CycleMoneyValue.ReadOnly = flag; } /// /// 조건식 폼 띄우기 /// /// /// private void bt_Modify_Click(object sender, EventArgs e) { var subForm = new IndicatorLoadForm(); if (subForm.ShowDialog() == DialogResult.OK) { _saveResultData.SellSetupList = ((List)subForm.selectData["SellSetupList"]); _saveResultData.BuySetupList = ((List)subForm.selectData["BuySetupList"]); _saveResultData.BuyRegularExpression = subForm.selectData["BuyRegularExpression"].ToString(); _saveResultData.SellRegularExpression = subForm.selectData["SellRegularExpression"].ToString(); lb_IndiName.Text = subForm.selectData["Name"].ToString(); SetIndicator = true; } } /// /// Start Button Event /// /// /// private void bt_Start_Click(object sender, EventArgs e) { if (!SetIndicator) { MessageBox.Show("Indicator Setting 필요"); } else { int.TryParse(tb_CycleCount.Text, out cycleCount); double.TryParse(tb_CycleMoneyValue.Text, out cycleBuyMoney); if (!RealTimeMode) { if (cycleCount == 0 || cycleBuyMoney == 0) { MessageBox.Show("한번에 구입 할 종목 수, 종목 별 구입 금액 입력 바람(Double, Int)"); return; } } if (!ThreadFlag) { SetReadOnly(true); bt_Start.Enabled = false; thread = new Thread(ThreadFunction); thread.Start(); //실시간 검증에는 동작하지 않음 if (!RealTimeMode) { orderThread = new Thread(OrderThreadFunction); orderThread.Start(); } ThreadFlag = true; cb_UserList.Enabled = false; cb_AccountList.Enabled = false; } } } /// /// Stop Button Event /// /// /// private void bt_Stop_Click(object sender, EventArgs e) { ThreadFlag = false; if (!RealTimeMode && thread != null) thread.Abort(); if (!RealTimeMode && orderThread != null) orderThread.Abort(); bt_Start.Enabled = true; rd_buyer.Enabled = true; rd_Realtime.Enabled = true; if (rd_buyer.Checked) SetReadOnly(false); cb_UserList.Enabled = true; cb_AccountList.Enabled = true; /* * 종료 된 후 데이터 처리 소스는 여기서 처리 * 1. Grid에 Stop 후 데이터 처리 * 2. 종료 후 데이터 저장 처리 */ } /// /// 실시간 Order 현황 Check(1초에 한번씩 호출할까 2초에 한번할까 고민중 /// private void OrderThreadFunction() { InsertLog("Order Thread Start"); while (ThreadFlag) { //Oder List는 Wait, Watch로 구분되어 있는 List 최신 100개, Done, Cancel로 구분되어 있는 List 최신 100개를 표기함 var orderList = CommonProperty.UpbitApi.GetOrderListInformation(UpbitOrderListTypes.waitwatch); orderList.AddRange(CommonProperty.UpbitApi.GetOrderListInformation(UpbitOrderListTypes.donecancel)); this.BeginInvoke(new Action(delegate () { tl_RealtimeOrder.Nodes.Clear(); foreach (var item in orderList) { tl_RealtimeOrder.AppendNode(new object[] { item.Uuid, item.CoinFullName, item.Side, item.OrderPrice, item.State, item.Volume, item.ExecutedVolume, item.CreateOrderTime.ToString("yyyy-MM-dd HH:mm:ss") }, null); } var tempData = orderList.Select(t => t.Uuid).Distinct().ToList(); var count = tempData.Count + buyList.Count; buyCycleCount = count; lb_BuyMarketCount.Text = buyCycleCount.ToString(); })); //1초에 한번씩 부르게 처리 Thread.Sleep(1000); } InsertLog("Order Thread End"); } /// /// 실제 동작 Thread /// private void ThreadFunction() { #region Default Setting //시간 체크 var sw = new System.Diagnostics.Stopwatch(); var dataList = new Dictionary>(); var sellIndicator = _saveResultData.SellSetupList.OfType().ToList(); var buyIndicator = _saveResultData.BuySetupList.OfType().ToList(); var totalIndicatorList = new List(); totalIndicatorList.AddRange(sellIndicator); totalIndicatorList.AddRange(buyIndicator); var candleTypeList = totalIndicatorList.Select(t => t.MCandleTimeType).ToList(); candleTypeList.Add(MCandleTimeType.M1); candleTypeList = candleTypeList.Distinct().ToList(); //사고 파는 rex 데이터 가져오는 부분 var buyRex = _saveResultData.BuyRegularExpression; var sellRex = _saveResultData.SellRegularExpression; var buyScript = new InteratorScripter(); buyScript.regularExpressionString = buyRex; var sellScript = new InteratorScripter(); sellScript.regularExpressionString = sellRex; var scriptDic = new Dictionary(); //TODO 모든 조건 데이터 미리 계산 필요 //save 데이터 _saveResultData.StartDateTime = DateTime.Now; //구매 데이터 var saveFlag = true; //구매 후 데이터 여러개 계속 겹치는 경우 제외하기 위한 List var overlapBuyManagementList = new Dictionary(); int firstFlag = 0; RealtimeDataSettingManager realTimeMsg = null; var standardTime = DateTime.Now; this.BeginInvoke(new Action(delegate () { lb_StartDate.Text = standardTime.ToString("yyyy-MM-dd HH:mm:ss"); })); if (RealTimeMode) InsertLog("실시간 검증 구동 중.."); else InsertLog("Buyer 구동 중.."); var cnt = 0; sw.Start(); #endregion while (ThreadFlag) { try { #region 기본 데이터 Setting if (firstFlag == 0) { realTimeMsg = new RealtimeDataSettingManager(null, this.payment); dataList = realTimeMsg.GetRealTimeCandleData(candleTypeList, 200); foreach (var item in realTimeMsg.coinFullNameList) { if (cb_IncludeClearIndicator.Checked) overlapBuyManagementList[item] = false; else overlapBuyManagementList[item] = true; } foreach (var item in totalIndicatorList) { var dic = new Dictionary(); foreach (var codeFullname in realTimeMsg.coinFullNameList) { if (cb_IncludeClearIndicator.Checked) dic[codeFullname] = false; else dic[codeFullname] = true; } CommonProperty.subIndicatorStateManagement[item.IndexString] = dic; } // TODO 이미 구매되어 있는 종목에 대한 기본 데이터 Setting 필요 firstFlag = 1; InsertLog("Deafult Data Setting 완료.."); sw.Stop(); InsertLog("Default 계산 시간 : " + sw.Elapsed.ToString()); } else { if (firstFlag == 1) { sw.Restart(); dataList = realTimeMsg.ProcessRecentlyCandleDatas(dataList, candleTypeList); sw.Stop(); if (sw.Elapsed.TotalSeconds > 1) InsertLog("Ticker 계산 시간 : " + sw.Elapsed.ToString()); } } #endregion //Ticker 초기화 if (DateTime.Now.Minute == 0 && saveFlag) { saveFlag = false; } else if (DateTime.Now.Minute > 1) saveFlag = true; foreach (var item in realTimeMsg.coinFullNameList) { #region Indicator 계산 -> Code full name 으로 for문 돌면서 계산 처리 var dic = new Dictionary>(); var distinctIndicator = IndicatorSetupManager.Distinct(totalIndicatorList); sw.Restart(); foreach (var key in dataList.Keys) { var candleDataList = dataList[key].FirstOrDefault(t => t.ConGoodsData.CoinFullName == item); dic[key] = candleDataList.CandleDataLists_Im; var indicatorSetUpList = distinctIndicator.Where(t => t.MCandleTimeType == key).ToList(); foreach (var setup in indicatorSetUpList) { setup.excute(dic[key], true); } } sw.Stop(); if (sw.Elapsed.TotalSeconds > 1) InsertLog("Execute 계산 시간 : " + sw.Elapsed.ToString()); var minDateTime = dic[MCandleTimeType.M1].Select(t => t.DateTime).Min(); var maxDateTime = dic[MCandleTimeType.M1].Select(t => t.DateTime).Max().AddHours(1); sw.Restart(); dic = IndicatorSetupManager.CalculateIndicator(dic, totalIndicatorList, minDateTime, maxDateTime, cb_UpperMainIndi.Checked, item, true); sw.Stop(); if (sw.Elapsed.TotalSeconds > 1) InsertLog("Indicator Calculate 계산 시간 : " + sw.Elapsed.ToString()); #endregion #region 사고 파는 로직 //최신 데이터 가져오기 var recentlyData = dic[MCandleTimeType.M1][dic[MCandleTimeType.M1].Count - 1]; if (recentlyData == null) continue; /* * 로직 설명 * 1. Buy List에 있는지 확인 * 2. 없을 경우 추가(Buy Script 처리) * 3. Buy List에 있을 경우 Sell Script 확인 * 4. Sell 처리 후 결과 처리 */ //없을 시 -> BUY if (!buyList.Any(t => t.ConGoodsData.CoinFullName == item)) { if (!buyFlag) continue; var flag = buyScript.DicExcuteScript(recentlyData.CalculateFlag); if (flag) { if (overlapBuyManagementList[item]) { InsertLog("Insert Buy Logic"); if (!RealTimeMode) { if (buyCycleCount >= cycleCount) continue; } sw.Restart(); //if (resultDataList.Select(t => t.MarketData).ToList().Any(t => t.CoinFullName == item)) continue; var time = DateTime.Now; var buyData = new CandleDataList(); buyData.CandleDataLists_Im.Add(recentlyData); var valDic = ResultLogicManager.GetIndicateValue(recentlyData.CalculateFlag, totalIndicatorList, recentlyData); recentlyData.BuyValue = OrderbookManager.GetAskPrice(CalculateManager.GetConditionValue(buyRex, valDic, recentlyData.CalculateFlag)); recentlyData.BuyTime = time; buyData.ConGoodsData = dataList[MCandleTimeType.M1].FirstOrDefault(t => t.ConGoodsData.CoinFullName == item).ConGoodsData; // 임시 -> 실시간은 1개만 삿다고 처리 if (RealTimeMode) { recentlyData.BuyCount = 1; } else { recentlyData.BuyCount = Math.Round(cycleBuyMoney / recentlyData.BuyValue, 8); buyData.Uuid = OrderBuy(buyData.ConGoodsData.CoinFullName, recentlyData.BuyCount, recentlyData.BuyValue); buyCycleCount++; } buyList.Add(buyData); overlapBuyManagementList[item] = false; InsertLog(buyData.ConGoodsData.CoinFullName + " Market Data 구입 DateTime : " + time.ToString("yyyy-MM-dd HH:mm:ss")); //grid 처리 구매항목, 구매 가격, 구매 개수, 구매시점 this.BeginInvoke(new Action(delegate () { tl_BuyList.AppendNode(new object[] {buyData.ConGoodsData.CoinFullName, recentlyData.BuyValue, recentlyData.BuyCount, time.ToString("yyyy-MM-dd HH:mm:ss") }, null); lb_BuyMarketCount.Text = buyList.Count.ToString(); })); sw.Stop(); if (sw.Elapsed.TotalSeconds > 1) InsertLog("Buy 계산 로직 : " + sw.Elapsed.ToString()); } } else { overlapBuyManagementList[item] = true; } } else { var flag = sellScript.DicExcuteScript(recentlyData.CalculateFlag); if (flag) { sw.Restart(); var buyItem = buyList.FirstOrDefault(x => x.ConGoodsData.CoinFullName == item); var time = DateTime.Now; var sellItem = recentlyData; var valDic = ResultLogicManager.GetIndicateValue(sellItem.CalculateFlag, totalIndicatorList, sellItem); sellItem.SellValue = OrderbookManager.GetAskPrice(CalculateManager.GetConditionValue(sellRex, valDic, sellItem.CalculateFlag)); sellItem.SellTime = time; var buyCandleData = buyItem.CandleDataLists_Im[0]; var sellVal = sellItem.SellValue; var result = new SearchResult(); var rate = ((sellVal - buyCandleData.BuyValue) / buyCandleData.BuyValue) * 100; result.FirstEarningRate = rate; result.EndDate = sellItem.DateTime; result.CoinFullName = buyItem.ConGoodsData.CoinFullName; result.MarketData = buyItem.ConGoodsData; result.BuyEarningRate.Add(rate); result.BuyValueList.Add(buyCandleData.BuyValue); result.BuyList.ConGoodsData = buyItem.ConGoodsData; result.BuyList.CandleDataLists_Im.Add(buyCandleData); result.StartDate = buyCandleData.DateTime; result.SellData = sellItem; result.SellValue = sellVal; result.TotalEarningRate = rate; result.WinCount = rate > 0 ? 1 : 0; result.LoseCount = rate < 0 ? 1 : 0; result.FirstWinFlag = rate > 0 ? true : false; result.BuyCount = buyItem.CandleDataLists_Im[0].BuyCount; result.SellCount = buyItem.CandleDataLists_Im[0].BuyCount; // buycount 에서 sell count를 제외한 양이 미체결 량 if (!RealTimeMode) { recentlyData.BuyCount = Math.Round(cycleBuyMoney / recentlyData.BuyValue, 8); result.SellCount = OrderSell(buyItem.ConGoodsData.CoinFullName, buyItem.Uuid, sellVal); buyCycleCount--; } resultDataList.Add(result); //처리한 buyList는 삭제 buyList.Remove(buyItem); InsertLog(buyItem.ConGoodsData.CoinFullName + " Market Data 판매 DateTime : " + time.ToString("yyyy-MM-dd HH:mm:ss")); this.BeginInvoke(new Action(delegate () { //Grid 처리 TreeListNode buyNode = null; foreach (TreeListNode node in tl_BuyList.Nodes) { if (node.GetValue("BuyMarket").ToString() == buyItem.ConGoodsData.CoinFullName) { buyNode = node; break; } } var moneyVal = (sellVal * (1 - 0.0015) - buyCandleData.BuyValue * (1 - 0.0015)) * result.SellCount; tl_Result.AppendNode(new object[] { buyNode.GetValue("BuyMarket"), buyNode.GetValue("BuyValue"), buyNode.GetValue("BuyCount"), buyNode.GetValue("BuyDateTime"), sellVal, result.SellCount, time.ToString("yyyy-MM-dd HH:mm:ss"), result.FirstEarningRate, moneyVal }, null); tl_BuyList.Nodes.Remove(buyNode); lb_BuyMarketCount.Text = buyList.Count.ToString(); })); if (sw.Elapsed.TotalSeconds > 1) InsertLog("Sell 계산 로직 : " + sw.Elapsed.ToString()); } } #endregion } cnt++; if (cnt % 60 == 0) { InsertLog("Scanning......" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); cnt = 0; } } catch (Exception e) { InsertLog("Error : " + e.Message); } } #region Thread 종료 후 후 처리 할 로직 if (RealTimeMode) { } else { } #endregion SaveData(); InsertLog("Thread Out"); } /// /// 데이터 저장 기능 /// /// public void SaveData() { if (resultDataList.Count > 0) { //수익 률 계산 로직 -> 이거는 추후 빼도 될듯 double totalVal = 0; double win = 0; double lose = 0; double firstProfitRate = 0; double firstWin = 0; //승률, 수익률 계산 foreach (var item in resultDataList) { if (item.FirstWinFlag) firstWin++; firstProfitRate += item.FirstEarningRate; win += item.WinCount; lose += item.LoseCount; totalVal += item.TotalEarningRate; } double winRate; if (win == 0) { winRate = 0; } else if (lose == 0) { winRate = 100; } else { winRate = (win * 100 / (win + lose)); } _saveResultData.ResultDataCount = resultDataList.Count; _saveResultData.FirstWinRate = (firstWin * 100) / resultDataList.Count; _saveResultData.FirstProfitRate = firstProfitRate; _saveResultData.WinRate = winRate; _saveResultData.ProfitRate = totalVal / resultDataList.Count; _saveResultData.SearchResultList = resultDataList; _saveResultData.Name = _saveResultData.SavedDateTime.ToString("yyyy-MM-dd HH:mm:ss") + " Realtime Result"; _saveResultData.Type = "Data"; _saveResultData.SaveResultType = "Realtime"; _saveResultData.ExchangePlace = "Upbit"; _saveResultData.ParentId = null; _saveResultData.EndDateTime = DateTime.Now; _saveResultData.SavedDateTime = DateTime.Now; //buy list가 남아 있을 시 if (buyList.Count > 0) { foreach (var item in buyList) { var result = new SearchResult(); var buyCandleData = item.CandleDataLists_Im[0]; result.CoinFullName = item.ConGoodsData.CoinFullName; result.MarketData = item.ConGoodsData; result.BuyValueList.Add(buyCandleData.BuyValue); result.BuyList.ConGoodsData = item.ConGoodsData; result.BuyList.CandleDataLists_Im.Add(buyCandleData); result.StartDate = buyCandleData.DateTime; result.BuyCount = buyCandleData.BuyCount; resultDataList.Add(result); } } _saveResultData.SearchResultList = resultDataList; DBConnection.QueryInsertExcuter_SavedResultData(_saveResultData); _saveResultData.StartDateTime = DateTime.Now; resultDataList.Clear(); MessageBox.Show("데이터 저장 완료"); } } /// /// 데이터 저장 기능 /// /// /// private void bt_Save_Click(object sender, EventArgs e) { SaveData(); } /// /// Text Area 에 Log 입력 /// /// private void InsertLog(string log) { this.BeginInvoke(new Action(delegate () { if (lb_log.Items.Count > 500) { lb_log.Items.Clear(); } lb_log.Items.Insert(0, log); })); } /// /// Thread 가 살아 있는지 확인 /// /// /// private void bt_alive_Click(object sender, EventArgs e) { if (thread != null && thread.IsAlive) InsertLog("Realtime Thread Alive."); else InsertLog("Realtime Thread Death."); if (orderThread != null && orderThread.IsAlive) InsertLog("Order List Thread Alive."); else InsertLog("Order List Thread Death."); } /// /// 구매 함수 /// Price가 0일경우 현재가 구매 /// return data는 uuid임 /// /// /// /// private string OrderBuy(string coinFullName, double volume, double price) { var side = UpbitOrderSide.bid; var ordType = UpbitOrderType.limit; if (price == 0) ordType = UpbitOrderType.price; var returnData = CommonProperty.UpbitApi.TryOrder(coinFullName, side, volume, price, ordType); InsertLog("------------------------------------"); InsertLog("Buy Return Data : " + JsonConvert.SerializeObject(returnData)); InsertLog("------------------------------------"); return returnData.Uuid; } /// /// 판매하는 함수 /// price가 0일 경우 지정가 판매 /// return data는 총 판매 체결량임 /// /// /// /// private double OrderSell(string coinFullName, string uuid, double price) { var side = UpbitOrderSide.ask; var ordType = UpbitOrderType.limit; if (price == 0) ordType = UpbitOrderType.market; // 판매시 Order Cancel 진행 OrderCancel(coinFullName, uuid); var orderData = CommonProperty.UpbitApi.GetOrderInformation(uuid); var returnData = CommonProperty.UpbitApi.TryOrder(coinFullName, side, orderData.ExecutedVolume, price, ordType); InsertLog("------------------------------------"); InsertLog("Sell Return Data : " + JsonConvert.SerializeObject(returnData)); InsertLog("------------------------------------"); return orderData.ExecutedVolume; } /// /// Order 취소 함수 /// /// /// private void OrderCancel(string coinFullName, string uuid ="") { InsertLog("------------------------------------"); InsertLog("Order Cancel"); var orderList = CommonProperty.UpbitApi.GetOrderListInformation(UpbitOrderListTypes.waitwatch, uuid); foreach (var item in orderList) { if (item.State == "wait" || item.State == "watch") { if(item.CoinFullName == coinFullName) { var returnData = CommonProperty.UpbitApi.CancelOrder(item.Uuid); InsertLog("Order Cancel Data : " + JsonConvert.SerializeObject(returnData)); } } } InsertLog("------------------------------------"); } /// /// User 변경 /// /// /// private void cb_UserList_SelectedIndexChanged(object sender, EventArgs e) { //CommonProperty.ChangeUpbitApiKey. var flag = CommonProperty.ChangeUpbitApiKey(cb_UserList.Text); if (flag) { this.accounts = CommonProperty.UpbitApi.GetAccounts(); var list = this.accounts.Select(t => t.Currency).ToList(); foreach (var item in list) cb_AccountList.Items.Add(item); } else MessageBox.Show("해당 사용자는 Key List에 존재하지 않습니다."); } /// /// Payment 변경 /// /// /// private void cb_AccountList_SelectedIndexChanged(object sender, EventArgs e) { var account = this.accounts.FirstOrDefault(t => t.Currency == cb_AccountList.Text); if (account == null) { MessageBox.Show("해당 사용자의 Account가 정상적이지 않습니다."); } else { var subStr = "원"; if (cb_AccountList.Text == "KRW") { this.payment = PaymentType.KRW; } else if (cb_AccountList.Text == "BTC") { this.payment = PaymentType.BTC; subStr = "Bit Coin"; } else if (cb_AccountList.Text == "USDT") { this.payment = PaymentType.USDT; subStr = "USDT"; } lb_TotalMoney.Text = account.Balance.ToString() + subStr; } } private void bt_BuyStop_Click(object sender, EventArgs e) { buyFlag = false; lb_BuyFlag.Text = "매수 정지"; } private void bt_BuyStart_Click(object sender, EventArgs e) { buyFlag = true; lb_BuyFlag.Text = "매수 진행 중"; } } }