mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(frontend):优化股票K线图功能
- 在 App.d.ts 中添加 GetStockKLine 函数声明 - 在 app.go 中实现 GetStockKLine 方法- 在 App.js 中添加 GetStockKLine 函数导出 - 在 package.json 中添加 echarts依赖 - 在 stock.vue 中实现 K 线图展示功能- 优化 K 线图数据处理和图表配置
This commit is contained in:
parent
db3594af77
commit
71d8822d15
4
app.go
4
app.go
@ -1043,3 +1043,7 @@ func (a *App) RemoveGroup(groupId int) string {
|
||||
return "移除失败"
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetStockKLine(stockCode, stockName string, days int64) *[]data.KLineData {
|
||||
return data.NewStockDataApi().GetHK_KLineData(stockCode, "day", days)
|
||||
}
|
||||
|
@ -1124,7 +1124,7 @@ func (receiver StockDataApi) GetHK_KLineData(stockCode string, kLineType string,
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
return K
|
||||
}
|
||||
//logger.SugaredLogger.Infof("resp:%s", resp.Body())
|
||||
logger.SugaredLogger.Infof("resp:%s", resp.Body())
|
||||
json.Unmarshal(resp.Body(), &res)
|
||||
code, _ := convertor.ToInt(res["code"])
|
||||
if code != 0 {
|
||||
|
@ -77,7 +77,7 @@ func TestGetKLineData(t *testing.T) {
|
||||
}
|
||||
func TestGetHK_KLineData(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
k := NewStockDataApi().GetHK_KLineData("sh600171", "day", 30)
|
||||
k := NewStockDataApi().GetHK_KLineData("hk01810", "day", 1)
|
||||
jsonData, _ := json.Marshal(*k)
|
||||
markdownTable, err := JSONToMarkdownTable(jsonData)
|
||||
if err != nil {
|
||||
|
28
frontend/package-lock.json
generated
28
frontend/package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@vavt/cm-extension": "^1.8.0",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"echarts": "^5.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
@ -1593,6 +1594,20 @@
|
||||
"date-fns": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
|
||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0",
|
||||
"zrender": "5.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
|
||||
@ -2314,6 +2329,19 @@
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
|
||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
"@vavt/cm-extension": "^1.8.0",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"echarts": "^5.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
@ -1 +1 @@
|
||||
cf858d682535e094e087a036e009d7f8
|
||||
99aeae4d0e7cbe900b379d3e7d2f44d7
|
@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue'
|
||||
import * as echarts from 'echarts';
|
||||
import {
|
||||
Follow,
|
||||
GetAIResponseResult,
|
||||
@ -22,7 +23,7 @@ import {
|
||||
AddGroup,
|
||||
GetGroupList,
|
||||
AddStockGroup,
|
||||
RemoveStockGroup, RemoveGroup
|
||||
RemoveStockGroup, RemoveGroup,GetStockKLine
|
||||
} from '../../wailsjs/go/main/App'
|
||||
import {
|
||||
NAvatar,
|
||||
@ -59,6 +60,14 @@ const danmus = ref([])
|
||||
const ws = ref(null)
|
||||
const dialog = useDialog()
|
||||
const toolbars = [0];
|
||||
|
||||
const upColor = '#ec0000';
|
||||
const upBorderColor = '';
|
||||
const downColor = '#00da3c';
|
||||
const downBorderColor = '';
|
||||
const kLineChartRef = ref(null);
|
||||
|
||||
|
||||
const handleProgress = (progress) => {
|
||||
console.log(`Export progress: ${progress.ratio * 100}%`);
|
||||
};
|
||||
@ -522,7 +531,7 @@ async function updateData(result) {
|
||||
delete results.value[result.key]
|
||||
}
|
||||
|
||||
console.log("updateData",result)
|
||||
//console.log("updateData",result)
|
||||
}
|
||||
|
||||
|
||||
@ -592,6 +601,313 @@ function showFenshi(code,name){
|
||||
|
||||
modalShow2.value=true
|
||||
}
|
||||
|
||||
function calculateMA(dayCount,values) {
|
||||
var result = [];
|
||||
for (var i = 0, len = values.length; i < len; i++) {
|
||||
if (i < dayCount) {
|
||||
result.push('-');
|
||||
continue;
|
||||
}
|
||||
var sum = 0;
|
||||
for (var j = 0; j < dayCount; j++) {
|
||||
sum += +values[i - j][1];
|
||||
}
|
||||
result.push((sum / dayCount).toFixed(2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function handleKLine(){
|
||||
GetStockKLine(data.code,data.name,365).then(result => {
|
||||
console.log("GetStockKLine",result)
|
||||
const chart = echarts.init(kLineChartRef.value);
|
||||
const categoryData = [];
|
||||
const values = [];
|
||||
const volumns=[];
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let resultElement=result[i]
|
||||
console.log("resultElement:{}",resultElement)
|
||||
categoryData.push(resultElement.day)
|
||||
let flag=resultElement.close>resultElement.open?1:-1
|
||||
values.push([
|
||||
resultElement.open,
|
||||
resultElement.close,
|
||||
resultElement.low,
|
||||
resultElement.high
|
||||
])
|
||||
volumns.push([i,resultElement.volume/100,flag])
|
||||
}
|
||||
//console.log("categoryData",categoryData)
|
||||
//console.log("values",values)
|
||||
let option = {
|
||||
backgroundColor: '#ffffff',
|
||||
color:['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
|
||||
animation: false,
|
||||
legend: {
|
||||
bottom: 10,
|
||||
left: 'center',
|
||||
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
padding: 10,
|
||||
textStyle: {
|
||||
color: '#000'
|
||||
},
|
||||
position: function (pos, params, el, elRect, size) {
|
||||
const obj = {
|
||||
top: 10
|
||||
};
|
||||
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
|
||||
return obj;
|
||||
}
|
||||
// extraCssText: 'width: 170px'
|
||||
},
|
||||
axisPointer: {
|
||||
link: [
|
||||
{
|
||||
xAxisIndex: 'all'
|
||||
}
|
||||
],
|
||||
label: {
|
||||
backgroundColor: '#777'
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
show: false,
|
||||
seriesIndex: 5,
|
||||
dimension: 2,
|
||||
pieces: [
|
||||
{
|
||||
value: 1,
|
||||
color: downColor
|
||||
},
|
||||
{
|
||||
value: -1,
|
||||
color: upColor
|
||||
}
|
||||
]
|
||||
},
|
||||
grid: [
|
||||
{
|
||||
left: '10%',
|
||||
right: '8%',
|
||||
height: '50%'
|
||||
},
|
||||
{
|
||||
left: '10%',
|
||||
right: '8%',
|
||||
top: '63%',
|
||||
height: '16%'
|
||||
}
|
||||
],
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: categoryData,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
splitLine: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
axisPointer: {
|
||||
z: 100
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
gridIndex: 1,
|
||||
data: categoryData,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false },
|
||||
axisLabel: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
scale: true,
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
scale: true,
|
||||
gridIndex: 1,
|
||||
splitNumber: 2,
|
||||
axisLabel: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false }
|
||||
}
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
xAxisIndex: [0, 1],
|
||||
start: 50,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
show: true,
|
||||
xAxisIndex: [0, 1],
|
||||
type: 'slider',
|
||||
top: '85%',
|
||||
start: 50,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: '日K',
|
||||
type: 'candlestick',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: upColor,
|
||||
color0: downColor,
|
||||
// borderColor: upBorderColor,
|
||||
// borderColor0: downBorderColor
|
||||
},
|
||||
markPoint: {
|
||||
label: {
|
||||
formatter: function (param) {
|
||||
return param != null ? param.value + '' : '';
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: '最高',
|
||||
type: 'max',
|
||||
valueDim: 'highest'
|
||||
},
|
||||
{
|
||||
name: '最低',
|
||||
type: 'min',
|
||||
valueDim: 'lowest'
|
||||
},
|
||||
{
|
||||
name: '平均收盘价',
|
||||
type: 'average',
|
||||
valueDim: 'close'
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
formatter: function (param) {
|
||||
return param.name + '<br>' + (param.data.coord || '');
|
||||
}
|
||||
}
|
||||
},
|
||||
markLine: {
|
||||
symbol: ['none', 'none'],
|
||||
data: [
|
||||
[
|
||||
{
|
||||
name: 'from lowest to highest',
|
||||
type: 'min',
|
||||
valueDim: 'lowest',
|
||||
symbol: 'circle',
|
||||
symbolSize: 10,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'max',
|
||||
valueDim: 'highest',
|
||||
symbol: 'circle',
|
||||
symbolSize: 10,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
{
|
||||
name: 'min line on close',
|
||||
type: 'min',
|
||||
valueDim: 'close'
|
||||
},
|
||||
{
|
||||
name: 'max line on close',
|
||||
type: 'max',
|
||||
valueDim: 'close'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA5',
|
||||
type: 'line',
|
||||
data: calculateMA(5,values),
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA10',
|
||||
type: 'line',
|
||||
data: calculateMA(10,values),
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA20',
|
||||
type: 'line',
|
||||
data: calculateMA(20,values),
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'MA30',
|
||||
type: 'line',
|
||||
data: calculateMA(30,values),
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '成交量(手)',
|
||||
type: 'bar',
|
||||
xAxisIndex: 1,
|
||||
yAxisIndex: 1,
|
||||
itemStyle: {
|
||||
color: '#7fbe9e'
|
||||
},
|
||||
data: volumns
|
||||
}
|
||||
]
|
||||
};
|
||||
chart.setOption(option);
|
||||
})
|
||||
}
|
||||
function showK(code,name){
|
||||
data.code=code
|
||||
data.name=name
|
||||
@ -602,9 +918,9 @@ function showK(code,name){
|
||||
if(code.startsWith('gb_')){
|
||||
data.kURL='http://image.sinajs.cn/newchart/usstock/daily/'+data.code.replace("gb_","")+'.gif'+"?t="+Date.now()
|
||||
}
|
||||
modalShow3.value=true
|
||||
//https://image.sinajs.cn/newchart/usstock/daily/dji.gif
|
||||
//https://image.sinajs.cn/newchart/hk_stock/daily/06030.gif?1740729404273
|
||||
modalShow3.value=true
|
||||
}
|
||||
|
||||
|
||||
@ -1190,8 +1506,9 @@ function delStockGroup(code,name,groupId){
|
||||
<n-modal v-model:show="modalShow2" :title="data.name" style="width: 600px" :preset="'card'">
|
||||
<n-image :src="data.fenshiURL" />
|
||||
</n-modal>
|
||||
<n-modal v-model:show="modalShow3" :title="data.name" style="width: 600px" :preset="'card'">
|
||||
<n-image :src="data.kURL" />
|
||||
<n-modal v-model:show="modalShow3" :title="data.name" style="width: 900px" :preset="'card'" @after-enter="handleKLine">
|
||||
<!-- <n-image :src="data.kURL" />-->
|
||||
<div ref="kLineChartRef" style="width: 850px; height: 500px;"></div>
|
||||
</n-modal>
|
||||
|
||||
<n-modal transform-origin="center" v-model:show="modalShow4" preset="card" style="width: 800px;" :title="'['+data.name+']AI分析结果'" >
|
||||
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -35,6 +35,8 @@ export function GetGroupStockList(arg1:number):Promise<Array<data.GroupStock>>;
|
||||
|
||||
export function GetPromptTemplates(arg1:string,arg2:string):Promise<any>;
|
||||
|
||||
export function GetStockKLine(arg1:string,arg2:string,arg3:number):Promise<any>;
|
||||
|
||||
export function GetStockList(arg1:string):Promise<Array<data.StockBasic>>;
|
||||
|
||||
export function GetVersionInfo():Promise<models.VersionInfo>;
|
||||
|
@ -66,6 +66,10 @@ export function GetPromptTemplates(arg1, arg2) {
|
||||
return window['go']['main']['App']['GetPromptTemplates'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function GetStockKLine(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['GetStockKLine'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function GetStockList(arg1) {
|
||||
return window['go']['main']['App']['GetStockList'](arg1);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user