资源描述
案卷号
00001
日期
软件具体设计说明书(例)
作 者:
完成日期:
签 收 人:
签收日期:
修改情况统计:
版本号
修改同意人
修改人
安装日期
签收人
目录
1 引言 1
1.1 编写目标 1
1.2 范围 1
1.3 定义 1
1.4 参考资料 1
2 总体设计 1
2.1 需求要求 1
2.2 运行环境 2
2.3 基础设计概念和处理步骤 2
2.4 结构 2
2.5 功效需求和程序关系 2
2.6 人工处理过程 2
2.7 还未处理问题 3
3 接口设计 3
3.1 用户接口 3
3.2 外部接口 3
3.3 内部接口 3
4 运行设计 3
4.1 运行模块组合 3
4.2 运行控制 3
4.3 运行时间 4
5 系统数据结构设计 4
5.1 逻辑结构设计关键点 4
5.2 物理结构设计关键点 4
5.3 数据结构和程序关系 4
6 系统犯错处理设计 5
6.1 犯错信息 5
6.2 补救方法 5
6.3 系统维护设计 5
1 引言
1.1 编写目标
伴随证券交易电子化程度不停提升,券商对于多种业务提出了新要求,为了满足券商发展需求,愈加好为用户提供服务,现结合原有各版本证券交易软件优点和特点,开发一套采取Client/Server结构证券交易软件管理系统(SQL版)。本系统从底层给予优化,使整个系统运行速度得到较大提升,经过重新优化数据库内部结构,使系统可扩充性得到极大提升。
本说明书给出SQL版证券交易系统设计说明,包含最终实现软件必需满足功效、性能、接口和用户界面、隶属工具程序功效和设计约束等。
目标在于:
§ 为编码人员提供依据;
§ 为修改、维护提供条件;
§ 项目责任人将按计划书要求部署和控制开发工作全过程;
§ 项目质量确保组将按此计划书做阶段性和总结性质量验证和确定。
本说明书预期读者包含:
§ 项目开发人员,尤其是编码人员;
§ 软件维护人员;
§ 技术管理人员;
§ 实施软件质量确保计划专门人员;
§ 参与本项目开发进程各阶段验证、确定和负责为最终项目验收、判定提供对应汇报相关人员。
§ 合作各方相关部门复杂人;项目责任人和全体参与人员。
1.2 范围
说明:
a. 待开发软件系统名称:模拟股票交易系统
b. 列出本项目标任务提出者、开发者、用户和将运行该项软件单位。
1.3 定义
列出本文件中用到专门术语定义和缩写词原词组。
本汇报用到术语符合国家标准《软件工程术语(GB/T11475-1995)》。
1.4 参考资料
列出要用到参考资料,如:
a. 本项目标经核准计划任务书或协议、上级机关批文;
b. 属于本项目标其它已发表文件;
c. 本文件中各处引用文件、资料,包含所要用到软件开发标准。
列出这些文件标题、文件编号、发表日期和出版单位,说明能够得到这些文件资料起源。
2 总体设计
2.1 需求要求
说明对本系统关键输入输出项目、处理功效性能要求,具体说明可参见《需求分析说明书》。
2.2 运行环境
简明地说明对本系统运行环境(包含硬件环境和支持环境)要求,具体说明参见《需求分析说明书》。
§ 数据库服务器
飞跃Pro
内存128MB以上
硬盘9GB
100M 网卡
§ 应用服务器
飞跃Pro
内存64MB以上
硬盘4GB
100M 网卡
§ 网络配置
100M / 10M
§ 工作站(柜台)
P100以上
内存8MB以上
硬盘1G以上
100M/10M网卡
软件
§ 操作系统
Windows NT 4.0以上
§ 数据库管理系统
SQL Server
§ 相关软件工具
Windows NT Workstation/Windows NT server
Windows Professional/ Server
开发工具
§ 平台:Windows95/98、Windows NT、Windows
§ 开发工具:visual stidio sp1,C#.Net
测试环境
Windows31、Windows95/98、Windows NT、Windows
2.3 基础设计概念和处理步骤
说明本系统基础设计概念和处理步骤,尽可能使用图表形式。
营业部系统一共有四个对象,即用户、职员、市场和银行,市场概念是交易所细化,比如上海证券交易所A股和B股就是两个市场,有了市场概念我们就能够把交易所这个概念细化,并使同一个市场共性更突出。银行则经过银证转账业务介入,并成为营业部系统不可或缺组成部分。
上述四个对象经过部分业务步骤进行相互操作从而形成整个交易活动。所以整个系统模型能够表述为图2-1
设计时需要将营业部系统所使用多种信息分为描述四个对象信息和描述业务步骤信息。因为四个对象相对而言是一个稳定型信息,而业务步骤则较易改变,且营业部之间差异很大,所以应将四个对象尽可能定型,而将多种业务步骤尽可能做成组件,方便营业部可依据实际需求组装成适合自己系统。
依据以上思想,在设计对象模型时应充足考虑到可扩展性,尽可能做到抽象化、参数化,从而使对象需求改变时不致影响系统结构。
图 2.1
2.4 结构
用一览表及框图形式说明本系统系统元素(各层模块、子程序、公用程序等)划分,扼要说明每个系统元素标识符和功效,分层次地给出各元素之间控制和被控制关系。
本系统采取c/s模式3层结构
根据不一样会话来划分话能够分为3大系统模块
局域网
数据库
柜台管理
查询管理
报表管理
资金管理
数据转换
银证转账
委托服务
日终管理
系统管理
系统监控
接口处理子系统
系统维护子系统
图2-2 交易系统体系结构
用户端登陆模块:
最关键交易系统模块结构图以下:
股票信息公布
经过修改我认为每次由用户端每5秒去查询一次服务器更新信息不可取,因为这会加重服务端和用户端负担,尤其是服务器端运算。
修改后实现变更为:用户一开始登陆后取得一次服务器全部股票目前信息。而服务器端每次发生交易后,给每一个在线用户发送目前交易需要更新股票信息,这么就减轻了用户机和服务端信息
2.5 功效需求和程序关系
(该关系由需求分析汇报编写者依据结构图说明)
本条用一张以下矩阵图说明各项功效需求实现同各块程序分配关系:
获取并发送用户请求
绘制分时图
MD5加密解密
发送用户交易请求
接收并识别用户请求
调用数据层查询
撮合交易
服务器返回用户端信息
用户登陆
√
√
√
√
查看用户持仓
√
√
√
实时指数
√
√
√
交易委托
√
√
√
√
√
√
√
取消交易
√
√
√
√
√
√
2.6 人工处理过程
说明在本软件系统工作过程中不得不包含人工处理过程(假如有话)。
没有完成股票管理模块设计,所以股票必需从数据库后台添加
假如有新股发行,还必需添加相关股票交易队列
2.7 还未处理问题
说明在概要设计过程中还未处理而设计者认为在系统完成之前必需处理各个问题。
3 接口设计
3.1 用户接口
说明将向用户提供命令和它们语法结构,和软件回复信息。
向用户提供简单易用UI,和帮助文档。
用户端将提供以下功效
首先弹出用户登陆框,供用户输入用户名和密码
菜单项提供个股查询和分时图按钮
菜单栏下是选项卡,提供股票实时信息和个股分时图栏
提供用户交易界面和交易按钮和查看用户盈亏按键
3.2 外部接口
说明本系统同外界全部接口安排包含软件和硬件之间接口、本系统和各支持软件之间接口关系。
采取基于正确公开标准部件和技术以确保最大程度协作能力和和第三方系统和部件集成简便性。这类标准包含但不限于以下多个:
§ 网络协议和标准 (TCP/IP, HTTP, SSL, etc)
§ 语言(SQL, C#.net, etc.)
§ 数据库连接性(ADO。net)
3.3 内部接口
说明本系统之内各个系统元素之间接口安排。
逻辑层和数据访问层经过以经stockDataModel接口,来限定访问stockData类型数据
用户端经过调用buyStock(stockData)和sellStock(stockData)来访问逻辑层,在这个函数中包含了访问逻辑层接口dealTransaction(stockData)
经过AdoFactory访问不一样数据库
用户端登陆协议
D(二字节)+(用户名字长度)(4字节)+(用户名字)+(用户密码长度)(4字节)+(用户密码);
用户买卖协议
B(二字节)+(股票ID)(4字节)+(股票数量)(4字节)
S(二字节)+(股票ID)(4字节)+(股票数量)(4字节)
查询交易信息并返回给用户端
C(二字节)
具体有拆包解包类
using System;
using System.Collections.Generic;
using System.Text;
namespace ProjectCenterTradingSys
{
public class Protocal
{
private byte[] messagebuffer;
private byte[] messagelength;
public byte[] messagebag;
//该函数是将字符串转换为字节数组
public byte[] StringtoByte(string stringInfo)
{
messagebuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(stringInfo);
return messagebuffer;
}
//该函数将整型转换为个字节
public byte[] InttoByte(int number)
{
messagelength=BitConverter.GetBytes(number);
return messagelength;
}
//将浮点型转换为个字节
public byte[] DoubletoByte(double price)
{
byte[] pricebyte = BitConverter.GetBytes(price);
return pricebyte;
}
//合并一个字符串(字节数组)和她长度作为一个包
public byte[] Combinarray(byte[] messle, byte[] messinfo)
{
messagebag=new byte[messle.Length+messinfo.Length];
int index;
for (index = 0; index < messle.Length; index++)
messagebag[index] = messagelength[index];
for (int index1 = 0; index1 < messinfo.Length; index1++)
messagebag[index + index1] = messagebuffer[index1];
return messagebag;
}
//解包头
public byte[] BagHead(char head)
{
byte[] headbyte = BitConverter.GetBytes(head);
return headbyte;
}
//读包头
public char DeBagHead(byte[] buffer)
{
char headinfo = BitConverter.ToChar(buffer, 0);
return headinfo;
}
//该函数为解包信息为字符串!
public string deMessgeBag(byte[] Messagebag,int start,out int next)
{
next = BitConverter.ToInt32(Messagebag, start);
string message = System.Text.ASCIIEncoding.ASCII.GetString(Messagebag, start + 4, next);
return message;
}
4 运行设计
4.1 运行模块组合
说明对系统施加不一样外界运行控制时所引发多种不一样运行模块组合,说明每种运行所历经内部模块和支持软件。
4.2 运行控制
说明每一个外界运行控制方法方法和操作步骤。
4.3 运行时间
说明每种运行模块组合将占用多种资源时间。
5 系统数据结构设计
5.1 逻辑结构设计关键点
给出本系统内所使用每个数据结构名称、标识符和它们之中每个数据项、统计、文卷和系标识、定义、长度及它们之间层次或表格相互关系。
用户端类图:
windowForm:Form
Private:
userLogDialog
userNametextBox
userPasswordtextBox
userlogOKbotton
userlogCanselbutton
tabPage
MenuBar
stockRealtimeGraphitem
stock Quote Dialog
dataGridView
userBuyStockID
userBuyStockcount
userBuyStockprice
userBuyStockButton
...sell
userStocklistView
userStockLookButton
send Mesto Server(string Info)
//该函数用来向主机发送请求
协议U:发送用户名,密码
B:buy股票id,count,price,user
S: sell....
Q:查询信息
Receive MesFromServer()
(接上)
MD5encrypt(string)
//以下全部要经过sendMestoServer
//向主机发送信息
logOK_press(event,handle);
stockQuoteitem_press(e,h);
buyStockButton_press(e,h);
sellStockButton_press(e,h);
stocklookButton_press(e,h);
//////
该函数调用drawPicture画图
stockRealtimeGraphitem_press(e,h)
Class RealTime Graph
Private
stockID
//动态数组存放股票价格
ArrayList stockPrice[]
Public:
//在windowform类中recievemess
后更新目前价格,即在数组后添加一项最新价格
updatePrice(price,sotckPrice)
drawPicture(stockID,stockPrice)
Class stockData
订单号 public int ListID;
public int UsrID;
public string StockIndex;
public flout Price;
public int Count;
public bool Isbuy;
该类即为向服务端传送数据时包
服务器端
StockQueue
Private
stockData data
stockData next
Public
DeleteQueueHead();
AddStockData();
Class TradeService
该类还要补充若干个StockQueue类型组员变量
private void StartListening()
{
byte[] ipadre = new byte[] { 10, 82, 14, 47};
IPAddress ip=new IPAddress(ipadre);
m_Tcplisten = new TcpListener(ip,m_Port);
m_Tcplisten.Start();
while (true)
{
try
{
Socket s = m_Tcplisten.AcceptSocket();
clientSocket = s;
m_serverThread = new Thread(new ThreadStart(serviceClient));//多线程deal各个连接用户socket
m_serverThread.Start();
}
catch (Exception E)
{
Console.WriteLine(E.ToString());
}
}
}
如以上startlistening代码所表示,监听发明一个连接用户端套接字,再用多线程处理该连接,而服务器端则继续监听新套接字。
这么关键交易代码就能够放入ServiceClient这个函数中,当有新用户信息连入时,即可进行查询数据库,对比插入股票队列等工作
Class ClientInfo
//这个类统计了用户端socket
数据访问层类图
Class ADOSQLserver
Private
dataSet
//ds下可有4个dataTable
userTable
stockTable
User_stockTable
tempTable
Public:
////验证用户信息
Bool CheckUserlogin(string usridstring password);
Bool CheckUserMoney(string userID);
Bool CheckUserStockCount(string userID);
////交易成功修改用户和股票信息
Void updateUserTable(Class stockData)
Void updateStockTable(Class stockData)
Void updateUser_stockTable(Class stockData)
///还未成功交易放入临时表,
Void updateTemTable(Class stockData)
注意,每次交易成功要删除临时表信息
Void deleteInfo(Class stockData)
Class stockData
订单号 public int ListID;
public int UsrID;
public string StockIndex;
public int Prince;
public int Count;
public bool Isbuy;
该类即为向服务端传送数据时包
相关交易算法具体设计
5.2 撮合算法
在前文中,我们已经提到了,撮合算法是整个交易所乃至整个证券仿真系统关键部分。此算法成功是否,直接影响着仿真系统是否能实现和实现效率高低。
根据真实交易标准,撮合算法分为连续竞价和集中竞价两种方法。
下面我们将分别对这两种方法进行实现。
5.2.1 连续竞价
连续竞价是在绝大部分交易时间使用撮合算法。
连续竞价标准:
1.) 价格优先标准:价格较高买入申报优先于价格较低买入申报,价格较低卖出申报优先于价格较高卖出申报。
2.) 时间优先标准:同价位申报、依据申报时序决定优先次序,即买卖方向、价格相同,先申报者先于后申报者。前后次序按证券交易所主机接收申报时间确定。
在正常情况下,买队列第一笔(报价最高)报价一定小于卖队列第一笔(最低报价)报价。此时不发生撮合。一旦买卖队列价格发生了交叉,图2.3.1所表示,发生交叉那部分就会进行撮合。
而实际上,因为每一笔新来单子进入数列后全部会触发一次比较,所以每次触发撮合全部是由新单子促成。称为“来一笔撮合一次”,也就是连续竞价。
b
图2.3.1
连续竞价算法描述:
首先设定QueueStruct结构为元素买卖两个队列BuyQueue和SellQueue。
为了尽可能提升效率,降低资源占用,我们用静态数组构建这两个队列。
其中BuyQueue是时间优先、买价降序排序,而SellQueue是时间优先、卖价升序排序,在连续竞价条件下,能够确保BuyQueue[0]price小于SellQueue[0]price。
连续竞价算法以下:
1.) 接收一个新单子newlist,判定newlist是买单还是卖单;假如是买单,则转2,假如是卖单,则转B;
2.) 取卖单队列头SellQueue[0],if SellQueue[0].price>newlist.price,利用插入排序将newlist插入到买队列BuyQueue中,转1;
3.) if SellQueue[0].count>newlist.count,newlist完全撮合,SellQueue[0].count=SellQueue[0].count-newlist.count,转2;
4.) if SellQueue[0].count<=newlist.count,SellQueue[0]撮合,并将SellQueue[0]从SellQueue队列中删除,newlist.count=newlist.count-SellQueue[0].count,转2;
5.) 取买单队列头BuyQueue[0],if BuyQueue[0].price<newlist.price,利用插入排序将newlist插入到卖队列BuyQueue中,转1;
6.) if BuyQueue[0].count>newlist.count,newlist完全撮合,BuyQueue[0].count=BuyQueue[0].count-newlist.count,转1;
7.) if BuyQueue[0].count<=newlist.count,
BuyQueue[0]撮合, 并将BuyQueue[0]从BuyQueue队列中删除, newlist.count=newlist.count-BuyQueue[0].count,转5;
以下面步骤图5.2.2所表示:
图3.2.2
5.2.2 集合竞价
集合竞价是指对全部有效委托进行集中处理,深、沪两市集合竞价时间为交易日早晨9:15至9:25。
集合竞价标准:
§ 通常高于开盘价买单一定成交;
§ 通常低于开盘价卖单一定成交;
§ 通常高于开盘价卖单一定不成交;
§ 通常低于开盘价买单一定不成交;
集合竞价分四步完成:
第一步:确定有效委托在有涨跌幅限制情况下,有效委托是这么确定:
依据该只证券上一交易日收盘价和确定涨跌幅度来计算当日最高限价、 最低限价。有效价格范围就是该只证券最高限价、最低限价之间全部价位。
限价超出此范围委托为无效委托,系统作自动撤单处理。
第二步:系统依据竞价规则自动确定集合竞价成交价,这个价格就是当日开盘价, 全部高于开盘价买盘和全部低开开盘价卖盘均以此价格成交, 集合竞价成交价确定标准是:以此价格成交,能够得到最大成交量。
第三步:集中撮合处理全部买委托根据委托限价由高到低次序排列, 限价相同者根据进入系统时间前后排列;全部卖委托按委托限价由低到高次序排列 , 限价相同者根据进入系统时间前后排列。依序逐笔将排在前面买委托和卖委托配对成交,即根据"价格优先,相同价格下时间优先"成交次序依次成交,直至成交条件不满足为止,即不存在限价高于等于成交价叫买委托、或不存在限价低于等于成交价叫卖委托。
全部成交全部以同一成交价成交。 这同一成交价成交买卖单通常量全部是很大,图3.2.3所表示
图3.2.3所表示
第四步:行情揭示:
1.) 如该只证券成交量为零,则将成交价位揭示为开盘价、最近成交价、最高价、最低价,并揭示出成交量、成交金额。
2.) 剩下有效委托中,实际最高叫买价揭示为叫买揭示价,若最高叫买价不存在,则叫买揭示价揭示为空;实际最低叫卖价揭示为叫卖揭示价,若最低叫卖价不存在,则叫卖揭示价揭示为空。
集合竞价中未能成交委托,自动进入连续竞价。
根据这么标准和要求,我们设计了以下集合竞价撮合算法。图3.2.4所表示。
图3.2.4
集合竞价算法描述:
和连续竞价一样,首先设定QueueStruct结构为元素买卖两个队列BuyQueue和SellQueue。
为了尽可能提升效率,降低资源占用,我们用静态数组构建这两个队列。
其中BuyQueue是时间优先、买价降序排序,而SellQueue是时间优先、卖价升序排序。在开市到开盘这段时间内,买卖单已经分别进入了买卖队列内排好了序。
一旦宣告开盘,则触发集合撮合,以下:
§ 判定两队列是否全部不为空,如是,转2;如否,转21;
§ 判定BuyQueue[0].prince和SellQueue[0].prince之差,如大于等于0,转3:如小于0,转21;
§ 定义int i=j=0;M、N分别为买卖两队列非空元素个数;BOOL k;QueueStruct Buy=BuyQueue[0];Sell=SellQueue[0];Buy1;Sell1;转4;
§ 判定BuyQueue[i].prince和SellQueue[j].prince之差,如大于等于0,转5:如小于0,转14;
§ 判定Buy.count和Sell.count之差,如大于0,转6;如小于等于0,转9;
§ j++; k=true; Sell1.count=Sell.count;Sell.count=Sell.count+SellQueue[iSellQueue].count;转7;
§ 判定j是否小于N,如是,转4;如不是,转8;
§ 开盘价为BuyQueue[i].price;总成交量为Sell.count;统计成交数据及回报,并返回;
§ i++;k=false; Buy1.count=Buy.count;Buy.count=Buy.count+BuyQueue[i].count;转10;
§ 判定i是否小于M,如是,转4;如不是,转11;
§ 判定Buy.count和Sell.count之差,如小于0,转12;如等于0,转13;
§ 开盘价为SellQueue[j].price;总成交量为Buy.count;统计成交数据及回报,并返回;
§ 开盘价为(SellQueue[j].price+BuyQueue[i-1].price)/2;总成交量为sell.count;统计成交数据及回报,并返回;
§ 判定k值,如为true,转15;如为false,转18;
§ 判定Buy1.count和Sell1.count之差,如大于0,转16;如小于0,转17;
§ 开盘价为BuyQueue[i].price;总成交量为Sell1.count;统计成交数据及回报,并返回;
§ 开盘价为(SellQueue[j-1].price+BuyQueue[i-1].price)/2;总成交量为Sell1.count;统计成交数据及回报,并返回;
§ 判定Buy1.count和Sell.count之差,如小于0,转19;如等于0,转20;
§ 开盘价为SellQueue[j].price;总成交量为Buy1.count;统计成交数据及回报,并返回;
§ 开盘价为(SellQueue[j].price+BuyQueue[i-1].price)/2;总成交量为Buy1.count;统计成交数据及回报,并返回;
§ 开盘价为昨日收盘价,成交量为0;保留全部数据至开盘进入连续竞价撮合;
5.2.3 买卖队列排序
上面我们介绍了撮合算法关键部分,但实际上在撮合前后全部要对两个买卖队列进行一定插入和排列处理,这在整个算法中也是很关键部分。下面我们就来具体介绍一下。
对全部排列和插入我们考虑了效率问题以后,最终统一使用了二分插入排序法。
在单子进入队列时,我们首先统计出目前队列中非空数据个数,然后再经过新单子和目前队列中间值价格比较,确定新单子在队列前半部分还是后半部分,然后再取该区域中间值和之比较,直到确定新单子应在位置。
以下列代码所表示:
int low=0; int high=N-1; //N为队列中非空元素个数
while(low<=high)
{
int m=(low+high)/2;
if(newlist->price<SellQueue[m].price)
high=m-1;
else low=m+1;
}
for(int i=N-1; i>=high+1; --i)
{
SellQueue[i+1]=SellQueue[i];
}
SellQueue[high+1]=*newlist;
这是卖队列排序,对于买队列排序和之相同,只是价格排列是由高到底。在这里不再赘述。
这种插入排序方法完全符合了撮合算法中价格优先、时间优先要求,而且效率也是比较高。
在集合竞价前和连续竞价后进行插入排序全部是这么进行,而在集合竞价撮合以后,对两队列重新排列,我们首先使用了memset函数将前面已全部成交t个元素清空,然后将t到N(原
展开阅读全文