Refactor frontend workspaces and centralize API contracts
Organize domain APIs and types under common/apis, shared components under named folders, and private workspace logic alongside each page. Use Bearer sessions, explicit response envelopes, session-aware routing and streams, and configurable login captcha presentation for the deployed backend contract. Preserve news, economic calendar, crawler, market, chat, Skills, settings, and demo trading workflows with responsive layouts and explicit configuration saves. Validate with frontend lint, type checking, 22 test groups, production builds, and deployed desktop/mobile page checks.
This commit is contained in:
1 parent
77b33e5e77
commit
bc480fb806
102 files changed
+4716
-1707
No files matched your search
@@ -0,0 +1,46 @@
|
||||
import type * as Auth from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 获取当前身份 */
|
||||
export function getIdentityApi() {
|
||||
return request<Auth.GetIdentityResponseData>({
|
||||
url: "/auth/me",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 撤销当前会话 */
|
||||
export function revokeSessionApi() {
|
||||
return request<Auth.RevokeSessionResponseData>({
|
||||
url: "/auth/logout",
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
|
||||
/** 获取登录验证码模式及挑战 */
|
||||
export function getCaptchaApi() {
|
||||
return request<Auth.GetCaptchaResponseData>({
|
||||
url: "/auth/captcha",
|
||||
method: "get",
|
||||
anonymous: true,
|
||||
notify: false
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交登录凭据 */
|
||||
export function loginApi(body: Auth.LoginForm) {
|
||||
return request<Auth.LoginResponseData>({
|
||||
url: "/auth/login",
|
||||
method: "post",
|
||||
data: body,
|
||||
anonymous: true
|
||||
})
|
||||
}
|
||||
|
||||
/** 一次性短票据,只用于行情 WebSocket 握手。 */
|
||||
export function createWebSocketTicketApi() {
|
||||
return request<Auth.WebSocketTicket>({
|
||||
url: "/auth/ws-ticket",
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export interface Identity {
|
||||
username: string
|
||||
is_admin: boolean
|
||||
}
|
||||
|
||||
export type GetIdentityResponseData = Identity
|
||||
|
||||
export type RevokeSessionResponseData = null
|
||||
|
||||
export type Captcha = { enabled: false } | {
|
||||
enabled: true
|
||||
captcha_id: string
|
||||
image: string
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
export interface LoginForm {
|
||||
username: string
|
||||
password: string
|
||||
captcha_id?: string
|
||||
code?: string
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
token: string
|
||||
token_type: "bearer"
|
||||
expires_at: string
|
||||
}
|
||||
|
||||
export type GetCaptchaResponseData = Captcha
|
||||
|
||||
export type LoginResponseData = LoginResult
|
||||
|
||||
export interface WebSocketTicket {
|
||||
ticket: string
|
||||
expires_in: number
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type * as Calendar from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 按日期范围查询经济日历 */
|
||||
export function listCalendarApi(start: string, days: number) {
|
||||
return request<Calendar.ListCalendarResponseData>({
|
||||
url: "/calendar",
|
||||
method: "get",
|
||||
params: { start, days }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取本期事件修订记录 */
|
||||
export function calendarRevisionsApi(id: number) {
|
||||
return request<Calendar.CalendarRevisionsResponseData>({
|
||||
url: `/calendar/${id}/revisions`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取同指标历次公布值 */
|
||||
export function calendarHistoryApi(id: number, indicator: string) {
|
||||
return request<Calendar.CalendarHistoryResponseData>({
|
||||
url: `/calendar/${id}/history`,
|
||||
method: "get",
|
||||
params: { indicator }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取当前官方历史序列 */
|
||||
export function calendarSeriesApi(id: number, indicator: string) {
|
||||
return request<Calendar.CalendarSeriesResponseData>({
|
||||
url: `/calendar/${id}/series`,
|
||||
method: "get",
|
||||
params: { indicator }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
export interface CalendarItem {
|
||||
release_id: number
|
||||
indicator: string
|
||||
name: string
|
||||
country_code: string
|
||||
country_name: string
|
||||
event_name: string
|
||||
title: string
|
||||
period: string | null
|
||||
stage: string | null
|
||||
source: string
|
||||
unit: string
|
||||
importance: number
|
||||
scheduled_at: string
|
||||
status: "scheduled" | "waiting" | "delayed" | "released" | "withdrawn" | "error" | "historical" | "unavailable"
|
||||
actual: string | null
|
||||
previous: string | null
|
||||
forecast: string | null
|
||||
previous_basis: string | null
|
||||
impact: "pending" | "bullish" | "bearish" | "neutral" | "uncertain"
|
||||
impact_basis: "forecast" | "previous" | null
|
||||
explanation: string
|
||||
release_url: string | null
|
||||
schedule_url: string
|
||||
first_received_at: string | null
|
||||
last_checked_at: string | null
|
||||
error: string | null
|
||||
revision_no: number
|
||||
}
|
||||
|
||||
export interface CalendarData {
|
||||
items: CalendarItem[]
|
||||
countries: { code: string, name: string }[]
|
||||
worker: { at: string, sources: Record<string, { source: string, checked_at: string, error: string | null }> } | null
|
||||
server_time: string
|
||||
}
|
||||
|
||||
export interface CalendarRevision {
|
||||
revision_no: number
|
||||
captured_at: string
|
||||
payload: { title: string, scheduled_at: string, schedule_status: string, values: Record<string, { actual: string | null, previous: string | null }> }
|
||||
}
|
||||
|
||||
export interface CalendarHistory {
|
||||
release_id: number
|
||||
scheduled_at: string
|
||||
title: string
|
||||
period: string | null
|
||||
stage: string | null
|
||||
actual: string
|
||||
previous: string | null
|
||||
forecast: string | null
|
||||
first_recorded_actual: string | null
|
||||
first_received_at: string
|
||||
release_url: string | null
|
||||
revised: boolean
|
||||
}
|
||||
|
||||
export type ListCalendarResponseData = CalendarData
|
||||
|
||||
export type CalendarRevisionsResponseData = CalendarRevision[]
|
||||
|
||||
export type CalendarHistoryResponseData = CalendarHistory[]
|
||||
|
||||
export interface CalendarSeriesValue {
|
||||
period: string
|
||||
stage: string
|
||||
value: string
|
||||
captured_at: string
|
||||
source_url: string
|
||||
}
|
||||
|
||||
export type CalendarSeriesResponseData = CalendarSeriesValue[]
|
||||
@@ -0,0 +1,59 @@
|
||||
import type * as Chat from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
import { consumeStream } from "@/http/stream"
|
||||
|
||||
/** 读取当前对话及生成状态 */
|
||||
export function getChatSessionApi() {
|
||||
return request<Chat.GetChatSessionResponseData>({
|
||||
url: "/chat/session",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 重置当前对话,可按选择清空上下文 */
|
||||
export function resetChatApi(clearContext: boolean) {
|
||||
return request<Chat.ResetChatResponseData>({
|
||||
url: "/chat/reset",
|
||||
method: "post",
|
||||
data: { clear_context: clearContext }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取 Chat 上下文 */
|
||||
export function getChatContextApi() {
|
||||
return request<Chat.GetChatContextResponseData>({
|
||||
url: "/chat/context",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 保存 Chat 上下文版本 */
|
||||
export function saveChatContextApi(content: string) {
|
||||
return request<Chat.SaveChatContextResponseData>({
|
||||
url: "/chat/context",
|
||||
method: "put",
|
||||
data: { content }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取 Chat 上下文历史 */
|
||||
export function listChatContextVersionsApi() {
|
||||
return request<Chat.ListChatContextVersionsResponseData>({
|
||||
url: "/chat/context/versions",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** POST 只发送一次;这里声明 SSE 事件协议,生命周期由 Chat 管理。 */
|
||||
export function sendChatApi(body: Chat.SendChatRequestData, signal: AbortSignal, version: number, onEvent: Chat.ChatStreamHandler) {
|
||||
return consumeStream<Chat.ChatStreamEvent["data"]>("/api/chat/messages", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
signal
|
||||
}, version, () => {}, (name, data) => {
|
||||
// SSE 将事件名与 JSON 分开发送;在协议边界恢复同一事件的关联类型。
|
||||
const event = { name, data } as Chat.ChatStreamEvent
|
||||
onEvent(event)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import type { Effort } from "../settings/type"
|
||||
|
||||
export interface ToolCall {
|
||||
id: string
|
||||
name: string
|
||||
arguments: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface MessageMetadata {
|
||||
id: number
|
||||
provider_id: number | null
|
||||
usage: { input_tokens?: number, output_tokens?: number } | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export type ChatMessage = MessageMetadata & (
|
||||
| { role: "user", content: { text: string } }
|
||||
| { role: "assistant", content: { text: string, tool_calls: ToolCall[], stopped?: boolean, error?: string, thinking_blocks?: unknown[] } }
|
||||
| { role: "tool", content: { tool_call_id: string, name: string, result: string } }
|
||||
)
|
||||
|
||||
export interface ChatSession {
|
||||
id: number
|
||||
created_at: string
|
||||
messages: ChatMessage[]
|
||||
estimated_tokens: number
|
||||
generating: boolean
|
||||
}
|
||||
|
||||
export interface ChatContext {
|
||||
id: number | null
|
||||
content: string
|
||||
source: "user" | "chat" | "reset" | null
|
||||
created_at: string | null
|
||||
}
|
||||
|
||||
export type GetChatSessionResponseData = ChatSession
|
||||
|
||||
export type ResetChatResponseData = null
|
||||
|
||||
export type GetChatContextResponseData = ChatContext
|
||||
|
||||
export type SaveChatContextResponseData = ChatContext
|
||||
|
||||
export type ListChatContextVersionsResponseData = ChatContext[]
|
||||
|
||||
export interface SendChatRequestData {
|
||||
text: string
|
||||
provider_id: number
|
||||
reasoning_effort: Effort | null
|
||||
}
|
||||
|
||||
export type ChatStreamEvent
|
||||
= | { name: "prompt", data: { estimated_tokens: number, context_window: number, provider: string } }
|
||||
| { name: "thinking", data: Record<string, never> }
|
||||
| { name: "done", data: Record<string, never> }
|
||||
| { name: "context_updated", data: { id: number } }
|
||||
| { name: "delta", data: { text: string } }
|
||||
| { name: "tool_call", data: ToolCall }
|
||||
| { name: "tool_result", data: { id: string, name: string, preview: string } }
|
||||
| { name: "skill_draft", data: { skill_id: number, path: string, version: number } }
|
||||
| { name: "usage", data: { input_tokens: number, output_tokens: number } }
|
||||
| { name: "error", data: { message: string } }
|
||||
|
||||
export type ChatStreamHandler = (event: ChatStreamEvent) => void
|
||||
@@ -0,0 +1,45 @@
|
||||
import type * as Crawler from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
import { consumeStream } from "@/http/stream"
|
||||
|
||||
/** 读取新闻与日历来源状态 */
|
||||
export function listSourcesApi() {
|
||||
return request<Crawler.ListSourcesResponseData>({
|
||||
url: "/crawler/sources",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改新闻来源设置 */
|
||||
export function patchSourceApi(code: string, body: Crawler.SourcePatch) {
|
||||
return request<Crawler.PatchSourceResponseData>({
|
||||
url: `/crawler/sources/${code}`,
|
||||
method: "patch",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取来源历史日志 */
|
||||
export function listLogsApi(source: string, limit = 200) {
|
||||
return request<Crawler.ListLogsResponseData>({
|
||||
url: "/crawler/logs",
|
||||
method: "get",
|
||||
params: { source, limit }
|
||||
})
|
||||
}
|
||||
|
||||
/** 请求重启新闻采集 */
|
||||
export function restartCrawlerApi(source: string) {
|
||||
return request<Crawler.RestartCrawlerResponseData>({
|
||||
url: "/crawler/restart",
|
||||
method: "post",
|
||||
data: { source }
|
||||
})
|
||||
}
|
||||
|
||||
/** 只读订阅,重连由订阅生命周期负责。 */
|
||||
export function subscribeCrawlerApi(signal: AbortSignal, version: number, onOpen: () => void, onMessage: (data: Crawler.CrawlLog) => void) {
|
||||
return consumeStream<Crawler.CrawlLog>("/api/crawler/logs/stream", { signal }, version, onOpen, (name, data) => {
|
||||
if (name === "log") onMessage(data)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
export interface ProcessStatus {
|
||||
state: "running" | "stopped" | "restarting"
|
||||
pid: number | null
|
||||
started_at: string | null
|
||||
next_run_at: string
|
||||
}
|
||||
|
||||
export interface SourceInfo {
|
||||
code: string
|
||||
name: string
|
||||
region_code: string | null
|
||||
list_url: string
|
||||
enabled: boolean
|
||||
interval_seconds: number
|
||||
backfill_hours: number
|
||||
consecutive_failures: number
|
||||
last_success_at: string | null
|
||||
last_error: string | null
|
||||
paused_until: string | null
|
||||
supports_history: boolean
|
||||
history_target: string | null
|
||||
history_position: string | null
|
||||
history_done: boolean
|
||||
process: ProcessStatus | null
|
||||
today_new: number
|
||||
today_revised: number
|
||||
total_items: number
|
||||
}
|
||||
|
||||
export interface SourcesInfo {
|
||||
manager_alive: boolean
|
||||
db_size_bytes: number
|
||||
sources: SourceInfo[]
|
||||
calendar_sources: CalendarSourceInfo[]
|
||||
}
|
||||
|
||||
export interface CalendarSourceInfo {
|
||||
code: string
|
||||
name: string
|
||||
region_code: string
|
||||
worker_alive: boolean
|
||||
heartbeat_at: string | null
|
||||
schedule_checked_at: string | null
|
||||
schedule_success_at: string | null
|
||||
latest_result_at: string | null
|
||||
next_event_at: string | null
|
||||
overdue_count: number
|
||||
history_start: string | null
|
||||
history_end: string | null
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
export type ListSourcesResponseData = SourcesInfo
|
||||
|
||||
export interface CrawlLog {
|
||||
id: number
|
||||
source_code: string
|
||||
run_id: string | null
|
||||
ts: string
|
||||
level: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface SourcePatch {
|
||||
enabled?: boolean
|
||||
interval_seconds?: number
|
||||
/** ISO 时间表示回补到该时间;null 表示取消回补 */
|
||||
history_target?: string | null
|
||||
}
|
||||
|
||||
export type PatchSourceResponseData = null
|
||||
|
||||
export type ListLogsResponseData = CrawlLog[]
|
||||
|
||||
export type RestartCrawlerResponseData = null
|
||||
@@ -0,0 +1,59 @@
|
||||
import type * as Market from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 读取研究行情交易对 */
|
||||
export function listSymbolsApi() {
|
||||
return request<Market.ListSymbolsResponseData>({
|
||||
url: "/market/symbols",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增研究行情交易对 */
|
||||
export function addSymbolApi(market: Market.Market, symbol: string) {
|
||||
return request<Market.AddSymbolResponseData>({
|
||||
url: "/market/symbols",
|
||||
method: "post",
|
||||
data: { market, symbol }
|
||||
})
|
||||
}
|
||||
|
||||
/** 移出研究行情交易对 */
|
||||
export function deleteSymbolApi(id: number) {
|
||||
return request<Market.DeleteSymbolResponseData>({
|
||||
url: `/market/symbols/${id}`,
|
||||
method: "delete"
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询交易所可用交易对 */
|
||||
export function listTradableApi(market: Market.Market) {
|
||||
return request<Market.ListTradableResponseData>({
|
||||
url: "/market/exchange-info",
|
||||
method: "get",
|
||||
params: { market }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取币安原生 K 线 */
|
||||
export function listKlinesApi(params: Market.ListKlinesParamsRequestData) {
|
||||
return request<Market.ListKlinesResponseData>({
|
||||
url: "/market/klines",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取币安 24 小时行情 */
|
||||
export function getTickerApi(market: Market.Market, symbol: string) {
|
||||
return request<Market.GetTickerResponseData>({
|
||||
url: "/market/ticker",
|
||||
method: "get",
|
||||
params: { market, symbol }
|
||||
})
|
||||
}
|
||||
|
||||
/** 使用一次性票据连接行情 WebSocket */
|
||||
export function createMarketSocketApi(ticket: string) {
|
||||
return new WebSocket(`${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/api/market/ws`, ["alphapulse.market.v1", `ticket.${ticket}`])
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
export type Market = "spot" | "um"
|
||||
|
||||
export interface IntervalProgress {
|
||||
done: boolean
|
||||
percent: number
|
||||
}
|
||||
|
||||
export interface MarketSymbol {
|
||||
id: number
|
||||
market: Market
|
||||
symbol: string
|
||||
base: string
|
||||
quote: string
|
||||
price_precision: number
|
||||
volume_precision: number
|
||||
status: "pending" | "backfilling" | "ready" | "error" | "delisted"
|
||||
is_default: boolean
|
||||
added_at: string
|
||||
last_error: string | null
|
||||
progress: Record<string, IntervalProgress>
|
||||
}
|
||||
|
||||
export interface TradableSymbol {
|
||||
symbol: string
|
||||
base: string
|
||||
quote: string
|
||||
}
|
||||
|
||||
/** 币安 24hr ticker 原始字段(只列用到的) */
|
||||
export interface Ticker24h {
|
||||
lastPrice: string
|
||||
priceChangePercent: string
|
||||
highPrice: string
|
||||
lowPrice: string
|
||||
volume: string
|
||||
quoteVolume: string
|
||||
}
|
||||
|
||||
/** [open_time, open, high, low, close, volume, quote_volume] */
|
||||
export type KlineRow = [number, number, number, number, number, number, number]
|
||||
|
||||
export type ListSymbolsResponseData = MarketSymbol[]
|
||||
|
||||
export type AddSymbolResponseData = null
|
||||
|
||||
export type DeleteSymbolResponseData = null
|
||||
|
||||
export type ListTradableResponseData = TradableSymbol[]
|
||||
|
||||
export type ListKlinesResponseData = KlineRow[]
|
||||
|
||||
export interface ListKlinesParamsRequestData { market: Market, symbol: string, interval: string, end?: number, limit?: number }
|
||||
|
||||
export type GetTickerResponseData = Ticker24h
|
||||
|
||||
export interface KlineMessage {
|
||||
t: number
|
||||
o: string
|
||||
h: string
|
||||
l: string
|
||||
c: string
|
||||
v: string
|
||||
q: string
|
||||
x: boolean
|
||||
}
|
||||
|
||||
export interface TickerMessage {
|
||||
c: string
|
||||
o: string
|
||||
h: string
|
||||
l: string
|
||||
v: string
|
||||
q: string
|
||||
E: number
|
||||
}
|
||||
|
||||
export interface Subscription {
|
||||
market: Market
|
||||
symbol: string
|
||||
interval: string
|
||||
}
|
||||
|
||||
export type MarketMessage
|
||||
= | { type: "kline", market: Market, symbol: string, interval: string, data: KlineMessage }
|
||||
| { type: "ticker", market: Market, symbol: string, interval: null, data: TickerMessage }
|
||||
| { type: "error", message: string }
|
||||
@@ -0,0 +1,36 @@
|
||||
import type * as News from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
import { consumeStream } from "@/http/stream"
|
||||
|
||||
/** 分页查询新闻 */
|
||||
export function listNewsApi(params: News.NewsQuery) {
|
||||
return request<News.ListNewsResponseData>({
|
||||
url: "/news",
|
||||
method: "get",
|
||||
params,
|
||||
paramsSerializer: { indexes: null }
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取新闻详情 */
|
||||
export function getNewsApi(id: number) {
|
||||
return request<News.GetNewsResponseData>({
|
||||
url: `/news/${id}`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取新闻修订记录 */
|
||||
export function listRevisionsApi(id: number) {
|
||||
return request<News.ListRevisionsResponseData>({
|
||||
url: `/news/${id}/revisions`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 只读订阅,重连由订阅生命周期负责。 */
|
||||
export function subscribeNewsApi(signal: AbortSignal, version: number, onOpen: () => void, onMessage: (data: News.NewsEvent) => void) {
|
||||
return consumeStream<News.NewsEvent>("/api/news/stream", { signal }, version, onOpen, (name, data) => {
|
||||
if (name === "news") onMessage(data)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
export interface NewsBrief {
|
||||
id: number
|
||||
source_code: string
|
||||
url: string
|
||||
title: string
|
||||
summary: string
|
||||
excerpt: string
|
||||
author: string | null
|
||||
tags: string[]
|
||||
is_paywalled: boolean
|
||||
published_at: string
|
||||
first_seen_at: string
|
||||
revision_no: number
|
||||
}
|
||||
|
||||
export interface NewsQuery {
|
||||
source?: string[]
|
||||
q?: string
|
||||
start?: string
|
||||
end?: string
|
||||
/** 从 1 开始 */
|
||||
page?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface ListNewsResponseData { items: NewsBrief[], total: number }
|
||||
|
||||
export interface NewsDetail extends NewsBrief {
|
||||
content_html: string
|
||||
source_updated_at: string | null
|
||||
last_seen_at: string
|
||||
extra: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface Revision {
|
||||
revision_no: number
|
||||
title: string
|
||||
summary: string
|
||||
content_html: string
|
||||
captured_at: string
|
||||
}
|
||||
|
||||
export interface NewsEvent {
|
||||
id: number
|
||||
source_code: string
|
||||
kind: "new" | "revised"
|
||||
}
|
||||
|
||||
export type GetNewsResponseData = NewsDetail
|
||||
|
||||
export type ListRevisionsResponseData = Revision[]
|
||||
@@ -0,0 +1,81 @@
|
||||
import type * as Settings from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 读取模型配置(不返回密钥) */
|
||||
export function listProvidersApi() {
|
||||
return request<Settings.ListProvidersResponseData>({
|
||||
url: "/settings/providers",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增模型配置 */
|
||||
export function createProviderApi(body: Settings.ProviderForm) {
|
||||
return request<Settings.CreateProviderResponseData>({
|
||||
url: "/settings/providers",
|
||||
method: "post",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改模型配置;空密钥保持原值 */
|
||||
export function patchProviderApi(id: number, body: Partial<Settings.ProviderForm>) {
|
||||
return request<Settings.PatchProviderResponseData>({
|
||||
url: `/settings/providers/${id}`,
|
||||
method: "patch",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除模型配置 */
|
||||
export function deleteProviderApi(id: number) {
|
||||
return request<Settings.DeleteProviderResponseData>({
|
||||
url: `/settings/providers/${id}`,
|
||||
method: "delete"
|
||||
})
|
||||
}
|
||||
|
||||
/** 测试模型连接;按后端 SDK 重试上限等待 */
|
||||
export function testProviderApi(id: number) {
|
||||
return request<Settings.TestProviderResponseData>({
|
||||
url: `/settings/providers/${id}/test`,
|
||||
method: "post",
|
||||
data: null,
|
||||
timeout: 1900000
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取当前模拟盘交易所设置 */
|
||||
export function getExchangeApi() {
|
||||
return request<Settings.GetExchangeResponseData>({
|
||||
url: "/settings/exchange",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 切换交易所或保存其凭证 */
|
||||
export function putExchangeApi(body: Settings.PutExchangeBodyRequestData) {
|
||||
return request<Settings.PutExchangeResponseData>({
|
||||
url: "/settings/exchange",
|
||||
method: "put",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除当前交易所凭证 */
|
||||
export function deleteExchangeApi() {
|
||||
return request<Settings.DeleteExchangeResponseData>({
|
||||
url: "/settings/exchange",
|
||||
method: "delete"
|
||||
})
|
||||
}
|
||||
|
||||
/** 测试当前模拟盘连接 */
|
||||
export function testExchangeApi() {
|
||||
return request<Settings.TestExchangeResponseData>({
|
||||
url: "/settings/exchange/test",
|
||||
method: "post",
|
||||
data: null,
|
||||
timeout: 30000
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
export type Protocol = "openai" | "anthropic"
|
||||
|
||||
export type Effort = "low" | "medium" | "high" | "xhigh" | "max"
|
||||
|
||||
export interface Provider {
|
||||
id: number
|
||||
name: string
|
||||
protocol: Protocol
|
||||
base_url: string
|
||||
api_key_hint: string
|
||||
model: string
|
||||
max_tokens: number
|
||||
context_window: number
|
||||
/** 为空表示不传,由模型决定 */
|
||||
reasoning_effort: Effort | null
|
||||
is_default: boolean
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export type ListProvidersResponseData = Provider[]
|
||||
|
||||
export interface ProviderForm {
|
||||
name: string
|
||||
protocol: Protocol
|
||||
base_url: string
|
||||
/** 编辑时为空表示不修改 */
|
||||
api_key: string
|
||||
model: string
|
||||
max_tokens: number
|
||||
context_window: number
|
||||
/** 为空表示不传,由模型决定 */
|
||||
reasoning_effort: Effort | null
|
||||
is_default: boolean
|
||||
}
|
||||
|
||||
export interface TestResult {
|
||||
ok: boolean
|
||||
elapsed_ms: number
|
||||
reply: string | null
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export type CreateProviderResponseData = Provider
|
||||
|
||||
export type PatchProviderResponseData = Provider
|
||||
|
||||
export type DeleteProviderResponseData = null
|
||||
|
||||
export type TestProviderResponseData = TestResult
|
||||
|
||||
export type ExchangeName = "binance" | "bybit"
|
||||
|
||||
export interface Exchange {
|
||||
exchange: ExchangeName
|
||||
configured_exchanges: ExchangeName[]
|
||||
configured: boolean
|
||||
api_key_hint: string | null
|
||||
/** 是否有币种在交易:交易中不能修改 key */
|
||||
running: boolean
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface ExchangeTestResult {
|
||||
ok: boolean
|
||||
elapsed_ms: number
|
||||
available_usdt: string | null
|
||||
wallet_usdt: string | null
|
||||
/** 双向持仓模式;引擎使用单向持仓,启动时会切换 */
|
||||
dual_side_position: boolean | null
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export type GetExchangeResponseData = Exchange
|
||||
|
||||
export type PutExchangeResponseData = Exchange
|
||||
|
||||
export interface PutExchangeBodyRequestData { exchange?: ExchangeName, api_key?: string, api_secret?: string }
|
||||
|
||||
export type DeleteExchangeResponseData = Exchange
|
||||
|
||||
export type TestExchangeResponseData = ExchangeTestResult
|
||||
@@ -0,0 +1,71 @@
|
||||
import type * as Skills from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 读取 Skills 树 */
|
||||
export function listSkillTreeApi() {
|
||||
return request<Skills.ListSkillTreeResponseData>({
|
||||
url: "/skills/tree",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增文件夹或 Skill */
|
||||
export function createSkillNodeApi(body: Skills.CreateSkillNodeBodyRequestData) {
|
||||
return request<Skills.CreateSkillNodeResponseData>({
|
||||
url: "/skills/nodes",
|
||||
method: "post",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改 Skill 元信息 */
|
||||
export function patchSkillNodeApi(id: number, body: Skills.PatchSkillNodeBodyRequestData) {
|
||||
return request<Skills.PatchSkillNodeResponseData>({
|
||||
url: `/skills/nodes/${id}`,
|
||||
method: "patch",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 移动 Skill 节点 */
|
||||
export function moveSkillNodeApi(id: number, parentId: number | null, index: number) {
|
||||
return request<Skills.MoveSkillNodeResponseData>({
|
||||
url: `/skills/nodes/${id}/move`,
|
||||
method: "post",
|
||||
data: { parent_id: parentId, index }
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除 Skill 节点 */
|
||||
export function deleteSkillNodeApi(id: number) {
|
||||
return request<Skills.DeleteSkillNodeResponseData>({
|
||||
url: `/skills/nodes/${id}`,
|
||||
method: "delete"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取 Skill 历史版本 */
|
||||
export function listSkillVersionsApi(id: number) {
|
||||
return request<Skills.ListSkillVersionsResponseData>({
|
||||
url: `/skills/${id}/versions`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 发布指定 Skill 版本 */
|
||||
export function publishSkillVersionApi(id: number, versionId: number) {
|
||||
return request<Skills.PublishSkillVersionResponseData>({
|
||||
url: `/skills/${id}/publish`,
|
||||
method: "post",
|
||||
data: { version_id: versionId }
|
||||
})
|
||||
}
|
||||
|
||||
/** 保存 Skill 草稿,不自动发布 */
|
||||
export function saveSkillDraftApi(id: number, content: string, note = "") {
|
||||
return request<Skills.SaveSkillDraftResponseData>({
|
||||
url: `/skills/${id}/versions`,
|
||||
method: "post",
|
||||
data: { content, note }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
export type NodeKind = "folder" | "skill"
|
||||
|
||||
export interface SkillNode {
|
||||
id: number
|
||||
parent_id: number | null
|
||||
kind: NodeKind
|
||||
name: string
|
||||
path: string
|
||||
sort_order: number
|
||||
priority: number
|
||||
enabled: boolean
|
||||
published_version_no: number | null
|
||||
latest_version_no: number | null
|
||||
has_draft: boolean
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface SkillVersion {
|
||||
id: number
|
||||
version_no: number
|
||||
content: string
|
||||
source: "user" | "chat"
|
||||
note: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export type ListSkillTreeResponseData = SkillNode[]
|
||||
|
||||
export type CreateSkillNodeResponseData = SkillNode
|
||||
|
||||
export interface CreateSkillNodeBodyRequestData { parent_id: number | null, kind: NodeKind, name: string, priority?: number, content?: string }
|
||||
|
||||
export type PatchSkillNodeResponseData = SkillNode
|
||||
|
||||
export interface PatchSkillNodeBodyRequestData { name?: string, priority?: number, enabled?: boolean }
|
||||
|
||||
export type MoveSkillNodeResponseData = null
|
||||
|
||||
export type DeleteSkillNodeResponseData = null
|
||||
|
||||
export type ListSkillVersionsResponseData = SkillVersion[]
|
||||
|
||||
export type PublishSkillVersionResponseData = SkillNode
|
||||
|
||||
export type SaveSkillDraftResponseData = SkillVersion
|
||||
@@ -0,0 +1,152 @@
|
||||
import type * as Trading from "./type"
|
||||
import { request } from "@/http/axios"
|
||||
|
||||
/** 读取模拟盘账户与全局分析状态 */
|
||||
export function getStatusApi() {
|
||||
return request<Trading.GetStatusResponseData>({
|
||||
url: "/trading/status",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 开始指定币种的自动交易 */
|
||||
export function startTradingApi(symbol: string) {
|
||||
return request<Trading.StartTradingResponseData>({
|
||||
url: `/trading/symbols/${symbol}/start`,
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
|
||||
/** 停止指定币种的新分析并撤销入场单 */
|
||||
export function stopTradingApi(symbol: string) {
|
||||
return request<Trading.StopTradingResponseData>({
|
||||
url: `/trading/symbols/${symbol}/stop`,
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
|
||||
/** 设置该币的交易资金:各币合计不超过账户权益 */
|
||||
export function patchSymbolApi(symbol: string, body: Trading.PatchSymbolBodyRequestData) {
|
||||
return request<Trading.PatchSymbolResponseData>({
|
||||
url: `/trading/symbols/${symbol}`,
|
||||
method: "patch",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 更新指定的全局交易配置字段 */
|
||||
export function patchConfigApi(body: Trading.PatchConfigBodyRequestData) {
|
||||
return request<Trading.PatchConfigResponseData>({
|
||||
url: "/trading/config",
|
||||
method: "patch",
|
||||
data: body
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询模拟盘可交易币种 */
|
||||
export function listSymbolOptionsApi() {
|
||||
return request<Trading.ListSymbolOptionsResponseData>({
|
||||
url: "/trading/symbol-options",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取交易币种列表 */
|
||||
export function listSymbolsApi() {
|
||||
return request<Trading.ListSymbolsResponseData>({
|
||||
url: "/trading/symbols",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增交易币种 */
|
||||
export function addSymbolApi(symbol: string) {
|
||||
return request<Trading.AddSymbolResponseData>({
|
||||
url: "/trading/symbols",
|
||||
method: "post",
|
||||
data: { symbol }
|
||||
})
|
||||
}
|
||||
|
||||
/** 移出交易币种,保留历史 */
|
||||
export function deleteSymbolApi(symbol: string) {
|
||||
return request<Trading.DeleteSymbolResponseData>({
|
||||
url: `/trading/symbols/${symbol}`,
|
||||
method: "delete"
|
||||
})
|
||||
}
|
||||
|
||||
/** 分页读取交易时间线 */
|
||||
export function listEventsApi(params: Trading.ListEventsParamsRequestData) {
|
||||
return request<Trading.ListEventsResponseData>({
|
||||
url: "/trading/events",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/** 筛选已平仓历史及汇总 */
|
||||
export function listPositionHistoryApi(params: Trading.PositionHistoryQuery) {
|
||||
return request<Trading.ListPositionHistoryResponseData>({
|
||||
url: "/trading/positions/history",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取历史仓位币种 */
|
||||
export function listPositionHistorySymbolsApi() {
|
||||
return request<Trading.ListPositionHistorySymbolsResponseData>({
|
||||
url: "/trading/positions/history/symbols",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取新闻信号前向统计 */
|
||||
export function getNewsSignalsApi() {
|
||||
return request<Trading.GetNewsSignalsResponseData>({
|
||||
url: "/trading/news-signals",
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取当前执行计划与订单流水 */
|
||||
export function getSymbolPlanApi(symbol: string) {
|
||||
return request<Trading.GetSymbolPlanResponseData>({
|
||||
url: `/trading/symbols/${symbol}/plan`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
|
||||
/** 排队手动分析;202 仅表示已受理 */
|
||||
export function requestAnalysisApi(symbol: string) {
|
||||
return request<Trading.RequestAnalysisResponseData>({
|
||||
url: `/trading/symbols/${symbol}/analyze`,
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
|
||||
/** 一键平仓(同时停止该币的行情交易)、清除挂单:由执行器在几秒内执行,结果写入时间线 */
|
||||
export function manualActionApi(symbol: string, action: "close" | "cancel") {
|
||||
return request<Trading.ManualActionResponseData>({
|
||||
url: `/trading/symbols/${symbol}/manual/${action}`,
|
||||
method: "post"
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取分析记录列表 */
|
||||
export function listAnalysesApi(params: Trading.ListAnalysesParamsRequestData) {
|
||||
return request<Trading.ListAnalysesResponseData>({
|
||||
url: "/trading/analyses",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/** 读取分析详情与工具调用过程 */
|
||||
export function getAnalysisApi(id: number) {
|
||||
return request<Trading.GetAnalysisResponseData>({
|
||||
url: `/trading/analyses/${id}`,
|
||||
method: "get"
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
import type { Effort } from "../settings/type"
|
||||
|
||||
/** 以下数值字段都是交易所返回的十进制字符串,避免浮点误差 */
|
||||
export interface SnapshotPosition {
|
||||
symbol: string
|
||||
side: "LONG" | "SHORT"
|
||||
amount: string
|
||||
entry_price: string
|
||||
break_even_price: string
|
||||
mark_price: string
|
||||
unrealized_pnl: string
|
||||
liquidation_price: string
|
||||
notional: string
|
||||
margin: string
|
||||
leverage: number
|
||||
margin_type: string
|
||||
}
|
||||
|
||||
export interface SnapshotOrder {
|
||||
symbol: string
|
||||
client_id: string
|
||||
side: "BUY" | "SELL"
|
||||
type: string
|
||||
price: string
|
||||
quantity: string
|
||||
filled: string
|
||||
reduce_only: boolean
|
||||
time: number
|
||||
}
|
||||
|
||||
export interface SnapshotAlgoOrder {
|
||||
symbol: string
|
||||
client_id: string
|
||||
side: "BUY" | "SELL"
|
||||
type: string
|
||||
trigger_price: string
|
||||
quantity: string
|
||||
close_position: boolean
|
||||
reduce_only: boolean
|
||||
time: number
|
||||
}
|
||||
|
||||
export interface Snapshot {
|
||||
exchange: "binance" | "bybit"
|
||||
updated_at: string
|
||||
/** 最近一次成功同步交易所的时间 */
|
||||
synced_at: string | null
|
||||
error: string | null
|
||||
account: { wallet_balance: string, margin_balance: string, available_balance: string, unrealized_pnl: string } | null
|
||||
positions: SnapshotPosition[]
|
||||
orders: SnapshotOrder[]
|
||||
algo_orders: SnapshotAlgoOrder[]
|
||||
}
|
||||
|
||||
export interface TradingStatus {
|
||||
exchange: "binance" | "bybit"
|
||||
configured: boolean
|
||||
provider_id: number | null
|
||||
reasoning_effort: Effort | null
|
||||
/** trader 进程离线时为空 */
|
||||
snapshot: Snapshot | null
|
||||
today: { analyses: number, limit: number, input_tokens: number, output_tokens: number }
|
||||
}
|
||||
|
||||
export interface TradingSymbol {
|
||||
symbol: string
|
||||
added_at: string
|
||||
analysis_requested_at: string | null
|
||||
analyzing: boolean
|
||||
/** 该币是否在交易;停止时分析为试运行,不挂新的入场单 */
|
||||
running: boolean
|
||||
running_changed_at: string | null
|
||||
/** 该币的交易资金,十进制字符串;未设置时不能开始交易 */
|
||||
capital_usdt: string | null
|
||||
/** 实际用于仓位计算的资金:min(交易资金, 账户权益) */
|
||||
effective_capital: string | null
|
||||
/** 执行器正在管理的计划 */
|
||||
plan_id: number | null
|
||||
plan_status: "pending" | "open" | "closing" | null
|
||||
close_deadline: string | null
|
||||
}
|
||||
|
||||
export interface SymbolOption {
|
||||
symbol: string
|
||||
base: string
|
||||
}
|
||||
|
||||
export interface TradingEvent {
|
||||
id: number
|
||||
created_at: string
|
||||
symbol: string | null
|
||||
analysis_id: number | null
|
||||
kind: string
|
||||
level: "info" | "warn" | "error"
|
||||
message: string
|
||||
data: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export type GetStatusResponseData = TradingStatus
|
||||
|
||||
export type StartTradingResponseData = null
|
||||
|
||||
export type StopTradingResponseData = null
|
||||
|
||||
export type PatchSymbolResponseData = null
|
||||
|
||||
export interface PatchSymbolBodyRequestData { capital_usdt: string | null }
|
||||
|
||||
export type PatchConfigResponseData = TradingStatus
|
||||
|
||||
export interface PatchConfigBodyRequestData { provider_id?: number | null, reasoning_effort?: Effort | null, daily_analysis_limit?: number }
|
||||
|
||||
export type ListSymbolOptionsResponseData = SymbolOption[]
|
||||
|
||||
export type ListSymbolsResponseData = TradingSymbol[]
|
||||
|
||||
export type AddSymbolResponseData = null
|
||||
|
||||
export type DeleteSymbolResponseData = null
|
||||
|
||||
export type ListEventsResponseData = TradingEvent[]
|
||||
|
||||
export interface ListEventsParamsRequestData { symbol?: string, before?: number, limit?: number }
|
||||
|
||||
export interface PlanCondition {
|
||||
direction: "above" | "below"
|
||||
price: string
|
||||
timeframe: "mark" | "1m" | "5m" | "15m" | "1h" | "4h"
|
||||
basis: string
|
||||
}
|
||||
|
||||
/** submit_trade_plan 的参数原样保存;数值为字符串 */
|
||||
export interface TradeDecision {
|
||||
action: "plan" | "keep" | "exit" | "wait"
|
||||
summary: string
|
||||
reasons: string
|
||||
risks: string
|
||||
reevaluate: PlanCondition[]
|
||||
direction: "long" | "short" | null
|
||||
confidence: "high" | "medium" | "low" | null
|
||||
/** 区间与挂单价;更早格式的分析没有这个字段 */
|
||||
setup?: { zone_low: string, zone_high: string, entry_price?: string, basis: string } | null
|
||||
valid_hours?: string | null
|
||||
}
|
||||
|
||||
/** 程序按止损和交易资金算出的数量与风险 */
|
||||
export interface EntrySizing {
|
||||
price: string
|
||||
stop: string
|
||||
stop_pct: string
|
||||
quantity: string
|
||||
notional: string
|
||||
leverage: string
|
||||
risk_amount: string
|
||||
risk_at_stop: string
|
||||
risk_pct: string
|
||||
capped_by_leverage: boolean
|
||||
}
|
||||
|
||||
export interface Sizing {
|
||||
direction: "long" | "short"
|
||||
setup: { zone_low: string, zone_high: string }
|
||||
/** 限价、止损和数量 */
|
||||
entry: EntrySizing
|
||||
atr: string
|
||||
position_qty: string
|
||||
}
|
||||
|
||||
export interface Analysis {
|
||||
id: number
|
||||
symbol: string
|
||||
reasons: string[]
|
||||
dry_run: boolean
|
||||
status: "running" | "done" | "failed"
|
||||
action: TradeDecision["action"] | null
|
||||
decision: TradeDecision | null
|
||||
sizing: Sizing | null
|
||||
error: string | null
|
||||
model: string | null
|
||||
reasoning_effort: string | null
|
||||
usage: { input_tokens: number, output_tokens: number } | null
|
||||
started_at: string
|
||||
finished_at: string | null
|
||||
plan_status: PlanStatus | null
|
||||
valid_until: string | null
|
||||
first_fill_at: string | null
|
||||
close_deadline: string | null
|
||||
closed_at: string | null
|
||||
close_reason: string | null
|
||||
result: { realized_pnl: string, commission: string, net: string } | null
|
||||
}
|
||||
|
||||
/** 与协议无关的消息格式,同 Chat */
|
||||
export type TranscriptMessage
|
||||
= | { role: "user", text: string }
|
||||
| { role: "assistant", text: string, tool_calls: { id: string, name: string, arguments: Record<string, unknown> }[] }
|
||||
| { role: "tool", tool_call_id: string, name: string, result: string }
|
||||
|
||||
export interface AnalysisDetail extends Analysis {
|
||||
text: string | null
|
||||
transcript: TranscriptMessage[] | null
|
||||
}
|
||||
|
||||
export type PlanStatus = "proposed" | "pending" | "open" | "closing" | "closed" | "canceled" | "replaced" | "stale" | "applied"
|
||||
|
||||
/** 执行器的订单流水 */
|
||||
export interface OrderRow {
|
||||
id: number
|
||||
purpose: "entry" | "stop" | "tp" | "close"
|
||||
leg: number | null
|
||||
side: "BUY" | "SELL"
|
||||
type: string
|
||||
price: string | null
|
||||
trigger_price: string | null
|
||||
quantity: string
|
||||
status: string
|
||||
filled_qty: string
|
||||
avg_price: string | null
|
||||
error: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
/** 由当前交易所成交记录还原的已平仓仓位(含程序外的手动仓位);数值为十进制字符串 */
|
||||
export interface PositionHistory {
|
||||
symbol: string
|
||||
direction: "long" | "short"
|
||||
opened_at: string
|
||||
closed_at: string
|
||||
quantity: string
|
||||
entry_price: string
|
||||
exit_price: string
|
||||
realized_pnl: string
|
||||
commission: string
|
||||
net: string
|
||||
source: "program" | "manual"
|
||||
/** 手动仓位没有关联计划,为空 */
|
||||
analysis_id: number | null
|
||||
close_reason: string | null
|
||||
}
|
||||
|
||||
export interface PositionHistoryQuery {
|
||||
symbol?: string
|
||||
/** 按平仓时间筛选,ISO 时间 */
|
||||
start?: string
|
||||
end?: string
|
||||
outcome?: "profit" | "loss"
|
||||
source?: "program" | "manual"
|
||||
page: number
|
||||
limit: number
|
||||
}
|
||||
|
||||
export interface PositionHistoryPage {
|
||||
/** 按筛选条件统计全部仓位 */
|
||||
summary: { count: number, wins: number, net: string, commission: string }
|
||||
total: number
|
||||
items: PositionHistory[]
|
||||
}
|
||||
|
||||
export type ListPositionHistoryResponseData = PositionHistoryPage
|
||||
|
||||
export type ListPositionHistorySymbolsResponseData = string[]
|
||||
|
||||
export type SignalHorizon = "1h" | "4h"
|
||||
|
||||
/** 新闻信号前向记录:按模型标注方向(利多做多、利空做空)交易的统计;收益单位 bp */
|
||||
export interface NewsSignalStat {
|
||||
category: "price_report" | "event" | "irrelevant"
|
||||
symbol: string
|
||||
horizon: SignalHorizon
|
||||
n: number
|
||||
mean_bp: number | null
|
||||
t: number | null
|
||||
/** 扣除来回手续费后的平均收益 */
|
||||
net_bp: number | null
|
||||
win_rate: number | null
|
||||
}
|
||||
|
||||
export interface NewsSignalItem {
|
||||
news_id: number
|
||||
published_at: string
|
||||
source_code: string
|
||||
title: string
|
||||
category: NewsSignalStat["category"]
|
||||
direction: "bullish" | "bearish" | "neutral"
|
||||
/** 进场后的原始涨跌(bp,未按方向取正负);发布未满 4 小时为空 */
|
||||
returns: Record<string, Record<SignalHorizon, number>> | null
|
||||
}
|
||||
|
||||
export interface NewsSignals {
|
||||
/** 前向记录的起点:之后发布的新闻才计入 */
|
||||
start: string
|
||||
labeled: number
|
||||
filled: number
|
||||
stats: NewsSignalStat[]
|
||||
recent: NewsSignalItem[]
|
||||
}
|
||||
|
||||
export type GetNewsSignalsResponseData = NewsSignals
|
||||
|
||||
export interface GetSymbolPlanResponseData { plan: Analysis | null, orders: OrderRow[] }
|
||||
|
||||
export type RequestAnalysisResponseData = null
|
||||
|
||||
export type ManualActionResponseData = null
|
||||
|
||||
export type ListAnalysesResponseData = Analysis[]
|
||||
|
||||
export interface ListAnalysesParamsRequestData { symbol?: string, limit?: number }
|
||||
|
||||
export type GetAnalysisResponseData = AnalysisDetail
|
||||
Reference in new issue
Block a user