feat: 增加股票现价显示,日线改为后复权数据
This commit is contained in:
@@ -42,6 +42,20 @@ public class NumberHelper {
|
|||||||
return NumberUtil.decimalFormat("0.00%", value);
|
return NumberUtil.decimalFormat("0.00%", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatPriceDouble(Double value) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return NumberUtil.decimalFormat("0.00", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatPriceDouble(Integer value) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return NumberUtil.decimalFormat("0.00", value);
|
||||||
|
}
|
||||||
|
|
||||||
public static Double parseDouble(String value) {
|
public static Double parseDouble(String value) {
|
||||||
if (StrUtil.isBlank(value)) {
|
if (StrUtil.isBlank(value)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ public interface DailyRepository extends SimpleRepository<Daily> {
|
|||||||
@Query("select min(daily.tradeDate) from Daily daily")
|
@Query("select min(daily.tradeDate) from Daily daily")
|
||||||
LocalDate findMinTradeDate();
|
LocalDate findMinTradeDate();
|
||||||
|
|
||||||
|
@Query("from Daily daily where daily.stock.id = ?1 order by daily.tradeDate desc limit 1")
|
||||||
|
Optional<Daily> findLatest(Long stockId);
|
||||||
|
|
||||||
@EntityGraph(attributePaths = {"stock"})
|
@EntityGraph(attributePaths = {"stock"})
|
||||||
@Override
|
@Override
|
||||||
Optional<Daily> findOne(Predicate predicate);
|
Optional<Daily> findOne(Predicate predicate);
|
||||||
|
|||||||
@@ -58,4 +58,8 @@ public class StockService extends SimpleServiceSupport<Stock> {
|
|||||||
Sort.by(Sort.Direction.ASC, Daily_.TRADE_DATE)
|
Sort.by(Sort.Direction.ASC, Daily_.TRADE_DATE)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Daily> findDailyLatest(Long stockId) {
|
||||||
|
return dailyRepository.findLatest(stockId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.lanyuanxiaoyao.leopard.server.controller;
|
package com.lanyuanxiaoyao.leopard.server.controller;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
||||||
import com.lanyuanxiaoyao.leopard.core.helper.NumberHelper;
|
import com.lanyuanxiaoyao.leopard.core.helper.NumberHelper;
|
||||||
import com.lanyuanxiaoyao.leopard.core.service.StockService;
|
import com.lanyuanxiaoyao.leopard.core.service.StockService;
|
||||||
@@ -108,6 +109,18 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockD
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("daily/current/{id}")
|
||||||
|
public GlobalResponse<Map<String, Object>> dailyCurrent(@PathVariable("id") Long id) {
|
||||||
|
var daily = stockService.findDailyLatest(id);
|
||||||
|
return GlobalResponse.responseMapData(Map.of(
|
||||||
|
"date", daily.map(Daily::getTradeDate).map(LocalDate::toString).orElse("/"),
|
||||||
|
"open", daily.map(Daily::getOpen).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
|
||||||
|
"close", daily.map(Daily::getClose).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
|
||||||
|
"low", daily.map(Daily::getLow).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE),
|
||||||
|
"high", daily.map(Daily::getHigh).map(NumberHelper::formatPriceDouble).orElse(NumberHelper.FINANCE_NULL_DOUBLE)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("daily/{id}")
|
@GetMapping("daily/{id}")
|
||||||
public GlobalResponse<Map<String, Object>> dailyCharts(@PathVariable("id") Long id) {
|
public GlobalResponse<Map<String, Object>> dailyCharts(@PathVariable("id") Long id) {
|
||||||
var data = stockService.findDailyRecent(id, 100);
|
var data = stockService.findDailyRecent(id, 100);
|
||||||
@@ -115,7 +128,7 @@ public class StockController extends SimpleControllerSupport<Stock, Void, StockD
|
|||||||
var yList = new ArrayList<List<Double>>();
|
var yList = new ArrayList<List<Double>>();
|
||||||
for (var daily : data) {
|
for (var daily : data) {
|
||||||
xList.add(daily.getTradeDate().toString());
|
xList.add(daily.getTradeDate().toString());
|
||||||
yList.add(List.of(daily.getOpen(), daily.getClose(), daily.getLow(), daily.getHigh()));
|
yList.add(List.of(daily.getHfqOpen(), daily.getHfqClose(), daily.getHfqLow(), daily.getHfqHigh()));
|
||||||
}
|
}
|
||||||
return GlobalResponse.responseMapData(Map.of(
|
return GlobalResponse.responseMapData(Map.of(
|
||||||
"xList", xList, "yList", yList
|
"xList", xList, "yList", yList
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import Overview from './pages/Overview.tsx'
|
|||||||
import Root from './pages/Root.tsx'
|
import Root from './pages/Root.tsx'
|
||||||
import Test from './pages/Test.tsx'
|
import Test from './pages/Test.tsx'
|
||||||
import StockList from './pages/stock/StockList.tsx'
|
import StockList from './pages/stock/StockList.tsx'
|
||||||
import StockDetail from './pages/stock/StockDetail.tsx'
|
|
||||||
import TaskList from './pages/task/TaskList.tsx'
|
import TaskList from './pages/task/TaskList.tsx'
|
||||||
import TaskTemplateList from './pages/task/TaskTemplateList.tsx'
|
import TaskTemplateList from './pages/task/TaskTemplateList.tsx'
|
||||||
import TaskScheduleList from './pages/task/TaskScheduleList.tsx'
|
import TaskScheduleList from './pages/task/TaskScheduleList.tsx'
|
||||||
@@ -35,10 +34,6 @@ const routes: RouteObject[] = [
|
|||||||
path: 'list',
|
path: 'list',
|
||||||
Component: StockList,
|
Component: StockList,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'detail/:id',
|
|
||||||
Component: StockDetail,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "collection",
|
path: "collection",
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -571,7 +571,7 @@ export function stockListColumns(idField: string = 'id', extraColumns: Array<Col
|
|||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: '简称',
|
label: '简称',
|
||||||
width: 150,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fullname',
|
name: 'fullname',
|
||||||
@@ -782,37 +782,61 @@ export function stockListColumns(idField: string = 'id', extraColumns: Array<Col
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{type: 'divider'},
|
],
|
||||||
"100日线数据",
|
},
|
||||||
|
{type: 'divider'},
|
||||||
|
{
|
||||||
|
type: 'service',
|
||||||
|
api: `get:${commonInfo.baseUrl}/stock/daily/current/\${${idField}}`,
|
||||||
|
body: [
|
||||||
|
"现价 (${date})",
|
||||||
{
|
{
|
||||||
type: 'chart',
|
className: 'my-2',
|
||||||
height: 500,
|
type: 'property',
|
||||||
api: `get:${commonInfo.baseUrl}/stock/daily/\${${idField}}`,
|
column: 4,
|
||||||
config: {
|
items: [
|
||||||
backgroundColor: '#fff',
|
{label: '开盘价', content: '${open}'},
|
||||||
animation: true,
|
{label: '收盘价', content: '${close}'},
|
||||||
animationDuration: 1000,
|
{label: '最高价', content: '${high}'},
|
||||||
tooltip: {
|
{label: '最低价', content: '${low}'},
|
||||||
trigger: 'axis',
|
],
|
||||||
axisPointer: {
|
},
|
||||||
type: 'cross',
|
],
|
||||||
},
|
},
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
{
|
||||||
borderColor: '#333',
|
type: 'chart',
|
||||||
borderWidth: 1,
|
title: '100日线数据',
|
||||||
textStyle: {
|
height: 500,
|
||||||
color: '#fff',
|
api: `get:${commonInfo.baseUrl}/stock/daily/\${${idField}}`,
|
||||||
fontSize: 12,
|
config: {
|
||||||
},
|
title: {
|
||||||
padding: 12,
|
text: '100日线数据',
|
||||||
formatter: function (params: any) {
|
subtext: '后复权数据',
|
||||||
const param = params[0]
|
},
|
||||||
const open = toNumber(param.data[1]).toFixed(2)
|
backgroundColor: '#fff',
|
||||||
const close = toNumber(param.data[2]).toFixed(2)
|
animation: true,
|
||||||
const lowest = toNumber(param.data[3]).toFixed(2)
|
animationDuration: 1000,
|
||||||
const highest = toNumber(param.data[4]).toFixed(2)
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
},
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||||
|
borderColor: '#333',
|
||||||
|
borderWidth: 1,
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
padding: 12,
|
||||||
|
formatter: function (params: any) {
|
||||||
|
const param = params[0]
|
||||||
|
const open = toNumber(param.data[1]).toFixed(2)
|
||||||
|
const close = toNumber(param.data[2]).toFixed(2)
|
||||||
|
const lowest = toNumber(param.data[3]).toFixed(2)
|
||||||
|
const highest = toNumber(param.data[4]).toFixed(2)
|
||||||
|
|
||||||
return `<div class="text-center font-bold mb-2">${param.name}</div>
|
return `<div class="text-center font-bold mb-2">${param.name}</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<span>开盘:</span>
|
<span>开盘:</span>
|
||||||
<span class="font-bold ml-4">${open}</span>
|
<span class="font-bold ml-4">${open}</span>
|
||||||
@@ -829,89 +853,86 @@ export function stockListColumns(idField: string = 'id', extraColumns: Array<Col
|
|||||||
<span>最高:</span>
|
<span>最高:</span>
|
||||||
<span class="font-bold ml-4">${highest}</span>
|
<span class="font-bold ml-4">${highest}</span>
|
||||||
</div>`
|
</div>`
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: '10%',
|
|
||||||
right: '10%',
|
|
||||||
top: '10%',
|
|
||||||
bottom: '15%',
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
data: '${xList || []}',
|
|
||||||
axisLine: {
|
|
||||||
lineStyle: {
|
|
||||||
color: '#e0e0e0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#666',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
scale: true,
|
|
||||||
axisLine: {
|
|
||||||
lineStyle: {
|
|
||||||
color: '#e0e0e0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#666',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
formatter: function (value: number) {
|
|
||||||
return value.toFixed(2)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
lineStyle: {
|
|
||||||
type: 'dashed',
|
|
||||||
color: '#f0f0f0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataZoom: [
|
|
||||||
{
|
|
||||||
type: 'inside',
|
|
||||||
start: 0,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
show: true,
|
|
||||||
type: 'slider',
|
|
||||||
top: '90%',
|
|
||||||
start: 0,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'candlestick',
|
|
||||||
data: '${yList || []}',
|
|
||||||
itemStyle: {
|
|
||||||
color: '#eb5454',
|
|
||||||
color0: '#4aaa93',
|
|
||||||
borderColor: '#eb5454',
|
|
||||||
borderColor0: '#4aaa93',
|
|
||||||
borderWidth: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"12月线数据",
|
grid: {
|
||||||
],
|
left: '2%',
|
||||||
|
right: '2%',
|
||||||
|
top: '15%',
|
||||||
|
bottom: '15%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: '${xList || []}',
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#e0e0e0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
scale: true,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#e0e0e0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
formatter: function (value: number) {
|
||||||
|
return value.toFixed(2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed',
|
||||||
|
color: '#f0f0f0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
type: 'slider',
|
||||||
|
top: '90%',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'candlestick',
|
||||||
|
data: '${yList || []}',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#eb5454',
|
||||||
|
color0: '#4aaa93',
|
||||||
|
borderColor: '#eb5454',
|
||||||
|
borderColor0: '#4aaa93',
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user