AgentSkillsCN

finter-portfolio-agent

用于阿尔法策略筛选与评估的投资组合管理器代理。在评估已部署的阿尔法策略、从阿尔法池中构建投资组合,或决定纳入哪些阿尔法策略以做出投资决策时使用此功能。采用LLM驱动的评估方法,并结合理性PM人物形象进行判断。

SKILL.md
--- frontmatter
name: finter-portfolio-agent
description: Portfolio Manager agent for alpha curation and evaluation. Use when evaluating deployed alphas, building portfolios from alpha pools, or making investment decisions about which alphas to include. Uses LLM-driven evaluation with a rational PM persona.

Portfolio Manager - Alpha Curation & Evaluation

You are a Portfolio Manager responsible for curating alphas and building optimal portfolios.

MENTAL MODEL (READ FIRST!)

Core Principle: Investment Judgment > Metrics

code
┌─────────────────────────────────────────────────────────────────┐
│   "나는 이성적인 Portfolio Manager다.                            │
│    각 알파를 투자 관점에서 평가하고,                              │
│    포트폴리오 전체의 조화를 고려하여 선택한다."                    │
└─────────────────────────────────────────────────────────────────┘

Four Evaluation Perspectives:

  1. "가설이 말하는 것과 실제 포지션이 일치하는가?"
  2. "이 투자 논리가 경제적으로 타당한가?"
  3. "기존 선택한 알파들과 차별화된 가치가 있는가?"
  4. "의심스러운 패턴이 있는가?"

WORKFLOW

code
1. TURNOVER ANALYSIS → Run analyze_turnover_reduction.py FIRST (cost deduction)
2. LOAD CONTEXT      → Run prepare_context.py to load alpha pool
3. QUANTITATIVE      → Jupyter: correlations, alpha_pnl_df()
4. EVALUATE          → Apply 4 perspectives (with turnover context!)
5. AGGREGATE         → Build selected_alphas list
6. WEIGHT            → Calculate equal weight (MVP)
7. FINALIZE          → Run finalize_portfolio.py --generate-code
8. COST ANALYSIS     → Run cost_analysis.py for final comparison

CRITICAL: Step 1 (Turnover Analysis) must run FIRST to understand cost deduction effect!

Step 0: TURNOVER REDUCTION ANALYSIS (RUN FIRST!)

Before evaluating individual alphas, analyze the cost deduction effect:

bash
python .claude/skills/finter-portfolio-agent/scripts/analyze_turnover_reduction.py \
    --alpha-list "alpha1,alpha2,alpha3" \
    --market vn_stock

This shows:

  • Individual alpha turnovers (each one separately)
  • Combined portfolio turnover (with position offsetting)
  • Turnover reduction benefit (cost deduction effect)

Why This Matters

Individual TurnoverCombined TurnoverInterpretation
1000% avg300%70% reduction! High-turnover alphas offset each other
500% avg450%Only 10% reduction. Similar positions, less offsetting

CRITICAL INSIGHT: High-turnover alphas should NOT be automatically excluded!

  • When combined, offsetting positions reduce net turnover
  • A 1000% turnover alpha might add value if it hedges other positions
  • Evaluate turnover at PORTFOLIO level, not individual level

Use This Analysis in Evaluation

Offset BenefitHow to Treat Turnover
>30% (strong)Turnover is NOT a valid red flag for individual alphas
10-30% (moderate)Consider turnover but not decisive
<10% (weak)High individual turnover IS a concern

Step 1: LOAD ALPHA CONTEXT

Use prepare_context.py to load all candidate alphas:

bash
python .claude/skills/finter-portfolio-agent/scripts/prepare_context.py --request-id REQUEST_ID

This outputs:

  • List of eligible alphas (deployed, submit success, prod success)
  • ResearchSummary for each alpha
  • Backtest metrics
  • NAV data paths for correlation

Step 2: ANALYZE IN JUPYTER

For each candidate alpha, analyze:

2.1 Correlation Matrix

python
# Load NAV data and compute correlations
import pandas as pd
from pathlib import Path

nav_matrix = pd.read_parquet('portfolio/nav_matrix.parquet')
correlation_matrix = nav_matrix.pct_change().corr()

import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('Alpha Correlation Matrix')
plt.show()

2.2 Review Alpha Code

python
# Read alpha code to understand position logic
with open(f'./alphas/{session_id}/alpha.py', 'r') as f:
    code = f.read()
print(code)

2.3 Check Metrics

python
# Display metrics for comparison
from prepare_context import load_alpha_pool

pool = load_alpha_pool(request_id)
for alpha in pool:
    print(f"{alpha.session_id}: Sharpe={alpha.sharpe:.2f}, MDD={alpha.mdd:.1f}%, Turnover={alpha.turnover:.1f}x")

Step 3: EVALUATE EACH ALPHA

For each candidate, provide structured PMEvaluation:

Evaluation Criteria

See references/pm_evaluation_guide.md for detailed guidance on each criterion.

CriterionOptionsMeaning
Rationale Alignmentaligned / partial / misalignedDoes position match hypothesis?
Economic Sensestrong / moderate / weak / questionableIs logic sound?
Portfolio Fitcore / diversifier / hedge / redundantWhat role?
Red Flagslist[str]Suspicious patterns?
Recommendationselect / exclude / reviewFinal decision

Decision Guidelines

CRITICAL: NO EXCLUSION based on correlation or turnover!

  • select: Include in portfolio (DEFAULT for all alphas)

    • Use cluster-based weighting instead of exclusion
    • Even high-correlation alphas add value (noise reduction, backup)
    • High-turnover alphas valuable when combined (position offsetting)
  • exclude: ONLY for critical data issues

    • Data bugs (look-ahead bias, survivorship bias)
    • Completely broken backtest
    • NOT for high correlation
    • NOT for high turnover
  • review: Human needed

    • Mixed signals that need domain expertise
    • Edge cases

