Files
AutoSeller/CybosHelper.cs

601 lines
19 KiB
C#

using System;
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
{
public class PriceNode
{
public int m_iTime;
public int m_iPrice;
public int m_iAskPrice;
public int m_iBidPrice;
public int m_iAccDealCnt;
public int m_iDealCnt;
public double m_dAvg;
public double m_dSD;
public double m_dSellLimit;
public int m_iSellCnt;
}
public class STOCK_CUR_ITEM
{
public AutoSeller m_Listener = null;
public CybosHelper m_CybosHelper = null;
public string m_strCode;
public string m_strCodeName;
public DSCBO1Lib.StockCur m_StockCur = null;
public DSCBO1Lib.StockJpbid m_Jpbid = null;
public int[] m_aiBidPrice = new int[10];
public int[] m_aiBidCount = new int[10];
public int[] m_aiAskPrice = new int[10];
public int[] m_aiAskCount = new int[10];
public int m_iOpenigPrice = 0;
public int m_iCurPrice = 0;
public int m_iMaxPrice = 0;
public int m_iCheckCount = 0;
public int m_iTrailingCount = 0;
// real time, price time, price
public List<PriceNode> m_PriceList = new List<PriceNode>();
public double m_dCheckCountLimit = Config.GetBidCount();
int m_iPrevTime = 0;
public void OnRecievedPrice()
{
bool bOpeningHour = (m_StockCur.GetHeaderValue(19) == '2');
m_iCurPrice = m_StockCur.GetHeaderValue(13);
if (m_iOpenigPrice == 0)
m_iOpenigPrice = m_iCurPrice;
int iTime = m_StockCur.GetHeaderValue(18);
int iConclusionCnt = m_StockCur.GetHeaderValue(17);
int iAccDealCnt = m_StockCur.GetHeaderValue(9);
int iDealCnt = m_StockCur.GetHeaderValue(17);
int iTimeDiff = 0;
if(m_iPrevTime > 0)
iTimeDiff = ((iTime/10000)*60*60 + ((iTime%10000)/100)*60 + (iTime%100)) - ((m_iPrevTime/10000)*60*60 + ((m_iPrevTime%10000)/100)*60 + (m_iPrevTime%100));
if(bOpeningHour == true)
{
DateTime RealTime = DateTime.Now;
int iRealTime = RealTime.Hour * 10000 + RealTime.Minute * 100 + RealTime.Second;
List<int> lastNPrice = null;
if (Config.GetListSize() == 0)
lastNPrice = m_PriceList.Select(s => s.m_iPrice).ToList();
else
lastNPrice = m_PriceList.Skip(Math.Max(0, m_PriceList.Count - (Config.GetListSize()-1))).Select(s => s.m_iPrice).ToList();
lastNPrice.Add(m_iCurPrice);
double dAverage = lastNPrice.Average();
double sumOfSquaresOfDifferences = lastNPrice.Select(val => Math.Pow((val - dAverage)/m_CybosHelper.GetUnitValue(val), 2)).Sum();
double dStdDev = Math.Sqrt(sumOfSquaresOfDifferences / lastNPrice.Count);
if(dStdDev >= Config.GetFastSD())
{
if(m_iCurPrice >= dAverage)
m_dCheckCountLimit += Config.GetFastUp();
else
m_dCheckCountLimit -= Config.GetFastDown();
}
else if(dStdDev <= Config.GetSlowSD())
{
if(m_iCurPrice >= dAverage)
m_dCheckCountLimit += Config.GetSlowUp();
else
m_dCheckCountLimit -= Config.GetSlowDown();
}
m_dCheckCountLimit -= iTimeDiff*Config.GetTimeDown();
if(m_iCurPrice > m_iMaxPrice)
m_iTrailingCount = 0;
m_iMaxPrice = Math.Max(m_iCurPrice, m_iMaxPrice);
int iAskPrice = m_StockCur.GetHeaderValue(7);
int iBidPrice = m_StockCur.GetHeaderValue(8);
if(m_iCurPrice*iConclusionCnt > Config.GetIgnorePrice())
{
if(m_iCurPrice <= iBidPrice)
m_iCheckCount++;
else
m_iCheckCount = 0;
}
if(m_iCheckCount >= m_dCheckCountLimit && m_Listener.IsSelling() == true)
{
m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
Util.Log(Util.LOG_TYPE.SELL, string.Format("[조건 완료 매도] {0} ({1}회) (현재가 {2:n0}원, 최고가 {3:n0}원)",
m_strCodeName,
Config.GetBidCount(),
m_iCurPrice,
m_iMaxPrice));
}
if(m_iCurPrice < (m_iMaxPrice*(100.0f-Config.GetTrailingRate())/100.0f))
m_iTrailingCount++;
if(m_iTrailingCount >= Config.GetTrailingCnt() && m_Listener.IsSelling() == true)
{
m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
Util.Log(Util.LOG_TYPE.SELL, string.Format("[트레일링 매도] {0} ({1}% {2}회) (현재가 {3:n0}원, 최고가 {4:n0}원)",
m_strCodeName,
Config.GetTrailingRate(),
Config.GetTrailingCnt(),
m_iCurPrice,
m_iMaxPrice));
}
if(iTimeDiff >= Config.GetTimeLimit() && m_Listener.IsSelling() == true)
{
m_CybosHelper.SellItem(m_strCode, m_Listener.GetSellableCount(m_strCode), m_aiAskPrice[0]);
Util.Log(Util.LOG_TYPE.SELL, string.Format("[시간제한 매도] {0} ({1}초) (현재가 {2:n0}원, 최고가 {3:n0}원)",
m_strCodeName,
iTimeDiff,
m_iCurPrice,
m_iMaxPrice));
}
m_PriceList.Add(new PriceNode {
m_iTime = iTime,
m_iPrice = m_iCurPrice,
m_iAskPrice = iAskPrice,
m_iBidPrice = iBidPrice,
m_iAccDealCnt = iAccDealCnt,
m_iDealCnt = iDealCnt,
m_dAvg = dAverage,
m_dSD = dStdDev,
m_dSellLimit = m_dCheckCountLimit,
m_iSellCnt = m_iCheckCount,
});
}
m_Listener.OnReceivedCurPrice(m_strCode, m_iCurPrice, m_iMaxPrice, m_dCheckCountLimit, m_iCheckCount, bOpeningHour);
m_iPrevTime = iTime;
}
public void OnReceivedCall()
{
for(int i=0; i<5; i++)
{
m_aiBidPrice[i] = m_Jpbid.GetHeaderValue(4 + 4*i);
m_aiBidCount[i] = m_Jpbid.GetHeaderValue(6 + 4*i);
m_aiAskPrice[i] = m_Jpbid.GetHeaderValue(3 + 4*i);
m_aiAskCount[i] = m_Jpbid.GetHeaderValue(5 + 4*i);
}
for (int i = 0; i < 5; i++)
{
m_aiBidPrice[i+5] = m_Jpbid.GetHeaderValue(28 + 4*i);
m_aiBidCount[i+5] = m_Jpbid.GetHeaderValue(30 + 4*i);
m_aiAskPrice[i+5] = m_Jpbid.GetHeaderValue(27 + 4*i);
m_aiAskCount[i+5] = m_Jpbid.GetHeaderValue(29 + 4*i);
}
m_Listener.OnReceivedCall(m_strCode, 0, m_aiBidPrice, m_aiBidCount, m_aiAskPrice, m_aiAskCount);
}
}
AutoSeller m_Listener = null;
CPUTILLib.CpCybos m_CPCybos = new CPUTILLib.CpCybos();
CPFORETRADELib.CpForeTdUtil m_CPUtil = new CPFORETRADELib.CpForeTdUtil();
DSCBO1Lib.CpConclusion m_CpConclusion = new DSCBO1Lib.CpConclusion();
CPTRADELib.CpTd6033 m_6033 = new CPTRADELib.CpTd6033();
Dictionary<string, STOCK_CUR_ITEM> m_aStockCur = new Dictionary<string, STOCK_CUR_ITEM>();
object m_NCItemLock = new object();
public CybosHelper(AutoSeller Listener)
{
m_Listener = Listener;
}
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.VERVOSE, "[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();
}
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));
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));
}
}
public List<AutoSeller.ITEM> UpdateItems()
{
List<AutoSeller.ITEM> aItems = new List<AutoSeller.ITEM>();
if (Config.GetAccount() == "")
return aItems;
m_6033.SetInputValue(0, Config.GetAccount());
m_6033.SetInputValue(1, Config.GetSubAccount());
m_6033.BlockRequest2(1);
string strCodeList = "";
bool bContinue = true;
while(bContinue == true)
{
int iCount = m_6033.GetHeaderValue(7);
for(int i = 0; i < iCount; i++)
{
AutoSeller.ITEM Item = new AutoSeller.ITEM();
Item.m_strCodeName = m_6033.GetDataValue(0, i);
if(Item.m_strCodeName != "")
{
Item.m_strCode = m_6033.GetDataValue(12, i);
Item.m_iItemCnt = m_6033.GetDataValue(7, i);
Item.m_iAssessedValue = m_6033.GetDataValue(9, i);
Item.m_iValuationGains = m_6033.GetDataValue(10, i);
Item.m_dYield = m_6033.GetDataValue(11, i);
Item.m_iAvailableQuantity = m_6033.GetDataValue(15, i);
Item.m_dBookUnitPrice = m_6033.GetDataValue(17, i);
Item.m_iProfitUnitPrice = m_6033.GetDataValue(18, i);
if(strCodeList.Length > 0)
strCodeList += ",";
strCodeList += Item.m_strCode;
aItems.Add(Item);
}
}
bContinue = (m_6033.Continue != 0);
if(bContinue == true)
m_6033.BlockRequest2(1);
}
if (strCodeList.Length > 0)
{
DSCBO1Lib.StockMst2 StockMst = new DSCBO1Lib.StockMst2();
StockMst.SetInputValue(0, strCodeList);
StockMst.BlockRequest2(1);
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<string> aRemoveKeys = new List<string>();
foreach(var Item in m_aStockCur)
{
if(aItems.Any(s => s.m_strCode == Item.Key) == false)
aRemoveKeys.Add(Item.Key);
}
foreach(var Key in aRemoveKeys)
{
m_aStockCur[Key].m_StockCur.Unsubscribe();
m_aStockCur[Key].m_Jpbid.Unsubscribe();
SavePrice(m_aStockCur[Key]);
m_aStockCur.Remove(Key);
}
return aItems;
}
static int iUpdateNCIdx = 1;
public List<AutoSeller.NCITEM> UpdateNC()
{
int iIdx = iUpdateNCIdx++;
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("UpdateNC Start ({0}, {1}, {2}, {3})", iIdx,
GetLimitRemainCountRQ(), GetLimitRemainCountSB(), GetLimitRemainCountTrade()
));
while(GetLimitRemainCountTrade() < 1)
Thread.Sleep(500);
List<AutoSeller.NCITEM> NCItems = new List<AutoSeller.NCITEM>();
CPTRADELib.CpTd5339 CP5339 = new CPTRADELib.CpTd5339();
CP5339.SetInputValue(0, Config.GetAccount());
CP5339.SetInputValue(1, Config.GetSubAccount());
CP5339.BlockRequest2(1);
bool bContinue = true;
while(bContinue)
{
int iCnt = CP5339.GetHeaderValue(5);
Console.WriteLine("icnt : {0}", iCnt);
for(int i = 0; i<iCnt; i++)
{
AutoSeller.NCITEM Item = new AutoSeller.NCITEM();
Item.m_strCode = CP5339.GetDataValue(3, i);
Item.m_strCodeName = CP5339.GetDataValue(4, i);
Item.m_strDesc = CP5339.GetDataValue(5, i);
Item.m_iOrderPrice = CP5339.GetDataValue(7, i);
Item.m_iRemainCnt = CP5339.GetDataValue(11, i);
Item.m_bAsk = (CP5339.GetDataValue(13, i) == "1");
Item.m_iOrderNo = CP5339.GetDataValue(1, i);
Item.m_iOrgOrderNo = CP5339.GetDataValue(2, i);
NCItems.Add(Item);
}
bContinue = (CP5339.Continue != 0);
if(bContinue)
CP5339.BlockRequest2(1);
}
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("UpdateNC End ({0})", iIdx));
return 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(strCode, strCodeName, bBid, iConclusionCnt, iRemainCnt, iBookValue);
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));
break;
case 2: // 확인
Util.Log(Util.LOG_TYPE.VERVOSE, string.Format("{0}:{1} {2} 확인 - {3} ({4}:{5})", strCodeName, strCode, strBidOrAsk, strOrderCondition, iOrderNumber, iOrgOrderNumber));
break;
case 3: // 거부
Util.Log(Util.LOG_TYPE.VERVOSE, 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 == true)
{
if(m_aStockCur.ContainsKey(strCode) == true)
{
var priceList = m_aStockCur[strCode].m_PriceList;
List<Tuple<int, int>> PriceDealCntList = new List<Tuple<int, int>>();
for (int i = 0; i < Math.Min(5, priceList.Count); i++)
PriceDealCntList.Add(new Tuple<int, int>(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)
{
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);
JpBid.BlockRequest2(1);
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;
}
static int iSellIdx = 1;
public void SellItem(string strCode, int iCnt, int iAskPrice)
{
int iIdx = iSellIdx++;
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("SellItem Start ({0})", iIdx));
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");
Td0311.BlockRequest2(1);
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("SellItem End ({0})", iIdx));
}
static int iCancelIdx = 1;
public void CancelItem(string strCode, int iOrgOrderNo)
{
int iIdx = iCancelIdx++;
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CancelItem Start ({0})", iIdx));
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);
Td0314.BlockRequest2(1);
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CancelItem End ({0})", iIdx));
}
static int iCorrectIdx = 1;
public void CorrectionItem(string strCode, int iOrgOrderNo, int iCnt, int iAskPrice)
{
int iIdx = iCorrectIdx++;
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CorrectionItem Start ({0})", iIdx));
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);
Td0313.BlockRequest2(1);
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CorrectionItem End ({0})", iIdx));
}
}
}