最近比较忙,一直没时间写博客。今天我们就开讲股票模拟交易软件。
股票模拟交易软件主要包括以下几个部分:
1.股票行情服务
2.交易终端
3.柜台系统
4.中间件(我们的叫法,其实就是一个中间处理流程)
5.报盘系统
6.撮合系统
7.成交回报系统
8.清算系统
一、行情服务我们采用socket长连接的方式从行情服务器上获取数据
首先我们登录行情服务器:
if (userLogin.ChenckUserLoginConnection(serverAddress, serverPort, ref socket))
{
if (userLogin.SendLoginMessage(serverUsername, serverPassword, socket))
{
marketFlags = new MarketModel.TDFDefine_Data_MarketFlag[MarketModel.MarketFlagArrayLength];
loginAnswer = new MarketModel.TDFDefine_LoginAnswer();
if (userLogin.ReceiveLoginAnswer(ref loginAnswer, ref marketFlags, socket))
{
IOHelper.WriteLog("接收登陆信息成功");
//获取证劵代码列表
LstockModel = OfferStockCodeList(-1, ref errorMessage);
StockIndex stockIndex = new StockIndex();
//获取本日编号与股票代码索引表,建立行情空表
stockIndex.CreateStockIndexList(LstockModel, ref stockIndexLists_Code, ref immediateMarketModels, ref errorMessage);
//获取行情代码
OfferImmediateMarketModel(nReceiveDataFlags, 1012, ref errorMessage);
loginState = true;
errorMessage = 0;
}
else
{
IOHelper.WriteLog("接收登陆信息失败");
loginState = false;
errorMessage = 3;
}
}
else
{
IOHelper.WriteLog("发送登录请求失败");
loginState = false;
errorMessage = 2;
}
}
else
{
errorMessage = 1;
loginState = false;
IOHelper.WriteLog("连接行情服务器失败,请查看日志文件!");
}
}
然后填充我们需要的内存表
//获取证劵代码列表
LstockModel = OfferStockCodeList(-1, ref errorMessage);
StockIndex stockIndex = new StockIndex();
//获取本日编号与股票代码索引表,建立行情空表
stockIndex.CreateStockIndexList(LstockModel, ref stockIndexLists_Code, ref immediateMarketModels, ref errorMessage);
接下来我们获取行情:
OfferImmediateMarketModel(nReceiveDataFlags, 1012, ref errorMessage);
再看我们发送的行情数据请求
public int SendRequestMarketData(string MarketFlag, Socket socket, int nReceiveDataFlags)
{
MarketModel.TDFDefine_UnionMsgHeadMarketData unionMsgHeadMarketData = new MarketModel.TDFDefine_UnionMsgHeadMarketData();
unionMsgHeadMarketData.tmsg.sFlags = Convert.ToUInt16(MarketModel.TDFTELEFLAGS);
unionMsgHeadMarketData.tmsg.sDataType = Convert.ToUInt16(MarketModel.ID_TDFTELE_REQDATA);
unionMsgHeadMarketData.tmsg.nTime = 0;
unionMsgHeadMarketData.tmsg.nOrder = 0;
unionMsgHeadMarketData.trmd.chMarketFlag = MarketFlag;
unionMsgHeadMarketData.trmd.nFlags = nReceiveDataFlags == 1 ? MarketModel.ID_HDFDATAFLAGS_RETRANSALTE : 0x00;
unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_HDFDATAFLAGS_NOTRANSACTION;
unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_HDFDATAFLAGS_NOABQUEUE;
unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_HDFDATAFLAGS_NOINDEX;
//unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_HDFDATAFLAGS_NOMARKETOVERVIEW;
unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_TDFTELE_CLOSE;
//unionMsgHeadMarketData.trmd.nFlags |= DataDefine.ID_HDFDATAFLAGS_COMPRESSED; //启用数据压缩
byte[] nbyte = StructTransform.StructToBytes(unionMsgHeadMarketData);
int i = socket.Send(nbyte, nbyte.Length, 0);
return i;
}
有的朋友可能不知道为什么会有|=了,其实unionMsgHeadMarketData.trmd.nFlags |= MarketModel.ID_HDFDATAFLAGS_NOTRANSACTION;等价于
unionMsgHeadMarketData.trmd.nFlags = unionMsgHeadMarketData.trmd.nFlags | MarketModel.ID_HDFDATAFLAGS_NOTRANSACTION;
与运算符:同位上面00得0,01得1,11得1,采用与运算符来做能够节省控制变量。不同与运算符的请自己Google上查一下。
在获取行情服务器时还有一个特点就是我们采用了大量的结构体。为什么采用结构体呢?因为结构体的长度是确定的,这样我们在发送和接收行情数据就方便很多了。
我们来看看怎么取结构体的长度:
Marshal.SizeOf(typeof(MarketModel.TDFDefine_MsgHead))
获取结构体:MarketModel.TDFDefine_MsgHead 的长度
Marshal 记得要引入命名空间 System.Runtime.InteropServices;
我们再来看看下面几个方法:
方法一:
public static byte[] StructToBytes(object structObj)
{
//得到结构体的大小
int size = Marshal.SizeOf(structObj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(structObj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
方法二:
/// <summary>
/// byte数组转结构体
/// </summary>
/// <param name="bytes">byte数组</param>
/// <param name="type">结构体类型</param>
/// <returns>转换后的结构体</returns>
public static object BytesToStuct(byte[] bytes, Type type)
{
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes, 0, structPtr, size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, type);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return obj;
}
方法三:
public static byte[] BytesCopy(byte[] bytSource, byte[] bytTag, int startIndex, int size)
{
try
{
IntPtr bytePtr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytSource, 0, bytePtr, size);
Marshal.Copy(bytePtr, bytTag, startIndex, size);
Marshal.FreeHGlobal(bytePtr);
return bytTag;
}
catch { return null; }
}
方法四:
public static object BytesToStruct(byte[] structObj, int startIndex, int size, Type type)
{
IntPtr structPtr = Marshal.AllocHGlobal(size);
Marshal.Copy(structObj, startIndex, structPtr, size);
object obj = Marshal.PtrToStructure(structPtr, type);
Marshal.FreeHGlobal(structPtr);
return obj;
}
方法一:将结构体转化成为二进制数组
方法二:把二进制数组还原成为结构体
方法三:不用循环的方式拷贝数组中的部分
方法四:把二进制数组中的部分还原成为结构体
以上四个方法是常用的结构体和二进制数组中间的转化方法,有兴趣的朋友可以好好的了解一下。
本次先讲到这里吧。