跳至主要内容

投资组合仪表盘

投资组合仪表盘提供所有 AI 交易订单的汇总视图,包括总投入资金、当前 NAV、盈亏以及按策略分类的明细。在 React 中可使用 useAIPortfolio hook,在任何平台上可使用 getAIPortfolio API 方法。

useAIPortfolio Hook

import { useAIPortfolio } from '@one_deploy/sdk';
import type { UseAIPortfolioResult } from '@one_deploy/sdk';

UseAIPortfolioResult

interface UseAIPortfolioResult {
/** Aggregated portfolio summary. Null until the first successful fetch. */
portfolio: AIPortfolioSummary | null;

/** True while the initial fetch is in progress. */
isLoading: boolean;

/** Error object if the fetch failed, otherwise null. */
error: Error | null;

/** Re-fetch portfolio data. */
refetch: () => Promise<void>;
}

基本用法

import { useAIPortfolio } from '@one_deploy/sdk';

function PortfolioDashboard() {
const { portfolio, isLoading, error, refetch } = useAIPortfolio();

if (isLoading) return <Text>Loading portfolio...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!portfolio) return <Text>No portfolio data</Text>;

return (
<View style={styles.container}>
<Text style={styles.heading}>AI Trading Portfolio</Text>

<View style={styles.statsRow}>
<StatCard label="Total Invested" value={`$${portfolio.totalInvested.toFixed(2)}`} />
<StatCard label="Current NAV" value={`$${portfolio.currentNav.toFixed(2)}`} />
</View>

<View style={styles.statsRow}>
<StatCard
label="Total P&L"
value={`${portfolio.totalPnl >= 0 ? '+' : ''}$${portfolio.totalPnl.toFixed(2)}`}
/>
<StatCard
label="Return"
value={`${portfolio.totalPnlPercent >= 0 ? '+' : ''}${portfolio.totalPnlPercent.toFixed(2)}%`}
/>
</View>

<View style={styles.statsRow}>
<StatCard label="Active Orders" value={String(portfolio.activeOrders)} />
<StatCard label="Completed" value={String(portfolio.completedOrders)} />
</View>

<Pressable onPress={refetch}>
<Text style={styles.refreshButton}>Refresh</Text>
</Pressable>
</View>
);
}

AIPortfolioSummary 类型

interface AIPortfolioSummary {
/** Total capital invested across all orders (in USD). */
totalInvested: number;

/** Current total net asset value across all active orders. */
currentNav: number;

/** Total profit/loss amount (currentNav - totalInvested for active orders + realised P&L). */
totalPnl: number;

/** Total profit/loss as a percentage. */
totalPnlPercent: number;

/** Number of currently active orders. */
activeOrders: number;

/** Number of completed orders (cycle ended). */
completedOrders: number;

/** Number of redeemed orders (early withdrawal). */
redeemedOrders: number;

/** Number of paused orders. */
pausedOrders: number;

/** Per-strategy breakdown. */
strategyBreakdown: AIStrategyBreakdown[];

/** Per-chain breakdown. */
chainBreakdown: AIChainBreakdown[];

/** Historical daily NAV snapshots for portfolio-level charting. */
navHistory: AINavSnapshot[];
}

interface AIStrategyBreakdown {
strategyId: string;
strategyName: string;
category: StrategyCategory;
invested: number;
currentNav: number;
pnl: number;
pnlPercent: number;
orderCount: number;
}

interface AIChainBreakdown {
chainId: number;
chainName: string;
invested: number;
currentNav: number;
pnl: number;
orderCount: number;
}

getAIPortfolio API 方法

对于非 React 环境或需要直接控制的情况,请使用引擎客户端。

import { OneEngineClient } from '@one_deploy/sdk';

const engine = new OneEngineClient({
apiKey: process.env.ONE_API_KEY!,
projectId: process.env.ONE_PROJECT_ID!,
});

const portfolio = await engine.getAIPortfolio({
includeNavHistory: true,
includeBreakdowns: true,
});

console.log('Total invested:', portfolio.totalInvested);
console.log('Current NAV:', portfolio.currentNav);
console.log('P&L:', portfolio.totalPnl);
console.log('Active orders:', portfolio.activeOrders);

