股票交易系统

效果展示,如下动图:

首先简述一下股票交易规则:

  • 买卖股票,股民可以自行选择股票的买入或卖出价格和股票的数量,但是用户不一定马上就交易成功,只有当股票价格低于买入价才有机会买入,高于卖出价才有机会卖出。所以需要做委托处理。所以我们通常说的是委托下单,而不是说下单。
  • 股票买卖以100股为单位,100股也称为1手。
  • 同一股票可以多次买入,可以多次卖出,但是卖出的总股数不能高于买入的总股数,并且需要在资金的支持下才能进行。
  • 在交易时间的时候,股票的价格是随时波动的
  • 等待交易的卖家的委托价格都要比现价高,等待交易的买家的委托价格都要比现价低,
  • 红色(涨):当股票价格比前一交易日的收盘价高时,通常用红色来表示涨幅。
  • 绿色(跌):当股票价格比前一交易日的收盘价低时,通常用绿色来表示跌幅。
  • 细节:如果股票原来为红色,股票下跌却又没有接到前一天的收盘价低的时候,会变为绿色,然后又转变为红色。绿股同理。
  •  然后还有总资产与可用资产的区别,总资产不一定都可用。
  • 可用资产:可用资产是总资产减去委托买入所损耗的金额,无论交易成功与否
  • 总资产:   总资产是在交易成功之后,各股的涨幅买入金额的上可用资产的值
  • 仓位:指的是的支出资金占总资金的百分比,并且指的是交易成功之后的支出资金,而不是委托状态的支出资金。
  • 盈率:盈率=(成本价-现价)/成本价*100% 只有在交易成功后才涉及到,并且会随着现价的波动而波动。
  • 总盈率:用户买入股票后单股的盈率之和。
  • 系统功能:

        1.程序启动后给用户提供100w的资金,进行模拟炒股

        2.用户可以通过输入股票代码查看股票详细信息

        3.用户可以对股票进行买进,卖出

        4.查看委托状态操作

        5.查看仓库状态操作

        6.用户可以对自选股进行增删查

        7.用户可以通过键盘上的上下键来控制“→”选择操作,回车键确认

        8.用户可以对股票进行排序,通过键盘上的上下键来控制“↓ ↓”选择需要排序的数据,提供现价和涨幅的排序,回撤一次,股票进行降序排序,再次回撤进行升序排序,循环往复直到把箭头移走。

设计概要:

1.窗口以及光标设置

        1.1.设计合适的窗口大小

        1.2.设置窗口名称

        1.3.封装光标隐藏函数和光标显示函数

        1.4.封装光标坐标设置函数--SetPos

2.股票信息获取 

        因为股票信息量一些大,所以我们可以通过先把它写到文件里然后从文件里读取,该片文章的股票信息参考是6月9日的股票市场。

3.交易页面设计

        我们可以做一个小区间内的随机数让股票价格在一个小范围内波动,该系统比较简洁,就不去考虑股票前一天的收盘价了,我们直接以现价与开盘价做比较,现价高于开盘价就呈现红色,反之为绿色。并且提供菜单。

4.主逻辑

        设计两条线程,一条做股票的界面展示,一条做背后的主逻辑,我在在这简称为打印线和逻辑线,逻辑线接收用户的信息并做处理,并反馈给打印线(通过共用资源)。两个线程互相配合完成工作。

5.头文件的声明

        5.1.股票信息:使用用一个结构体来维护,成员包括,股票名称,股票代码,开盘价,现价,涨幅等等。

        5.2.委托状态:设计到状态的处理,使用枚举类型更为适应,成员包括,未委托,委托待买入,委托待卖出,买入交易成功,卖出交易成功。

        5.3.持股信息:持股信息使用结构体来维护更为适应,并且成员需要有股票信息,然后做成链表。

        5.4.仓库信息: 使用结构体维护成员包括:总资产,可用资产,总盈率,仓位和持股信息

        5.6头文件的部分声明:

编译环境的配置 

