股票K线功能实现
股票K线功能实现
目标
- 🎯任务9:理解股票T和T-1概念,实现成交量对⽐功能🍐✏️
- 🎯任务10:理解个股涨跌幅度统计功能🍐✏️
- 分析业务,SQL落地;
- 完善不存在数据的区间默认回显功能;
- 🎯任务11:理解个股分时线业务,并实现功能🍐✏️
- 🎯任务12:理解个股⽇K线业务,并实现功能🍐✏️
1. 股票成交量对⽐功能 🎯
股票成交量对⽐功能
功能描述:统计A股⼤盘T⽇和T-1⽇成交量对⽐功能(成交量为沪深两市成交量之和)

stock_market_index_info
表结构相关字段:


功能描述:统计A股⼤盘
T⽇和T-1⽇成交量
对⽐功能(成交量为沪深两市成交量之和)服务路径:
/api/quot/stock/tradeAmt
服务⽅法:
GET
请求参数:无
响应数据格式:
{
"code": 1,
"data": {
"amtList": [
{"count": 3926392,
"time":"202112310930"
},
{"count": 3926392,
"time":"202112310931"
},
...
],//T⽇每分钟成交量信息
"yesAmtList":[
{"count": 3926392,
"time":"202112310930"
},
...
]//T-1⽇每分钟成交量信息
}
}
- 注意事项:如果当前⽇期不在股票交易⽇,则按照前⼀个有效股票交易⽇作为T⽇查询
- 返回值:
R<Map<String,List>>
代码操作
SQL分析
-- 思路:通过逻辑获取T⽇开盘时间和当前时间⽇期范围,ge: 2022-01-03 09:30:00 到 2022-01-03 14:40:00
-- 那么T-1⽇⽇期范围则为:2022-01-02 09:30:00 到 2022-01-0214:40:00
-- 我们可分别统计T⽇和T-1⽇国内A股⼤盘交易量,然后再讲数据组装即可
-- 1.统计T⽇交易量数据信息(T-1⽇SQL结构⼀致)
-- 自己写。。。。
-- SQL语句添加order by 保证查询的数据是按照⽇期排序
提示:使用mysql中的when case
参考答案点击这里查看(需要密码)👈part4_股票成交量对⽐功能sql分析.txt
- 定义服务访问接⼝⽅法
/**
* 功能描述:统计国内A股⼤盘T⽇和T-1⽇成交量对⽐功能(成交量
为沪市和深市成交量之和)
* @return
*/
@ApiOperation("统计T日和T-1日大盘成交量对比功能")
@GetMapping("/stock/tradeAmt")
public R<Map<String, List>> getMarketTradeAmtCompare() {
return stockService.getMarketTradeAmtCompare();
}
- 定义服务接⼝⽅法和实现
- 2.1 XXXXService接口方法
/**
* 功能描述:统计国内A股⼤盘T⽇和T-1⽇成交量对⽐功能(成交量
为沪市和深市成交量之和)
* @return
*/
R<Map<String, List>> getMarketTradeAmtCompare();
- 2.2 StockServiceImpl实现类代码
@Autowired
private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
/**
* 功能描述:统计国内A股⼤盘T⽇和T-1⽇成交量对⽐功能(成交量
为沪市和深市成交量之和)
* map结构示例:
* {
* "volList": [{"count": 3926392,"time":
"202112310930"},......],
* "yesVolList":[{"count": 3926392,"time":
"202112310930"},......]
* }
* @return
*/
@Override
public R<Map<String, List>> getMarketTradeAmtCompare() {
//获取T日股票的交易范围
//目标1:T日最新截止时间
//T日开盘时间
//TODO 获得起止时间
//查询T日的大盘成交量流水
//目标2:统计T-1日数据
//TODO 获得起止时间
//查询T日的大盘成交量流水
//组装数据
Map<String,List> maps=new HashMap<>();
maps.put("amtList",tInfos);
maps.put("yesAmtList",preTInfos);
//响应数据
return R.ok(maps);
}
注意:当前⽆法获取实时的数据,选择已存在的合适的时间范围查询即可;
- 定义mapper接⼝⽅法和xml
- 3.1. 在StockMarketIndexInfoMapper接⼝下定义
/**
*
* @param preTStartTime 开始时间
* @param preTEndTime 结束时间
* @param innerCodes 市场编号
* @return
*/
List<Map> getMarketTradeAmtInfos(@Param("preTStartTime") Date preTStartTime, @Param("preTEndTime") Date preTEndTime, @Param("innerCodes") List<String> innerCodes);
- 3.2. 在StockMarketIndexInfoMapper.xml下定义绑定sql
<select id="getMarketTradeAmtInfos" resultType="java.util.Map">
select
date_format(smi.cur_time,'%Y%m%d%H%i') as time,
sum(smi.trade_amount) as count
from
stock_market_index_info as smi
where
smi.cur_time between #{preTStartTime} and #{preTEndTime}
and smi.market_code in
<foreach collection="innerCodes" item="mCode" open="(" separator="," close=")">
#{mCode}
</foreach>
group by time
order by smi.cur_time asc;
</select>
参考答案点击这里查看(需要密码)👈part4_股票成交量对⽐功能代码实现.txt
- web接口测试
接口链接:
- 接口测试效果
2. 个股分时涨跌幅度统计功能 🎯
个股分时涨跌幅度统计功能
功能说明:统计当前时间下(精确到分钟),A股在各个涨跌区间股票的数量;

