smithery.ai

financial-data

金融数据处理技能:交易数据导? Use when: 需要处理交易记录、导?

First seen May 23, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,513 B
  • docs SUMMARY.md 302 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Financial Data (金融数据处理)

💰 核心理念: 金融数据是投资决策的基础,必须确保数据准确性、完整性和一致性。垃圾进,垃圾出。

🔴 第一原则:数据验证优先

❌ 错误做法: 直接导入数据,假设数据正确
✅ 正确做法: 导入 → 验证 → 清洗 → 再验证 → 存储

❌ 错误做法: "这是券商数据,应该没问题"
✅ 正确做法: 任何外部数据都要经过完整验证流程

When to Use This Skill

使用此技能当你需要:

  • 从 IBKR、Gmail、Google Drive 导入交易数据
  • 处理持仓快照数据
  • 计算风险指标(VaR、回撤、夏普比率等)
  • 数据清洗和格式转换
  • 验证数据完整性和一致性
  • 处理多币种数据转换

Not For / Boundaries

此技能不适用于:

  • 实时行情数据获取(参考 api-integration skill)
  • AI 分析和建议生成(参考 agent 相关代码)
  • 数据库 schema 变更(参考 database-migration skill)

Quick Reference

🎯 数据处理工作流

数据源 → 获取原始数据 → 格式验证 → 数据清洗 → 业务验证 → 存储 → 确认
   ↓                                                    ↓
 IBKR/Gmail/Drive                                    失败 → 记录错误 → 人工处理

📋 数据导入前必问清单

问题 目的
1. 数据源是什么? 确定解析格式(XML/CSV/JSON)
2. 数据时间范围? 避免重复导入或遗漏
3. 币种是什么? 确定汇率转换需求
4. 有没有已存在的数据? 决定是覆盖还是增量更新
5. 数据量有多大? 评估是否需要分批处理

✅ 数据质量检查清单

检查项 说明 严重程度
必填字段完整 ticker, date, quantity 等 🔴 阻断
数值范围合理 价格 > 0, 数量 ≠ 0 🔴 阻断
日期格式正确 YYYY-MM-DD 🔴 阻断
币种有效 USD/HKD/CNY 🟡 警告
无重复记录 同一交易不重复 🟡 警告
数据连续性 无缺失日期 🟢 提示

数据源集成指南

1. IBKR Flex Query 导入

IBKR 是主要数据源,通过 Flex Query API 获取数据。

配置要求:

// 环境变量
VITE_CORS_PROXY_URL=https://your-proxy.workers.dev

// Flex Query 配置
const IB_TOKEN = "your_token";
const IB_QUERY_ID = "your_query_id";

数据获取流程:

1. 请求生成报表 (SendRequest)
2. 等待报表生成 (轮询 GetStatement)
3. 解析 XML 响应
4. 提取各类数据:
   - EquitySummaryByReportDateInBase → 账户摘要
   - OpenPosition → 持仓数据
   - Trade → 交易记录
   - ChangeInNAV → 净值变化
   - CashReportCurrency → 多币种现金

关键代码位置:

  • client/src/services/ibkrFlexQuery.ts - IBKR 数据获取
  • client/src/services/ibkrData.ts - IBKR 数据处理

2. Gmail 导入(交易确认邮件)

从券商确认邮件中提取交易数据。

支持的邮件格式:

  • IBKR 交易确认
  • 富途牛牛交易确认
  • 老虎证券交易确认

解析流程:

1. 通过 Gmail API 获取邮件
2. 解析邮件正文(HTML/纯文本)
3. 使用正则表达式提取交易信息
4. 验证并格式化数据

3. Google Drive 导入(CSV/Excel)

从 Google Drive 导入历史数据文件。

支持的文件格式:

  • CSV(推荐)
  • Excel (.xlsx)

CSV 格式要求:

date,ticker,action,quantity,price,fee,currency,notes
2025-01-15,AAPL,BUY,100,185.50,1.00,USD,加仓
2025-01-16,AAPL,SELL,50,188.00,1.00,USD,止盈

数据验证规则

交易记录验证

// 必填字段验证
const requiredFields = ['date', 'ticker', 'action', 'quantity', 'price'];

// 数值范围验证
const validations = {
  price: (v: number) => v > 0,
  quantity: (v: number) => v !== 0,
  fee: (v: number) => v >= 0,
};

// 枚举值验证
const validActions = ['BUY', 'SELL', 'SHORT', 'COVER', 'DEPOSIT', 'WITHDRAW'];
const validCurrencies = ['USD', 'HKD', 'CNY'];
const validMarkets = ['US', 'HK', 'CN'];

持仓数据验证