一、手搓股票信息

        股票信息量比较大,考虑把它放到文件里,然后运行程序的时候再从仓库里取,如下:

        每一支股票的信息我们用一个结构体类型来储存,然后再把所有股票放在一个结构体数组里面,因为涉及到股票信息在整个工程中会被频繁使用,所以把它做成全局变量,数组成员个数size随时会改变,所以用一个#define在头文件定义。要注意以下一点:

 向文件里读取信息:

	FILE* pf = fopen("stcok.txt", "r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	for (int i = 0; i < size; i++)
	{
		fscanf(pf, "%s %s %f %f %f %f %f %f %f %f %f %f",
			arr[i].name, arr[i].code, &arr[i].price, &arr[i].begin,
			&arr[i].max, &arr[i].min, &arr[i].amount, &arr[i].rate,
			&arr[i].sumh, &arr[i].money, &arr[i].value, &arr[i].ratio);
	}

二、交易页面设计

1.函数封装 

 color函数,SetPos函数,FCursor函数,OCursor函数的封装:

void FCursor()//关闭光标
{
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO Info;
	GetConsoleCursorInfo(houtput, &Info);
	Info.bVisible = false;
	SetConsoleCursorInfo(houtput, &Info);
}
void OCursor()//显示光标
{
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO Info;
	GetConsoleCursorInfo(houtput, &Info);
	Info.bVisible = true;
	SetConsoleCursorInfo(houtput, &Info);
}
void color(int x)//设置控制台颜色
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
}
void SetPos(int x, int y)//设置光标的坐标
{
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);//GetStdHandle获取柄,HANDLE接收柄
	COORD pos = { x,y };//坐标
	SetConsoleCursorPosition(houtput, pos);
}

2.线程创建:

	//将仓库信息和持股信息封装送入线程中
	HoldStock* head = NULL;
	Warehouse wh = { 1000000,1000000,0,100.00,NULL };
	HW pw;
	pw.r = 1, pw.hs = &head, pw.ws = &wh;
	HANDLE thp1 = NULL, thp2 = NULL;
	// 初始化临界区
	InitializeCriticalSection(&cs);
	// 创建线程
	thp1 = CreateThread(NULL, 0, Print,(LPVOID)&pw, 0, NULL);//这里不能把pw直接传过去!!
	thp2 = CreateThread(NULL, 0, Run, (LPVOID)&pw, 0, NULL);//
	if (!thp1 || !thp2)
	{
		exit(-1);
	}
	// 等待线程结束
	WaitForSingleObject(thp1, INFINITE);
	WaitForSingleObject(thp2, INFINITE);
	// 销毁临界区
	DeleteCriticalSection(&cs);
	// 关闭线程句柄
	CloseHandle(thp1);
	CloseHandle(thp2);

        接下来进入打印线,进入打印线后先给数据解封装,因为考虑到股票信息会在整个工程中打印输出,所有先直接做一个死循环,并且在开头和结尾上锁和解锁。其他问题先不考虑。

        在输出前先要设置光标位置,以便信息在准确的位置输出,SetPos用来设置光标信息,color用来设置控制台颜色。

2.价格波动模拟 

        这个当然需要使用随机数来获取,举一个例子如下,这只是个例子还可以用其它方法来模拟。

		srand((unsigned int)time(NULL));
		for (int i = 0; i < size; i++)
		{
			int u = rand() % 5;
			switch (u)
			{
			case 0:
				break;
			case 1:
				arr[i].price += 0.01;
				break;
			case 2:
				arr[i].price += 0.1;
				break;
			case 3:
				arr[i].price -= 0.01;
				break;
			case 4:
				arr[i].price -= 0.1;
				break;
			}
			arr[i].amount = (arr[i].price - arr[i].begin) / arr[i].begin * 100;
			if (arr[i].price > arr[i].max)
				arr[i].max = arr[i].price;
			if (arr[i].price < arr[i].min)
				arr[i].min = arr[i].price;
		}

 

其它信息的输出和这个类似,这里就不再展示。 

三、菜单设计--menu()

  • 买入 
  • 卖出 
  • 查看股票  
  • 添加自选股 
  • 删除自选股
  • 查看自选股
  • 查看委托
  • 查看仓库

        菜单,给用户提供8个操作,得用户选定后用switch语句进行分开处理,其中自选股的添加删除查找操作比较简单,就是链表的增删查,对这三个点就不再过多叙述。查看股票的话我们可以通过用户输入个证劵代码去查找对应股票的数组下标,然后利用双线程资源共享的特点把输出下标进行更改。接下来看主要来分析一下其它操作。

        当然除此之外还有排序功能,这个可以用qsort函数去处理,这样比较高效。

四、链表设计及更新--List

        为了方便,考虑创建一个链表来做数据的储存,对于自选股的增删查使用链表再合适不过了,而这个我们可以把自选股和交易股做成同一支链表

        有的人可能会想,自选股也可能是交易股啊!对,不过没关系。添加自选股的时候我们进行插入节点。进行委托下单的时候也插入节点。而委托状态就可以区分他们。自选股的委托状态一定是未委托,而交易股委托状态一定不是未委托。

        链表的更新,因为链表里储存的是交易股和自选择股,股票的价格涨幅等等都会随时变化,所以需要随时的更新,因为股票信息的数组是全局变量,只需要封装一个函数把对应的股票赋上新的值。

