#include "TickChart.h"
|
#include "../common_nopch/StringUtil.h"
|
#include "../common_nopch/TimeUtil.h"
|
#include "TickDataUtil.h"
|
#include "JueJinDataUtil.h"
|
#include <wx/dcbuffer.h>
|
using namespace std;
|
#define COLOR_RED wxColor(128,0,0)
|
#define COLOR_RED_TEXT wxColor(225,0,0)
|
#define COLOR_GREEN_TEXT wxColor(0,225,0)
|
#define COLOR_WHITE_TEXT wxColor(255,255,255)
|
#define COLOR_YELLOW wxColor(255,128,32)
|
#define COLOR_UNDERLYING_LINE wxColor(133,202,255)
|
// Õ¤¸ñÏßÊýÁ¿
|
#define GRID_LINE_ROW_COUNT 15
|
#define GRID_LINE_COL_COUNT 9
|
#define GRID_MARGIN Padding({ 60,20,50,25})
|
#define MAX_RATE_FLOAT 0.7
|
#define POINT_REDIUS 2
|
|
|
|
wxPoint TickChart::convertTickDataToPoint(TickData tickData, wxRect area, float scale)
|
{
|
int X_COUNT = this->xScopeSeconds / this->timeSpace;// 4Сʱ5·ÖÖÓÄÒÀ¨µÄËùÓеã
|
int width = (tickData.time / this->timeSpace) * (area.width - GRID_MARGIN.left - GRID_MARGIN.right) / X_COUNT;
|
int x = area.x + GRID_MARGIN.left + width;
|
int y = area.y + GRID_MARGIN.top + (area.height - GRID_MARGIN.top - GRID_MARGIN.bottom) * (maxYRate - tickData.rate * scale) / (maxYRate * 2);
|
return wxPoint(x, y);
|
}
|
|
wxPoint TickChart::convertTickDataToPoint(TickTradeData tickData, wxRect area)
|
{
|
int X_COUNT = this->xScopeSeconds / this->timeSpace;// Õû¸öXÖáµÄËùÓеã
|
int width = (TickDataUtil::timeStrToSecondsAsAxisX(tickData.time, xStartTimeSeconds) / this->timeSpace) * (area.width - GRID_MARGIN.left - GRID_MARGIN.right) / X_COUNT;
|
int x = area.x + GRID_MARGIN.left + width;
|
int y = area.y + GRID_MARGIN.top + (area.height - GRID_MARGIN.top - GRID_MARGIN.bottom) * (maxYRate - tickData.rate) / (maxYRate * 2);
|
return wxPoint(x, y);
|
}
|
|
TickData TickChart::pointToTick(wxPoint point, wxRect area)
|
{
|
// ¾àÀë×ó±ßÔµ±ßµÄ¾àÀë
|
int x = point.x - area.x - GRID_MARGIN.left;
|
int y = point.y - area.y - GRID_MARGIN.top;
|
int width = area.GetWidth() - GRID_MARGIN.left - GRID_MARGIN.right;
|
int height = area.GetHeight() - GRID_MARGIN.top - GRID_MARGIN.bottom;
|
int seconds = x * this->xScopeSeconds / width;
|
seconds = seconds / this->timeSpace * this->timeSpace;
|
// ½«Ê±¼äsתΪ×Ö·û´®
|
float rate = maxYRate - y * maxYRate * 2 / height;
|
TickData tickData;
|
tickData.rate = rate;
|
tickData.time = seconds;
|
tickData.price = "";
|
return tickData;
|
}
|
|
void TickChart::drawAxis(wxDC& dc, wxRect area)
|
{
|
//WidgetsRenderUtil::drawText(dc, "²âÊÔ123", wxPoint(area.x, area.y), 100,50);
|
Padding padding = GRID_MARGIN;
|
// »æÖÆÕ¤¸ñ
|
int row_line_count = GRID_LINE_ROW_COUNT;
|
int column_line_count = GRID_LINE_COL_COUNT;
|
|
list<LineInfo*> grides_rows;
|
|
double rowSpace = (area.height - padding.top - padding.bottom) / ((row_line_count - 1) * 1.0f);
|
for (int i = 0; i < row_line_count; i++) {
|
// »æÖƺáÏòÕ¤¸ñ
|
LineInfo* lineInfo = new LineInfo();
|
lineInfo->lineWidth = 1;
|
if (i == row_line_count / 2) {
|
lineInfo->lineWidth = 3;
|
}
|
lineInfo->style = wxPENSTYLE_SOLID;
|
lineInfo->start = wxPoint(area.x + padding.left, area.y + padding.top + (int)(rowSpace * (i)) - lineInfo->lineWidth / 2);
|
lineInfo->end = wxPoint(area.x + area.width - padding.right, area.y + padding.top + (int)(rowSpace * (i)) - lineInfo->lineWidth / 2);
|
grides_rows.push_back(lineInfo);
|
}
|
|
|
for (list<LineInfo*>::iterator e = grides_rows.begin(); e != grides_rows.end(); ++e) {
|
try {
|
LineInfo* lineInfo = *e;
|
wxPen dashedPen(COLOR_RED, lineInfo->lineWidth, lineInfo->style);
|
dc.SetPen(dashedPen);
|
dc.DrawLine(lineInfo->start, lineInfo->end);
|
}
|
catch (...) {
|
|
}
|
}
|
|
list<LineInfo*> grides_cols;
|
double columnSpace = (area.width - padding.left - padding.right) / ((column_line_count - 1) * 1.0f);
|
for (int i = 0; i < column_line_count; i++) {
|
// »æÖƺáÏòÕ¤¸ñ
|
LineInfo* lineInfo = new LineInfo();
|
lineInfo->lineWidth = 1;
|
lineInfo->start = wxPoint(area.x + padding.left + (int)(columnSpace * (i)), area.y + padding.top);
|
lineInfo->end = wxPoint(area.x + padding.left + (int)(columnSpace * (i)), area.y + area.height - padding.bottom - 1);
|
if (i % 2 == 1) {
|
lineInfo->style = wxPENSTYLE_DOT;
|
}
|
else {
|
lineInfo->style = wxPENSTYLE_SOLID;
|
}
|
if (i == column_line_count / 2) {
|
lineInfo->lineWidth = 2;
|
}
|
grides_cols.push_back(lineInfo);
|
}
|
|
for (list<LineInfo*>::iterator e = grides_cols.begin(); e != grides_cols.end(); ++e) {
|
try {
|
LineInfo* lineInfo = *e;
|
wxPen dashedPen(COLOR_RED, lineInfo->lineWidth, lineInfo->style);
|
dc.SetPen(dashedPen);
|
dc.DrawLine(lineInfo->start, lineInfo->end);
|
}
|
catch (...) {
|
|
}
|
}
|
|
//---------------»æÖÆLabel-------------------
|
AxisYLabel ylabels[GRID_LINE_ROW_COUNT];
|
const int UP_COUNT = (GRID_LINE_ROW_COUNT - 1) / 2;
|
for (int i = 0; i < GRID_LINE_ROW_COUNT; i++) {
|
AxisYLabel label;
|
float rate = (UP_COUNT - i) * maxYRate / UP_COUNT;
|
label.rightLabel = StringUtil::toString(abs(rate), 2).append("%");
|
label.leftLabel = StringUtil::toString((100 + rate) * preClosePrice / 100, 3);
|
ylabels[i] = AxisYLabel(label);
|
}
|
dc.SetPen(COLOR_RED);
|
dc.SetBrush(COLOR_RED);
|
wxPen dashedPen(COLOR_RED, 1, wxPENSTYLE_SOLID);
|
dc.SetPen(dashedPen);
|
int index = 0;
|
|
|
|
for (list<LineInfo*>::iterator e = grides_rows.begin(); e != grides_rows.end(); ++e) {
|
if (index == 0) {
|
dc.SetTextForeground(COLOR_RED_TEXT);
|
}
|
else if (index == grides_rows.size() / 2) {
|
dc.SetTextForeground(COLOR_WHITE_TEXT);
|
}
|
else if (index == grides_rows.size() / 2 + 1) {
|
dc.SetTextForeground(COLOR_GREEN_TEXT);
|
}
|
try {
|
LineInfo* lineInfo = *e;
|
// »æÖÆYÖá×ó²à±êÇ©
|
wxSize textSize = dc.GetTextExtent(ylabels[index].leftLabel);
|
dc.DrawText(ylabels[index].leftLabel, wxPoint(lineInfo->start.x - textSize.GetWidth() - 2, lineInfo->start.y - textSize.GetHeight() / 2));
|
// »æÖÆYÖáÓÒ²à±êÇ©
|
textSize = dc.GetTextExtent(ylabels[index].rightLabel);
|
dc.DrawText(ylabels[index].rightLabel, wxPoint(lineInfo->end.x + 2, lineInfo->end.y - textSize.GetHeight() / 2));
|
}
|
catch (...) {
|
|
}
|
index++;
|
}
|
|
// »æÖÆÊ±¼ä
|
dc.SetTextForeground(COLOR_RED_TEXT);
|
if (timeSpaceType == TIME_SPACE_BAR)
|
{
|
wxString times[5] = { "09:30:00","10:30:00","11:30:00","14:00:00","15:00:00" };
|
|
for (int i = 0; i < 5; i++) {
|
wxPoint p = convertTickDataToPoint(TickData({ TickDataUtil::timeStrToSecondsAsAxisX(times[i], xStartTimeSeconds) ,0,"" }), area);
|
wxString text = times[i].substr(0, 5);
|
wxSize size = dc.GetTextExtent(text);
|
|
wxPoint point(p.x - size.GetWidth() / 2, area.y + area.height - GRID_MARGIN.bottom + 2);
|
|
dc.DrawText(text, point);
|
}
|
}
|
else if (timeSpaceType == TIME_SPACE_TICK) {
|
|
int spaceSeconds = this->xScopeSeconds / 4;
|
wxString times[5];
|
times[0] = xStartTime;
|
for (int i = 0; i < 5; i++) {
|
times[i] = TickDataUtil::tradTimeAdd(xStartTime, this->xScopeSeconds * i / 4);
|
}
|
|
for (int i = 0; i < 5; i++) {
|
wxPoint p = convertTickDataToPoint(TickData({ TickDataUtil::timeStrToSecondsAsAxisX(times[i], xStartTimeSeconds) ,0,"" }), area);
|
wxString text = times[i];
|
wxSize size = dc.GetTextExtent(text);
|
wxPoint point(p.x - size.GetWidth() / 2, area.y + area.height - GRID_MARGIN.bottom + 2);
|
dc.DrawText(text, point);
|
}
|
}
|
}
|
|
void TickChart::drawMouse(wxDC& dc, wxRect area, wxPoint point)
|
{
|
int x = mousePos.x;
|
int y = mousePos.y;
|
|
if (x >= area.x + GRID_MARGIN.left && x <= area.x + area.width - GRID_MARGIN.right) {
|
|
if (y >= area.y + GRID_MARGIN.top && y <= area.y + area.height - GRID_MARGIN.bottom) {
|
|
if (point.x >= 0 && point.y >= 0) {
|
TickData tickData = pointToTick(point, area);
|
|
|
|
dc.SetPen(COLOR_WHITE_TEXT);
|
// ºáÏß
|
dc.DrawLine(wxPoint(area.x + GRID_MARGIN.left, area.y + point.y), wxPoint(area.x + area.width - GRID_MARGIN.right, area.y + point.y));
|
// ÊúÏß
|
dc.DrawLine(wxPoint(area.x + point.x, area.y + GRID_MARGIN.top), wxPoint(area.x + point.x, area.y + area.height - GRID_MARGIN.bottom));
|
// »æÖÆÊ±¼ä
|
wxPoint timePoint = wxPoint(area.x + point.x, area.y + area.height - GRID_MARGIN.bottom);
|
|
|
|
wxString timeText = TickDataUtil::axisXSecondsToTimeStr(tickData.time, xStartTimeSeconds, timeSpaceType == TIME_SPACE_TICK ? TRUE : FALSE);
|
|
wxSize size = dc.GetTextExtent(timeText);
|
timePoint.x -= size.GetWidth() / 2; timePoint.y += 1;
|
dc.SetPen(wxColor(64, 0, 128));
|
dc.SetBrush(wxColor(64, 0, 128));
|
// ÏÔʾʱ¼ä
|
dc.DrawRectangle(wxRect(timePoint, size));
|
dc.SetTextForeground(COLOR_RED_TEXT);
|
dc.DrawLabel(timeText, wxRect(timePoint, size), wxALIGN_CENTER);
|
// ÏÔʾ±ÈÀý
|
float rate = tickData.rate;
|
string rateText = StringUtil::to_string(tickData.rate, 2);
|
rateText.append("%");
|
size = dc.GetTextExtent(rateText);
|
wxPoint ratePoint = wxPoint(area.x + area.width - GRID_MARGIN.right, area.y + point.y);
|
ratePoint.y -= size.GetHeight() / 2;
|
ratePoint.x += 1;
|
dc.DrawRectangle(wxRect(ratePoint, size));
|
int r = (int)round(rate * 10000) / 100;
|
if (r == 0) {
|
dc.SetTextForeground(COLOR_WHITE_TEXT);
|
}
|
else if (r > 0) {
|
dc.SetTextForeground(COLOR_RED_TEXT);
|
}
|
else {
|
dc.SetTextForeground(COLOR_GREEN_TEXT);
|
}
|
dc.DrawLabel(rateText, wxRect(ratePoint, size), wxALIGN_CENTER);
|
}
|
}
|
}
|
}
|
|
void TickChart::drawTickLine(wxDC& dc, wxRect area)
|
{
|
// ---------------»æÖÆKÏß----------------
|
int index = 0;
|
wxPointList pointList;
|
dc.SetPen(COLOR_WHITE_TEXT);
|
for (std::list<TickData>::iterator e = cbTickDatas.begin(); e != cbTickDatas.end(); e++) {
|
//µã¶¨Î»×ø±ê
|
TickData tickData = *e;
|
wxPoint point = convertTickDataToPoint(tickData, area);
|
pointList.Append(new wxPoint(point.x, point.y));
|
index++;
|
}
|
dc.DrawLines(&pointList);
|
//ÊͷŵãÄÚ´æ
|
pointList.Clear();
|
}
|
|
void TickChart::drawUnderlyingTickLine(wxDC& dc, wxRect area)
|
{
|
// ---------------»æÖÆ¹ÉÆ±KÏß----------------
|
int index = 0;
|
wxPointList pointList;
|
dc.SetPen(COLOR_UNDERLYING_LINE);
|
for (std::list<TickData>::iterator e = underlyingTickDatas.begin(); e != underlyingTickDatas.end(); e++) {
|
//µã¶¨Î»×ø±ê
|
TickData tickData = *e;
|
wxPoint point = convertTickDataToPoint(tickData, area, underlyingScale);
|
pointList.Append(new wxPoint(point.x, point.y));
|
index++;
|
}
|
dc.DrawLines(&pointList);
|
//ÊͷŵãÄÚ´æ
|
pointList.Clear();
|
}
|
|
void TickChart::drawBuyPoint(wxDC& dc, wxPoint point)
|
{
|
dc.DrawCircle(point, POINT_REDIUS);
|
}
|
|
void TickChart::drawSellPoint(wxDC& dc, wxPoint point)
|
{
|
dc.SetPen(wxColor(59, 123, 191));
|
dc.SetBrush(wxColor(59, 123, 191));
|
dc.DrawCircle(point, POINT_REDIUS);
|
}
|
|
|
|
void TickChart::drawBuyAndSellPointInfo(wxDC& paint, wxRect area, wxPoint mousePoint)
|
{
|
paint.SetPen(wxColour(255, 255, 255, 125));
|
paint.SetBrush(wxColour(255, 255, 255, 125));
|
// Èç¹ûÔÚÂòµãÖÜΧ4¸öÏñËØ
|
// »æÖÆÂòÂôµã
|
for (std::list<TickTradeData>::iterator e = buyPoints.begin(); e != buyPoints.end(); e++) {
|
TickTradeData tickData = *e;
|
wxPoint point = convertTickDataToPoint(*e, area);
|
if (abs(mousePoint.x - point.x) < POINT_REDIUS * 2 && abs(mousePoint.y - point.y) < POINT_REDIUS * 2) {
|
// ¼ÆËãλÖÃ
|
wxString text = wxString("³É½»Ê±¼ä£º").Append(tickData.time).Append("\n");
|
text.Append("ÕÇ·ù£º").Append(StringUtil::to_string(tickData.rate)).Append("%").Append("\n");
|
text.Append("³É½»¼Û£º").Append(StringUtil::to_string( stod( tickData.price.ToStdString().c_str()),3)).Append("Ôª\n");
|
text.Append("³É½»Á¿£º").Append(to_string(tickData.volume)).Append("\n");
|
text.Append("³É½»¶î£º").Append(tickData.money).Append("Ôª");
|
drawInfo(paint, wxPoint(point.x + POINT_REDIUS, point.y + POINT_REDIUS), text, {});
|
break;
|
}
|
}
|
|
for (std::list<TickTradeData>::iterator e = sellPoints.begin(); e != sellPoints.end(); e++) {
|
TickTradeData tickData = *e;
|
wxPoint point = convertTickDataToPoint(*e, area);
|
if (abs(mousePoint.x - point.x) < POINT_REDIUS * 2 && abs(mousePoint.y - point.y) < POINT_REDIUS * 2) {
|
wxString text = wxString("³É½»Ê±¼ä£º").Append(tickData.time).Append("\n");
|
text.Append("ÕÇ·ù£º").Append(StringUtil::to_string(tickData.rate)).Append("%").Append("\n");
|
text.Append("³É½»¼Û£º").Append(StringUtil::to_string(stod(tickData.price.ToStdString().c_str()), 3)).Append("Ôª\n");
|
text.Append("³É½»Á¿£º").Append(to_string(tickData.volume)).Append("\n");
|
text.Append("³É½»¶î£º").Append(tickData.money).Append("Ôª");
|
drawInfo(paint, wxPoint(point.x + POINT_REDIUS, point.y + POINT_REDIUS), text, {});
|
break;
|
}
|
}
|
}
|
|
void TickChart::drawInfo(wxDC& paint, wxPoint position, wxString content, std::list<ColorIndexInfo> colorIndexes)
|
{
|
|
std::vector<wxString> substrings;
|
size_t start = 0;
|
size_t end = content.find("\n");
|
while (end != std::string::npos) {
|
substrings.push_back(content.substr(start, end - start));
|
start = end + 1;
|
end = content.find("\n", start);
|
}
|
|
substrings.push_back(content.substr(start, end));
|
int maxWidth = 0;
|
int lineHeight = 0;
|
for (std::vector<wxString>::iterator e = substrings.begin(); e != substrings.end(); ++e) {
|
|
wxString text = *e;
|
wxSize size = paint.GetTextExtent(text);
|
if (size.GetWidth() > maxWidth) {
|
maxWidth = size.GetWidth();
|
}
|
|
if (size.GetHeight() > lineHeight) {
|
lineHeight = size.GetHeight();
|
}
|
}
|
|
int padding = 2;
|
|
|
wxRect rect = wxRect(wxPoint(position.x, position.y), wxSize(maxWidth + padding * 2, lineHeight * substrings.size()));
|
paint.DrawRoundedRectangle(rect, 2);
|
std::list<ColorIndexInfo> colors;
|
WidgetsRenderUtil::drawText(&paint, content, wxPoint(rect.GetPosition().x + padding, rect.GetPosition().y), rect.GetSize().GetWidth() - padding * 2, lineHeight, colorIndexes);
|
}
|
|
void TickChart::drawUnderlyingLatestRate(wxDC& paint, wxRect area)
|
{
|
if (1 > 0) {
|
return;
|
}
|
if (underlyingTickDatas.size() > 0) {
|
// »æÖÆTickÏß×îеĵã
|
TickData lastTickData = underlyingTickDatas.back();
|
wxPoint point = convertTickDataToPoint(lastTickData, area, underlyingScale);
|
|
// »æÖÆÎÄ×Ö
|
wxString rateText = " Õý¹É£º";
|
if (lastTickData.rate < 0) {
|
paint.SetTextForeground(COLOR_GREEN_TEXT);
|
rateText.Append(StringUtil::to_string(lastTickData.rate));
|
rateText.Append("%");
|
}
|
else {
|
paint.SetTextForeground(COLOR_RED_TEXT);
|
rateText.Append(L"+");
|
rateText.Append(StringUtil::to_string(lastTickData.rate));
|
rateText.Append("%");
|
}
|
rateText.Append(" ");
|
wxSize rateSize = paint.GetTextExtent(rateText);
|
int x = area.x + area.GetWidth() - rateSize.GetWidth()- GRID_MARGIN.right;
|
int y = area.y + area.GetHeight() - rateSize.GetHeight() - GRID_MARGIN.bottom;
|
paint.DrawRoundedRectangle(wxPoint(x, y), rateSize, 0);
|
|
paint.DrawText(rateText, wxPoint(x,y));
|
}
|
}
|
|
void TickChart::drawUnderlyingQuotes(wxDC& paint, wxRect area)
|
{
|
if (underlyingTickDatas.size() <= 0) {
|
return;
|
}
|
TickData tick = underlyingTickDatas.back();
|
if (tick.quotes.size() <= 0) {
|
return;
|
}
|
wxSize size = paint.GetTextExtent("Íò");
|
int lineHeight = (int)(size.GetHeight() * 1.1);
|
int contentHeight = size.GetHeight();
|
int contentWidth = contentHeight * 3;
|
int height = lineHeight*10;
|
int width = contentWidth * 2;
|
|
|
|
// »æÖÆÂòÂôµµ¿Ú
|
wxPoint middlePoint = wxPoint(area.GetLeft()+ GRID_MARGIN.left, area.GetBottom() - GRID_MARGIN.bottom - height/2);
|
if (stoi(TimeUtil::format(TimeUtil::getNowTimeStamp(), "%H%M%S").c_str())<120000) {
|
middlePoint.x = area.GetRight() - GRID_MARGIN.right - width;
|
}
|
|
|
wxColour textColor = wxColor(100, 100, 100);
|
|
paint.SetBrush(wxColor(255,255,255,128));
|
paint.SetPen(*wxTRANSPARENT_PEN);
|
paint.SetTextForeground(textColor);
|
//paint.DrawRoundedRectangle(wxRect(wxPoint(middlePoint.x, middlePoint.y - height/2), wxSize(width, height)),5);
|
|
int index = 0;
|
for (std::list<Quote>::iterator e = tick.quotes.begin(); e != tick.quotes.end(); ++e) {
|
Quote qu = *e;
|
// »æÖÆÂò
|
wxString price;
|
wxString money;
|
|
if (qu.bid_volume > 0) {
|
// ÓÐÂò1
|
price = wxString(StringUtil::to_string(qu.bid_price).c_str());
|
money = StringUtil::to_string( qu.bid_price * qu.bid_volume/10000,1);
|
money.Append("Íò");
|
}
|
else {
|
price = "-";
|
money = "-";
|
}
|
|
|
paint.DrawLabel(price, wxRect(wxPoint(middlePoint.x, middlePoint.y + lineHeight * index), wxSize(contentWidth, contentHeight)), wxALIGN_LEFT);
|
paint.DrawLabel(money, wxRect(wxPoint(middlePoint.x + contentWidth, middlePoint.y + lineHeight * index), wxSize(contentWidth, contentHeight)), wxALIGN_RIGHT);
|
|
// »æÖÆÂô
|
if (qu.ask_price > 0) {
|
// ÓÐÂò1
|
price = wxString(StringUtil::to_string(qu.ask_price).c_str());
|
money = StringUtil::to_string(qu.ask_price * qu.ask_volume / 10000, 1);
|
money.Append("Íò");
|
}
|
else {
|
price = "-";
|
money = "-";
|
}
|
|
paint.DrawLabel(price, wxRect(wxPoint(middlePoint.x, middlePoint.y - lineHeight * (index+1)), wxSize(contentWidth, contentHeight)), wxALIGN_LEFT);
|
paint.DrawLabel(money, wxRect(wxPoint(middlePoint.x + contentWidth, middlePoint.y - lineHeight * (index+1)), wxSize(contentWidth, contentHeight)), wxALIGN_RIGHT);
|
|
if (index == 0) {
|
paint.SetBrush(textColor);
|
paint.SetPen(textColor);
|
paint.DrawLine(wxPoint(middlePoint.x, middlePoint.y - (lineHeight-contentHeight)/2), wxPoint(middlePoint.x + width, middlePoint.y- (lineHeight - contentHeight) /2));
|
}
|
index += 1;
|
}
|
|
|
|
|
|
}
|
|
TickChart::TickChart(wxWindow* parent, wxWindowID id, int timeSpace, TIME_SPACE_TYPE timeSpaceType, wxString xStartTime, wxString xEndTime, const wxPoint& pos, const wxSize& size) : wxControl(parent, id, pos, size, wxBORDER_NONE), timeSpace(timeSpace), timeSpaceType(timeSpaceType), xStartTime(xStartTime), xEndTime(xEndTime), maxYRate(20), limitUpRate(20), preClosePrice(10.00)
|
{
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
mousePos = wxPoint(-1, -1);
|
Bind(wxEVT_PAINT, &TickChart::OnPaint, this);
|
Bind(wxEVT_MOTION, &TickChart::OnMouseMove, this);
|
Bind(wxEVT_LEFT_DCLICK, &TickChart::OnDoubleClick, this);
|
mouseDC = new wxClientDC(this);
|
this->xStartTimeSeconds = TickDataUtil::timeStrToSeconds(xStartTime);
|
this->xScopeSeconds = TickDataUtil::tradTimeSub(xEndTime, xStartTime);
|
cout << xStartTime << "==>" << xEndTime << ":" << this->xScopeSeconds << endl;
|
// ³õʼ»¯Êý¾Ý
|
cbCodeInfo = { "","",0 };
|
underlyingCodeInfo = { "","",0 };
|
underlyingScale = 1.0f;
|
}
|
|
void TickChart::OnPaint(wxPaintEvent& event)
|
{
|
try {
|
wxBufferedPaintDC dc(this);
|
PrepareDC(dc);
|
/*wxMemoryDC dc;
|
wxBitmap bitmap(GetSize());
|
dc.SelectObject(bitmap);*/
|
|
dc.SetBackground(wxBrush(*wxBLACK));
|
// »æÖƱ³¾°
|
dc.SetPen(*wxBLACK);
|
dc.SetBrush(*wxBLACK);
|
dc.DrawRectangle(GetClientRect());
|
dc.SetTextForeground(*wxBLACK);
|
// »æÖÆ×ø±êÖá
|
dc.SetPen(COLOR_RED);
|
wxRect rect = GetClientRect();
|
|
// »æÖÆ×ø±êÖá
|
drawAxis(dc, rect);
|
// »æÖÆÃû³Æ
|
if (cbCodeInfo.codeName.Length() > 0) {
|
dc.SetTextForeground(COLOR_RED_TEXT);
|
|
wxSize s = dc.GetTextExtent(cbCodeInfo.codeName);
|
|
dc.DrawText(cbCodeInfo.codeName, wxPoint((rect.GetWidth() - s.GetWidth()) / 2, 0));
|
}
|
|
drawTickLine(dc, rect);
|
|
drawUnderlyingTickLine(dc, rect);
|
|
// »æÖƳɱ¾Ïß
|
TickData costRateInfo;
|
costRateInfo.rate = costRate;
|
costRateInfo.time = 0;
|
costRateInfo.price = "0.00";
|
wxPoint startPoint = convertTickDataToPoint(costRateInfo, rect);
|
costRateInfo.time = xScopeSeconds;
|
wxPoint endPoint = convertTickDataToPoint(costRateInfo, rect);
|
|
wxPen dashedPen(COLOR_YELLOW, 1, wxPENSTYLE_DOT);
|
dc.SetPen(dashedPen);
|
dc.DrawLine(startPoint, endPoint);
|
|
|
|
// »æÖÆÂòÂôµã
|
for (std::list<TickTradeData>::iterator e = buyPoints.begin(); e != buyPoints.end(); e++) {
|
if (TickDataUtil::tradTimeSub((*e).time, xStartTime) < 0) {
|
continue;
|
}
|
if (TickDataUtil::tradTimeSub((*e).time, xEndTime) > 0) {
|
continue;
|
}
|
TickTradeData info = *e;
|
wxPoint point = convertTickDataToPoint(*e, rect);
|
wxColour color;
|
switch (info.colorType)
|
{
|
|
case 1:
|
color = wxColor(232, 62, 37);
|
break;
|
case 3:
|
color = wxColor(106, 254, 193);
|
break;
|
default:
|
color = wxColor(232, 62, 37);
|
break;
|
}
|
dc.SetPen(color);
|
dc.SetBrush(color);
|
drawBuyPoint(dc, point);
|
}
|
|
for (std::list<TickTradeData>::iterator e = sellPoints.begin(); e != sellPoints.end(); e++) {
|
if (TickDataUtil::tradTimeSub((*e).time, xStartTime) < 0) {
|
continue;
|
}
|
if (TickDataUtil::tradTimeSub((*e).time, xEndTime) > 0) {
|
continue;
|
}
|
wxPoint point = convertTickDataToPoint(*e, rect);
|
drawSellPoint(dc, point);
|
}
|
drawMouse(dc, rect, mousePos);
|
|
drawBuyAndSellPointInfo(dc, rect, mousePos);
|
|
drawUnderlyingLatestRate(dc, rect);
|
|
drawUnderlyingQuotes(dc, rect);
|
//pdc.Clear();
|
// ²ÉÓÃË«»º´æ»æÖÆ
|
|
//pdc.Blit(0, 0, GetSize().GetWidth(), GetSize().GetHeight(), &dc, 0, 0);
|
// ¸üнçÃæ
|
Update();
|
|
}
|
catch (...) {
|
}
|
}
|
|
void TickChart::OnMouseMove(wxMouseEvent& event)
|
{
|
event.Skip();
|
mousePos = event.GetPosition();
|
Refresh();
|
}
|
|
void TickChart::OnDoubleClick(wxMouseEvent& event)
|
{
|
event.Skip();
|
wxPoint point = event.GetPosition();
|
if (timeSpaceType != TIME_SPACE_BAR) {
|
return;
|
}
|
int x_time = -1;
|
int line_index = 0;// µÚ¼¸ÌõÏß
|
|
for (std::list<TickTradeData>::iterator e = buyPoints.begin(); e != buyPoints.end(); ++e) {
|
TickTradeData tick = *e;
|
wxPoint p = convertTickDataToPoint(tick, GetClientRect());
|
if (abs(p.x - point.x) < POINT_REDIUS && abs(p.y - point.y) < POINT_REDIUS) {
|
x_time = TickDataUtil::timeStrToSecondsAsAxisX(tick.time, xStartTimeSeconds);
|
line_index = 0;
|
break;
|
}
|
}
|
if (x_time < 0) {
|
for (std::list<TickTradeData>::iterator e = sellPoints.begin(); e != sellPoints.end(); ++e) {
|
TickTradeData tick = *e;
|
wxPoint p = convertTickDataToPoint(tick, GetClientRect());
|
if (abs(p.x - point.x) < POINT_REDIUS && abs(p.y - point.y) < POINT_REDIUS) {
|
x_time = TickDataUtil::timeStrToSecondsAsAxisX(tick.time, xStartTimeSeconds);
|
line_index = 0;
|
break;
|
}
|
}
|
}
|
|
if (x_time < 0) {
|
// ÅжÏË«»÷Êǵã»÷ÔÚÄĸùÏßÉÏ
|
for (std::list<TickData>::iterator e = underlyingTickDatas.begin(); e != underlyingTickDatas.end(); ++e) {
|
TickData tick = *e;
|
wxPoint p = convertTickDataToPoint(tick, GetClientRect());
|
if (abs(p.x - point.x) < 4 && abs(p.y - point.y) < 4) {
|
x_time = tick.time;
|
line_index = 1;
|
break;
|
}
|
}
|
}
|
|
|
if (x_time < 0) {
|
// ÅжÏË«»÷Êǵã»÷ÔÚÄĸùÏßÉÏ
|
for (std::list<TickData>::iterator e = cbTickDatas.begin(); e != cbTickDatas.end(); ++e) {
|
TickData tick = *e;
|
wxPoint p = convertTickDataToPoint(tick, GetClientRect());
|
if (abs(p.x - point.x) < 4) {
|
x_time = tick.time;
|
line_index = 0;
|
break;
|
}
|
}
|
}
|
|
|
|
if (x_time >= 0) {
|
// »ñÈ¡¸½½ü5·ÖÖÓµÄtickÐÅÏ¢
|
|
int start_time = x_time - 300;
|
int end_time = x_time + 300;
|
|
wxString start_time_str = TickDataUtil::axisXSecondsToTimeStr(start_time, xStartTimeSeconds);
|
cout << "tickÏêÇ飺" << start_time_str << endl;
|
if (TickDataUtil::tradTimeSub(start_time_str, "09:30:00") < 0) {
|
start_time_str = "09:30:00";
|
}
|
|
|
wxString end_time_str = TickDataUtil::axisXSecondsToTimeStr(end_time, xStartTimeSeconds);
|
cout << "tickÏêÇ飺" << end_time_str << endl;
|
if (TickDataUtil::tradTimeSub(end_time_str, "15:00:00") > 0) {
|
end_time_str = "15:00:00";
|
}
|
|
if (this->tickDetailCallback) {
|
if (line_index == 0) {
|
this->tickDetailCallback(cbCodeInfo, start_time_str, end_time_str, buyPoints, sellPoints, costRate, parentContext);
|
}
|
else if (line_index == 1) {
|
this->tickDetailCallback(underlyingCodeInfo, start_time_str, end_time_str, buyPoints, sellPoints, costRate, parentContext);
|
}
|
}
|
}
|
}
|
|
|
void TickChart::Init(CodeBasicInfo cb, CodeBasicInfo underlying, void* parentContext, std::function<void(CodeBasicInfo, wxString, wxString, std::list<TickTradeData>, std::list<TickTradeData>, float, void*)> tickCllback)
|
{
|
this->parentContext = parentContext;
|
this->tickDetailCallback = tickCllback;
|
this->cbCodeInfo = cb;
|
this->underlyingCodeInfo = underlying;
|
if (this->cbCodeInfo.code.Length() > 0) {
|
this->preClosePrice = cb.preClosePrice;
|
double limitUpPrice = std::round((cb.preClosePrice * 1.2) * 1000) / 1000;
|
double limitUpRate = std::round(((limitUpPrice - cb.preClosePrice) / cb.preClosePrice) * 10000) / 100;
|
this->maxYRate = limitUpRate;
|
this->maxYRateUnderlying = 0;
|
this->limitUpRate = limitUpRate;
|
}
|
else if (this->underlyingCodeInfo.code.Length() > 0) {
|
float rate = 1.2;
|
if (underlyingCodeInfo.code.find("00") == 0 || underlyingCodeInfo.code.find("60") == 0) {
|
rate = 1.1;
|
}
|
this->preClosePrice = underlyingCodeInfo.preClosePrice;
|
double limitUpPrice = std::round((underlyingCodeInfo.preClosePrice * rate) * 1000) / 1000;
|
double limitUpRate = std::round(((limitUpPrice - underlyingCodeInfo.preClosePrice) / underlyingCodeInfo.preClosePrice) * 10000) / 100;
|
this->maxYRate = limitUpRate;
|
this->maxYRateUnderlying = 0;
|
this->limitUpRate = limitUpRate;
|
}
|
|
|
this->cbTickDatas.clear();
|
this->underlyingTickDatas.clear();
|
this->buyPoints.clear();
|
this->sellPoints.clear();
|
this->costRate = 0;
|
|
//ÇëÇó֮ǰµÄÊý¾Ý
|
long now = TimeUtil::getNowTimeStamp();
|
string day = TimeUtil::format(now, "%Y-%m-%d");
|
string time = TimeUtil::format(now, "%H:%M:%S");
|
if (TickDataUtil::tradTimeSub(time, xStartTime) >= 0) {
|
if (TickDataUtil::tradTimeSub(time, xEndTime) > 0) {
|
time = xEndTime;
|
}
|
// 09:25Ö®ºó¿ªÊ¼ÀÊý¾Ý
|
string startTime = string("").append(day).append(" ").append(xStartTime);
|
string endTime = string("").append(day).append(" ").append(time);
|
if (TickDataUtil::tradTimeSub(startTime, endTime) < this->timeSpace) {
|
|
if (timeSpaceType == TIME_SPACE_BAR)
|
{
|
|
if (underlyingCodeInfo.code.Length() > 0)
|
{
|
// ÀÈ¡Õý¹ÉÊý¾Ý
|
DataArray<Bar>* tempUDatas = JueJinDataUtil::getBarDatas(JueJinDataUtil::getSymbol(underlyingCodeInfo.code.ToStdString()), startTime, endTime);
|
for (int i = 0; i < tempUDatas->count(); i++)
|
{
|
auto datas = tempUDatas->at(i);
|
if (datas.close < 0.0001) {
|
continue;
|
}
|
wxString time_str = TimeUtil::format((long)datas.bob, "%H:%M:%S");
|
float price = datas.open;
|
AddUnderlyingTickData(time_str, 100 * (price - underlyingCodeInfo.preClosePrice) / underlyingCodeInfo.preClosePrice,std::list<Quote>(), FALSE);
|
|
if (i == tempUDatas->count() - 1) {
|
// ¼ÓÔØBarÏßÎÞ·¨¸²¸ÇµÄʱ¼ä¶ÎÊý¾Ý
|
time_str = TimeUtil::format((long)datas.eob, "%H:%M:%S");
|
wxString tickStartTime = "";
|
tickStartTime.Append(day).Append(" ");
|
tickStartTime.Append(time_str);
|
DataArray<Tick>* tempTickDatas = JueJinDataUtil::getTickDatas(JueJinDataUtil::getSymbol(underlyingCodeInfo.code.ToStdString()), tickStartTime.ToStdString(), endTime);
|
|
for (int j = 0; j < tempTickDatas->count(); j++)
|
{
|
auto datas = tempTickDatas->at(j);
|
if (datas.price < 0.0001) {
|
continue;
|
}
|
|
std::list<Quote> quotes;
|
for (int n = 0; n < 5; n++) {
|
quotes.push_back(datas.quotes[n]);
|
}
|
AddUnderlyingTickData(TimeUtil::format((long)datas.created_at, "%H:%M:%S"), 100 * (datas.price - underlyingCodeInfo.preClosePrice) / underlyingCodeInfo.preClosePrice, quotes, FALSE);
|
}
|
}
|
|
|
}
|
Tick tick = JueJinDataUtil::currentTick(JueJinDataUtil::getSymbol(underlyingCodeInfo.code.ToStdString()));
|
std::list<Quote> quotes;
|
for (int n = 0; n < 5; n++) {
|
quotes.push_back(tick.quotes[n]);
|
}
|
AddUnderlyingTickData(TimeUtil::format((long)tick.created_at, "%H:%M:%S"), 100 * (tick.price - underlyingCodeInfo.preClosePrice) / underlyingCodeInfo.preClosePrice, quotes, FALSE);
|
}
|
|
if (cbCodeInfo.code.Length() > 0) {
|
//ÐèÒª´óÓÚtimeSpace²ÅÄÜÀÊý¾Ý
|
DataArray<Bar>* tempDatas = JueJinDataUtil::getBarDatas(JueJinDataUtil::getSymbol(cbCodeInfo.code.ToStdString()), startTime, endTime);
|
|
|
for (int i = 0; i < tempDatas->count(); i++)
|
{
|
auto datas = tempDatas->at(i);
|
if (datas.close < 0.0001) {
|
continue;
|
}
|
string time_str = TimeUtil::format((long)datas.bob, "%H:%M:%S");
|
float price = datas.open;
|
AddTickData(time_str, 100 * (price - preClosePrice) / preClosePrice, to_string(datas.close), FALSE);
|
if (i == tempDatas->count() - 1) {
|
// ¼ÓÔØBarÏßÎÞ·¨¸²¸ÇµÄʱ¼ä¶ÎÊý¾Ý
|
time_str = TimeUtil::format((long)datas.eob, "%H:%M:%S");
|
price = datas.close;
|
wxString tickStartTime = "";
|
tickStartTime.Append(day).Append(" ");
|
tickStartTime.Append(time_str);
|
DataArray<Tick>* tempTickDatas = JueJinDataUtil::getTickDatas(JueJinDataUtil::getSymbol(cbCodeInfo.code.ToStdString()), tickStartTime.ToStdString(), endTime);
|
|
for (int j = 0; j < tempTickDatas->count(); j++)
|
{
|
auto datas = tempTickDatas->at(j);
|
if (datas.price < 0.0001) {
|
continue;
|
}
|
AddTickData(TimeUtil::format((long)datas.created_at, "%H:%M:%S"), 100 * (datas.price - preClosePrice) / preClosePrice, to_string(datas.price), FALSE);
|
}
|
}
|
}
|
}
|
|
}
|
else if (timeSpaceType == TIME_SPACE_TICK) {
|
if (cbCodeInfo.code.Length() > 0) {
|
//ÐèÒª´óÓÚtimeSpace²ÅÄÜÀÊý¾Ý
|
DataArray<Tick>* tempDatas = JueJinDataUtil::getTickDatas(JueJinDataUtil::getSymbol(cbCodeInfo.code.ToStdString()), startTime, endTime);
|
for (int i = 0; i < tempDatas->count(); i++)
|
{
|
auto datas = tempDatas->at(i);
|
if (datas.price < 0.0001) {
|
continue;
|
}
|
AddTickData(TimeUtil::format((long)datas.created_at, "%H:%M:%S"), 100 * (datas.price - preClosePrice) / preClosePrice, to_string(datas.price), FALSE);
|
}
|
}
|
|
if (underlyingCodeInfo.code.Length() > 0) {
|
// ÀÈ¡Õý¹ÉÊý¾Ý
|
DataArray<Tick>* tempUDatas = JueJinDataUtil::getTickDatas(JueJinDataUtil::getSymbol(underlyingCodeInfo.code.ToStdString()), startTime, endTime);
|
for (int i = 0; i < tempUDatas->count(); i++)
|
{
|
auto datas = tempUDatas->at(i);
|
if (datas.price < 0.0001) {
|
continue;
|
}
|
AddUnderlyingTickData(TimeUtil::format((long)datas.created_at, "%H:%M:%S"), 100 * (datas.price - underlyingCodeInfo.preClosePrice) / underlyingCodeInfo.preClosePrice, std::list<Quote>(), FALSE);
|
}
|
}
|
|
}
|
}
|
}
|
Refresh();
|
}
|
|
void TickChart::AddTickData(wxString time, float rate, wxString price, bool refresh)
|
{
|
if (TickDataUtil::tradTimeSub(time, xEndTime) > 0) {
|
return;
|
}
|
|
int seconds = TickDataUtil::timeStrToSecondsAsAxisX(time, TickDataUtil::timeStrToSeconds(xStartTime));
|
if (!cbTickDatas.empty())
|
{
|
TickData last = cbTickDatas.back();
|
if (last.time > seconds) {
|
return;
|
}
|
if (seconds / this->timeSpace != last.time / this->timeSpace) {
|
cbTickDatas.push_back(TickData({ seconds , rate, price }));
|
}
|
else {
|
cbTickDatas.back().price = price;
|
cbTickDatas.back().time = seconds;
|
cbTickDatas.back().rate = rate;
|
cout << "×îºóÒ»ÌõÊý¾Ý£º" << cbTickDatas.back().time << "-" << cbTickDatas.back().price << endl;
|
}
|
|
float newMaxYRate = abs(rate) + MAX_RATE_FLOAT;
|
if (newMaxYRate > maxYRate) {
|
maxYRate = min(newMaxYRate, limitUpRate);
|
if (maxYRateUnderlying > 0) {
|
underlyingScale = maxYRate / maxYRateUnderlying / 2;
|
}
|
}
|
}
|
else {
|
cbTickDatas.push_back(TickData({ seconds , rate, price }));
|
if (abs(rate) < limitUpRate - MAX_RATE_FLOAT) {
|
maxYRate = abs(rate) + MAX_RATE_FLOAT;
|
if (maxYRateUnderlying > 0) {
|
underlyingScale = maxYRate / maxYRateUnderlying / 2;
|
}
|
}
|
}
|
if (refresh) {
|
Refresh();
|
}
|
}
|
|
void TickChart::AddUnderlyingTickData(wxString time, float rate, list<Quote> quotes, bool refresh)
|
{
|
if (TickDataUtil::tradTimeSub(time, xEndTime) > 120) {
|
return;
|
}
|
|
int seconds = TickDataUtil::timeStrToSecondsAsAxisX(time, xStartTimeSeconds);
|
if (!underlyingTickDatas.empty())
|
{
|
TickData last = underlyingTickDatas.back();
|
if (last.time > seconds) {
|
return;
|
}
|
if (seconds / this->timeSpace != last.time / this->timeSpace) {
|
underlyingTickDatas.push_back(TickData({ seconds , rate, "", quotes }));
|
}
|
else {
|
underlyingTickDatas.back().price = "";
|
underlyingTickDatas.back().time = seconds;
|
underlyingTickDatas.back().rate = rate;
|
underlyingTickDatas.back().quotes = quotes;
|
cout << "×îºóÒ»ÌõÊý¾Ý£º" << underlyingTickDatas.back().time << "-" << underlyingTickDatas.back().price << endl;
|
}
|
|
float newMaxYRate = abs(rate) + MAX_RATE_FLOAT;
|
if (newMaxYRate > maxYRateUnderlying) {
|
maxYRateUnderlying = min(newMaxYRate, limitUpRate);
|
}
|
}
|
else {
|
underlyingTickDatas.push_back(TickData({ seconds , rate, "", quotes }));
|
}
|
// Õý¹É²»¸¡¶¯
|
float newMaxYRate = abs(rate);
|
if (newMaxYRate > maxYRateUnderlying) {
|
maxYRateUnderlying = min(newMaxYRate, limitUpRate);
|
}
|
|
if (refresh) {
|
Refresh();
|
}
|
|
}
|
|
void TickChart::AddBuyPoint(wxString time, float rate, wxString price, int volume, wxString money, int type)
|
{
|
|
|
AddBuyPoint(TickTradeData({ time , rate, price,volume, money, type }));
|
}
|
|
void TickChart::AddBuyPoint(TickTradeData data)
|
{
|
buyPoints.push_back(data);
|
Refresh();
|
}
|
|
void TickChart::SetBuyPoint(std::list<TickTradeData> datas)
|
{
|
buyPoints.clear();
|
for (std::list<TickTradeData>::iterator e = datas.begin(); e != datas.end(); ++e) {
|
buyPoints.push_back(*e);
|
}
|
}
|
|
void TickChart::AddSellPoint(wxString time, float rate, wxString price, int volume, wxString money)
|
{
|
AddSellPoint(TickTradeData({ time , rate, price ,volume, money }));
|
}
|
|
void TickChart::AddSellPoint(TickTradeData data)
|
{
|
sellPoints.push_back(data);
|
Refresh();
|
}
|
|
void TickChart::SetSellPoint(std::list<TickTradeData> datas)
|
{
|
sellPoints.clear();
|
for (std::list<TickTradeData>::iterator e = datas.begin(); e != datas.end(); ++e) {
|
sellPoints.push_back(*e);
|
}
|
}
|
|
void TickChart::SetCostRate(wxString code, float rate, wxString price)
|
{
|
this->costRate = rate;
|
if (this->costRate > limitUpRate) {
|
this->costRate = limitUpRate;
|
}
|
Refresh();
|
}
|