// 持仓一致性检查
function validatePositions(positions: Position[], transactions: Transaction[]) {
  // 1. 计算交易累计数量
  const calculatedQty = calculateFromTransactions(transactions);
  
  // 2. 与持仓数量对比
  for (const pos of positions) {
    const expected = calculatedQty[pos.ticker] || 0;
    if (pos.quantity !== expected) {
      console.warn(`持仓不一致: ${pos.ticker} 实际=${pos.quantity} 计算=${expected}`);
    }
  }
}

净值数据验证

// 净值连续性检查
function validateNetWorthHistory(records: NetWorthRecord[]) {
  const sorted = records.sort((a, b) => a.date.localeCompare(b.date));
  
  for (let i = 1; i < sorted.length; i++) {
    const prev = sorted[i - 1];
    const curr = sorted[i];
    
    // 检查日期连续性(工作日)
    const daysDiff = getBusinessDaysDiff(prev.date, curr.date);
    if (daysDiff > 1) {
      console.warn(`净值数据缺失: ${prev.date} 到 ${curr.date}`);
    }
    
    // 检查异常波动(单日变化超过 10%)
    const changePercent = (curr.netWorth - prev.netWorth) / prev.netWorth * 100;
    if (Math.abs(changePercent) > 10) {
      console.warn(`异常波动: ${curr.date} 变化 ${changePercent.toFixed(2)}%`);
    }
  }
}

数据清洗最佳实践

1. 股票代码标准化

// 统一股票代码格式
function normalizeSymbol(symbol: string, market: Market): string {
  switch (market) {
    case 'HK':
      // 港股:补齐到 5 位数字
      return symbol.replace(/^0+/, '').padStart(5, '0');
    case 'CN':
      // A股:保持 6 位数字
      return symbol.padStart(6, '0');
    case 'US':
    default:
      // 美股:大写字母
      return symbol.toUpperCase().replace(/[^A-Z]/g, '');
  }
}

2. 日期格式标准化

