using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AutoSellerNS { public class CybosHelper { AutoSeller m_Listener = null; CPUTILLib.CpCybos m_CPCybos = new CPUTILLib.CpCybos(); CPUTILLib.CpCodeMgr m_CPCodeMgr = new CPUTILLib.CpCodeMgr(); CPFORETRADELib.CpForeTdUtil m_CPUtil = new CPFORETRADELib.CpForeTdUtil(); DSCBO1Lib.CpConclusion m_CpConclusion = new DSCBO1Lib.CpConclusion(); Dictionary m_aStockCur = new Dictionary(); object m_RequestRQLock = new object(); object m_RequestTRLock = new object(); CPTRADELib.CpTd6033 m_CP6033 = new CPTRADELib.CpTd6033(); CPTRADELib.CpTd5339 m_CP5339 = new CPTRADELib.CpTd5339(); CPTRADELib.CpTd5342 m_CP5342 = new CPTRADELib.CpTd5342(); ConcurrentDictionary m_SellingCode = new ConcurrentDictionary(); ConcurrentDictionary m_ItemsInCorrection = new ConcurrentDictionary(); ConcurrentQueue m_TRQueue = new ConcurrentQueue(); DateTime m_MarketStartTime; DateTime m_MarketEndTime; public CybosHelper(AutoSeller Listener) { m_Listener = Listener; m_CP6033.Received += CP6033_Received; m_CP5339.Received += CP5339_Received; } public void InitCybos() { short iResult = m_CPUtil.TradeInit(); switch (iResult) { case -1: Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 오류"); break; case 0: Util.Log(Util.LOG_TYPE.VERBOSE, "[TradeInit] 로그인 되었습니다"); break; case 1: Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 업무 키 입력 잘못됨"); break; case 2: Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 계좌 비밀번호가 잘못되었습니다"); break; case 3: Util.Log(Util.LOG_TYPE.ERROR, "[TradeInit] 취소되었습니다"); break; } m_Listener.SetAccountList(m_CPUtil.AccountNumber); m_CpConclusion.Received += CpConclusion_Received; m_CpConclusion.Subscribe(); int iStartTime = (int)m_CPCodeMgr.GetMarketStartTime(); int iEndTime = (int)m_CPCodeMgr.GetMarketEndTime(); m_MarketStartTime = DateTime.Parse(string.Format("{0:00}:{1:00}:{2:00}", iStartTime/100, iStartTime%100, 0)); m_MarketEndTime = DateTime.Parse(string.Format("{0:00}:{1:00}:{2:00}", iEndTime / 100, iEndTime % 100, 0)); } public async Task InitCybosAsync() { await Task.Run(() => { InitCybos(); }); } public void SavePrice(STOCK_CUR_ITEM item) { if (Directory.Exists(Util.GetLogPath()) == false) Directory.CreateDirectory(Util.GetLogPath()); string strToday = DateTime.Now.ToString("yyyy-MM-dd"); string strFilePath = Util.GetLogPath() + "/price-" + strToday + item.m_strCodeName + ".csv"; string strMessage = "시간, 동시호가, 현재가, 매도호가, 매수호가, 거래량, 순간체결량, 평균, 표준편차, 매도 제한, 매도 수, 시가 대비"; File.AppendAllText(strFilePath, strMessage + Environment.NewLine, new UTF8Encoding(true)); lock(item.m_PriceList) { foreach (var node in item.m_PriceList) { strMessage = string.Format("{0}:{1}:{2}, , {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12:0.00}", node.m_iTime / 10000, (node.m_iTime / 100) % 100, node.m_iTime % 100, node.m_iPrice, node.m_iAskPrice, node.m_iBidPrice, node.m_iAccDealCnt, node.m_iDealCnt, node.m_dAvg, node.m_dSD, node.m_dSellLimit, node.m_iSellCnt, (node.m_iPrice - item.m_PriceList[0].m_iPrice) * 100 / (double)item.m_PriceList[0].m_iPrice); File.AppendAllText(strFilePath, strMessage + Environment.NewLine, new UTF8Encoding(true)); } } } int iDibWaitCnt = 0; void BlockRequest(DSCBO1Lib.IDib dib) { //lock (m_RequestRQLock) { while (GetLimitRemainCountRQ() < 2) { iDibWaitCnt++; Thread.Sleep(500); iDibWaitCnt--; } var ret = dib.BlockRequest2(0); if (ret == -1) Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest RQ 오류"); else if (ret == 1) Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest RQ 수신대기"); } } void BlockRequestTRProc() { if (m_TRQueue.Count == 0) return; lock (m_RequestTRLock) { CPTRADELib.ICpTdDib tr; bool bContinue = true; while (bContinue == true && m_TRQueue.TryDequeue(out tr) == true && GetLimitRemainCountTrade() > 0) { var ret = tr.BlockRequest2(0); if (ret == -1) Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest TR 오류"); else if (ret == 1) Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest TR 수신대기"); bContinue = (ret == 0); } } } //int iTdDibWaitCnt = 0; void BlockRequest(CPTRADELib.ICpTdDib dib) { m_TRQueue.Enqueue(dib); BlockRequestTRProc(); ////lock (m_RequestTRLock) //{ // while (GetLimitRemainCountTrade() < 2) // { // iTdDibWaitCnt++; // Thread.Sleep(500); // iTdDibWaitCnt--; // } // var ret = dib.BlockRequest2(0); // if (ret == -1) // Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest TR 오류"); // else if (ret == 1) // Util.Log(Util.LOG_TYPE.DEBUG, "BlockRequest TR 수신대기"); //} } public void UpdateItems() { if (Config.GetAccount() == "") return; m_CP6033.SetInputValue(0, Config.GetAccount()); m_CP6033.SetInputValue(1, Config.GetSubAccount()); m_CP6033.SetInputValue(2, 50); string Msg1 = m_CP6033.GetDibMsg1(); string Msg2 = m_CP6033.GetDibMsg2(); int iStatus = m_CP6033.GetDibStatus(); if (iStatus == 1) return; m_CP6033.Request(); } private void CP6033_Received() { List aItems = new List(); string strCodeList = ""; bool bContinue = true; while (bContinue == true) { int iCount = m_CP6033.GetHeaderValue(7); for (int i = 0; i < iCount; i++) { AutoSeller.ITEM Item = new AutoSeller.ITEM(); Item.m_strCodeName = m_CP6033.GetDataValue(0, i); if (Item.m_strCodeName != "") { Item.m_strCode = m_CP6033.GetDataValue(12, i); Item.m_iItemCnt = m_CP6033.GetDataValue(7, i); Item.m_iAssessedValue = m_CP6033.GetDataValue(9, i) / 1000; Item.m_iValuationGains = m_CP6033.GetDataValue(10, i) / 1000; Item.m_dYield = m_CP6033.GetDataValue(11, i); Item.m_iAvailableQuantity = m_CP6033.GetDataValue(15, i); Item.m_dBookUnitPrice = m_CP6033.GetDataValue(17, i); Item.m_iProfitUnitPrice = m_CP6033.GetDataValue(18, i); if (strCodeList.Length > 0) strCodeList += ","; strCodeList += Item.m_strCode; aItems.Add(Item); } } bContinue = (m_CP6033.Continue != 0); bContinue = false; if (bContinue == true) BlockRequest(m_CP6033); } if (strCodeList.Length > 0) { DSCBO1Lib.StockMst2 StockMst = new DSCBO1Lib.StockMst2(); StockMst.SetInputValue(0, strCodeList); BlockRequest(StockMst); int iCnt = StockMst.GetHeaderValue(0); for (int i = 0; i < iCnt; i++) { string strCode = StockMst.GetDataValue(0, i); int iPrice = StockMst.GetDataValue(3, i); AutoSeller.ITEM Item = aItems.FirstOrDefault(s => s.m_strCode == strCode); if (Item != default(AutoSeller.ITEM)) Item.m_iCurPrice = iPrice; } } // sync List aRemoveKeys = new List(); lock (m_aStockCur) { foreach (var Item in m_aStockCur) { if (aItems.Any(s => s.m_strCode == Item.Key) == false) { lock (m_aStockCur[Item.Key]) { m_aStockCur[Item.Key].m_StockCur.Unsubscribe(); m_aStockCur[Item.Key].m_Jpbid.Unsubscribe(); SavePrice(m_aStockCur[Item.Key]); aRemoveKeys.Add(Item.Key); } } } foreach (var key in aRemoveKeys) m_aStockCur.Remove(key); } m_Listener.UpdateItemCallback(aItems); } public void UpdateNC() { m_CP5339.SetInputValue(0, Config.GetAccount()); m_CP5339.SetInputValue(1, Config.GetSubAccount()); string Msg1 = m_CP5339.GetDibMsg1(); string Msg2 = m_CP5339.GetDibMsg2(); int iStatus = m_CP5339.GetDibStatus(); if (iStatus == 1) return; m_CP5339.Request(); } private void CP5339_Received() { List NCItems = new List(); bool bContinue = true; while (bContinue) { int iCnt = m_CP5339.GetHeaderValue(5); for (int i = 0; i < iCnt; i++) { AutoSeller.NCITEM Item = new AutoSeller.NCITEM(); Item.m_strCode = m_CP5339.GetDataValue(3, i); Item.m_strCodeName = m_CP5339.GetDataValue(4, i); Item.m_strDesc = m_CP5339.GetDataValue(5, i); Item.m_iOrderPrice = m_CP5339.GetDataValue(7, i); Item.m_iRemainCnt = m_CP5339.GetDataValue(11, i); Item.m_bAsk = (m_CP5339.GetDataValue(13, i) == "1"); Item.m_iOrderNo = m_CP5339.GetDataValue(1, i); Item.m_iOrgOrderNo = m_CP5339.GetDataValue(2, i); if (Item.m_iOrgOrderNo == 0) Item.m_iOrgOrderNo = Item.m_iOrderNo; NCItems.Add(Item); } bContinue = (m_CP5339.Continue != 0); if (bContinue) BlockRequest(m_CP5339); } m_Listener.UpdateNCItemCallback(NCItems); } private void CpConclusion_Received() { string strType = m_CpConclusion.GetHeaderValue(14); int iType = 0; int.TryParse(strType, out iType); bool bBid = (m_CpConclusion.GetHeaderValue(12) == "2"); string strCode = m_CpConclusion.GetHeaderValue(9); string strCodeName = m_CpConclusion.GetHeaderValue(2); int iConclusionCnt = m_CpConclusion.GetHeaderValue(3); int iPrice = m_CpConclusion.GetHeaderValue(4); int iOrderNumber = m_CpConclusion.GetHeaderValue(5); int iOrgOrderNumber = m_CpConclusion.GetHeaderValue(6); string strCancel = m_CpConclusion.GetHeaderValue(16); string strOrderCondition = m_CpConclusion.GetHeaderValue(19); int iBookValue = m_CpConclusion.GetHeaderValue(21); int iRemainCnt = m_CpConclusion.GetHeaderValue(23); string strBidOrAsk = bBid ? "매수" : "매도"; if (strOrderCondition == "0") strOrderCondition = "없음"; else if (strOrderCondition == "1") strOrderCondition = "IOC"; else if (strOrderCondition == "2") strOrderCondition = "FOK"; switch (iType) { case 1: // 체결 m_Listener.UpdateItem(); m_Listener.UpdateNCItem(); Util.Log(bBid?Util.LOG_TYPE.BUY_CONCLUSION:Util.LOG_TYPE.SELL_CONCLUSION, string.Format("{0}:{1} {2} 체결 ({3:n0}원 {4}주) - {5}", strCodeName, strCode, strBidOrAsk, iPrice, iConclusionCnt, strOrderCondition)); break; case 2: // 확인 Util.Log(Util.LOG_TYPE.VERBOSE, string.Format("{0}:{1} {2} 확인 - {3} ({4}:{5})", strCodeName, strCode, strBidOrAsk, strOrderCondition, iOrderNumber, iOrgOrderNumber)); break; case 3: // 거부 Util.Log(Util.LOG_TYPE.VERBOSE, string.Format("{0}:{1} {2} 거부", strCodeName, strCode, strBidOrAsk)); break; case 4: // 접수 m_Listener.UpdateNCItem(); Util.Log(bBid ? Util.LOG_TYPE.BUY : Util.LOG_TYPE.SELL, string.Format("{0}:{1} {2} 접수 ({3:n0}원 {4}주) - {5}", strCodeName, strCode, strBidOrAsk, iPrice, iConclusionCnt, strOrderCondition)); if (bBid == false) { if(m_aStockCur.ContainsKey(strCode) == true) { lock(m_aStockCur[strCode].m_PriceList) { var priceList = m_aStockCur[strCode].m_PriceList; List> PriceDealCntList = new List>(); for (int i = 0; i < Math.Min(5, priceList.Count); i++) PriceDealCntList.Add(new Tuple(priceList[i].m_iPrice, priceList[i].m_iDealCnt)); SimulationHelper.InsertSimulationLog(strCode, strCodeName, PriceDealCntList); } } } break; } } public int GetLimitRemainCountTrade() { return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_TRADE_REQUEST); } public int GetLimitRemainCountRQ() { return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_NONTRADE_REQUEST); } public int GetLimitRemainCountSB() { return m_CPCybos.GetLimitRemainCount(CPUTILLib.LIMIT_TYPE.LT_SUBSCRIBE); } public bool IsConnected() { return (m_CPCybos.IsConnect==1); } public STOCK_CUR_ITEM GetItem(string strCode) { if (m_aStockCur.ContainsKey(strCode) == false) return null; return m_aStockCur[strCode]; } public void Subscribe(AutoSeller.ITEM Item) { if(m_aStockCur.Any(s => s.Key == Item.m_strCode) == false) { lock (m_aStockCur) { STOCK_CUR_ITEM StockCur = new STOCK_CUR_ITEM(); StockCur.m_Listener = m_Listener; StockCur.m_CybosHelper = this; StockCur.m_strCode = Item.m_strCode; StockCur.m_strCodeName = Item.m_strCodeName; DSCBO1Lib.StockJpbid2 JpBid = new DSCBO1Lib.StockJpbid2(); JpBid.SetInputValue(0, Item.m_strCode); BlockRequest(JpBid); for (int i = 0; i < 10; i++) { StockCur.m_aiAskPrice[i] = JpBid.GetDataValue(0, i); StockCur.m_aiAskCount[i] = JpBid.GetDataValue(2, i); StockCur.m_aiBidPrice[i] = JpBid.GetDataValue(1, i); StockCur.m_aiBidCount[i] = JpBid.GetDataValue(3, i); } StockCur.m_StockCur = new DSCBO1Lib.StockCur(); StockCur.m_StockCur.SetInputValue(0, Item.m_strCode); StockCur.m_StockCur.Received += StockCur.OnRecievedPrice; StockCur.m_StockCur.Subscribe(); StockCur.m_Jpbid = new DSCBO1Lib.StockJpbid(); StockCur.m_Jpbid.SetInputValue(0, Item.m_strCode); StockCur.m_Jpbid.Received += StockCur.OnReceivedCall; StockCur.m_Jpbid.SubscribeLatest(); StockCur.m_iCurPrice = Item.m_iCurPrice; StockCur.m_iMaxPrice = Item.m_iCurPrice; m_aStockCur.Add(Item.m_strCode, StockCur); } } } public int GetUnitValue(int iValue) { int iUnit = 1; if(iValue < 1000) iUnit = 1; else if(iValue < 5000) iUnit = 5; else if(iValue < 10000) iUnit = 10; else if(iValue < 50000) iUnit = 50; else if(iValue < 100000) iUnit = 100; else if(iValue < 500000) iUnit = 500; else iUnit = 1000; return iUnit; } int GetCallUnitValue(int iValue) { int iUnit = GetUnitValue(iValue); return (iValue/iUnit)*iUnit; } public DateTime GetMarketStartTime() { return m_MarketStartTime; } public DateTime GetMarketEndTime() { return m_MarketEndTime; } public async Task SellItem(string strCode, int iCnt, int iAskPrice) { if (iCnt <= 0 || m_SellingCode.ContainsKey(strCode) == true) return; m_SellingCode.TryAdd(strCode, iAskPrice); await Task.Run(() => { int iSellPrice = iAskPrice - GetUnitValue(iAskPrice); CPTRADELib.CpTd0311 Td0311 = new CPTRADELib.CpTd0311(); Td0311.SetInputValue(0, "1"); Td0311.SetInputValue(1, Config.GetAccount()); Td0311.SetInputValue(2, Config.GetSubAccount()); Td0311.SetInputValue(3, strCode); Td0311.SetInputValue(4, iCnt); Td0311.SetInputValue(5, iSellPrice); Td0311.SetInputValue(8, "01"); BlockRequest(Td0311); int val; m_SellingCode.TryRemove(strCode, out val); }); } public async Task SellItem(string strCode, int iCnt) { if (iCnt <= 0 || m_SellingCode.ContainsKey(strCode) == true) return; m_SellingCode.TryAdd(strCode, 0); await Task.Run(() => { CPTRADELib.CpTd0311 Td0311 = new CPTRADELib.CpTd0311(); Td0311.SetInputValue(0, "1"); Td0311.SetInputValue(1, Config.GetAccount()); Td0311.SetInputValue(2, Config.GetSubAccount()); Td0311.SetInputValue(3, strCode); Td0311.SetInputValue(4, iCnt); Td0311.SetInputValue(8, "03"); BlockRequest(Td0311); int val; m_SellingCode.TryRemove(strCode, out val); }); } public async Task CancelItem(string strCode, int iOrgOrderNo) { await Task.Run(() => { CPTRADELib.CpTd0314 Td0314 = new CPTRADELib.CpTd0314(); Td0314.SetInputValue(1, iOrgOrderNo); Td0314.SetInputValue(2, Config.GetAccount()); Td0314.SetInputValue(3, Config.GetSubAccount()); Td0314.SetInputValue(4, strCode); Td0314.SetInputValue(5, 0); BlockRequest(Td0314); }); } public async Task CorrectionItem(string strCode, int iOrgOrderNo, int iCnt, int iAskPrice) { if (m_ItemsInCorrection.ContainsKey(iOrgOrderNo) == true) return; m_ItemsInCorrection.TryAdd(iOrgOrderNo, strCode); if (Config.GetMockTrading() == true) { await Task.Run(() => { CancelItem(strCode, iOrgOrderNo).Wait(); SellItem(strCode, iCnt, iAskPrice).Wait(); string strCode2; m_ItemsInCorrection.TryRemove(iOrgOrderNo, out strCode2); }); } else { await Task.Run(() => { int iSellPrice = iAskPrice; CPTRADELib.CpTd0313 Td0313 = new CPTRADELib.CpTd0313(); Td0313.SetInputValue(1, iOrgOrderNo); Td0313.SetInputValue(2, Config.GetAccount()); Td0313.SetInputValue(3, Config.GetSubAccount()); Td0313.SetInputValue(4, strCode); Td0313.SetInputValue(5, 0); Td0313.SetInputValue(6, iSellPrice); BlockRequest(Td0313); string strCode2; m_ItemsInCorrection.TryRemove(iOrgOrderNo, out strCode2); }); } } public async void GetConclusion() { List data = new List(); await Task.Run(() => { m_CP5342.SetInputValue(0, Config.GetAccount()); m_CP5342.SetInputValue(1, Config.GetSubAccount()); m_CP5342.SetInputValue(2, 20); m_CP5342.SetInputValue(3, "1"); BlockRequest(m_CP5342); int iCnt = m_CP5342.GetHeaderValue(8); bool bContinue = true; while (bContinue) { for (int i = 0; i < Math.Min(iCnt, 20); i++) { var newData = new CONCLUSION { strCode = m_CP5342.GetDataValue(0, i), strCodeName = m_CP5342.GetDataValue(1, i), iConclusionCnt = (int)m_CP5342.GetDataValue(3, i), iFee = (int)m_CP5342.GetDataValue(4, i), iSpecialTax = (int)m_CP5342.GetDataValue(5, i), // 농특세 strSellBuy = m_CP5342.GetDataValue(10, i), iTransactionTax = (int)m_CP5342.GetDataValue(12, i), // 거래세 strDate = m_CP5342.GetDataValue(13, i), iCommitedAmount = (int)m_CP5342.GetDataValue(22, i), // 약정금액 iSettlementAmount = (int)m_CP5342.GetDataValue(24, i), // 정산금액 }; int iIdx = data.FindLastIndex(t => t.strCode == newData.strCode); if (iIdx < 0) data.Add(newData); else data.Insert(iIdx + 1, newData); } iCnt -= 20; bContinue = (iCnt > 0); if (bContinue) BlockRequest(m_CP5342); } m_Listener.UpdateConclusionCallback(data); }); } public class CONCLUSION { public string strCode; public string strCodeName; public int iConclusionCnt; public int iFee; public int iSpecialTax; public string strSellBuy; public int iTransactionTax; public string strDate; public int iCommitedAmount; public int iSettlementAmount; public override string ToString() { return $"{strCodeName} {strSellBuy} {iSettlementAmount:n}"; } } } }