方法签名

getAIPortfolio(include?: AIPortfolioInclude): Promise<AIPortfolioSummary>
interface AIPortfolioInclude {
/** Include the navHistory array. Defaults to false. */
includeNavHistory?: boolean;

/** Include strategyBreakdown and chainBreakdown. Defaults to false. */
includeBreakdowns?: boolean;

/** Number of days of NAV history to include. Defaults to 30. */
navHistoryDays?: number;
}

getPortfolioStats API 方法

一个更轻量的端点,仅返回顶层汇总数据,不包含分类明细或历史记录。适用于摘要徽标或通知计数。

const stats = await engine.getPortfolioStats();

console.log('Active orders:', stats.activeOrders);
console.log('Total P&L:', stats.totalPnl);
console.log('Total invested:', stats.totalInvested);

方法签名

getPortfolioStats(): Promise<AIPortfolioStats>
interface AIPortfolioStats {
totalInvested: number;
currentNav: number;
totalPnl: number;
totalPnlPercent: number;
activeOrders: number;
completedOrders: number;
redeemedOrders: number;
pausedOrders: number;
}

构建仪表盘视图

以下是一个更完整的示例,结合了汇总统计、策略明细和 NAV 图表。

import { useAIPortfolio } from '@one_deploy/sdk';
import type { AIStrategyBreakdown, AINavSnapshot } from '@one_deploy/sdk';

function FullDashboard() {
const { portfolio, isLoading, error } = useAIPortfolio();

if (isLoading) return <ActivityIndicator />;
if (error) return <Text>Failed to load portfolio</Text>;
if (!portfolio) return <Text>No data</Text>;

return (
<ScrollView style={styles.container}>
{/* Top-level stats */}
<View style={styles.header}>
<Text style={styles.navValue}>${portfolio.currentNav.toFixed(2)}</Text>
<Text
style={[
styles.pnl,
{ color: portfolio.totalPnl >= 0 ? '#22c55e' : '#ef4444' },
]}
>
{portfolio.totalPnl >= 0 ? '+' : ''}
${portfolio.totalPnl.toFixed(2)} ({portfolio.totalPnlPercent.toFixed(2)}%)
</Text>
</View>

{/* Order counts */}
<View style={styles.countsRow}>
<Badge label="Active" count={portfolio.activeOrders} />
<Badge label="Paused" count={portfolio.pausedOrders} />
<Badge label="Completed" count={portfolio.completedOrders} />
<Badge label="Redeemed" count={portfolio.redeemedOrders} />
</View>

{/* NAV History Chart */}
{portfolio.navHistory.length > 0 && (
<View style={styles.chartContainer}>
<Text style={styles.sectionTitle}>Portfolio Value</Text>
<NavChart data={portfolio.navHistory} />
</View>
)}

{/* Strategy Breakdown */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>By Strategy</Text>
{portfolio.strategyBreakdown.map((sb: AIStrategyBreakdown) => (
<View key={sb.strategyId} style={styles.breakdownRow}>
<Text style={styles.strategyName}>{sb.strategyName}</Text>
<Text style={styles.category}>{sb.category}</Text>
<Text>Invested: ${sb.invested.toFixed(2)}</Text>
<Text>NAV: ${sb.currentNav.toFixed(2)}</Text>
<Text
style={{ color: sb.pnl >= 0 ? '#22c55e' : '#ef4444' }}
>
P&L: {sb.pnl >= 0 ? '+' : ''}${sb.pnl.toFixed(2)} ({sb.pnlPercent.toFixed(2)}%)
</Text>
<Text>Orders: {sb.orderCount}</Text>
</View>
))}
</View>

{/* Chain Breakdown */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>By Chain</Text>
{portfolio.chainBreakdown.map((cb) => (
<View key={cb.chainId} style={styles.breakdownRow}>
<Text>{cb.chainName}</Text>
<Text>Invested: ${cb.invested.toFixed(2)}</Text>
<Text>NAV: ${cb.currentNav.toFixed(2)}</Text>
<Text>Orders: {cb.orderCount}</Text>
</View>
))}
</View>
</ScrollView>
);
}

后续步骤