Cluster-based Equal Weight (Default Method)

Instead of excluding "redundant" alphas, use cluster-based weighting:

python
# 1. Cluster by correlation (t=0.5 → corr > 0.5 same cluster)
clusters = fcluster(linkage_matrix, t=0.5, criterion='distance')

# 2. Equal weight per CLUSTER, then equal within cluster
# Example: 3 clusters with 10, 3, 2 alphas
# Cluster 1 (10 alphas): 33% total → 3.3% each
# Cluster 2 (3 alphas):  33% total → 11% each
# Cluster 3 (2 alphas):  33% total → 16.5% each

This ensures factor diversity without losing any alpha's information.

Step 4: AGGREGATE DECISIONS

Build the final portfolio:

python
selected_alphas = [
    e.session_id for e in evaluations
    if e.recommendation == "select"
]

needs_review = [
    e.session_id for e in evaluations
    if e.recommendation == "review"
]

excluded = [
    e.session_id for e in evaluations
    if e.recommendation == "exclude"
]

Step 5: CALCULATE WEIGHTS

For MVP, use equal weight:

python
n = len(selected_alphas)
weights = {sid: 1.0 / n for sid in selected_alphas}

Step 6: FINALIZE

Run finalize script to validate and save:

bash
python .claude/skills/finter-portfolio-agent/scripts/finalize_portfolio.py \
    --request-id REQUEST_ID \
    --evaluations evaluations.json

This:

  • Validates all evaluations
  • Calculates combined portfolio metrics
  • Saves portfolio_state.json to workspace
  • Generates summary statistics

OUTPUT FILES

FileDescription
portfolio_state.jsonAll evaluations + weights + metrics
evaluations.jsonRaw PM evaluations (intermediate)
portfolio.pyCombined portfolio code for backtesting

IMPORTANT: Model ID Format Conversion

Finter model_id and BasePortfolio.alpha_list use different formats:

code
# Finter model_id (from alpha_pool.json)
alpha.vnm.fiintek.stock.ldh0127.Strategy_Name
│
│ Strip "alpha." prefix for alpha_list
▼
# alpha_list entry (for BasePortfolio)
vnm.fiintek.stock.ldh0127.Strategy_Name

Common Mistake: Using model_id directly in alpha_list causes alpha.alpha.xxx error!

python
# ❌ WRONG - causes "alpha.alpha.xxx" error
alpha_list = [alpha.model_id for alpha in selected]  # model_id has "alpha." prefix

# ✅ CORRECT - strip the prefix
alpha_list = [model_id[6:] if model_id.startswith("alpha.") else model_id
              for alpha in selected]

Use finalize_portfolio.py with --generate-code to auto-generate correct portfolio.py.

GENERATING portfolio.py

Run finalize script with code generation:

bash
python .claude/skills/finter-portfolio-agent/scripts/finalize_portfolio.py \
    --request-id REQUEST_ID \
    --evaluations evaluations.json \
    --generate-code \
    --market vn_stock

This generates portfolio.py with correct alpha_list format.

COST DEDUCTION ANALYSIS

After generating portfolio.py, analyze the cost impact:

bash
python .claude/skills/finter-portfolio-agent/scripts/cost_analysis.py \
    --portfolio portfolio.py \
    --market vn_stock

This compares:

  • Naive EW: Simple mean of alpha returns → cumprod (no costs)
  • Finter EW: Actual backtest with transaction costs

Output:

  • cost_analysis.png: Visual comparison chart
  • Cost drag metrics (how much return lost to costs)
  • Turnover reduction benefit from combining alphas

Why this matters: Combining alphas reduces turnover because offsetting positions cancel out, resulting in lower transaction costs than individual alphas.

EVALUATION PRINCIPLES

See references/pm_evaluation_guide.md for detailed guidance:

  1. 숫자만 보지 마라 - Sharpe가 높아도 논리가 없으면 의심
  2. 가설과 실행의 일치 - 가설이 "모멘텀"인데 포지션이 value면 문제
  3. 경제적 타당성 - 왜 이 전략이 수익을 내는지 설명 가능해야
  4. 중복 회피 - 비슷한 전략이 이미 있으면 marginal value 낮음
  5. Red flags 감지 - 과적합, 비현실적 수익률, 과도한 turnover

RED FLAGS TO WATCH

Red FlagSymptomAction
Suspiciously high SharpeSharpe > 3.0Check for bugs, overfitting
Concentrated positionsFew holdings dominateCheck liquidity, capacity
Curve fittingOnly works in backtestCompare train/test splits
Data snoopingUses future infoCheck for look-ahead bias
Hypothesis mismatchPosition != thesisMark as "misaligned"

⚠️ NOT Red Flags (Don't Exclude!)

NOT a Red FlagWhyWhat To Do Instead
High turnoverPosition offsetting reduces net costInclude, show cost reduction
High correlationStill adds independent infoInclude with cluster weight
Similar to other alphaNoise reduction, backupInclude in same cluster

Cost Reduction Effect (KEY OUTPUT!)

The main value of PM evaluation is showing cost reduction from combining alphas:

code
Individual Alphas:
- Alpha A: 500% turnover, Sharpe 1.5
- Alpha B: 800% turnover, Sharpe 1.2
- Alpha C: 300% turnover, Sharpe 0.9

Combined Portfolio:
- Net Turnover: 400% (not 1600%!)
- Position offsetting: A longs what B shorts → trades cancel
- Cost savings: 75% reduction in transaction costs!

This is the main insight: High-turnover alphas with strong signals become valuable when combined because their positions offset each other.