五、买入处理--Buy()

在买入处理函数中需要做的事如下:

  • 判断股票是否存在。
  • 判断可用金额是否充足
  • 判断是否是以100股为单位买入

如果用户输入不符合交易规则,要做相应的提示输出。并且需要用户重新输入

符合交易规则后的处理:

  • 将委托状态置为委托待买入。
  • 将买入价存储到持股信息中。
  • 将买入股数储到持股信息中。
  • 可用资金减掉本次交易的资金量。

最后反馈给客户委托成功。

六、卖出处理--Sell()

在买入处理函数中需要做的事如下:

  • 判断该支股票是否被买入(委托状态是否为成功买入)。
  • 判断持有股数是否充足
  • 判断是否是以100股为单位卖出

如果用户输入不符合交易规则,要做相应的提示输出。并且需要用户重新输入

符合交易规则后的处理:

  • 将委托状态置为委托待卖出。
  • 将卖出价存储到持股信息中。
  • 将卖出股数储到持股信息中。

最后反馈给客户委托成功。

七、处理委托--EnDealt()

        处理买入:如果现价比卖入价低,交易成功

        处理卖出:如果卖出价比现价高,交易成功,成功卖出后可用资产需要增加,并且记录盈率,因为卖出股票后它的盈率就不随现价的波动而波动,可能会丢失导致后面的总资产无法准确计算,所以需要记录。

代码如下:

void EnDealt(HoldStock* ch,Warehouse* cw)//处理委托
{
	//不用保存ch的初始值,因为它只是临时变量
	while (ch)
	{
		if (ch->en == BuyWait && ch->stk.price < ch->cost)//现价比卖入价低,交易成功
			ch->en = BuyOK;
		if (ch->en == SellWait && ch->stk.price <ch->out)// 现价比卖出价高,交易成功
		{
			ch->pf = (ch->out-ch->cost) / ch->cost * 100;//计算盈率
			cw->own += ch->pf / 100 * ch->cost * ch->sm;//计算可用资产
			ch->en = SellOK;//交易成功
		}
		ch = ch->next;
	}	
}

委托展示--EnShow()

        在写这个函数要注意一个细节,先需要思考一下需要展示的是哪些股票信息?只要不是未委托的股票都需要展示,而委托卖出,和卖出成功的股票是需要打印两次的,因为委托卖出和卖出成功的股票一定是买入成功的,也需要打印,而委托状态只能储存一个状态。而这个问题可以用一个新加一个un变量来解决。

void EnShow(HoldStock* head)//展示委托//!!
{
	SetPos(38, 22);
	printf(" 代码    名称    现价   涨幅");
	int i = 1, un = 1;
	while (head)
	{
		if (head->en != NO)
		{
			SetPos(38, 22 + i);
			printf("%s %s", head->stk.code, head->stk.name);
			head->stk.amount > 0 ? color(116) : color(114); printf("%7.2f  %.2f%% ", head->stk.price, head->stk.amount); color(112);
			if (un==1)
			{
				SetPos(69, 22 + i); printf("买:%.2f", head->cost);
			}
			else
			{
				SetPos(69, 22 + i); printf("卖:%.2f", head->out);
			}
			SetPos(78, 22 + i);
			if (un == 1)
			{
				if (head->en == BuyWait)
				{
					color(121); printf("待买入   ");
				}
				else
					color(124); printf("交易成功");
				SetPos(87, 22 + i); printf("数量:%d股", head->sm); color(112);
			}
			else
			{
				if (head->en == SellWait)
				{
					color(121); printf("待卖出   ");
				}
				else
					color(124); printf("交易成功");
				SetPos(87, 22 + i); printf("数量:%d股", head->sel); color(112);
			}
			i++;
			if (head->en == SellWait || head->en == SellOK)//让要卖出的股票打印两次
				un = un == 0 ? 1 : 2;
		}
		if (un==2)
			un = 0;
		else
			head = head->next;
	}
}

八、更新仓库--WaDealt()

        需要对总资金,总盈率,仓位进行计算。而可用金额已经在买入处理,和委托处理中计算了,在这里不用处理。
        这里为了这方便。不用考虑原资产。原仓位。和总盈率。直接把它们都初始化一次重新开始计算。注意在计算总资产的时候要将卖出的股票的盈利加上

