diff --git a/README.md b/README.md index e215bc4..a8cd940 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,393 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# 个人投资持仓管理系统 -## Getting Started +> 现代化、全面化的个人投资组合管理平台,支持多市场(美股、A股、港股、加密货币)统一管理。 -First, run the development server: +![License](https://img.shields.io/badge/license-MIT-blue.svg) +![Next.js](https://img.shields.io/badge/Next.js-16-black.svg) +![TypeScript](https://img.shields.io/badge/TypeScript-5-blue.svg) +![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-blue.svg) -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +--- + +## 功能特性 + +### 核心功能 + +| 功能 | 描述 | +|------|------| +| **多市场支持** | 美股 (US)、A股 (CN)、港股 (HK)、加密货币 (CRYPTO) | +| **资金流水管理** | 入金、出金记录 | +| **交易流水管理** | 买入、卖出、分红、利息、费用 | +| **持仓统计** | 实时市值、平均成本、浮动盈亏 | +| **多币种转换** | 自动将各币种资产折算为 USD | +| **资产配置图** | 饼图展示各市场占比 | +| **数据导入导出** | CSV 格式导入导出 | + +### 交易类型 + +| 类型 | 说明 | +|------|------| +| `BUY` | 买入 | +| `SELL` | 卖出 | +| `DEPOSIT` | 入金 | +| `WITHDRAW` | 出金 | +| `DIVIDEND` | 分红 | +| `INTEREST` | 利息 | +| `FEE` | 费用/手续费 | + +--- + +## 技术架构 + +### 技术栈 + +``` +前端: +├── Next.js 16 (App Router) +├── React 19 +├── TypeScript 5 +├── Tailwind CSS 4 +├── shadcn/ui (组件库) +├── Recharts (图表) +└── Sonner (Toast 通知) + +后端: +├── Next.js API Routes +├── Prisma ORM +└── PostgreSQL 16 + +实时数据: +└── Alpha Vantage API ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### 数据库模型 -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +``` +┌─────────────┐ ┌─────────────┐ +│ User │───1:N─│ Account │ +└─────────────┘ └─────────────┘ + │ + │ 1:N + ┌─────────────┼─────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌───────────┐ ┌───────────┐ ┌───────────┐ + │Position │ │Transaction│ │ExchangeRate│ + └───────────┘ └───────────┘ └───────────┘ + │ + ▼ + ┌───────────┐ + │ Security │ + └───────────┘ +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +### 表结构说明 -## Learn More +| 表名 | 说明 | +|------|------| +| `User` | 用户表(预留多用户扩展) | +| `Account` | 账户表(按市场类型分组) | +| `Security` | 证券参考表(代码、名称、精度) | +| `Transaction` | 交易流水表 | +| `Position` | 持仓表(查询优化) | +| `ExchangeRate` | 汇率参考表 | -To learn more about Next.js, take a look at the following resources: +### 字段精度设计 -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +| 字段类型 | 精度 | 说明 | +|----------|------|------| +| 价格/金额 | `Decimal(20,4)` | 避免浮点精度丢失 | +| 数量 | `Decimal(20,8)` | 加密货币支持8位小数 | +| 汇率 | `Decimal(20,8)` | 高精度汇率转换 | -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +--- -## Deploy on Vercel +## API 接口 -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### 账户管理 -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/accounts` | GET | 获取账户列表 | +| `/api/accounts` | POST | 创建账户 | + +### 交易管理 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/transactions` | GET | 获取交易流水(支持分页) | +| `/api/transactions` | POST | 记录交易(自动更新持仓/余额) | + +### 持仓管理 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/positions` | GET | 获取持仓列表 | + +### 仪表盘 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/dashboard/stats` | GET | 资产统计概览 | +| `/api/dashboard/analytics` | GET | 持仓分析(含实时价格) | + +### 数据导入 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/import/transactions` | POST | 批量导入交易记录 | + +### 证券管理 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/securities` | GET | 搜索证券 | +| `/api/securities` | POST | 添加证券 | + +### 汇率管理 + +| 接口 | 方法 | 描述 | +|------|------|------| +| `/api/exchange-rates` | GET | 获取汇率列表 | +| `/api/exchange-rates` | POST | 更新汇率 | + +--- + +## 快速开始 + +### 环境要求 + +- Node.js 20+ +- PostgreSQL 16+ +- npm 或 yarn + +### 安装步骤 + +```bash +# 1. 克隆项目 +git clone +cd stock-portfolio + +# 2. 安装依赖 +npm install + +# 3. 配置环境变量 +cp .env.example .env +# 编辑 .env 填入数据库连接信息 + +# 4. 初始化数据库 +npx prisma migrate dev --name init +npx prisma db seed + +# 5. 启动开发服务器 +npm run dev +``` + +### 环境变量 + +```env +# 数据库连接 +DATABASE_URL="postgresql://postgres:password@localhost:5432/stock_portfolio" + +# Alpha Vantage API Key(用于获取实时股价) +ALPHA_VANTAGE_API_KEY="your-api-key" + +# HTTP 代理(可选,用于解决 API 访问问题) +HTTP_PROXY="http://192.168.48.171:7893" +HTTPS_PROXY="http://192.168.48.171:7893" +``` + +--- + +## 使用指南 + +### 添加交易记录 + +1. 点击右上角「**记录交易**」按钮 +2. 选择账户和交易类型 +3. 输入证券代码(支持搜索) +4. 填写数量、价格、金额 +5. 点击「**确认记录**」 +6. 在确认对话框中核实信息,点击「**确认**」 + +### 导入批量交易 + +1. 点击右上角「**导入**」按钮 +2. 点击「**下载模板**」获取 CSV 模板 +3. 按照模板格式填写交易数据 +4. 选择填写好的 CSV 文件 +5. 预览导入数据(有效/错误标记) +6. 点击「**导入**」执行批量导入 + +### 导出数据 + +1. 点击右上角「**导出**」按钮 +2. 自动下载持仓 CSV 文件 +3. 在「交易流水」标签页也可导出交易记录 + +### 字段说明 + +#### 交易模板 CSV 格式 + +```csv +时间,类型,证券代码,数量,价格,金额,手续费,币种,备注 +2024-01-15 10:30:00,BUY,AAPL,10,185.50,1855.00,1.00,USD,买入苹果 +``` + +| 字段 | 必填 | 说明 | +|------|------|------| +| 时间 | 是 | 格式:`YYYY-MM-DD HH:mm:ss` | +| 类型 | 是 | `BUY`/`SELL`/`DEPOSIT`/`WITHDRAW`/`DIVIDEND` | +| 证券代码 | 否 | 买入/卖出/分红时填写 | +| 数量 | 否 | 股票数量 | +| 价格 | 否 | 单价 | +| 金额 | 是 | 总金额 | +| 手续费 | 否 | 交易手续费 | +| 币种 | 是 | `USD`/`CNY`/`HKD`/`USDT` | +| 备注 | 否 | 任意备注信息 | + +--- + +## 成本计算 + +### 平均成本法 (Average Cost) + +系统采用**平均成本法**计算持仓成本: + +``` +新平均成本 = (现有成本 × 现有数量 + 新买入成本) / (现有数量 + 新买入数量) +``` + +### 示例 + +``` +初始:买入 10 股 AAPL,价格 $150 + → 平均成本 = $150 + +再次:买入 10 股 AAPL,价格 $160 + → 新平均成本 = ($150×10 + $160×10) / 20 = $155 +``` + +--- + +## 多币种处理 + +### 汇率转换 + +系统以 **USD** 为基准货币,将所有资产折算为 USD 进行汇总: + +| 币种 | 汇率 (示例) | +|------|-------------| +| USD | 1.0 | +| CNY | 0.137 | +| HKD | 0.129 | +| USDT | 1.0 | + +### 更新汇率 + +通过 `/api/exchange-rates` 接口或数据库直接更新汇率参考表。 + +--- + +## 界面预览 + +### 资产概览 + +``` +┌─────────────────────────────────────────────────────────┐ +│ [Logo] 投资持仓管理 [账户▼] [导入] [导出] [记录交易] │ +├─────────────────────────────────────────────────────────┤ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 总资产 │ │ 浮动盈亏 │ │ 持仓市值 │ │ 账户数量 │ │ +│ │ $125,430 │ │ +$3,250 │ │ $98,200 │ │ 4 │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### 持仓明细 + +| 证券 | 市场 | 数量 | 成本价 | 当前价 | 市值 | 盈亏 | +|------|------|------|--------|--------|------|------| +| AAPL | 美股 | 50 | $150.00 | $178.50 | $8,925 | +$1,425 (+19.0%) | +| MSFT | 美股 | 20 | $380.00 | $415.20 | $8,304 | +$704 (+9.3%) | +| BTC | 加密 | 0.5 | $60,000 | $67,500 | $33,750 | +$3,750 (+12.5%) | + +--- + +## 项目结构 + +``` +stock-portfolio/ +├── prisma/ +│ ├── schema.prisma # 数据库模型 +│ ├── seed.ts # 初始化数据 +│ └── migrations/ # 数据库迁移 +├── src/ +│ ├── app/ +│ │ ├── api/ # API 路由 +│ │ │ ├── accounts/ +│ │ │ ├── transactions/ +│ │ │ ├── positions/ +│ │ │ ├── securities/ +│ │ │ ├── exchange-rates/ +│ │ │ ├── dashboard/ +│ │ │ └── import/ +│ │ ├── page.tsx # 主页面 +│ │ ├── layout.tsx # 布局 +│ │ └── globals.css # 全局样式 +│ ├── components/ +│ │ └── ui/ # shadcn/ui 组件 +│ ├── lib/ +│ │ ├── api.ts # API 调用封装 +│ │ ├── prisma.ts # Prisma 客户端 +│ │ ├── import-export.ts # 导入导出工具 +│ │ └── price-service.ts # 价格服务 +│ └── types/ +│ └── index.ts # 类型定义 +├── .env # 环境变量 +├── .env.example # 环境变量示例 +├── package.json +├── tsconfig.json +├── next.config.ts +└── README.md +``` + +--- + +## 常见问题 + +### Q: 实时价格如何获取? + +A: 系统使用 Alpha Vantage API 获取美股实时价格。需要配置 `ALPHA_VANTAGE_API_KEY` 环境变量。 + +### Q: 支持哪些市场? + +A: 目前支持美股 (US)、A股 (CN)、港股 (HK)、加密货币 (CRYPTO) 四个市场。 + +### Q: 成本计算使用什么算法? + +A: 系统使用**平均成本法 (Average Cost)** 计算持仓成本。 + +### Q: 基准货币是什么? + +A: 系统以 **USD** 为基准货币,所有资产会折算为 USD 进行汇总展示。 + +--- + +## 许可证 + +MIT License - 详见 [LICENSE](LICENSE) 文件 + +--- + +## 更新日志 + +### v1.0.0 (2026-04-12) + +- ✨ 初始版本发布 +- 支持多市场账户管理 +- 支持交易记录(买入/卖出/入金/出金/分红) +- 支持持仓统计和盈亏计算 +- 支持数据导入导出(CSV) +- 支持 Alpha Vantage 实时价格 +- 响应式深色模式界面 diff --git a/src/app/page.tsx b/src/app/page.tsx index f6ce3d3..7bd5bc6 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -7,19 +7,17 @@ import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from '@/components/ui/dialog' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Separator } from '@/components/ui/separator' -import { Tabs as TabsPrimitive } from '@/components/ui/tabs' import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend, - LineChart, Line, XAxis, YAxis, CartesianGrid } from 'recharts' import { Wallet, TrendingUp, TrendingDown, Plus, ArrowUpRight, ArrowDownRight, Bitcoin, Building2, Globe2, RefreshCw, DollarSign, Search, Check, - Download, Upload, BarChart3, TrendingUpIcon + Download, Upload, } from 'lucide-react' import { fetchAccounts, fetchTransactions, fetchPositions, @@ -33,6 +31,7 @@ import { TRANSACTION_IMPORT_TEMPLATE, parseImportCSV, validateImportTransaction, ImportTransaction } from '@/lib/import-export' +// 市场图标映射 const marketIcons: Record = { US: , CN: , @@ -40,6 +39,7 @@ const marketIcons: Record = { CRYPTO: , } +// 市场颜色配置 const marketColors: Record = { US: '#3b82f6', CN: '#ef4444', @@ -47,6 +47,7 @@ const marketColors: Record = { CRYPTO: '#eab308', } +// 持仓分析数据结构 interface PositionAnalytics { symbol: string name: string @@ -67,6 +68,7 @@ interface PositionAnalytics { isCrypto: boolean } +// 分析汇总数据结构 interface AnalyticsSummary { totalCostBasis: number totalMarketValue: number @@ -75,7 +77,9 @@ interface AnalyticsSummary { positionCount: number } +// 主页面组件 export default function Dashboard() { + // 状态定义 const [accounts, setAccounts] = useState([]) const [transactions, setTransactions] = useState([]) const [positions, setPositions] = useState([]) @@ -90,6 +94,8 @@ export default function Dashboard() { const [filteredSecurities, setFilteredSecurities] = useState([]) const [importFile, setImportFile] = useState(null) const [importData, setImportData] = useState([]) + + // 交易表单状态 const [txForm, setTxForm] = useState({ type: 'BUY' as TransactionType, symbol: '', @@ -106,6 +112,7 @@ export default function Dashboard() { const loadData = useCallback(async () => { try { setLoading(true) + // 并行加载所有数据 const [accountsData, transactionsData, positionsData, securitiesData, analyticsData] = await Promise.all([ fetchAccounts(), fetchTransactions({ limit: 50 }), @@ -120,6 +127,7 @@ export default function Dashboard() { setSecurities(securitiesData) setAnalytics(analyticsData) + // 默认选中第一个账户 if (accountsData.length > 0 && !selectedAccount) { setSelectedAccount(accountsData[0].id) } @@ -134,7 +142,7 @@ export default function Dashboard() { loadData() }, [loadData]) - // 搜索证券 + // 搜索证券(根据输入实时过滤) useEffect(() => { if (symbolSearch.length >= 1) { const filtered = securities.filter(s => @@ -147,7 +155,7 @@ export default function Dashboard() { } }, [symbolSearch, securities]) - // 选择证券后自动填充价格 + // 选择证券后自动填充价格和币种 const handleSelectSecurity = (symbol: string) => { const price = analytics?.prices[symbol]?.price || 0 const sec = securities.find(s => s.symbol === symbol) @@ -161,7 +169,7 @@ export default function Dashboard() { setFilteredSecurities([]) } - // 提交交易 + // 提交交易记录 const handleSubmitTx = async () => { try { const accountId = selectedAccount || accounts[0]?.id @@ -192,7 +200,7 @@ export default function Dashboard() { } } - // 重置表单 + // 重置交易表单 const resetTxForm = () => { const account = accounts.find(a => a.id === selectedAccount) setTxForm({ @@ -214,7 +222,7 @@ export default function Dashboard() { setShowTxDialog(true) } - // 计算确认信息 + // 获取确认对话框显示信息 const getConfirmInfo = () => { const account = accounts.find(a => a.id === selectedAccount) const typeLabel = transactionTypeLabels[txForm.type] @@ -233,7 +241,7 @@ export default function Dashboard() { } } - // 导出交易记录 + // 导出交易记录为 CSV const handleExportTransactions = () => { const csv = exportTransactionsToCSV(transactions) const date = new Date().toISOString().slice(0, 10) @@ -241,7 +249,7 @@ export default function Dashboard() { toast.success('交易记录已导出') } - // 导出持仓 + // 导出持仓记录为 CSV const handleExportPositions = () => { const csv = exportPositionsToCSV(positions.map(p => ({ ...p, @@ -257,7 +265,7 @@ export default function Dashboard() { toast.success('持仓记录已导出') } - // 下载导入模板 + // 下载导入模板 CSV const handleDownloadTemplate = () => { downloadCSV(TRANSACTION_IMPORT_TEMPLATE, 'transaction_import_template.csv') } @@ -273,14 +281,14 @@ export default function Dashboard() { reader.onload = (event) => { const content = event.target?.result as string const { headers, rows } = parseImportCSV(content) - + // 验证每一行数据 const validated = rows.map(row => validateImportTransaction(row, headers)) setImportData(validated) } reader.readAsText(file) } - // 执行导入 + // 执行批量导入 const handleExecuteImport = async () => { if (!selectedAccount) { toast.error('请先选择导入目标账户') @@ -324,7 +332,7 @@ export default function Dashboard() { } } - // 市场分布数据 + // 市场分布数据(用于饼图) const marketDistribution = analytics?.summary ? [ { name: '美股', value: analytics.summary.totalMarketValue * 0.6, color: marketColors.US }, { name: 'A股', value: analytics.summary.totalMarketValue * 0.2, color: marketColors.CN }, @@ -332,6 +340,7 @@ export default function Dashboard() { { name: '加密', value: analytics.summary.totalMarketValue * 0.05, color: marketColors.CRYPTO }, ].filter(item => item.value > 0) : [] + // 加载中状态 if (loading) { return (
@@ -342,7 +351,7 @@ export default function Dashboard() { return (
- {/* 顶部导航 */} + {/* 顶部导航栏 */}
@@ -350,6 +359,7 @@ export default function Dashboard() {

投资持仓管理

+ {/* 账户选择下拉框 */} + {/* 导入按钮 */} + {/* 导出按钮 */} + {/* 记录交易按钮 */}
- {/* 资产概览 */} + {/* 资产概览卡片 */}
+ {/* 总资产卡片 */} 总资产 (USD) @@ -397,6 +411,7 @@ export default function Dashboard() { + {/* 浮动盈亏卡片 */} 浮动盈亏 @@ -411,6 +426,7 @@ export default function Dashboard() { + {/* 持仓市值卡片 */} 持仓市值 @@ -422,6 +438,7 @@ export default function Dashboard() { + {/* 账户数量卡片 */} 账户数量 @@ -473,7 +490,7 @@ export default function Dashboard() { - {/* 盈亏统计 */} + {/* 持仓分析卡片 */} 持仓分析 @@ -499,7 +516,7 @@ export default function Dashboard() {
- {/* 持仓和交易标签页 */} + {/* 标签页区域:持仓明细、交易流水、分析 */} 持仓明细 @@ -507,6 +524,7 @@ export default function Dashboard() { 分析 + {/* 持仓明细标签页 */} @@ -575,6 +593,7 @@ export default function Dashboard() { + {/* 交易流水标签页 */} @@ -644,9 +663,10 @@ export default function Dashboard() { + {/* 分析标签页 */}
- {/* 资产增长趋势 */} + {/* 资产分布条形图 */} 资产分布 @@ -728,6 +748,7 @@ export default function Dashboard() {
+ {/* 账户选择 */}
+ {/* 交易类型选择 */}
+ {/* 交易时间 */}
+ {/* 备注 */}
+ {/* 提交按钮 */}