股票模拟交易软件(一)待续

本文详细介绍了股票模拟交易软件的组成部分及其工作原理,重点讲解了行情服务模块如何通过socket长连接从行情服务器获取数据,包括登录认证过程、行情数据请求及处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近比较忙,一直没时间写博客。今天我们就开讲股票模拟交易软件。

股票模拟交易软件主要包括以下几个部分:

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;

与运算符:同位上面000011111,采用与运算符来做能够节省控制变量。不同与运算符的请自己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;

        }

方法一:将结构体转化成为二进制数组

方法二:把二进制数组还原成为结构体

方法三:不用循环的方式拷贝数组中的部分

方法四:把二进制数组中的部分还原成为结构体

以上四个方法是常用的结构体和二进制数组中间的转化方法,有兴趣的朋友可以好好的了解一下。

本次先讲到这里吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值