void WaDealt(Warehouse* cw, HoldStock* ch)//仓库调整
{
	float cv = 1000000.00;//总资产
	float prosum = 0.0;//总盈率
	float cu = 0.0;//仓位
	while (ch)
	{
		if (ch->en==BuyOK)
		{
			cv += (ch->stk.price - ch->cost) * ch->sm;
			prosum += (ch->stk.price - ch->cost)/ch->cost * 100;
			cu += ch->cost * ch->sm;
		}
		if (ch->en == SellOK)
		{
			cv += ch->pf / 100 * ch->cost * ch->sm;
			prosum += ch->pf;
		}
		ch = ch->next;
	}
	cw->coin = cv;
	cw->profit = prosum;
	cw->place = cu/10000;
}

仓库展示--WaShow()

        相比委托的展示,仓库的展示还比较简单,不过要注意一点,卖完的股票就不用展示就不用展示。如下:

void WaShow(Warehouse* ck, HoldStock* head)
{
	color(112); SetPos(34, 22); printf("仓库");
	SetPos(34, 23); ck->coin >= 1000000 ? color(116) : color(114); printf("总资产:%.2f ", ck->coin); color(112);
	ck->profit >= 0 ? color(116) : color(114); printf("盈率:%.2f%%  ", ck->profit); color(112); 
	printf("仓位:%.2f%% ", ck->place);
	printf("可用资产:%.2f  ", ck->own);
	HoldStock* ph = head;
	SetPos(34, 25);
	printf(" 代码     名称     现价 / 成本    涨幅 ");
	int i = 1;
	while (ph)
	{
		if (ph->en == BuyOK|| ph->en == SellWait||(ph->en==SellOK&&ph->sm-ph->sel>=100))
		{
			SetPos(34, 25 + i);
			printf("%s  %s", ph->stk.code, ph->stk.name);
			ph->stk.amount > 0 ? color(116) : color(114); printf("%8.2f/", ph->stk.price); color(112);
			printf("%.2f   ", ph->cost);
			ph->stk.amount >= 0 ? color(116) : color(114); printf("%.2f%%  ", ph->stk.amount); color(112);
			//SetPos(68, 25 + i);
			//ph->stk.money > 1000 ? printf("%.2f万", ph->stk.money) : printf("%.2f亿 ", ph->stk.money);
			ph->cost <= ph->stk.price ? color(116) : color(114); printf("盈率:%.2f%% ", (ph->stk.price - ph->cost) / ph->cost * 100); color(112);
			color(121); printf("持股:%d ", ph->sm-ph->sel); color(112);
			i++;
		}
		ph = ph->next;
	}
}

源码:由于源码代码量比较大,就不放在文章中,如果需要请私信我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/716344.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MS1112驱动开发(iio框架)

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

期货交易中的仓位管理

期货市场就像是一个复杂的游戏场所。由于期货高杠杆双向交易机制的影响&#xff0c;期货交易中错误的容忍度很低&#xff0c;所以期货交易系统中最重要的是风险控制。而风险控制体系最核心的是仓位管理&#xff0c;因为仓位的多少直接影响到潜在损失的大小。 仓位管理指的是账户…

做好程序前设计

不要小看任何一道编程题目&#xff01;一定一定一定要想好之后再动手&#xff01;&#xff01;&#xff01; 带上你的草稿本&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xf…

LLoCO技术:突破大型语言模型处理长文本的局限

在自然语言处理领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;虽然在众多任务上展现出了卓越的能力&#xff0c;但在处理长文本上下文时却遭遇了瓶颈。由于自注意力机制导致的计算和内存开销随序列长度呈二次方增长&#xff0c;使得这些模型在面对长文本时力不从心…

Vue3中的常见组件通信之`provide`、`inject`

Vue3中的常见组件通信之provide、inject 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-mo…

大数据的力量:推动战略决策和业务转型

在当前全球化的时代背景下&#xff0c;国际间的联系日益紧密&#xff0c;世界变得更加互联互通。面对各种危机&#xff0c;数据驱动决策和分析显得愈发重要。从医学研究到市场趋势分析&#xff0c;大数据技术在各个领域发挥着关键作用&#xff0c;推动着一场深刻的变革浪潮。 大…

关于伪标头那些事

前言 看到伪标头&#xff0c;不少同学可能会比较陌生&#xff0c;因为谁让它默默无闻呢&#xff1f; 当然博主把它比喻为一个来自传输层的“共享盒子”。提到共享&#xff0c;我想大家有所体会了。这里给大家贴一张直观的图例&#xff0c;可以静静观摩之。 Q&#xff1a;什么是…

高端品牌网站建设

随着互联网的快速发展&#xff0c;越来越多的企业开始意识到高端品牌网站建设对于企业发展的重要性。高端品牌网站建设不仅是企业形象展示的窗口&#xff0c;更是与消费者进行有效沟通和互动的桥梁。下面从设计、内容和用户体验三个方面&#xff0c;探讨高端品牌网站建设的重要…