- 功能描述:统计
当前时间
下(精确到分钟
),A股在各个涨跌区间
股票的数量; - 服务路径:
/api/quot/stock/updown
- 服务⽅法:
GET
- 前端请求频率:
每分钟
- 请求参数:
⽆
注意事项:如果当前不在股票有效时间内,则以最近最新的⼀个有效股票交易⽇作为查询时间点展示;
- 返回数据格式:
{ "code": 1, "data": { "time": "2021-12-31 14:58:00", "infos": [ { "count": 17, "title": "-3~0%" }, { "count": 2, "title": "-5~-3%" }, //省略...... ] } }
需求分析图解: 👇
-- 整体思路:先统计当前时间点下每⽀股票的涨幅和时间集合,然后再
将结果⼦查询将涨幅值转换成涨幅区间名称,
-- 最后再根据涨幅区间分组统计每⼀组对应的数量即可
-- 步骤1:统计当前时间下,每只股票的涨幅值
-- 自己写。。。。。。。
-- 步骤2:将步骤1的查询结果中数据转换为区间范围集合
-- 自己写。。。。。。。
-- 根据区间分组,统计各个区间数据量

参考答案点击这里查看(需要密码)👈part4_个股分时涨跌幅度统计sql分析.txt
个股分时K线⾏情功能代码操作
- 定义web服务接⼝
/**
* 查询当前时间下股票的涨跌幅度区间统计功能
* 如果当前⽇期不在有效时间内,则以最近的⼀个股票交易时间作为
查询点
* @return
*/
@GetMapping("/stock/updown")
public R<Map> getStockUpDown(){
return stockService.stockUpDownScopeCount();
}
定义服务接⼝⽅法与实现
- 服务接⼝⽅法
/** * 查询当前时间下股票的涨跌幅度区间统计功能 * 如果当前⽇期不在有效时间内,则以最近的⼀个股票交易时间作为 查询点 * @return */ R<Map> stockUpDownScopeCount();
- 接⼝实现
/** * 功能描述:统计在当前时间下(精确到分钟),股票在各个涨跌 区间的数量 * 如果当前不在股票有效时间内,则以最近的⼀个有效股票交易时 间作为查询时间点; * @return * 响应数据格式: * { * "code": 1, * "data": { * "time": "2021-12-31 14:58:00", * "infos": [ * { * "count": 17, * "title": "-3~0%" * }, * //... * ] * } */ @Override public R<Map> stockUpDownScopeCount() { 自己写。。。。 }
参考答案点击这里查看(需要密码)👈part4_个股分时涨跌幅度统计业务代码实现.txt
- 定义mapper接⼝和xml
- 在Mapper定义接⼝⽅法:
/** * 统计指定时间点下,各个涨跌区间内股票的个数 * @param avlDate * @return */ List<Map> stockUpDownScopeCount(@Param("avlDate") Date avlDate);
- 在Mapper.xml定义sql:
自己写。。。。
参考答案点击这里查看(需要密码)👈part4_个股分时涨跌幅度统计业务代码实现.txt
web接⼝测试 测试链接:http://localhost:8091/api/quot/stock/updown

问题思考
- 前端查询的数据是⽆序展示的,涨幅区间应该从
⼩到⼤顺序
展示; - 当前涨幅区间下如果没有对应的股票,则区间标题不会被展示,我们需要对⽆数据的区间默认为0给前端显示;
- 最终效果:
请问阁下怎么解决? 👈
3. 股票K线图功能
1.个股分时图行情功能 🎯
个股分时线⾏情

- 功能描述:查询
个股
的分时
⾏情数据,也就是统计指定股票T⽇每分钟
的交易数据; - 服务路径:
/api/quot/stock/screen/time-sharing
- 服务⽅法:
GET
- 前端请求频率:
每分钟请求
- 请求参数:

- 返回数据格式:
{ "code": 1, "data": [ { "date": "2021-12-31 09:25",//当前时间,精确到分钟 "tradeAmt": 63263,//当前交易量 "code": "000021",//股票编码 "lowPrice": 15.85,//最低价格 "preClosePrice": 15.85,//前收盘价格 "name": "深科技",//股票名称 "highPrice": 15.85,//最⾼价格 "openPrice": 15.85,//开盘价 "tradeVol": 1002718.55,//交易⾦额 "tradePrice": 15.85//当前价格(最新价格) }, //...... ] }
根据上述的返回数据结构,在stock_common
⼯程下添加实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Stock4MinuteDomain {
/**
* ⽇期,eg:202201280809
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone ="Asia/Shanghai")
private Date date;
/**
* 交易量
*/
private Long tradeAmt;
/**
* 股票编码
*/
private String code;
/**
* 最低价
*/
private BigDecimal lowPrice;
/**
* 前收盘价
*/
private BigDecimal preClosePrice;
/**
* 股票名称
*/
private String name;
/**
* 最⾼价
*/
private BigDecimal highPrice;
/**
* 开盘价
*/
private BigDecimal openPrice;
/**
* 当前交易总⾦额
*/
private BigDecimal tradeVol;
/**
* 当前价格
*/
private BigDecimal tradePrice;
}
个股分时K线⾏情功能代码操作
-- 分析:查询个股分时K线图,说⽩了就是查询指定股票在当前交易⽇产⽣的流⽔数据报表展示
-- 综合条件:1.股票ID 2.股票开盘时间 3.当前时间点
-- 自己写
sql执行效果:👇
参考答案点击这里查看(需要密码)👈part4_个股分时图行情功能sql分析.txt
- 定义web服务接⼝
/**
* 功能描述:查询单个个股的分时⾏情数据,也就是统计指定股票T⽇每分钟的交易数据;
* 如果当前⽇期不在有效时间内,则以最近的⼀个股票交易时间作为查询时间点
* @param code 股票编码
* @return
*/
@GetMapping("/stock/screen/time-sharing")
public R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code){
return stockService.stockScreenTimeSharing(code);
}
定义服务接⼝⽅法与实现
- 服务接⼝⽅法
R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code);
- 接⼝实现
自己写...
参考答案点击这里查看(需要密码)👈part4_个股分时图行情功能代码实现.txt
- 定义mapper接⼝和xml
- mapper接⼝⽅法
List<Stock4MinuteDomain> getStockInfoByCodeAndDate(@Param("stockCode") String stockCode,@Param("startTime") Date startTime,@Param("endTime") Date endTime);
- xml sql绑定:
自己写...
2. 股票K线图功能🎯
股票K线图功能

⽇K线就是将股票交易流⽔按天分组,然后统计出每天的交易数据,内容包含:⽇期、股票编码、名称、最⾼价、最低价、开盘价、收盘价、前收盘价、交易量;
需要注意的是这⾥的收盘价就是指每天最⼤交易时间点下对应的价格
- 功能描述:查询指定股票
每天
产⽣的数据,组装
成⽇K线数据;如果当⼤盘尚未收盘
,则以最新的交易价格
作为当天的收盘价格; - 服务路径:
/api/quot/stock/screen/dkline
- 服务⽅法:
GET
- 前端请求频率:
每分钟
- 请求参数:
- 返回数据格式:
{ "code": 1, "data": [ { "date": "2021-12-20 10:20",//⽇期 "tradeAmt": 28284252,//交易量(指收盘时的交易量,如果当天未收盘,则显示最新数据) "code": "000021",//股票编码 "lowPrice": 16,//最低价格(指收盘时记录的最低价,如果当天未收盘,则显示最新数据) "name": "深科技",//名称 "highPrice": 16.83,//最⾼价(指收盘时记录的最⾼价,如果当天未收盘,则显示最新数据) "openPrice": 16.8,//开盘价 "tradeVol": 459088567.58,//交易⾦额(指收盘时记录交易量,如果当天未收盘,则显示最新数据) "closePrice": 16.81,//当前收盘价(指收盘时的价格,如果当天未收盘,则显示最新cur_price) "preClosePrice": 16.81//前收盘价 }, //...... ] }
根据上述的返回数据结构,在stock_common
⼯程下添加实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Stock4EvrDayDomain {
/**
* ⽇期,eg:202201280809
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone ="Asia/Shanghai")
private Date date;
/**
* 交易量
*/
private Long tradeAmt;
/**
* 股票编码
*/
private String code;
/**
* 最低价
*/
private BigDecimal lowPrice;
/**
* 股票名称
*/
private String name;
/**
* 最⾼价
*/
private BigDecimal highPrice;
/**
* 开盘价
*/
private BigDecimal openPrice;
/**
* 当前交易总⾦额
*/
private BigDecimal tradeVol;
/**
* 当前收盘价格指收盘时的价格,如果当天未收盘,则显示最新cur_price)
*/
private BigDecimal closePrice;
/**
* 前收盘价
*/
private BigDecimal preClosePrice;
}
个股分时K线⾏情功能代码操作
核⼼思路
SQL查询要划定⼀个默认的⽇期范围,这样可避免⼤数据量下全表查询⽽导致慢查询的问题;
在指定的⽇期范围内以
天
分组统计出每天⽇期的最⼤值
(收盘时间);根据获取的
最⼤⽇期组
,使⽤in
进⾏条件查询,进⽽获取⽇K线相关的数据;总之,股票
每天最后⼀条
数据就包含了当天最⾼价
、最低价
等相关信息了。
-- 说明:因为在股票流⽔中,开盘价、最⾼价、最低价、当前价等信息在每条记录中都会记录,所以我们更加关注的是每天的收盘价格,业务要求如果当前没有收盘,则以最新价格作为收盘价,所以该业务就可以转化成查询每天最⼤交易时间对应的信息;
-- 步骤1:查询指定股票在指定⽇期范围内每天的最⼤时间,说⽩了就是以天分组,求每天最⼤时间
select
max( sri.cur_time ) as closeDate
from
stock_rt_info as sri
where
sri.stock_code ='600021'
and sri.cur_time between '2021-12-22 09:30:00' and '2022-01-06 14:25:00'
group by
date_format( sri.cur_time, '%Y%m%d' )
-- 步骤2:以步骤1查询结果作为条件,同统计指定时间点下,股票的数据信息
自己写............
SQL执行效果: 👇
参考答案点击这里查看(需要密码)👈part4_个股分时K线⾏情功能sql分析.txt
- 定义web服务接⼝
/**
* 单个个股⽇K 数据查询 ,可以根据时间区间查询数⽇的K线数据
* @param stockCode 股票编码
*/
@GetMapping("/stock/screen/dkline")
public R<List<Map>> getDayKLinData(@RequestParam("code") String stockCode){
return stockService.stockCreenDkLine(stockCode);
}
定义服务接⼝⽅法与实现
- 服务接⼝⽅法
/** * 单个个股⽇K 数据查询 ,可以根据时间区间查询数⽇的K线数据 * @param stockCode 股票编码 */ R<List<Stock4EvrDayDomain>> stockCreenDkLine(String code);
- 接⼝实现
@Override public R<List<Stock4EvrDayDomain>> stockCreenDkLine(String code) { //1.获取查询的⽇期范围 //1.1 获取截⽌时间 DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now()); Date endTime = endDateTime.toDate(); //提供一个假时间 endTime=DateTime.parse("2022-01-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate(); //1.2 获取开始时间 DateTime startDateTime = endDateTime.minusDays(10); Date startTime = startDateTime.toDate(); //TODO MOCKDATA startTime=DateTime.parse("2021-12-22 09:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate(); //2.调⽤mapper接⼝获取查询的集合信息-⽅案1 List<Stock4EvrDayDomain> data= stockRtInfoMapper.getStockInfo4EvrDay(code,startTime,endTime); //3.组装数据,响应 return R.ok(data); }
- 定义mapper接⼝和xml
- 在StockRtInfoMapper定义接⼝⽅法:
/** * 查询指定⽇期范围内指定股票每天的交易数据 * @param stockCode 股票code * @param startTime 起始时间 * @param endTime 终⽌时间 * @return */ List<Stock4EvrDayDomain> getStockInfo4EvrDay(@Param("stockCode") String stockCode, @Param("startTime") Date startTime, @Param("endTime") Date endTime);
- 在StockRtInfoMapper.xml定义sql:
<select id="getStockInfo4EvrDay" resultType="com.itheima.stock.pojo.domain.Stock4EvrDayDomain"> 自己写。。。。。 </select>
参考答案点击这里查看(需要密码)👈part4_个股分时K线⾏情功能代码实现.txt
面试点:
- 你在项目中有没有遇到SQL性能问题,如何解决的?
以股票分时数据业务为例说明 1. 前期数据量少,看不出效果 2. 随着时间积累,数据量越来越大,则索引的优势显现; TYPE:ALL 全表扫描--->在大量数据查询时,如果看到ALL要小心 导致索引失效的原因:函数在条件中处理导致索引失效
- SQL如何实现行转列?
- xml存在大量特殊字符,如何处理?
- case when cdata