// 统一日期格式为 YYYY-MM-DD
function normalizeDate(dateStr: string): string {
  // 处理 IBKR 格式: 20250115
  if (/^\d{8}$/.test(dateStr)) {
    return `${dateStr.slice(0, 4)}-${dateStr.slice(4, 6)}-${dateStr.slice(6, 8)}`;
  }
  
  // 处理 MM/DD/YYYY 格式
  if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dateStr)) {
    const [m, d, y] = dateStr.split('/');
    return `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
  }
  
  // 已经是标准格式
  return dateStr;
}

3. 金额和汇率处理

// 汇率常量(应从实时数据获取)
const EXCHANGE_RATES = {
  USD_CNY: 7.04,
  HKD_CNY: 0.93,
};

// 转换为 CNY
function toCNY(amount: number, currency: Currency): number {
  switch (currency) {
    case 'USD':
      return amount * EXCHANGE_RATES.USD_CNY;
    case 'HKD':
      return amount * EXCHANGE_RATES.HKD_CNY;
    case 'CNY':
    default:
      return amount;
  }
}

4. 重复数据处理

// 交易记录去重
function deduplicateTransactions(transactions: Transaction[]): Transaction[] {
  const seen = new Set<string>();
  
  return transactions.filter(tx => {
    // 生成唯一键:日期 + 股票 + 动作 + 数量 + 价格
    const key = `${tx.date}_${tx.ticker}_${tx.action}_${tx.quantity}_${tx.price}`;
    
    if (seen.has(key)) {
      console.warn(`发现重复交易: ${key}`);
      return false;
    }
    
    seen.add(key);
    return true;
  });
}

风险指标计算

核心风险指标

指标 公式 说明
最大回撤 (峰值 - 谷值) / 峰值 历史最大亏损幅度
夏普比率 (收益率 - 无风险利率) / 波动率 风险调整后收益
VaR (95%) 历史分位数法 95% 置信度下的最大损失
胜率 盈利交易数 / 总交易数 交易成功率
盈亏比 平均盈利 / 平均亏损 风险回报比

计算示例

// 计算最大回撤
function calculateMaxDrawdown(netWorthHistory: number[]): {
  maxDrawdown: number;
  maxDrawdownPercent: number;
  peakDate: string;
  troughDate: string;
} {
  let peak = netWorthHistory[0];
  let maxDrawdown = 0;
  let maxDrawdownPercent = 0;
  
  for (const value of netWorthHistory) {
    if (value > peak) {
      peak = value;
    }
    
    const drawdown = peak - value;
    const drawdownPercent = drawdown / peak;
    
    if (drawdownPercent > maxDrawdownPercent) {
      maxDrawdown = drawdown;
      maxDrawdownPercent = drawdownPercent;
    }
  }
  
  return { maxDrawdown, maxDrawdownPercent, peakDate: '', troughDate: '' };
}

// 计算夏普比率
function calculateSharpeRatio(
  returns: number[],
  riskFreeRate: number = 0.02
): number {
  const avgReturn = returns.reduce((a, b) => a + b, 0) / returns.length;
  const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / returns.length;
  const stdDev = Math.sqrt(variance);
  
  // 年化
  const annualizedReturn = avgReturn * 252;
  const annualizedStdDev = stdDev * Math.sqrt(252);
  
  return (annualizedReturn - riskFreeRate) / annualizedStdDev;
}

数据库表结构

详细的数据库 schema 定义请参考:

  • references/data-schemas.md - 完整的数据结构定义

核心表概览

表名 用途 主键
transactions 交易记录 uuid
stock_positions 股票持仓快照 bigserial
option_positions 期权持仓快照 bigserial
dashboard_snapshots 每日驾驶舱快照 bigserial
risk_metrics 风险指标 bigserial
watchlist 观察列表 uuid

Examples

Example 1: 从 IBKR 导入数据

Input: "需要从 IBKR 导入最新的交易和持仓数据"

Steps:

  1. 调用 fetchIBKRFlexQuery() 获取数据
  2. 验证返回的数据完整性
  3. 调用 syncIBKRToSupabase() 同步到数据库
  4. 验证同步结果

Expected Output:

import { syncIBKRToSupabase } from '@/services/ibkrFlexQuery';

const result = await syncIBKRToSupabase(true, (stage, progress) => {
  console.log(`[${progress}%] ${stage}`);
});

if (result.success) {
  console.log('同步成功:', result.data);
} else {
  console.error('同步失败:', result.message);
}

Example 2: 验证持仓数据一致性

Input: "检查持仓数据是否与交易记录一致"

Steps:

  1. 获取所有交易记录
  2. 计算每个股票的累计持仓
  3. 与当前持仓对比
  4. 输出差异报告

Expected Output:

// 获取数据
const transactions = await getTransactions();
const positions = await getPositions();

// 计算预期持仓
const expectedPositions = calculatePositionsFromTransactions(transactions);

// 对比
for (const pos of positions) {
  const expected = expectedPositions[pos.ticker] || 0;
  if (pos.quantity !== expected) {
    console.warn(`❌ ${pos.ticker}: 实际=${pos.quantity}, 预期=${expected}`);
  } else {
    console.log(`✅ ${pos.ticker}: ${pos.quantity}`);
  }
}

Example 3: 计算风险指标

Input: "计算最近 30 天的风险指标"

Steps:

  1. 获取净值历史数据
  2. 计算日收益率
  3. 计算各项风险指标
  4. 存储到 risk_metrics 表

Expected Output:

// 获取净值数据
const netWorthHistory = await getNetWorthHistory(30);

// 计算日收益率
const returns = calculateDailyReturns(netWorthHistory);

// 计算风险指标
const metrics = {
  maxDrawdown: calculateMaxDrawdown(netWorthHistory),
  sharpeRatio: calculateSharpeRatio(returns),
  var95: calculateVaR(returns, 0.95),
  volatility: calculateVolatility(returns),
};

// 存储
await saveRiskMetrics(metrics);

常见问题处理

Q1: IBKR 数据获取失败

可能原因:

  1. Token 过期
  2. CORS 代理不可用
  3. 网络问题

解决方案:

// 1. 检查 Token 有效性
// 登录 IBKR 账户管理 → 报表 → Flex Queries → 检查 Token

// 2. 尝试备用代理
const FALLBACK_PROXIES = [
  'https://corsproxy.io/?',
  'https://api.allorigins.win/raw?url=',
];

// 3. 增加重试次数和超时时间

Q2: 数据重复导入

解决方案:

// 使用 upsert 而非 insert
const { error } = await supabase
  .from('transactions')
  .upsert(transactions, {
    onConflict: 'date,ticker,action,quantity,price',
    ignoreDuplicates: true
  });

Q3: 汇率数据不准确

解决方案:

// 1. 使用实时汇率 API
const rates = await fetchExchangeRates();

// 2. 或使用 IBKR 提供的汇率
const fxRate = trade.fxRateToBase;

References

  • references/data-schemas.md: 完整的数据结构定义
  • references/validation-rules.md: 数据验证规则详解
  • references/import-templates.md: 数据导入模板

Maintenance

  • Sources: 项目实际代码, IBKR API 文档, 金融数据处理最佳实践
  • Last Updated: 2025-01-01
  • Known Limits:

- IBKR Flex Query 有请求频率限制 - 历史数据导入需要手动触发 - 汇率数据可能有延迟