feat: add real-time price fetching and fix data delays

- Add get_ticker() method for real-time quotes across all markets

- Add get_realtime_price() service with ticker/kline fallback chain

- Fix yfinance end date issue for US stocks and futures

- Fix forex timezone parsing for Tiingo UTC timestamps

- Add retry mechanism with exponential backoff for Tiingo API

- Add API rate limiting for portfolio (3 concurrent, 0.3s interval)

- Add force refresh option to bypass price cache on manual refresh
This commit is contained in:
TIANHE
2026-01-13 00:21:33 +08:00
parent ac932aebf0
commit 714dd47c86
10 changed files with 643 additions and 78 deletions
+6 -4
View File
@@ -5,10 +5,11 @@ import request from '@/utils/request'
// ==================== Positions ====================
export function getPositions () {
export function getPositions (params = {}) {
return request({
url: '/api/portfolio/positions',
method: 'get'
method: 'get',
params
})
}
@@ -35,10 +36,11 @@ export function deletePosition (id) {
})
}
export function getPortfolioSummary () {
export function getPortfolioSummary (params = {}) {
return request({
url: '/api/portfolio/summary',
method: 'get'
method: 'get',
params
})
}
@@ -1076,14 +1076,14 @@ export default {
filterByGroup () {
// Filter is handled by computed property
},
// 刷新价格
// 刷新价格(强制刷新,跳过缓存)
async refreshPrices () {
if (this.isSyncing) return
this.isSyncing = true
try {
await Promise.all([
this.loadPositions(),
this.loadSummary()
this.loadPositions(true), // 强制刷新
this.loadSummary(true) // 强制刷新
])
this.lastSyncTime = new Date()
} finally {
@@ -1146,10 +1146,11 @@ export default {
]
}
},
async loadPositions () {
async loadPositions (forceRefresh = false) {
this.loadingPositions = true
try {
const res = await getPositions()
const params = forceRefresh ? { refresh: '1' } : {}
const res = await getPositions(params)
if (res && res.code === 1) {
this.positions = res.data || []
}
@@ -1159,9 +1160,9 @@ export default {
this.loadingPositions = false
}
},
async loadSummary () {
async loadSummary (forceRefresh = false) {
try {
const res = await getPortfolioSummary()
const res = await getPortfolioSummary(forceRefresh ? { refresh: '1' } : {})
if (res && res.code === 1) {
this.summary = res.data || this.summary
}