先做一个练习。
这是用tick生成K线的练习。
这里的tick是程序随机生成的。
通过这个练习,了解K线生成的逻辑。
练习效果如下图所示(加了一些花活儿,实盘所用的K线比这简单)。
下面是练习步骤。
一、在VisualStudio中新建项目“BarsTest”,这是一个C#的窗体程序,主窗体为“Form1.cs”,尺寸为576×540。
二、在该窗体中放一个PictureBox,命名为“panel”,尺寸为528×468。
三、将“Form1.cs”的源码修改为如下内容:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace BarsTest
{
public partial class Form1 : Form
{
int intvTicks = 60; //K线每分钟多少个tick
int blackAlpha = 255; //黑色的不透明度
int blackAlphaAdd = -45; //黑色不透明度的增量
int whiteAlpha = 255; //白色的不透明度
int whiteAlphaAdd = -30; //白色不透明度的增量
string whiteText1 = String.Empty; string whiteText2 = String.Empty; //白色文字
int panelWidth = 0; int panelHeight = 0; //画板尺寸
int leftMargin = 10; int rightMargin = 10; int topMargin = 0; int bottomMargin = 20; //画板边距
int diagramWidth = 0; int diagramHeight = 0; //K线组显示尺寸
int barWidth = 16; //K线宽度
int barsIntv = 0; //K线间距
int barsCount = 0; //画板中的K线数量
int startPrice = 5000; //初始价格
int uppLP = 10000; //涨停价
int lowLP = 2000; //跌停价
List<Bar> bars = new List<Bar>(); //K线组
bool newBarCreated = false; //画板上显示K线以后是否产生过新K线
int barsN = 0; //K线数量
int startI = 0; //画板中最左端K线的序号
int barsH = 0; //画板中所有K线的最高价
int barsL = 1000000; //画板中所有K线的最低价
double barsPP = 1; //画板纵向密度(价格每变化1,高度变化多少像素)
double endBarX = 0; //飞起来的末端K线左边的横坐标
int endBarInitialX = 0; //固定的末端K线左边的横左边
double endBarY = 0; //飞起来的末端K线开盘价的纵坐标
int endBarInitialY = 0; //固定的末端K线开盘价的纵坐标
int lastHY = 0; //上一根末端K线最高价的纵坐标
int lastLY = 0; //上一根末端K线最低价的纵坐标
int shiftDirectionX = 1; int shiftDirectionY = -1; double shiftX = 1.5; double shiftY = 1.5; //关于末端K线移动的几个变量
bool backHome = false; //末端K线是不是正在返回起点
Color barColor = Color.FromArgb(100, 100, 120); //K线默认颜色
Color endBarColor = Color.FromArgb(255, 120, 120, 120); //末端K线颜色
Color triColorB = Color.DarkOrange; //买条件单颜色
Color triColorS = Color.DarkSeaGreen; //卖条件单颜色
Color longCostColor = Color.Magenta; //多仓成本线颜色
Color shortCostColor = Color.Cyan; //空仓成本线颜色
Color orderColor = Color.White; //挂单线颜色
int endBarAlpha = 255; //末端K线不透明度
int brightestIndex = 0; //除了末端K线,最亮的K线的序号
int addBrightestIndexIntv = 2; //行情走多少个tick加一次brightestIndex
int addBrightestIndexTicks = 0; //最近干这事时的timer3Ticks
int textID = 4; //飞起来的K线的文字的序号
int winY = 0; //刚刚止盈的价格的纵坐标
List<Order> orderInfo = new List<Order>(); //挂单信息
int maxOrdID = 3; //最大报单序号
int vol = 0; //持仓手数
double cost = 0; //持仓平均成本
int stopPrice = 0; //止损价
int initialStopPrice = 0; //初始止损价
int stopStep = 3; //止损步长
int stepBH = 0; //有关买入后最高价的步长
int stepSL = 0; //有关卖出后最低价的步长
int assignedDirection = 0; //指定的开仓方向(防止同向反复开仓)
int deals = 0; //交易次数
int floatProfit = 0; //浮盈
int closeProfit = 0; //平盈
int maxProfit = 0; //盈利峰值
int maxReduce = 0; //最大回撤
int winLots = 0; //盈利手数
int winAmt = 0; //盈利交易的总额
int lossLots = 0; //亏损手数
int lossAmt = 0; //亏损交易的总额
public Form1()
{
InitializeComponent();
panelWidth = panel.Width; panelHeight = panel.Height; //测量画板尺寸
topMargin = panelHeight / 5; //预先确定画板上方空白高度
diagramWidth = panelWidth - leftMargin - rightMargin; //算出画板画图部分的宽度
diagramHeight = panelHeight - topMargin - bottomMargin; //算出画板画图部分的高度
timer2 = new System.Windows.Forms.Timer();
timer2.Interval = 250;
timer2.Tick += new System.EventHandler(this.timer2_Tick);
timer2.Stop();
timer3 = new System.Windows.Forms.Timer();
timer3.Interval = 25;
timer3.Tick += new System.EventHandler(this.timer3_Tick);
timer3.Stop();
#region //创建每分钟intvTicks个tick的K线N根
updateBarsCount(); //根据画板宽度、已经预先确定的K线宽度,算出画板可容纳多少根K线
barsN = randomInt(5, 10); //随机确定初始K线根数
if (barsN > barsCount) startI = barsN - barsCount; //初始K线根数不能超过画板容量
int O = startPrice; //这是首根初始K线的开盘价,由此开始计算每根初始K线的开、高、低、收
for (int i = 0; i < barsN; i++) //对每根初始K线来说
{
int change = 0; //在每分钟的intvTicks个tick中,每个tick的价格变化跳数在-1~1间取随机数,change是这1分钟的总数
int maxAdd = 0; //这期间,价格增加的跳数最多曾达到多少
int maxMinus = 0; //这期间,价格减少的跳数最多曾达到多少
for (int j = 0; j < intvTicks; j++) //遍历这intvTicks个tick
{
change += randomInt(-1, 1); //价格随机变化
if (O + change > uppLP) change = uppLP - O;
else if (O + change < lowLP) change = lowLP - O;
if (change > maxAdd) maxAdd = change;
if (change < maxMinus) maxMinus = change;
}
int H = O + maxAdd; //当前K线的最高价
int L = O + maxMinus; //当前K线的最低价
int C = O + change; //当前K线的收盘价
Bar bar = new Bar();
bar.O = O; bar.H = H; bar.L = L; bar.C = C;
bars.Add(bar); //将当前K线添加到K线组中
O = C; //当前K线的收盘价成为下一根K线的开盘价
}
updateBarsMainHLPP(); //更新画板上的最高价、最低价、纵向密度
endBarInitialX = leftMargin + (barsN - 1 - startI) * barsIntv;
endBarX = endBarInitialX;
endBarInitialY = priceToY(bars.Last().O);
endBarY = endBarInitialY;
brightestIndex = barsN - 2; //初始化有关画图的一些数据
refreshPanel(); //刷新画板
#endregion
timer2.Start();
}
/*更新画板可容纳的K线数量*/
void updateBarsCount()
{
barsIntv = (int)(Math.Ceiling((double)barWidth * 1.4)); //根据预设的K线宽度,计算K线间距
barsCount = diagramWidth / barsIntv; //根据画板宽度和K线间距,算出画板可容纳的K线数量
if (barsCount < 2) barsCount = 2;
}
/*更新画板上的最高价、最低价、纵向密度*/
void updateBarsMainHLPP()
{
int highest = 0; int lowest = 1000000;
int i = startI; while (i < barsN) //遍历画板上的K线,找出其最高价、最低价
{
int thisH = bars[i].H; if (thisH > highest) highest = thisH;
int thisL = bars[i].L; if (thisL < lowest) lowest = thisL;
i++;
}
barsH = highest; barsL = lowest;
updateBarsMainPP(); //计算纵向密度
}
/*更新纵向密度*/
void updateBarsMainPP()
{
int HL = barsH - barsL;
if (HL >= 0)
{
if (HL == 0) HL = 1;
barsPP = (double)diagramHeight / (double)HL; //纵向密度=画板高度 / 画板内K线所占高度
}
}
/*将价格转换为画板上的纵坐标*/
int priceToY(double price)
{
return topMargin + (int)(barsPP * (barsH - price));
}
/*根据价格price与末端K线开盘价的差距,计算price的纵坐标*/
int endPriceY(int price)
{
int endBarO = bars.Last().O; //末端K线的开盘价
int priceChange = endBarO - price; //price与末端K线开盘价的差距
int yChange = (int)(barsPP * priceChange); //price的纵坐标与末端K线开盘价纵坐标的差距
return (int)(endBarY + yChange); //price的纵坐标
}
/*刷新画板*/
void refreshPanel()
{
Bitmap bitmap = new Bitmap(panelWidth, panelHeight);
Graphics draw = Graphics.FromImage(bitmap);
Font font; SolidBrush fontBrush; int fontX; PointF point; string text;
draw.FillRectangle(new SolidBrush(panel.BackColor), 0, 0, panelWidth, topMargin); //抹去原来的图像
if (blackAlphaAdd != 0)
{
font = new Font("Georgia", 16); //“Ticks”
fontBrush = new SolidBrush(Color.FromArgb(whiteAlpha, 255, 160, 0));
point = new PointF(346, 178);
draw.DrawString("Ticks", font, fontBrush, point);
SolidBrush blackBrush = new SolidBrush(Color.FromArgb(blackAlpha, 0, 0, 0));
draw.FillRectangle(blackBrush, 343, 178, 138, 58);
}
if (whiteAlpha > 0)
{
if (!String.IsNullOrEmpty(whiteText1))
{
font = new Font("Arial", 12); //“to”
fontBrush = new SolidBrush(Color.FromArgb(whiteAlpha, 160, 160, 160));
point = new PointF(350, 220);
draw.DrawString(whiteText1, font, fontBrush, point);
}
if (!String.IsNullOrEmpty(whiteText2))
{
font = new Font("Arial", 20, FontStyle.Bold); //“Bars”
fontBrush = new SolidBrush(Color.FromArgb(whiteAlpha, 255, 255, 255));
point = new PointF(370, 211);
draw.DrawString(whiteText2, font, fontBrush, point);
}
}
int i = startI; while (i < barsN)
{
int barX; int endGrayBarX = -1;
Bar thisBar = bars[i];
int openY; int highY; int lowY; int closeY;
int endGrayOpenY = -1; int endGrayHighY = -1; int endGrayLowY = -1; int endGrayCloseY = -1;
if (timer3Ticks < 80 || i < barsN - 1) //固定K线坐标
{
barX = leftMargin + (i - startI) * barsIntv;
openY = priceToY(thisBar.O); highY = priceToY(thisBar.H);
lowY = priceToY(thisBar.L); closeY = priceToY(thisBar.C);
}
else //飞起来的K线的坐标
{
barX = (int)endBarX;
openY = (int)endBarY; highY = endPriceY(thisBar.H);
lowY = endPriceY(thisBar.L); closeY = endPriceY(thisBar.C);
endGrayBarX = leftMargin + (i - startI) * barsIntv;
endGrayOpenY = priceToY(thisBar.O); endGrayHighY = priceToY(thisBar.H);
endGrayLowY = priceToY(thisBar.L); endGrayCloseY = priceToY(thisBar.C);
}
Color thisBarColor;
if (i < barsN - 1) //历史K线颜色
{
int R = barColor.R; int G = barColor.G; int B = barColor.B;
double p = 1 / (double)3;
double d = 1 / Math.Pow(absInt(i - brightestIndex) + 1, p);
R = (int)(d * R); G = (int)(d * G); B = (int)(d * B);
thisBarColor = Color.FromArgb(R, G, B);
//thisBarColor = barColor;
}
else //末端K线颜色
{
thisBarColor = endBarColor;
}
#region //K线样式
string style;
if (closeY < openY) style = "Stroke"; //阳线
else if (closeY > openY) style = "Fill"; //阴线
else style = "Line"; //星线
#endregion
#region //末端固定K线和飞起来的文字
if (endGrayBarX >= 0)
{
Color endGrayBarColor;
int limit = 80;
if (endBarColor.R == 255) limit = 120;
if (endBarAlpha >= 255) endGrayBarColor = Color.FromArgb(limit, endBarColor.R, endBarColor.G, endBarColor.B);
else endGrayBarColor = Color.FromArgb(maxInt(limit, 255 - endBarAlpha), endBarColor.R, endBarColor.G, endBarColor.B);
int endGrayMinOC = minInt(endGrayOpenY, endGrayCloseY);
int endGrayMaxOC = maxInt(endGrayOpenY, endGrayCloseY);
Pen endGrayPen = new Pen(endGrayBarColor, 1);
SolidBrush endGrayBrush = new SolidBrush(endGrayBarColor);
draw.DrawLine(endGrayPen, endGrayBarX + barWidth / 2, endGrayHighY, endGrayBarX + barWidth / 2, endGrayMinOC);
if (style.Equals("Stroke")) draw.DrawRectangle(endGrayPen, endGrayBarX, endGrayMinOC, barWidth, endGrayMaxOC - endGrayMinOC);
else if (style.Equals("Fill")) draw.FillRectangle(endGrayBrush, endGrayBarX, endGrayMinOC, barWidth, endGrayMaxOC - endGrayMinOC);
else if (style.Equals("Line")) draw.DrawLine(endGrayPen, endGrayBarX, endGrayOpenY, endGrayBarX + barWidth, endGrayOpenY);
draw.DrawLine(endGrayPen, endGrayBarX + barWidth / 2, endGrayMaxOC, endGrayBarX + barWidth / 2, endGrayLowY);
font = new Font("Arial", 9);
fontBrush = new SolidBrush(endBarColor);
if (textID == 0)
{
if (timer3Ticks < 100)
{
point = new PointF(barX - 45, maxInt(0, highY));
text = "让";
}
else if (timer3Ticks < 115)
{
point = new PointF(barX - 45, maxInt(0, highY));
text = "让K";
}
else if (timer3Ticks < 130)
{
point = new PointF(barX - 45, maxInt(0, highY));
text = "让K线";
}
else if (timer3Ticks < 150)
{
point = new PointF(barX - 45, maxInt(0, highY));
text = "";
}
else if (timer3Ticks < 165)
{
point = new PointF(barX - 75, maxInt(0, highY));
text = "再";
}
else if (timer3Ticks < 180)
{
point = new PointF(barX - 75, maxInt(0, highY));
text = "再飞";
}
else if (timer3Ticks < 195)
{
point = new PointF(barX - 75, maxInt(0, highY));
text = "再飞一";
}
else if (timer3Ticks < 230)
{
point = new PointF(barX - 75, maxInt(0, highY));
text =