新增机构主力分析
This commit is contained in:
parent
3e2e2ffc90
commit
d88fc616f3
@ -1,6 +1,8 @@
|
|||||||
import type { AnalysisSnapshot } from '../../stores/quota'
|
import type { AnalysisSnapshot } from '../../stores/quota'
|
||||||
import type {
|
import type {
|
||||||
ChangeRiskItem,
|
ChangeRiskItem,
|
||||||
|
MainForceAnalysisData,
|
||||||
|
MainForceAnalysisItem,
|
||||||
MainForceTrendItem,
|
MainForceTrendItem,
|
||||||
NewsItem,
|
NewsItem,
|
||||||
PositionAnomalyItem,
|
PositionAnomalyItem,
|
||||||
@ -40,8 +42,8 @@ export function buildAnalysisMessages(payload: AiAnalysisPayload): Array<{
|
|||||||
}> {
|
}> {
|
||||||
const system = [
|
const system = [
|
||||||
payload.keywords.trim() || '你是一名专业的国内期货交易员和分析员。',
|
payload.keywords.trim() || '你是一名专业的国内期货交易员和分析员。',
|
||||||
'请仅基于用户提供的行情、盘口、大单分析、新闻、机构持仓、主力追踪(变盘预警、主力趋势机会)与主力异动数据给出交易建议。',
|
'请仅基于用户提供的行情、盘口、大单分析、新闻、机构持仓、主力分析(盈利席位榜)、主力追踪(变盘预警、主力趋势机会)与主力异动数据给出交易建议。',
|
||||||
'主力追踪与主力异动为全市场数据,请优先关注标注为「当前品种」的条目,并结合整体主力动向综合判断。',
|
'主力分析为当前品种盈利席位榜;主力追踪与主力异动为全市场数据,请优先关注标注为「当前品种」的条目,并结合整体主力动向综合判断。',
|
||||||
'文案中的单位与页面一致(手、万手、万、亿、百分比);不要编造未提供的数据;信息不足时偏向观望并说明原因。',
|
'文案中的单位与页面一致(手、万手、万、亿、百分比);不要编造未提供的数据;信息不足时偏向观望并说明原因。',
|
||||||
'必须只输出一个 JSON 对象,不要 Markdown 代码块,不要其它说明文字。',
|
'必须只输出一个 JSON 对象,不要 Markdown 代码块,不要其它说明文字。',
|
||||||
`JSON 格式:${OUTPUT_SCHEMA}`,
|
`JSON 格式:${OUTPUT_SCHEMA}`,
|
||||||
@ -195,10 +197,43 @@ function formatPositions(data: PositionsData | null): string {
|
|||||||
'',
|
'',
|
||||||
`亏损机构前${POSITION_TOP_N}名:`,
|
`亏损机构前${POSITION_TOP_N}名:`,
|
||||||
...formatProfitRows(data.profitLoss),
|
...formatProfitRows(data.profitLoss),
|
||||||
|
'',
|
||||||
|
'主力分析(盈利席位榜前20名):',
|
||||||
|
...formatMainForceAnalysis(data.mainForceAnalysis),
|
||||||
]
|
]
|
||||||
return lines.join('\n')
|
return lines.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatMainForceAnalysis(data: MainForceAnalysisData | null | undefined): string[] {
|
||||||
|
if (!data?.list?.length) return ['(无)']
|
||||||
|
const lines = [
|
||||||
|
`品种净持仓 ${fmtSignedInt(data.varietyNetQty)}|净增减 ${fmtSignedInt(data.varietyNetChange)}|日内情绪 ${formatPct(data.varietyDayMood)}|变盘风险 ${formatPct(data.varietyChangeRisk)}`,
|
||||||
|
]
|
||||||
|
for (const row of data.list.slice(0, 20)) {
|
||||||
|
lines.push(formatMainForceAnalysisRow(row))
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMainForceAnalysisRow(row: MainForceAnalysisItem): string {
|
||||||
|
const action =
|
||||||
|
row.netChange === 0
|
||||||
|
? '持仓不变'
|
||||||
|
: row.netChange > 0
|
||||||
|
? `加多${Math.abs(row.netChange)}手`
|
||||||
|
: `加空${Math.abs(row.netChange)}手`
|
||||||
|
return [
|
||||||
|
`${row.rank}. ${row.name}`,
|
||||||
|
`净持仓 ${fmtSignedInt(row.netQty)}`,
|
||||||
|
action,
|
||||||
|
`近一年盈亏 ${formatProfitAbs(row.yearProfit)}`,
|
||||||
|
`一周胜率 ${formatPct(row.weekRate)}`,
|
||||||
|
`多空流向 ${formatPct(row.dayMood)}(${row.flowLabel})`,
|
||||||
|
`3日情绪 ${formatPct(row.threeDayMood)}|5日情绪 ${formatPct(row.fiveDayMood)}`,
|
||||||
|
`价差持仓 ${row.priceDiffContract || '无'}`,
|
||||||
|
].join('|')
|
||||||
|
}
|
||||||
|
|
||||||
function formatPositionRows(list: PositionRow[], qtyLabel: string): string[] {
|
function formatPositionRows(list: PositionRow[], qtyLabel: string): string[] {
|
||||||
const top = list.slice(0, POSITION_TOP_N)
|
const top = list.slice(0, POSITION_TOP_N)
|
||||||
if (!top.length) return ['(无)']
|
if (!top.length) return ['(无)']
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { AnalysisSnapshot } from '../../stores/quota'
|
|||||||
export interface AiAnalysisPayload {
|
export interface AiAnalysisPayload {
|
||||||
/** 设置中的角色/分析关键词 */
|
/** 设置中的角色/分析关键词 */
|
||||||
keywords: string
|
keywords: string
|
||||||
/** 行情 + 新闻 + 机构持仓 + 主力追踪 + 主力异动等接口快照 */
|
/** 行情 + 新闻 + 机构持仓(含主力分析)+ 主力追踪 + 主力异动等接口快照 */
|
||||||
snapshot: AnalysisSnapshot
|
snapshot: AnalysisSnapshot
|
||||||
/** 大单手数阈值,与 UI 大单分析一致 */
|
/** 大单手数阈值,与 UI 大单分析一致 */
|
||||||
largeOrderLots: number
|
largeOrderLots: number
|
||||||
|
|||||||
91
src/api/jqka/mapMainForceAnalysis.ts
Normal file
91
src/api/jqka/mapMainForceAnalysis.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import type { MainForceAnalysisData, MainForceAnalysisItem } from '../../types'
|
||||||
|
import type {
|
||||||
|
JqkaMainForceAnalysisData,
|
||||||
|
JqkaMainForceAnalysisItem,
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
const TOP_N = 20
|
||||||
|
|
||||||
|
function toNum(v: string | number | null | undefined): number {
|
||||||
|
if (typeof v === 'number') return Number.isFinite(v) ? v : 0
|
||||||
|
if (v == null || v === '') return 0
|
||||||
|
const n = Number(v)
|
||||||
|
return Number.isFinite(n) ? n : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多空流向文案(对齐同花顺 H5):
|
||||||
|
* - day_mood < 15% → 持仓平稳
|
||||||
|
* - < 40% → 小幅流多/流空
|
||||||
|
* - < 90% → 中度流多/流空
|
||||||
|
* - ≥ 90% → 大幅流多/流空
|
||||||
|
* 方向由净持仓增减 f025n 决定(>0 流多,<0 流空)。
|
||||||
|
*/
|
||||||
|
export function buildFlowLabel(
|
||||||
|
dayMood: number,
|
||||||
|
netChange: number,
|
||||||
|
): { flowSide: MainForceAnalysisItem['flowSide']; flowLabel: string } {
|
||||||
|
if (dayMood < 0.15 || netChange === 0) {
|
||||||
|
return { flowSide: 'flat', flowLabel: '持仓平稳' }
|
||||||
|
}
|
||||||
|
const intens =
|
||||||
|
dayMood >= 0.9 ? '大幅' : dayMood >= 0.4 ? '中度' : '小幅'
|
||||||
|
if (netChange > 0) {
|
||||||
|
return { flowSide: 'long', flowLabel: `${intens}流多` }
|
||||||
|
}
|
||||||
|
return { flowSide: 'short', flowLabel: `${intens}流空` }
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapItem(
|
||||||
|
item: JqkaMainForceAnalysisItem,
|
||||||
|
rank: number,
|
||||||
|
): MainForceAnalysisItem {
|
||||||
|
const netChange = item.f025n
|
||||||
|
const dayMood = toNum(item.day_mood)
|
||||||
|
const flow = buildFlowLabel(dayMood, netChange)
|
||||||
|
const unitFunds = toNum(item.pre_pupil_flow)
|
||||||
|
|
||||||
|
return {
|
||||||
|
rank,
|
||||||
|
name: item.company,
|
||||||
|
netQty: item.f024n,
|
||||||
|
netChange,
|
||||||
|
changeFunds: netChange * unitFunds,
|
||||||
|
dayProfit: toNum(item.day_profit),
|
||||||
|
yearProfit: toNum(item.year_profit),
|
||||||
|
weekRate: toNum(item.week_rate),
|
||||||
|
dayMood,
|
||||||
|
flowSide: flow.flowSide,
|
||||||
|
flowLabel: flow.flowLabel,
|
||||||
|
threeDayMood: toNum(item.three_day_mood),
|
||||||
|
fiveDayMood: toNum(item.five_day_mood),
|
||||||
|
threeDayNetChange: item.three_day_f025n,
|
||||||
|
fiveDayNetChange: item.five_day_f025n,
|
||||||
|
priceDiffContract: (item.price_diff_contract || '').trim(),
|
||||||
|
date: item.date,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主力分析接口 → 页面 / AI 用数据。
|
||||||
|
* 仅取 position_list 前 20 条(接口顺序即盈利席位榜顺序)。
|
||||||
|
*/
|
||||||
|
export function mapJqkaMainForceAnalysis(
|
||||||
|
data: JqkaMainForceAnalysisData,
|
||||||
|
): MainForceAnalysisData {
|
||||||
|
const list = (data.position_list ?? [])
|
||||||
|
.slice(0, TOP_N)
|
||||||
|
.map((item, i) => mapItem(item, i + 1))
|
||||||
|
const vp = data.variety_position
|
||||||
|
const date =
|
||||||
|
list[0]?.date || vp?.date || new Date().toISOString().slice(0, 10)
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
date,
|
||||||
|
varietyNetQty: vp?.f024n ?? 0,
|
||||||
|
varietyNetChange: vp?.f025n ?? 0,
|
||||||
|
varietyDayMood: toNum(vp?.day_mood),
|
||||||
|
varietyChangeRisk: toNum(vp?.change_risk),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -103,6 +103,14 @@ export function mapJqkaDealPosition(data: JqkaDealPositionData): PositionsData {
|
|||||||
netShort,
|
netShort,
|
||||||
profitGain: [],
|
profitGain: [],
|
||||||
profitLoss: [],
|
profitLoss: [],
|
||||||
|
mainForceAnalysis: {
|
||||||
|
list: [],
|
||||||
|
date: '',
|
||||||
|
varietyNetQty: 0,
|
||||||
|
varietyNetChange: 0,
|
||||||
|
varietyDayMood: 0,
|
||||||
|
varietyChangeRisk: 0,
|
||||||
|
},
|
||||||
topTwentySum,
|
topTwentySum,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { jqkaHttp } from '../http'
|
|||||||
import type {
|
import type {
|
||||||
JqkaDealPositionData,
|
JqkaDealPositionData,
|
||||||
JqkaDealPositionResponse,
|
JqkaDealPositionResponse,
|
||||||
|
JqkaMainForceAnalysisData,
|
||||||
|
JqkaMainForceAnalysisResponse,
|
||||||
JqkaMainForceTraceData,
|
JqkaMainForceTraceData,
|
||||||
JqkaMainForceTraceResponse,
|
JqkaMainForceTraceResponse,
|
||||||
JqkaPositionProfitData,
|
JqkaPositionProfitData,
|
||||||
@ -142,3 +144,44 @@ export async function getMainForceTrace(
|
|||||||
|
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GetMainForceAnalysisParams {
|
||||||
|
/** 品种代码,如 FG */
|
||||||
|
variety: string
|
||||||
|
/**
|
||||||
|
* 查询日期 YYYY-MM-DD。
|
||||||
|
* 应与机构持仓 date 一致;缺省走持仓默认日期规则。
|
||||||
|
*/
|
||||||
|
date?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同花顺 — 主力分析(盈利席位榜)
|
||||||
|
* GET /futgwapi/api/market/dragon_tiger/v1/main_force_analysis?variety=&date=
|
||||||
|
*/
|
||||||
|
export async function getMainForceAnalysis(
|
||||||
|
params: GetMainForceAnalysisParams,
|
||||||
|
): Promise<JqkaMainForceAnalysisData> {
|
||||||
|
const variety = params.variety?.trim()
|
||||||
|
if (!variety) {
|
||||||
|
throw new Error('主力分析接口需要 variety')
|
||||||
|
}
|
||||||
|
const date = params.date ?? getPositionQueryDate()
|
||||||
|
|
||||||
|
const { data } = await jqkaHttp.get<JqkaMainForceAnalysisResponse>(
|
||||||
|
'/futgwapi/api/market/dragon_tiger/v1/main_force_analysis',
|
||||||
|
{
|
||||||
|
params: { variety, date },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (data.code !== 0) {
|
||||||
|
throw new Error(`主力分析接口失败: code=${data.code}, msg=${data.msg}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.data?.position_list) {
|
||||||
|
throw new Error('主力分析接口返回空 position_list')
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|||||||
@ -132,3 +132,67 @@ export interface JqkaMainForceTraceResponse {
|
|||||||
msg: string
|
msg: string
|
||||||
data: JqkaMainForceTraceData
|
data: JqkaMainForceTraceData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 主力分析 — 品种汇总(variety_position) */
|
||||||
|
export interface JqkaMainForceAnalysisVariety {
|
||||||
|
variety: string
|
||||||
|
market: string | null
|
||||||
|
f003n: number
|
||||||
|
f007n: number
|
||||||
|
f009n: number
|
||||||
|
f013n: number
|
||||||
|
f015n: number
|
||||||
|
f019n: number
|
||||||
|
f024n: number
|
||||||
|
f025n: number
|
||||||
|
percent: string
|
||||||
|
variety_name: string | null
|
||||||
|
date: string
|
||||||
|
day_mood: string
|
||||||
|
twenty_day_avg: string
|
||||||
|
change_risk: string
|
||||||
|
link_close_price: string
|
||||||
|
index_close_price: string
|
||||||
|
link_settle_price: string
|
||||||
|
max_funds: string
|
||||||
|
pupil_flow: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 主力分析 — 机构席位行(position_list) */
|
||||||
|
export interface JqkaMainForceAnalysisItem {
|
||||||
|
variety: string
|
||||||
|
company: string
|
||||||
|
f003n: number
|
||||||
|
f007n: number
|
||||||
|
f009n: number
|
||||||
|
f013n: number
|
||||||
|
f015n: number
|
||||||
|
f019n: number
|
||||||
|
f024n: number
|
||||||
|
f025n: number
|
||||||
|
date: string
|
||||||
|
variety_name: string | null
|
||||||
|
day_profit: string
|
||||||
|
year_profit: string
|
||||||
|
week_rate: string
|
||||||
|
day_mood: string
|
||||||
|
three_day_mood: string
|
||||||
|
five_day_mood: string
|
||||||
|
three_day_f025n: number
|
||||||
|
five_day_f025n: number
|
||||||
|
max_funds: string
|
||||||
|
price_diff_contract: string
|
||||||
|
pre_pupil_flow: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JqkaMainForceAnalysisData {
|
||||||
|
type: string
|
||||||
|
variety_position: JqkaMainForceAnalysisVariety | null
|
||||||
|
position_list: JqkaMainForceAnalysisItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JqkaMainForceAnalysisResponse {
|
||||||
|
code: number
|
||||||
|
msg: string
|
||||||
|
data: JqkaMainForceAnalysisData
|
||||||
|
}
|
||||||
|
|||||||
118
src/components/position/MainForceAnalysisTable.vue
Normal file
118
src/components/position/MainForceAnalysisTable.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<el-table
|
||||||
|
:data="list"
|
||||||
|
size="small"
|
||||||
|
stripe
|
||||||
|
class="main-force-table"
|
||||||
|
max-height="520"
|
||||||
|
>
|
||||||
|
<el-table-column prop="rank" label="名次" width="56" />
|
||||||
|
<el-table-column prop="name" label="会员简称" min-width="120" />
|
||||||
|
<el-table-column prop="netQty" label="净持仓" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="qtyClass(row.netQty)">{{ formatSignedInt(row.netQty) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="持仓变化" min-width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="changeClass(row.netChange)">
|
||||||
|
{{ formatPositionChange(row) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="yearProfit" label="近一年盈亏(估)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="row.yearProfit >= 0 ? 'price-up' : 'price-down'">
|
||||||
|
{{ formatProfitAbs(row.yearProfit) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="weekRate" label="一周胜率" width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatPct(row.weekRate) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="多空流向值" min-width="140">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="flowClass(row.flowSide)">
|
||||||
|
{{ formatPct(row.dayMood) }}({{ row.flowLabel }})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="价差持仓" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.priceDiffContract || '无' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { MainForceAnalysisItem } from '../../types'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
list: MainForceAnalysisItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function formatSignedInt(n: number): string {
|
||||||
|
if (!Number.isFinite(n)) return '—'
|
||||||
|
return `${n > 0 ? '+' : ''}${n}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPct(n: number): string {
|
||||||
|
return `${(n * 100).toFixed(1)}%`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFunds(n: number): string {
|
||||||
|
const abs = Math.abs(n)
|
||||||
|
if (abs >= 1e8) return `${(abs / 1e8).toFixed(2)}亿`
|
||||||
|
if (abs >= 1e4) return `${(abs / 1e4).toFixed(2)}万`
|
||||||
|
return abs.toFixed(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProfitAbs(n: number): string {
|
||||||
|
const abs = Math.abs(n)
|
||||||
|
if (abs >= 1e8) return `${(abs / 1e8).toFixed(2)}亿`
|
||||||
|
if (abs >= 1e4) return `${(abs / 1e4).toFixed(2)}万`
|
||||||
|
return `${abs.toFixed(0)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加多 / 加空 + 手数 +(资金估算) */
|
||||||
|
function formatPositionChange(row: MainForceAnalysisItem): string {
|
||||||
|
if (row.netChange === 0) return '不变'
|
||||||
|
const action = row.netChange > 0 ? '加多' : '加空'
|
||||||
|
const lots = Math.abs(row.netChange)
|
||||||
|
const fundsAbs = formatFunds(Math.abs(row.changeFunds))
|
||||||
|
// 加空资金为负,加多为正(与同花顺 H5 一致)
|
||||||
|
const fundsText = row.netChange < 0 ? `(-${fundsAbs})` : `(${fundsAbs})`
|
||||||
|
return `${action} ${lots} ${fundsText}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function qtyClass(n: number): string {
|
||||||
|
if (n > 0) return 'price-up'
|
||||||
|
if (n < 0) return 'price-down'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeClass(n: number): string {
|
||||||
|
if (n > 0) return 'price-up'
|
||||||
|
if (n < 0) return 'price-down'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function flowClass(side: MainForceAnalysisItem['flowSide']): string {
|
||||||
|
if (side === 'long') return 'price-up'
|
||||||
|
if (side === 'short') return 'price-down'
|
||||||
|
return 'flow-flat'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.main-force-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-flat {
|
||||||
|
color: var(--text-secondary, #909399);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -6,6 +6,7 @@
|
|||||||
<el-tab-pane label="成交量" name="volume" />
|
<el-tab-pane label="成交量" name="volume" />
|
||||||
<el-tab-pane label="净持仓" name="net" />
|
<el-tab-pane label="净持仓" name="net" />
|
||||||
<el-tab-pane label="机构盈利" name="profit" />
|
<el-tab-pane label="机构盈利" name="profit" />
|
||||||
|
<el-tab-pane label="主力分析" name="mainForce" />
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<span v-if="data.updatedAt" class="updated">数据日期 {{ data.updatedAt }}</span>
|
<span v-if="data.updatedAt" class="updated">数据日期 {{ data.updatedAt }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="cols">
|
<div v-else-if="tab === 'profit'" class="cols">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h3 class="long-title">盈利机构前20名</h3>
|
<h3 class="long-title">盈利机构前20名</h3>
|
||||||
<PositionDonut :list="profitGainAsRows" />
|
<PositionDonut :list="profitGainAsRows" />
|
||||||
@ -62,6 +63,13 @@
|
|||||||
<ProfitTable :list="data.profitLoss" />
|
<ProfitTable :list="data.profitLoss" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="cols single wide">
|
||||||
|
<div class="col">
|
||||||
|
<h3 class="volume-title">盈利席位榜前20名</h3>
|
||||||
|
<MainForceAnalysisTable :list="data.mainForceAnalysis?.list ?? []" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -72,6 +80,7 @@ import PositionDonut from './PositionDonut.vue'
|
|||||||
import PositionTable from './PositionTable.vue'
|
import PositionTable from './PositionTable.vue'
|
||||||
import PositionSumBar from './PositionSumBar.vue'
|
import PositionSumBar from './PositionSumBar.vue'
|
||||||
import ProfitTable from './ProfitTable.vue'
|
import ProfitTable from './ProfitTable.vue'
|
||||||
|
import MainForceAnalysisTable from './MainForceAnalysisTable.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: PositionsData
|
data: PositionsData
|
||||||
@ -131,6 +140,10 @@ const profitLossAsRows = computed(() => toDonutRows(props.data.profitLoss))
|
|||||||
max-width: 560px;
|
max-width: 560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cols.single.wide {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
.long-title {
|
.long-title {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
color: var(--long);
|
color: var(--long);
|
||||||
|
|||||||
@ -24,11 +24,13 @@ import type {
|
|||||||
} from '../api/baidu/wsTypes'
|
} from '../api/baidu/wsTypes'
|
||||||
import {
|
import {
|
||||||
getDealPosition,
|
getDealPosition,
|
||||||
|
getMainForceAnalysis,
|
||||||
getMainForceTrace,
|
getMainForceTrace,
|
||||||
getPositionProfitRank,
|
getPositionProfitRank,
|
||||||
} from '../api/jqka/position'
|
} from '../api/jqka/position'
|
||||||
import { getPositionAnomaly } from '../api/jqka/anomaly'
|
import { getPositionAnomaly } from '../api/jqka/anomaly'
|
||||||
import { mapJqkaDealPosition, mapJqkaPositionProfit } from '../api/jqka/mapPosition'
|
import { mapJqkaDealPosition, mapJqkaPositionProfit } from '../api/jqka/mapPosition'
|
||||||
|
import { mapJqkaMainForceAnalysis } from '../api/jqka/mapMainForceAnalysis'
|
||||||
import { mapJqkaMainForceTrace } from '../api/jqka/mapMainForceTrace'
|
import { mapJqkaMainForceTrace } from '../api/jqka/mapMainForceTrace'
|
||||||
import { mapJqkaPositionAnomaly } from '../api/jqka/mapAnomaly'
|
import { mapJqkaPositionAnomaly } from '../api/jqka/mapAnomaly'
|
||||||
import {
|
import {
|
||||||
@ -113,6 +115,14 @@ function emptyPositions(): PositionsData {
|
|||||||
netShort: [],
|
netShort: [],
|
||||||
profitGain: [],
|
profitGain: [],
|
||||||
profitLoss: [],
|
profitLoss: [],
|
||||||
|
mainForceAnalysis: {
|
||||||
|
list: [],
|
||||||
|
date: '',
|
||||||
|
varietyNetQty: 0,
|
||||||
|
varietyNetChange: 0,
|
||||||
|
varietyDayMood: 0,
|
||||||
|
varietyChangeRisk: 0,
|
||||||
|
},
|
||||||
topTwentySum: [],
|
topTwentySum: [],
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
}
|
}
|
||||||
@ -365,10 +375,10 @@ export const useQuotaStore = defineStore('quota', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拉取会员持仓 + 机构盈利 + 主力追踪 + 主力异动。
|
* 拉取会员持仓 + 机构盈利 + 主力分析 + 主力追踪 + 主力异动。
|
||||||
* 持仓日期:交易日 16:00 后查当天,否则查上一交易日;
|
* 持仓日期:交易日 16:00 后查当天,否则查上一交易日;
|
||||||
* 若遇节假日空数据则再往前最多试 5 个交易日。
|
* 若遇节假日空数据则再往前最多试 5 个交易日。
|
||||||
* 主力追踪 / 主力异动 date 与最终持仓 date 保持一致。
|
* 主力追踪 / 主力异动 / 主力分析 date 与最终持仓 date 保持一致。
|
||||||
* 盈利默认最近一个月、按当前品种查询。
|
* 盈利默认最近一个月、按当前品种查询。
|
||||||
*/
|
*/
|
||||||
async function fetchPositions() {
|
async function fetchPositions() {
|
||||||
@ -400,14 +410,26 @@ export const useQuotaStore = defineStore('quota', () => {
|
|||||||
throw new Error(`持仓无数据(已回溯至 ${date})`)
|
throw new Error(`持仓无数据(已回溯至 ${date})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const profitRaw = await getPositionProfitRank({ variety, contract })
|
const profitRaw = await getPositionProfitRank({ variety, contract })
|
||||||
const profit = mapJqkaPositionProfit(profitRaw)
|
const profit = mapJqkaPositionProfit(profitRaw)
|
||||||
mapped.profitGain = profit.profitGain
|
mapped!.profitGain = profit.profitGain
|
||||||
mapped.profitLoss = profit.profitLoss
|
mapped!.profitLoss = profit.profitLoss
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[quota] 拉取机构盈利失败', e)
|
console.error('[quota] 拉取机构盈利失败', e)
|
||||||
}
|
}
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const analysisRaw = await getMainForceAnalysis({ variety, date })
|
||||||
|
mapped!.mainForceAnalysis = mapJqkaMainForceAnalysis(analysisRaw)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[quota] 拉取主力分析失败', e)
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
])
|
||||||
|
|
||||||
positions.value = mapped
|
positions.value = mapped
|
||||||
positionsLoaded.value = true
|
positionsLoaded.value = true
|
||||||
@ -426,7 +448,7 @@ export const useQuotaStore = defineStore('quota', () => {
|
|||||||
})(),
|
})(),
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const anomalyRaw = await getPositionAnomaly({ date, limit: 3 })
|
const anomalyRaw = await getPositionAnomaly({ date, limit: 5 })
|
||||||
positionAnomaly.value = mapJqkaPositionAnomaly(anomalyRaw)
|
positionAnomaly.value = mapJqkaPositionAnomaly(anomalyRaw)
|
||||||
positionAnomalyLoaded.value = true
|
positionAnomalyLoaded.value = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -98,6 +98,50 @@ export interface ProfitRow {
|
|||||||
percent: number
|
percent: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 主力分析(盈利席位榜)行 */
|
||||||
|
export interface MainForceAnalysisItem {
|
||||||
|
rank: number
|
||||||
|
name: string
|
||||||
|
/** 净持仓 f024n */
|
||||||
|
netQty: number
|
||||||
|
/** 净持仓增减 f025n:>0 加多,<0 加空 */
|
||||||
|
netChange: number
|
||||||
|
/** 持仓变化资金估算(约等于 f025n × pre_pupil_flow) */
|
||||||
|
changeFunds: number
|
||||||
|
dayProfit: number
|
||||||
|
/** 近一年盈亏(估) */
|
||||||
|
yearProfit: number
|
||||||
|
/** 一周胜率 0~1 */
|
||||||
|
weekRate: number
|
||||||
|
/** 多空流向强度 0~1 */
|
||||||
|
dayMood: number
|
||||||
|
/** 流向方向:long=流多 / short=流空 / flat=平稳 */
|
||||||
|
flowSide: 'long' | 'short' | 'flat'
|
||||||
|
/** 如「中度流空」「持仓平稳」 */
|
||||||
|
flowLabel: string
|
||||||
|
threeDayMood: number
|
||||||
|
fiveDayMood: number
|
||||||
|
threeDayNetChange: number
|
||||||
|
fiveDayNetChange: number
|
||||||
|
/** 价差持仓原文;空则页面显示「无」 */
|
||||||
|
priceDiffContract: string
|
||||||
|
date: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 主力分析(当前品种盈利席位榜前 20) */
|
||||||
|
export interface MainForceAnalysisData {
|
||||||
|
list: MainForceAnalysisItem[]
|
||||||
|
date: string
|
||||||
|
/** 品种净持仓 */
|
||||||
|
varietyNetQty: number
|
||||||
|
/** 品种净持仓增减 */
|
||||||
|
varietyNetChange: number
|
||||||
|
/** 品种日内情绪 0~1 */
|
||||||
|
varietyDayMood: number
|
||||||
|
/** 品种变盘风险 0~1 */
|
||||||
|
varietyChangeRisk: number
|
||||||
|
}
|
||||||
|
|
||||||
/** 前 20 多空持仓汇总(按交易日) */
|
/** 前 20 多空持仓汇总(按交易日) */
|
||||||
export interface TopTwentySum {
|
export interface TopTwentySum {
|
||||||
tradeDate: string
|
tradeDate: string
|
||||||
@ -120,6 +164,8 @@ export interface PositionsData {
|
|||||||
profitGain: ProfitRow[]
|
profitGain: ProfitRow[]
|
||||||
/** 亏损机构前 20(day_profit < 0) */
|
/** 亏损机构前 20(day_profit < 0) */
|
||||||
profitLoss: ProfitRow[]
|
profitLoss: ProfitRow[]
|
||||||
|
/** 主力分析(盈利席位榜前 20 + 品种汇总) */
|
||||||
|
mainForceAnalysis: MainForceAnalysisData
|
||||||
/** 前 20 持仓量汇总 */
|
/** 前 20 持仓量汇总 */
|
||||||
topTwentySum: TopTwentySum[]
|
topTwentySum: TopTwentySum[]
|
||||||
updatedAt: string
|
updatedAt: string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user