祁 长 兴
(沈阳师范大学 软件学院, 沈阳 110034)
分时数据,是股票期货等金融领域以及生产生活中的面向过程控制等领域的实时数据,通过对大量分时数据做动态统计分析,可以掌握其分时波动规律,从而对产品的性质好坏做出判断,对数据发展趋势做出辅助预测。但是分时数据的数据量较大,产品种类繁多,通过画图可以直观反映指定时间段内数据的发展变化情况,更利于量化分析。
GDI(Graphics Device Interface Plus)是Windows系统下负责系统与绘图程序之间的信息交换的接口之一。本文采用了C#语言中的GDI以及GDI+技术,设计并实现了一个可以灵活拖拽在桌面窗口上的控件,以网络上开源的A股实时数据为实验测试数据,通过设置数据接口和显示图形属性设定来完成对分时数据的即时显示[1]。
根据控件的实现功能要求,控件在架构上分为数据的采集,数据的管理和处理,数据的图形加工处理3个模块。外部分时数据通过数据采集模块进入控件数据按时间点分布,包括商品代码,名称,时间点信息和时间点数据等,在模块内拆分为K线数据(4种)和普通商品线性数据(1个)。拆分后的数据进入数据管理模块,数据管理模块首先是管理所有的对象,包括商品对象、行情列表列对象以及画面对象,并提供相关数据的所有操作接口供程序内部管理数据使用,将相关分时数据保存在本地临时数据表中。数据图形加工模块对已经加工处理过的数据进行图形化显示[2-6]。
图1 控件的架构模型图Fig.1 Structure model of control
控件在设计上分为2大功能模块,图形绘制和数据管理(将数据采集和数据的管理设计在一起)。图形绘制的主要作用是按照控件初始化时给定的相关参数以及分时数据精确的绘制出图形,并且负责响应用户键盘和鼠标的输入操作。数据管理的主要作用是提供不同的数据类型并管理这些数据[7]。
数据管理类的主要作用是用于采集信息、存储信息和进行必要的信息处理,为图形绘制类提供必需的数据接口。
1) 数据管理类DataManager:该类中包含了3组对象:商品对象、行情列表列对象以及画面对象。该类提供的是这些类的添加、获取和更新接口,用于控件的所有对象的管理操作[11]。
2) 商品信息类Commodity:该类存储了关于该商品的所有数据,其中除了包括商品名称、商品代码、商品序号(记录添加的顺序,行情列表的默认排序字段)和昨日收盘价以外,还包括商品的行情列表数据,以及对应的线数据[12]。
3) 行情列表数据类ListSubItems:该类除了存储具体的数据值之外,还存储了绘制时的相关字段字体颜色和背景颜色。
4) 线数据管理对象LineDataManager:该类主要提供的就是所有线数据对象的数据添加接口,由于线数据根据不同的线类型对应的是不同的类,因此在添加数据时负责判断所属的线类型并添加对应的线数据对象。
5) 线数据抽象类LineData:该类是线数据的抽象类,所有类型的线数据均继承自该类。该类除了存储线数据基本单位对象以外,还提供了获取该线的最大值和最小值。其作用是在图形绘制时判断绘制区间的数据最大最小值范围。
6) 线数据基本单位类LineDataUnit:该类是线数据的基本单位抽象类,所有类型的线数据基本单位均继承自该类。其存储了2个基础数据即显示数据值以及日期时间。
7) 时间段类TimePeriod:该类提供了一个简单的接口来实现添加一个时间段的所有时间标签。
8) 控件对象StockChart:该类的作用是打包管理所有的管理对象和绘制对象,并对开发者提供统一的使用接口。
图形绘制结构的概念一共有2个:画面和区域。画面是构成基础界面的单位元素,从界面上看到的一个完整图像则是一个画面,区域是指每个画面中绘制不同类型的数据而划分的不同区间,其位置构成为从上到下,每个画面包含若干个区域。
1) 画面类ChartImage:指定了边框的宽度以及添加区域的方法和绘制画面时确定该画面中是否包含有柱线。
2) 区域类AreaClass:提供属性和方法,实例化一个具体的区域设置。
3) 线信息类LineInfoClass:定义一组相关的数据集合为一条线,如K线数据,该对象保存的是每一条线的相关参数,供数据管理对象实例化具体的线数据类,并提供对应的数据添加和更新方法。
4) 行情列表列对象类ListColumn:行情列表中每一列都有一个对应的对象来存储其相关的绘制属性,其中包括列名称、最小列宽度、列名称对齐方式、数据对齐方式、数据比较类型、是否可见、数据项字体颜色、是否在数据更新时改变背景色以及数据是否根据比较值显示涨跌符号等。
5) 行情列表绘制对象类ListClass:该类包含了所有行情列表的绘制方法及相关事件,是实际调用GDI接口及Graphics绘制对象绘制行情列表的类。
6) 分时走势图绘制对象类ChartClass:该类包含了所有走势图的绘制方法及相关事件,是实际调用GDI接口及Graphics绘制对象绘制走势图的类。
根据采集并处理过的数据信息分类,相应的图形的绘制包括行情列表的绘制和分时走势图的绘制[8-10]。
1) 基本功能
实现的功能主要是以列表的形式显示每一个股票的基础数据,控件提供了统一的配置接口,通过addColumn方法即可在行情列表添加一个列,同时也提供了ShowColumn和HideColumn方法以便在运行时动态的隐藏或显示某一列。图2为A股的行情列表图实例[12-15]。
图2 行情列表图Fig.2 Market list chart
2) 绘制算法
在行情列表中图形的绘制包括:全局重新绘制、绘制行选择线、绘制列调整线、列宽度调整、清空鼠标操作绘制层以及更新数据时背景变化[4]。
3) 绘制过程
该绘制过程主要分为2步:初始化画布及绘制数据。
初始化画布时,整个行情列表主要分为3个图层:数据层、鼠标操作标识层以及操作标志层。根据PictureBox容器大小初始化对应的Graphics对象及Bitmap对象。随后计算滚动条宽度,并根据画面高度和起始数据编号计算绘制的数据范围。
绘制数据时首先绘制行情列表列对象,从dataManager对象取到所有的列,逐个绘制。如果该列被设置为隐藏则不绘制跳过。其次判断如果该列在排序状态,则在列名称前绘制出排序方式,用箭头表示,整个一列都绘制蓝色的背景色。其中绘制行选择线的计算公式为
鼠标选择行index=鼠标点击Y坐标/单位行高度-1+rowStartIndex
(1)
3.2.1 基本功能
分时走势图功能的实现主要是依赖控件声明时对布局的定义。 在本设计的演示实例中, 分为2个画面, 一个是分时走势, 另一个是K线技术指标。 分时走势画面包含2个区域, 一个是实时走势, 另一个是对应时刻的成交量。K线技术指标也包含2个区域, 一个是K线数据, 另一个是对应的成交量。
3.2.2 绘制算法[16]
1) 坐标轴的绘制:对于纵坐标轴,系统定义了最小绘制行间隔。在绘制时首先根据控件尺寸高度计算出需要绘制的行数,横坐标轴单位是固定的,随着控件尺寸的变化决定的就是2个数据点的横向间隔。
2) 数据值的绘制:数据值绘制的重点是需要计算出该数据值在区域绘制的坐标中对应的坐标(X,Y)。其中横坐标的确定是由区间绘制的数据个数和绘制形态决定的,纵坐标是由区域最高值和最低值的相对比例决定的。
假设一个区域的宽度为Width,共n个数据,假设绘制的是第x个数据(1≤x≤n)的K线,横坐标区间为
[x×width/(n-1),x×width/n]
(2)
对于折线,其绘制横坐标为一个点,横坐标点为
(x×width/(n-1))
(3)
纵坐标的计算方式,假设绘制区域高度为Height,区域最大值为Max,最小值为Min,要绘制的数据Value的纵坐标Y计算公式
(4)
3) 柱形线的绘制
图3 K线图的结构Fig.3 Structure of K-line diagram
以K线为例,一个数据单元包含4个数据:开盘价、收盘价、最高价和最低价。一个K线的结构如图3。
假设收盘价高于开盘价,则3部分的纵坐标分别是:上影线:(最高价,收盘价),实体矩形:(收盘价,开盘价),下影线:(开盘价,最低价)。如果收盘价低于开盘价,则3部分的纵坐标分别是:上影线:(最高价,开盘价),实体矩形:(开盘价,收盘价),下影线:(收盘价,最低价)。
横坐标由定义的宽度决定,假设绘制区间宽度是spacing,定义的矩形宽度占中间的70%,两边各15%的间隔。因此横坐标计算如下:上影线和下影线:spacing÷2,实体矩形:spacing×0.15。据此加上该数据由公式(3)得出的全局横坐标即可。如图4,是商品“白云机场”的全局K线图[6-8]。
图4 全局K线图Fig.4 Global K-line diagram
本文主要论述了一个分时走势绘制控件的设计模型以及实现的算法和实现过程,实现的功能点主要参考了国内外主流的各大A股交易公司推出的行情软件。最终得到了一个能够在.NET平台上各种项目中快速嵌入的一个控件。该控件的接口设计合理,图形绘制准确,基本能满足用户读取数据和指标分析的要求。该代码生成的最终动态链接库只有80 KB左右,十分适合开发者进行二次开发。实现过程中,代码架构重构了多次,目的是为了尽可能的提高该控件的性能和美观度。由于GDI+本身的性能缺陷,在频繁的重绘操作下仍然可能出现CPU占用高的情况。亮点和创新点是对控件的适应能力,不论呈现设备尺寸大小如何,都能以准确美观的界面呈现用户想要的数据。另外接口定义十分灵活,不论用户的数据取自何处,都不会受控件接口的限制无法显示。不论是何种指标数据,都可以用统一的接口调用传入并绘制呈现。