使用代理IP常见问题有哪些?

代理IP在互联网数据收集和业务开展中发挥着重要作用&#xff0c;它充当用户客户端和网站服务器之间的“屏障”&#xff0c;可以保护用户的真实IP地址&#xff0c;并允许用户通过不同的IP地址进行操作。然而&#xff0c;在使用代理IP的过程中&#xff0c;用户经常会遇到一些问题…

php遇到的问题

1、 underfined at line 3 in xxx.php , 错误提示&#xff0c;注释这行代码 // error_reporting(DEBUG ? E_ALL : 0); 目录&#xff1a;config/config.php

爆火的“植物大战僵尸杂交版” ‍♂️【附带安装包】

一、 杂交版植物引言 什么&#xff01;你不会还没听过最近爆火的 《植物大战僵尸杂交版》吧&#xff1f;&#xff01;&#xff1f;&#xff01; 下面这些植物你是否未曾见过&#xff0c;或者已经非常熟悉呢&#xff1f; 二、&#x1f331;&#x1f9df;‍♂️ **杂交版介绍** &…

探索C嘎嘎的奇妙世界:第三关---缺省参数与函数重载

在c语言中,我们常常在对有参函数进行传参,这样的繁琐过程,C祖师爷对此进行了相关改进,多说无益,上干货: 1 缺省参数: 缺省参数是指在声明或定义函数时为函数的形参指定一个默认值&#xff08;默认参数&#xff09;。在调用该函数时&#xff0c;如果没有指定实参&#xff0c;则…

2376.统计特殊整数

链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int countSpecialNumbers(int n) {if (n < 0) {return -1;}std::string str to_string(n);std::vector<std::vector&l…

东南亚短视频:四川鑫悦里文化传媒有限公司

东南亚短视频&#xff1a;探寻异域的魅力与风情 随着短视频平台的兴起&#xff0c;世界各地的风土人情得以通过屏幕展现在我们眼前。而东南亚&#xff0c;这片充满异域魅力的土地&#xff0c;更是成为了短视频创作者们争相探索的热门目的地。今天&#xff0c;就让四川鑫悦里文…

【源码】2024运营版多商户客服系统/在线客服系统/手机客服/PC软件客服端

带客服工作台pc软件源代码&#xff0c;系统支持第三方系统携带参数打开客服链接&#xff0c;例如用户名、uid、头像等 支持多商家&#xff08;多站点&#xff09;支持多商家&#xff08;多站点&#xff09;&#xff0c;每个注册用户为一个商家&#xff0c;每个商家可以添加多个…

element-ui tabs+table 实现点击表格切换标签页

客户需求&#xff1a;点击主任务标签页中的表格 跳转到子任务所在的标签页 代码&#xff1a; 表格部分&#xff1a; <el-tabs type"border-card" :active-name"currentTab" tab-click"handleTabClick"><el-tab-pane class"table…

Fastjson漏洞之CVE-2022-25845

前言&#xff1a; 针对Fastjson之前已经介绍了&#xff0c;这里就不再重复了&#xff0c;漏洞CVE-2017-18349只能用来攻击>1.2.24版本的&#xff0c;CVE-2022-25845属于CVE-2017-18349的升级版&#xff0c;但是目前仅影响到1.2.83以下版本。CVE-2022-25845本质上是绕过了名…

理解并应用:JavaScript响应式编程与事件驱动编程的差异

背景介绍 在现代JavaScript开发中&#xff0c;响应式编程&#xff08;Reactive Programming&#xff09;和事件驱动编程&#xff08;Event-Driven Programming&#xff09;是两种非常重要且常用的编程范式。虽然它们都用于处理异步操作&#xff0c;但在理念和实现方式上存在显…

2 程序的灵魂—算法-2.4 怎样表示一个算法-2.4.2 用流程图表示算法-【例 2.9】

将例 2.4 求 1-1/21/3-1/41/99-1/100 的算用流程图表示。 一个流程图包括&#xff1a; 1. 表示相应操作的框&#xff1b; 2. 带箭头的流程线&#xff1b; 3. 框内外必要的文字说明。

Canvas倒计时

Canvas倒计时 前言 用Canvas绘制一个倒计时组件&#xff0c;显示距离新年还有多长时间&#xff0c;精确到秒&#xff0c;该倒计时需要实时更新 基础知识点 JS Date() 创建一个新Date对象的唯一方法是通过new 操作符&#xff0c;例如&#xff1a;let now new Date(); 若将…