simulation row 찾기 정교하게 수정
This commit is contained in:
2
AutoSeller.Designer.cs
generated
2
AutoSeller.Designer.cs
generated
@@ -907,7 +907,7 @@
|
|||||||
this.materialLabel3.Name = "materialLabel3";
|
this.materialLabel3.Name = "materialLabel3";
|
||||||
this.materialLabel3.Size = new System.Drawing.Size(165, 19);
|
this.materialLabel3.Size = new System.Drawing.Size(165, 19);
|
||||||
this.materialLabel3.TabIndex = 6;
|
this.materialLabel3.TabIndex = 6;
|
||||||
this.materialLabel3.Text = "Version : 2017.11.18.01";
|
this.materialLabel3.Text = "Version : 2018.01.01.20";
|
||||||
//
|
//
|
||||||
// materialLabel2
|
// materialLabel2
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace AutoSellerNS
|
|||||||
public int m_iOrderPrice;
|
public int m_iOrderPrice;
|
||||||
public string m_strDesc;
|
public string m_strDesc;
|
||||||
public bool m_bAsk;
|
public bool m_bAsk;
|
||||||
|
public int m_iOrderNo;
|
||||||
public int m_iOrgOrderNo;
|
public int m_iOrgOrderNo;
|
||||||
public DateTime m_Time;
|
public DateTime m_Time;
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ namespace AutoSellerNS
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
int iAskPrice = CurItem.m_aiAskPrice[0];
|
int iAskPrice = CurItem.m_aiAskPrice[0];
|
||||||
m_CybosHelper.CorrectionItem(NCItem.m_strCode, NCItem.m_iOrgOrderNo, NCItem.m_iRemainCnt, iAskPrice);
|
m_CybosHelper.CorrectionItem(NCItem.m_strCode, NCItem.m_iOrderNo, NCItem.m_iRemainCnt, iAskPrice);
|
||||||
NCItem.m_Time = DateTime.Now;
|
NCItem.m_Time = DateTime.Now;
|
||||||
Util.Log(Util.LOG_TYPE.SELL, string.Format("[{0}] 정정 주문 (주문번호: {1})", NCItem.m_strCodeName, NCItem.m_iOrgOrderNo));
|
Util.Log(Util.LOG_TYPE.SELL, string.Format("[{0}] 정정 주문 (주문번호: {1})", NCItem.m_strCodeName, NCItem.m_iOrgOrderNo));
|
||||||
}
|
}
|
||||||
@@ -249,19 +250,7 @@ namespace AutoSellerNS
|
|||||||
void UpdateNC()
|
void UpdateNC()
|
||||||
{
|
{
|
||||||
var NCItems = m_CybosHelper.UpdateNC();
|
var NCItems = m_CybosHelper.UpdateNC();
|
||||||
|
m_NCItems = NCItems;
|
||||||
//lock(m_NCItems)
|
|
||||||
{
|
|
||||||
m_NCItems.RemoveAll(s => NCItems.Any(t => t.m_iOrgOrderNo== s.m_iOrgOrderNo) == false);
|
|
||||||
foreach(var NCItem in NCItems)
|
|
||||||
{
|
|
||||||
if(m_NCItems.Any(s => s.m_iOrgOrderNo == NCItem.m_iOrgOrderNo) == false)
|
|
||||||
{
|
|
||||||
NCItem.m_Time = DateTime.Now;
|
|
||||||
m_NCItems.Add(NCItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lvNCItem.Items.Clear();
|
lvNCItem.Items.Clear();
|
||||||
if(lvNCItem.InvokeRequired)
|
if(lvNCItem.InvokeRequired)
|
||||||
@@ -270,6 +259,7 @@ namespace AutoSellerNS
|
|||||||
foreach(var NCItem in NCItems)
|
foreach(var NCItem in NCItems)
|
||||||
{
|
{
|
||||||
ListViewItem viewItem = new ListViewItem(new string[] {
|
ListViewItem viewItem = new ListViewItem(new string[] {
|
||||||
|
NCItem.m_iOrderNo.ToString(),
|
||||||
NCItem.m_iOrgOrderNo.ToString(),
|
NCItem.m_iOrgOrderNo.ToString(),
|
||||||
NCItem.m_strCode,
|
NCItem.m_strCode,
|
||||||
NCItem.m_strCodeName,
|
NCItem.m_strCodeName,
|
||||||
@@ -288,6 +278,7 @@ namespace AutoSellerNS
|
|||||||
foreach(var NCItem in NCItems)
|
foreach(var NCItem in NCItems)
|
||||||
{
|
{
|
||||||
ListViewItem viewItem = new ListViewItem(new string[] {
|
ListViewItem viewItem = new ListViewItem(new string[] {
|
||||||
|
NCItem.m_iOrderNo.ToString(),
|
||||||
NCItem.m_iOrgOrderNo.ToString(),
|
NCItem.m_iOrgOrderNo.ToString(),
|
||||||
NCItem.m_strCode,
|
NCItem.m_strCode,
|
||||||
NCItem.m_strCodeName,
|
NCItem.m_strCodeName,
|
||||||
|
|||||||
@@ -10,8 +10,23 @@ namespace AutoSellerNS
|
|||||||
{
|
{
|
||||||
public class CybosHelper
|
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 class STOCK_CUR_ITEM
|
||||||
{
|
{
|
||||||
|
|
||||||
public AutoSeller m_Listener = null;
|
public AutoSeller m_Listener = null;
|
||||||
public CybosHelper m_CybosHelper = null;
|
public CybosHelper m_CybosHelper = null;
|
||||||
public string m_strCode;
|
public string m_strCode;
|
||||||
@@ -23,6 +38,7 @@ namespace AutoSellerNS
|
|||||||
public int[] m_aiAskPrice = new int[10];
|
public int[] m_aiAskPrice = new int[10];
|
||||||
public int[] m_aiAskCount = new int[10];
|
public int[] m_aiAskCount = new int[10];
|
||||||
|
|
||||||
|
public int m_iOpenigPrice = 0;
|
||||||
public int m_iCurPrice = 0;
|
public int m_iCurPrice = 0;
|
||||||
public int m_iMaxPrice = 0;
|
public int m_iMaxPrice = 0;
|
||||||
public int m_iCheckCount = 0;
|
public int m_iCheckCount = 0;
|
||||||
@@ -30,30 +46,41 @@ namespace AutoSellerNS
|
|||||||
public int m_iTrailingCount = 0;
|
public int m_iTrailingCount = 0;
|
||||||
|
|
||||||
// real time, price time, price
|
// real time, price time, price
|
||||||
public List<Tuple<int, int, int>> m_PriceList = new List<Tuple<int, int, int>>();
|
public List<PriceNode> m_PriceList = new List<PriceNode>();
|
||||||
public double m_dCheckCountLimit = 5;
|
public double m_dCheckCountLimit = Config.GetBidCount();
|
||||||
|
|
||||||
int m_iPrevTime = 0;
|
int m_iPrevTime = 0;
|
||||||
|
|
||||||
public void OnRecievedPrice()
|
public void OnRecievedPrice()
|
||||||
{
|
{
|
||||||
bool bReal = (m_StockCur.GetHeaderValue(19) == '2');
|
bool bOpeningHour = (m_StockCur.GetHeaderValue(19) == '2');
|
||||||
m_iCurPrice = m_StockCur.GetHeaderValue(13);
|
m_iCurPrice = m_StockCur.GetHeaderValue(13);
|
||||||
|
if (m_iOpenigPrice == 0)
|
||||||
|
m_iOpenigPrice = m_iCurPrice;
|
||||||
int iTime = m_StockCur.GetHeaderValue(18);
|
int iTime = m_StockCur.GetHeaderValue(18);
|
||||||
int iConclusionCnt = m_StockCur.GetHeaderValue(17);
|
int iConclusionCnt = m_StockCur.GetHeaderValue(17);
|
||||||
|
int iAccDealCnt = m_StockCur.GetHeaderValue(9);
|
||||||
|
int iDealCnt = m_StockCur.GetHeaderValue(17);
|
||||||
|
|
||||||
int iTimeDiff = 0;
|
int iTimeDiff = 0;
|
||||||
if(m_iPrevTime > 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));
|
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(bReal == true)
|
if(bOpeningHour == true)
|
||||||
{
|
{
|
||||||
DateTime RealTime = DateTime.Now;
|
DateTime RealTime = DateTime.Now;
|
||||||
int iRealTime = RealTime.Hour * 10000 + RealTime.Minute * 100 + RealTime.Second;
|
int iRealTime = RealTime.Hour * 10000 + RealTime.Minute * 100 + RealTime.Second;
|
||||||
m_PriceList.Add(new Tuple<int, int, int>(iRealTime, iTime, m_iCurPrice));
|
|
||||||
double dAverage = (float)m_PriceList.Average(s => s.Item3);
|
List<int> lastNPrice = null;
|
||||||
double sumOfSquaresOfDifferences = m_PriceList.Select(val => Math.Pow((val.Item3-dAverage)/m_CybosHelper.GetUnitValue(val.Item3), 2)).Sum();
|
if (Config.GetListSize() == 0)
|
||||||
double dStdDev = Math.Sqrt(sumOfSquaresOfDifferences / m_PriceList.Count);
|
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(dStdDev >= Config.GetFastSD())
|
||||||
{
|
{
|
||||||
if(m_iCurPrice >= dAverage)
|
if(m_iCurPrice >= dAverage)
|
||||||
@@ -120,9 +147,23 @@ namespace AutoSellerNS
|
|||||||
m_iCurPrice,
|
m_iCurPrice,
|
||||||
m_iMaxPrice));
|
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, bReal);
|
m_Listener.OnReceivedCurPrice(m_strCode, m_iCurPrice, m_iMaxPrice, m_dCheckCountLimit, m_iCheckCount, bOpeningHour);
|
||||||
m_iPrevTime = iTime;
|
m_iPrevTime = iTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,11 +242,16 @@ namespace AutoSellerNS
|
|||||||
Directory.CreateDirectory(Util.GetLogPath());
|
Directory.CreateDirectory(Util.GetLogPath());
|
||||||
|
|
||||||
string strToday = DateTime.Now.ToString("yyyy-MM-dd");
|
string strToday = DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
string strFilePath = Util.GetLogPath() + "/price-" + strToday + item.m_strCodeName + ".txt";
|
string strFilePath = Util.GetLogPath() + "/price-" + strToday + item.m_strCodeName + ".csv";
|
||||||
string strMessage = "";
|
string strMessage = "시간, 동시호가, 현재가, 매도호가, 매수호가, 거래량, 순간체결량, 평균, 표준편차, 매도 제한, 매도 수, 시가 대비";
|
||||||
foreach (var pair in item.m_PriceList)
|
File.AppendAllText(strFilePath, strMessage + Environment.NewLine, new UTF8Encoding(true));
|
||||||
|
foreach (var node in item.m_PriceList)
|
||||||
{
|
{
|
||||||
strMessage = string.Format("[{0}] {1} ({2})", pair.Item2, pair.Item3, pair.Item1);
|
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));
|
File.AppendAllText(strFilePath, strMessage + Environment.NewLine, new UTF8Encoding(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,9 +371,8 @@ namespace AutoSellerNS
|
|||||||
Item.m_iOrderPrice = CP5339.GetDataValue(7, i);
|
Item.m_iOrderPrice = CP5339.GetDataValue(7, i);
|
||||||
Item.m_iRemainCnt = CP5339.GetDataValue(11, i);
|
Item.m_iRemainCnt = CP5339.GetDataValue(11, i);
|
||||||
Item.m_bAsk = (CP5339.GetDataValue(13, i) == "1");
|
Item.m_bAsk = (CP5339.GetDataValue(13, i) == "1");
|
||||||
|
Item.m_iOrderNo = CP5339.GetDataValue(1, i);
|
||||||
Item.m_iOrgOrderNo = CP5339.GetDataValue(2, i);
|
Item.m_iOrgOrderNo = CP5339.GetDataValue(2, i);
|
||||||
if(Item.m_iOrgOrderNo == 0)
|
|
||||||
Item.m_iOrgOrderNo = CP5339.GetDataValue(1, i);
|
|
||||||
|
|
||||||
NCItems.Add(Item);
|
NCItems.Add(Item);
|
||||||
}
|
}
|
||||||
@@ -388,7 +433,18 @@ namespace AutoSellerNS
|
|||||||
m_Listener.UpdateNCItem();
|
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));
|
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 (bBid == true)
|
||||||
SimulationHelper.InsertSimulationLog(strCode, strCodeName, iPrice);
|
{
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,11 +583,7 @@ namespace AutoSellerNS
|
|||||||
int iIdx = iCorrectIdx++;
|
int iIdx = iCorrectIdx++;
|
||||||
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CorrectionItem Start ({0})", iIdx));
|
Util.Log(Util.LOG_TYPE.DEBUG, string.Format("CorrectionItem Start ({0})", iIdx));
|
||||||
|
|
||||||
int iSellPrice = iAskPrice-GetUnitValue(iAskPrice);
|
int iSellPrice = iAskPrice;
|
||||||
|
|
||||||
//CancelItem(strCode, iOrgOrderNo);
|
|
||||||
//Thread.Sleep(1000);
|
|
||||||
//SellItem(strCode, iCnt, iAskPrice);
|
|
||||||
|
|
||||||
CPTRADELib.CpTd0313 Td0313 = new CPTRADELib.CpTd0313();
|
CPTRADELib.CpTd0313 Td0313 = new CPTRADELib.CpTd0313();
|
||||||
Td0313.SetInputValue(1, iOrgOrderNo);
|
Td0313.SetInputValue(1, iOrgOrderNo);
|
||||||
|
|||||||
@@ -27,179 +27,6 @@ namespace AutoSellerNS
|
|||||||
m_tbLogView = tbLog;
|
m_tbLogView = tbLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public void LoadTxt()
|
|
||||||
//{
|
|
||||||
// List<string> aItems = new List<string>();
|
|
||||||
// StockChart CPStockChart = new StockChart();
|
|
||||||
|
|
||||||
// foreach(string strLine in File.ReadLines(Util.GetSimulationPath()+"/0-input.txt", Encoding.UTF8))
|
|
||||||
// {
|
|
||||||
// var tokens = strLine.Split('\t');
|
|
||||||
|
|
||||||
// var strDate = tokens[0];
|
|
||||||
// var strNewTime = tokens[1];
|
|
||||||
// var strSimulationTime = tokens[2];
|
|
||||||
// var strCodeName = tokens[3];
|
|
||||||
// var strCode = tokens[4];
|
|
||||||
// var strStartPrice = tokens[5];
|
|
||||||
|
|
||||||
// DateTime StartDT;
|
|
||||||
// DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
|
||||||
// uint uiStartDate;
|
|
||||||
// uint.TryParse(StartDT.ToString("yyyyMMdd"), out uiStartDate);
|
|
||||||
|
|
||||||
// string strOutFileName = Util.GetSimulationPath() + "/" + strCodeName + "-" + StartDT.ToString("yyyy-MM-dd") + ".txt";
|
|
||||||
// if(File.Exists(strOutFileName) == true)
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// CPStockChart.SetInputValue(0, "A"+strCode);
|
|
||||||
// CPStockChart.SetInputValue(1, '1');
|
|
||||||
// CPStockChart.SetInputValue(2, uiStartDate);
|
|
||||||
// CPStockChart.SetInputValue(3, uiStartDate);
|
|
||||||
// CPStockChart.SetInputValue(4, 100000);
|
|
||||||
// CPStockChart.SetInputValue(5, new int[] { 0, 1, 5, 8, 9, 10, 11 });
|
|
||||||
// CPStockChart.SetInputValue(6, 'T');
|
|
||||||
// CPStockChart.BlockRequest2(1);
|
|
||||||
|
|
||||||
// int iFieldCnt = CPStockChart.GetHeaderValue(1);
|
|
||||||
// string[] astrFieldName = CPStockChart.GetHeaderValue(2);
|
|
||||||
|
|
||||||
// string strOutLine = "";
|
|
||||||
// for(int j = 0; j<iFieldCnt; j++)
|
|
||||||
// {
|
|
||||||
// strOutLine += astrFieldName[j];
|
|
||||||
// strOutLine += '\t';
|
|
||||||
// }
|
|
||||||
// File.AppendAllText(strOutFileName, strOutLine+Environment.NewLine, new UTF8Encoding(true));
|
|
||||||
// strOutLine = "";
|
|
||||||
|
|
||||||
// bool bContinue = true;
|
|
||||||
// while(bContinue == true)
|
|
||||||
// {
|
|
||||||
// int iCnt = CPStockChart.GetHeaderValue(3);
|
|
||||||
// for(int i = 0; i<iCnt; i++)
|
|
||||||
// {
|
|
||||||
// for(int j = 0; j<iFieldCnt; j++)
|
|
||||||
// {
|
|
||||||
// strOutLine += CPStockChart.GetDataValue(j, i);
|
|
||||||
// strOutLine += '\t';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// strOutLine += Environment.NewLine;
|
|
||||||
|
|
||||||
// if(i % 1000 == 999)
|
|
||||||
// {
|
|
||||||
// File.AppendAllText(strOutFileName, strOutLine, new UTF8Encoding(true));
|
|
||||||
// strOutLine = "";
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// bContinue = (CPStockChart.Continue==1);
|
|
||||||
// if(bContinue == true)
|
|
||||||
// CPStockChart.BlockRequest2(1);
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("RequestRQ({0}) ", m_CybosHelper.GetLimitRemainCountRQ()));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(strOutLine.Length > 0)
|
|
||||||
// {
|
|
||||||
// File.AppendAllText(strOutFileName, strOutLine, new UTF8Encoding(true));
|
|
||||||
// strOutLine = "";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("[Load] " + strOutFileName + " End"));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("[Load] All End"));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void LoadExcel()
|
|
||||||
//{
|
|
||||||
// List<string> aItems = new List<string>();
|
|
||||||
// StockChart CPStockChart = new StockChart();
|
|
||||||
// ExcelHandler Excel = null;
|
|
||||||
|
|
||||||
// foreach(string strLine in File.ReadLines(Util.GetSimulationPath()+"/0-input.txt", Encoding.UTF8))
|
|
||||||
// {
|
|
||||||
// var tokens = strLine.Split('\t');
|
|
||||||
|
|
||||||
// var strDate = tokens[0];
|
|
||||||
// var strNewTime = tokens[1];
|
|
||||||
// var strSimulationTime = tokens[2];
|
|
||||||
// var strCodeName = tokens[3];
|
|
||||||
// strCodeName = strCodeName.Trim();
|
|
||||||
// var strCode = tokens[4];
|
|
||||||
// var strStartPrice = tokens[5];
|
|
||||||
|
|
||||||
// DateTime StartDT;
|
|
||||||
// DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
|
||||||
|
|
||||||
// string strOutFileName = Util.GetSimulationPath() + "/" + StartDT.ToString("yyyy-MM-dd") + "-StockChart-" + strCodeName + ".xlsx";
|
|
||||||
// if(File.Exists(strOutFileName) == true)
|
|
||||||
// {
|
|
||||||
// if(StartDT.DayOfYear == DateTime.Now.DayOfYear)
|
|
||||||
// File.Delete(strOutFileName);
|
|
||||||
// else
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// CPStockChart.SetInputValue(0, "A"+strCode);
|
|
||||||
// CPStockChart.SetInputValue(1, '1');
|
|
||||||
// CPStockChart.SetInputValue(2, StartDT.ToString("yyyyMMdd"));
|
|
||||||
// CPStockChart.SetInputValue(3, StartDT.ToString("yyyyMMdd"));
|
|
||||||
// CPStockChart.SetInputValue(5, new int[] { 0, 1, 5, 8, 9, 10, 11 });
|
|
||||||
// CPStockChart.SetInputValue(6, 'T');
|
|
||||||
// CPStockChart.BlockRequest2(1);
|
|
||||||
|
|
||||||
// int iFieldCnt = CPStockChart.GetHeaderValue(1);
|
|
||||||
// string[] astrFieldName = CPStockChart.GetHeaderValue(2);
|
|
||||||
// List<object> aRow = new List<object>();
|
|
||||||
// List<object[]> aaRows = new List<object[]>();
|
|
||||||
|
|
||||||
// Excel = new ExcelHandler(strOutFileName, astrFieldName);
|
|
||||||
|
|
||||||
// bool bContinue = true;
|
|
||||||
// while(bContinue == true)
|
|
||||||
// {
|
|
||||||
// int iCnt = CPStockChart.GetHeaderValue(3);
|
|
||||||
// for(int i = 0; i<iCnt; i++)
|
|
||||||
// {
|
|
||||||
// for(int j = 0; j<iFieldCnt; j++)
|
|
||||||
// aRow.Add(CPStockChart.GetDataValue(j, i));
|
|
||||||
|
|
||||||
// if(aaRows.Count == 0)
|
|
||||||
// {
|
|
||||||
// aRow.Add(0);
|
|
||||||
// aRow.Add(0);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// aRow.Add((uint)aaRows.Last()[5]-(uint)aRow[5]);
|
|
||||||
// aRow.Add((uint)aaRows.Last()[6]-(uint)aRow[6]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// aaRows.Add(aRow.ToArray());
|
|
||||||
// aRow.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// bContinue = (CPStockChart.Continue==1);
|
|
||||||
// if(bContinue == true)
|
|
||||||
// CPStockChart.BlockRequest2(1);
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("RequestRQ({0}) ", m_CybosHelper.GetLimitRemainCountRQ()));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// aaRows.Reverse();
|
|
||||||
// Excel.AddRows(aaRows.ToArray());
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("[Load] " + strOutFileName + " End\n"));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("[Load] All End\n\n"));
|
|
||||||
// m_tbLogView.SelectionStart = m_tbLogView.TextLength;
|
|
||||||
// m_tbLogView.ScrollToCaret();
|
|
||||||
//}
|
|
||||||
|
|
||||||
public void LoadExcel2()
|
public void LoadExcel2()
|
||||||
{
|
{
|
||||||
List<string> aItems = new List<string>();
|
List<string> aItems = new List<string>();
|
||||||
@@ -211,12 +38,16 @@ namespace AutoSellerNS
|
|||||||
var tokens = strLine.Split('\t');
|
var tokens = strLine.Split('\t');
|
||||||
|
|
||||||
var strDate = tokens[0];
|
var strDate = tokens[0];
|
||||||
var strNewsTime = tokens[1];
|
var strSimulationTime = tokens[1];
|
||||||
var strSimulationTime = tokens[2];
|
var strCodeName = tokens[2];
|
||||||
var strCodeName = tokens[3];
|
|
||||||
strCodeName = strCodeName.Trim();
|
strCodeName = strCodeName.Trim();
|
||||||
var strCode = tokens[4];
|
var strCode = tokens[3];
|
||||||
var strStartPrice = tokens[5];
|
List<Tuple<int, int>> PriceDealCntList = new List<Tuple<int, int>>();
|
||||||
|
for (int i = 4; i < tokens.Length; i++)
|
||||||
|
{
|
||||||
|
var subToken = tokens[i].Split(':');
|
||||||
|
PriceDealCntList.Add(new Tuple<int, int>(int.Parse(subToken[0]), int.Parse(subToken[1])));
|
||||||
|
}
|
||||||
|
|
||||||
DateTime StartDT;
|
DateTime StartDT;
|
||||||
DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
||||||
@@ -281,137 +112,6 @@ namespace AutoSellerNS
|
|||||||
m_tbLogView.ScrollToCaret();
|
m_tbLogView.ScrollToCaret();
|
||||||
}
|
}
|
||||||
|
|
||||||
//public void StartSimuation()
|
|
||||||
//{
|
|
||||||
// m_tbLogView.AppendText(string.Format("[시뮬레이션 시작] 조건 : {0}회, 트레일링 : {1}%, {2}회\n", Config.GetBidCount(), Config.GetTrailingRate(), Config.GetTrailingCnt()));
|
|
||||||
|
|
||||||
// int iTotalProfit = 0;
|
|
||||||
// int iTotalBid = 0;
|
|
||||||
|
|
||||||
// foreach(string strLine in File.ReadLines(Util.GetSimulationPath()+"/0-input.txt", Encoding.UTF8))
|
|
||||||
// {
|
|
||||||
// var tokens = strLine.Split('\t');
|
|
||||||
|
|
||||||
// var strDate = tokens[0];
|
|
||||||
// var strNewTime = tokens[1];
|
|
||||||
// var strSimulationTime = tokens[2];
|
|
||||||
// var strCodeName = tokens[3];
|
|
||||||
// strCodeName = strCodeName.Trim();
|
|
||||||
// var strCode = tokens[4];
|
|
||||||
// var strStartPrice = tokens[5];
|
|
||||||
|
|
||||||
// DateTime StartDT;
|
|
||||||
// DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
|
||||||
|
|
||||||
// int iStartPrice;
|
|
||||||
// int.TryParse(strStartPrice, NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out iStartPrice);
|
|
||||||
|
|
||||||
// string strFileName = Util.GetSimulationPath() + "/" + StartDT.ToString("yyyy-MM-dd") + "-StockChart-" + strCodeName + ".xlsx";
|
|
||||||
|
|
||||||
|
|
||||||
// FileInfo newFile = new FileInfo(strFileName);
|
|
||||||
// ExcelPackage package = new ExcelPackage(newFile);
|
|
||||||
// ExcelWorksheet Sheet = package.Workbook.Worksheets["sheet"];
|
|
||||||
|
|
||||||
// int iMaxRow = Sheet.Dimension.Rows;
|
|
||||||
|
|
||||||
// DateTime RowDT;
|
|
||||||
// bool bStart = false;
|
|
||||||
// int iTotalAskCount = 0;
|
|
||||||
// int iTotalBidCount = 0;
|
|
||||||
// int iPrevAskCount = 0;
|
|
||||||
// int iPrevBidCount = 0;
|
|
||||||
// bool bBid = false;
|
|
||||||
// int iAskCount = 0;
|
|
||||||
// int iTrailingCount = 0;
|
|
||||||
// int iMaxPrice = int.MinValue;
|
|
||||||
// int iStockCount = 0;
|
|
||||||
// int iStartRow = 0;
|
|
||||||
// int iPrevPrice = 0;
|
|
||||||
// for(int iRow = 2; iRow<=iMaxRow; iRow++)
|
|
||||||
// {
|
|
||||||
// int iTime = (int)(double)Sheet.Cells[iRow, 2].Value;
|
|
||||||
// int iPrice = (int)(double)Sheet.Cells[iRow, 3].Value;
|
|
||||||
// iTotalAskCount = (int)(double)Sheet.Cells[iRow, 6].Value;
|
|
||||||
// iTotalBidCount = (int)(double)Sheet.Cells[iRow, 7].Value;
|
|
||||||
|
|
||||||
// bBid = (iTotalAskCount == iPrevAskCount);
|
|
||||||
// DateTime.TryParse(strDate+" "+string.Format("{0}:{1}:{2}", iTime/100, iTime%100, 59), out RowDT);
|
|
||||||
|
|
||||||
// iStockCount = 1000000/iStartPrice;
|
|
||||||
|
|
||||||
// if(bStart == false)
|
|
||||||
// {
|
|
||||||
// if(RowDT >= StartDT && iPrice==iStartPrice)
|
|
||||||
// {
|
|
||||||
// bStart = true;
|
|
||||||
// iStartRow = iRow;
|
|
||||||
// }
|
|
||||||
// else if((RowDT-StartDT).Minutes > 10)
|
|
||||||
// {
|
|
||||||
// InsertLog(string.Format("[{0,-10}] 매수 가격 찾기 실패", strCodeName));
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if(bBid == false)
|
|
||||||
// iAskCount++;
|
|
||||||
// else
|
|
||||||
// iAskCount = 0;
|
|
||||||
|
|
||||||
// if(iAskCount >= Config.GetBidCount())
|
|
||||||
// {
|
|
||||||
// m_tbLogView.AppendText(string.Format("[{0,-10}] 조건 매도 {1} line:{2} {3:n0}원 ({4} : {5:n2}%) (매수 : {6:n0}원, line:{7})\n",
|
|
||||||
// strCodeName,
|
|
||||||
// RowDT.ToString("yyyy-MM-dd HH:mm:00"),
|
|
||||||
// iRow,
|
|
||||||
// iPrice,
|
|
||||||
// iPrice-iStartPrice,
|
|
||||||
// (iPrice-iStartPrice)*100/(float)iStartPrice,
|
|
||||||
// iStartPrice,
|
|
||||||
// iStartRow
|
|
||||||
// ));
|
|
||||||
// iTotalProfit += (iPrice-iStartPrice)*iStockCount;
|
|
||||||
// iTotalBid += iStartPrice*iStockCount;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(iPrice > iMaxPrice)
|
|
||||||
// iTrailingCount = 0;
|
|
||||||
// else if(iPrice <= iMaxPrice*(100-Config.GetTrailingRate())/100)
|
|
||||||
// iTrailingCount++;
|
|
||||||
// iMaxPrice = Math.Max(iPrice, iMaxPrice);
|
|
||||||
|
|
||||||
// if(iTrailingCount >= Config.GetTrailingCnt())
|
|
||||||
// {
|
|
||||||
// m_tbLogView.AppendText(string.Format("[{0}] 트레일링 매도 {1} line:{2} {3:n0}원 ({4} : {5:n2}%) (매수 : {6:n0}원, line:{7})\n",
|
|
||||||
// strCodeName,
|
|
||||||
// RowDT.ToString("yyyy-MM-dd HH:mm:00"),
|
|
||||||
// iRow,
|
|
||||||
// iPrice,
|
|
||||||
// iPrice-iStartPrice,
|
|
||||||
// (iPrice-iStartPrice)*100/(float)iStartPrice,
|
|
||||||
// iStartPrice,
|
|
||||||
// iStartRow
|
|
||||||
// ));
|
|
||||||
// iTotalProfit += (iPrice-iStartPrice)*iStockCount;
|
|
||||||
// iTotalBid += iStartPrice*iStockCount;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// iPrevPrice = iPrice;
|
|
||||||
// iPrevAskCount = iTotalAskCount;
|
|
||||||
// iPrevBidCount = iTotalBidCount;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_tbLogView.AppendText(string.Format("[시뮬레이션 종료] 총수익 : {0:n0}원 : {1:n2}%\n\n", iTotalProfit, iTotalProfit*100/(float)iTotalBid));
|
|
||||||
// m_tbLogView.SelectionStart = m_tbLogView.TextLength;
|
|
||||||
// m_tbLogView.ScrollToCaret();
|
|
||||||
//}
|
|
||||||
|
|
||||||
void InsertLog(string strMsg)
|
void InsertLog(string strMsg)
|
||||||
{
|
{
|
||||||
m_tbLogView.Invoke(new Action(() => {
|
m_tbLogView.Invoke(new Action(() => {
|
||||||
@@ -433,6 +133,62 @@ namespace AutoSellerNS
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FindStartRow(ExcelWorksheet Sheet, string strSimulationTime, List<Tuple<int, int>> PriceDealCntList)
|
||||||
|
{
|
||||||
|
DateTime searchTime = new DateTime();
|
||||||
|
DateTime.TryParseExact(strSimulationTime, "HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out searchTime);
|
||||||
|
DateTime searchTimeMin = searchTime - new TimeSpan(0, 1, 0);
|
||||||
|
DateTime searchTimeMax = searchTime + new TimeSpan(0, 1, 0);
|
||||||
|
DateTime rowTime = new DateTime();
|
||||||
|
int iStartRow = 0;
|
||||||
|
for(int iRow=2; iRow< Sheet.Dimension.Rows; iRow++)
|
||||||
|
{
|
||||||
|
string time = Sheet.Cells[iRow, 1].Value?.ToString();
|
||||||
|
if (time.Length == 5)
|
||||||
|
time = "0" + time;
|
||||||
|
DateTime.TryParseExact(time, "HHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out rowTime);
|
||||||
|
|
||||||
|
if (rowTime >= searchTime)
|
||||||
|
{
|
||||||
|
iStartRow = iRow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<50; i++)
|
||||||
|
{
|
||||||
|
int iRow = iStartRow + i;
|
||||||
|
int iPriceCol = 3;
|
||||||
|
int iDealCntCol = 7;
|
||||||
|
|
||||||
|
var cell = Sheet.Cells[iStartRow+i, iPriceCol];
|
||||||
|
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
for (int j = 0; j < PriceDealCntList.Count && result; j++)
|
||||||
|
{
|
||||||
|
result = Sheet.Cells[iRow + j, iPriceCol].Value?.ToString() == PriceDealCntList[j].Item1.ToString() &&
|
||||||
|
Sheet.Cells[iRow + j, iDealCntCol].Value?.ToString() == PriceDealCntList[j].Item2.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == true)
|
||||||
|
return iRow;
|
||||||
|
|
||||||
|
iRow = iStartRow - i;
|
||||||
|
result = true;
|
||||||
|
for (int j = 0; j < PriceDealCntList.Count && result; j++)
|
||||||
|
{
|
||||||
|
result = Sheet.Cells[iRow + j, iPriceCol].Value?.ToString() == PriceDealCntList[j].Item1.ToString() &&
|
||||||
|
Sheet.Cells[iRow + j, iDealCntCol].Value?.ToString() == PriceDealCntList[j].Item2.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == true)
|
||||||
|
return iRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
object SimulationWork(object param)
|
object SimulationWork(object param)
|
||||||
{
|
{
|
||||||
object[] aParams = (object[])param;
|
object[] aParams = (object[])param;
|
||||||
@@ -455,18 +211,24 @@ namespace AutoSellerNS
|
|||||||
var tokens = strLine.Split('\t');
|
var tokens = strLine.Split('\t');
|
||||||
|
|
||||||
var strDate = tokens[0];
|
var strDate = tokens[0];
|
||||||
var strNewTime = tokens[1];
|
var strSimulationTime = tokens[1];
|
||||||
var strSimulationTime = tokens[2];
|
var strCodeName = tokens[2];
|
||||||
var strCodeName = tokens[3];
|
|
||||||
strCodeName = strCodeName.Trim();
|
strCodeName = strCodeName.Trim();
|
||||||
var strCode = tokens[4];
|
var strCode = tokens[3];
|
||||||
var strStartPrice = tokens[5];
|
List<Tuple<int, int>> PriceDealCntList = new List<Tuple<int, int>>();
|
||||||
|
for (int i = 4; i < tokens.Length; i++)
|
||||||
|
{
|
||||||
|
var subToken = tokens[i].Split(':');
|
||||||
|
PriceDealCntList.Add(new Tuple<int, int>(
|
||||||
|
int.Parse(subToken[0], NumberStyles.AllowThousands, CultureInfo.CurrentCulture),
|
||||||
|
int.Parse(subToken[1], NumberStyles.AllowThousands, CultureInfo.CurrentCulture)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DateTime StartDT;
|
DateTime StartDT;
|
||||||
DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
DateTime.TryParse(strDate+" "+strSimulationTime, out StartDT);
|
||||||
|
|
||||||
int iStartPrice;
|
int iStartPrice = PriceDealCntList[0].Item1;
|
||||||
int.TryParse(strStartPrice, NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out iStartPrice);
|
|
||||||
|
|
||||||
int iSellPrice = iStartPrice;
|
int iSellPrice = iStartPrice;
|
||||||
|
|
||||||
@@ -512,11 +274,21 @@ namespace AutoSellerNS
|
|||||||
Color AskBackColor = Color.FromArgb(127, 179, 243);
|
Color AskBackColor = Color.FromArgb(127, 179, 243);
|
||||||
Color MaxBackColor = Color.FromArgb(91, 205, 102);
|
Color MaxBackColor = Color.FromArgb(91, 205, 102);
|
||||||
|
|
||||||
|
|
||||||
|
iStartRow = FindStartRow(Sheet, strSimulationTime, PriceDealCntList);
|
||||||
|
if(iStartRow < 0)
|
||||||
|
{
|
||||||
|
InsertLog(string.Format("[{0}] [{1}] 매수 가격 찾기 실패\n",
|
||||||
|
StartDT.ToString("yyyy-MM-dd"),
|
||||||
|
strCodeName));
|
||||||
|
return new object[] { "매수 가격 찾기 실패", (iSellPrice - iStartPrice) * iStockCount };
|
||||||
|
}
|
||||||
|
|
||||||
double dPrevStdDev = 0;
|
double dPrevStdDev = 0;
|
||||||
string strReturnMsg = "";
|
string strReturnMsg = "";
|
||||||
int iPrevTime = 0;
|
int iPrevTime = 0;
|
||||||
|
|
||||||
for(int iRow = 2; iRow<=iMaxRow; iRow++)
|
for(int iRow = iStartRow; iRow<=iMaxRow; iRow++)
|
||||||
{
|
{
|
||||||
int iTime = (int)(double)Sheet.Cells[iRow, 1].Value;
|
int iTime = (int)(double)Sheet.Cells[iRow, 1].Value;
|
||||||
int iPrice = (int)(double)Sheet.Cells[iRow, 3].Value;
|
int iPrice = (int)(double)Sheet.Cells[iRow, 3].Value;
|
||||||
@@ -914,13 +686,15 @@ namespace AutoSellerNS
|
|||||||
List<Task<object>> aTasks = new List<Task<object>>();
|
List<Task<object>> aTasks = new List<Task<object>>();
|
||||||
foreach(string strLine in File.ReadLines(Util.GetSimulationPath()+"/0-input.txt", Encoding.UTF8))
|
foreach(string strLine in File.ReadLines(Util.GetSimulationPath()+"/0-input.txt", Encoding.UTF8))
|
||||||
{
|
{
|
||||||
var task = Task.Factory.StartNew<object>(() => SimulationWork(new object[] {
|
var task = Task.Factory.StartNew<object>(() =>
|
||||||
iListCnt,
|
SimulationWork(new object[] {
|
||||||
dFastSD, dFastUpCnt, dFastDownCnt,
|
iListCnt,
|
||||||
dSlowSD, dSlowUpCnt, dSlowDownCnt,
|
dFastSD, dFastUpCnt, dFastDownCnt,
|
||||||
iCompareType, strMethod,
|
dSlowSD, dSlowUpCnt, dSlowDownCnt,
|
||||||
strLine, dTimeSub, iTimeDiffLimit,
|
iCompareType, strMethod,
|
||||||
iIgnorePrice, bHalfTrailing }));
|
strLine, dTimeSub, iTimeDiffLimit,
|
||||||
|
iIgnorePrice, bHalfTrailing })
|
||||||
|
);
|
||||||
|
|
||||||
aTasks.Add(task);
|
aTasks.Add(task);
|
||||||
}
|
}
|
||||||
@@ -1001,17 +775,20 @@ namespace AutoSellerNS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void InsertSimulationLog(string strCode, string strCodeName, int iPrice)
|
public static void InsertSimulationLog(string strCode, string strCodeName, List<Tuple<int, int>> PriceDealCntList)
|
||||||
{
|
{
|
||||||
DateTime time = DateTime.Now;
|
DateTime time = DateTime.Now;
|
||||||
string strPath = Util.GetSimulationPath() + "/0-input-" + time.Date.ToString("yyyy-MM-dd") + ".txt";
|
string strPath = Util.GetSimulationPath() + "/0-input-" + time.Date.ToString("yyyy-MM-dd") + ".txt";
|
||||||
File.AppendAllText(strPath, string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
|
string strLine = string.Format("{0}\t{1}\t{2}\t{3}",
|
||||||
time.ToString("yyyy-MM-dd"),
|
time.ToString("yyyy-MM-dd"),
|
||||||
time.ToString("hh:mm:ss"),
|
time.ToString("hh:mm:ss"),
|
||||||
time.ToString("hh:mm:ss"),
|
|
||||||
strCodeName,
|
strCodeName,
|
||||||
strCode,
|
strCode.Substring(1));
|
||||||
iPrice), new UTF8Encoding(true));
|
foreach (var pair in PriceDealCntList)
|
||||||
|
strLine += "\t" + pair.Item1.ToString() + ":" + pair.Item2.ToString();
|
||||||
|
strLine += "\n";
|
||||||
|
|
||||||
|
File.AppendAllText(strPath, strLine, new UTF8Encoding(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user