Skip to content

How to Use AI Agents as Portfolio Advisors Instead of Market Pickers

Problem

When I tried to use AI to pick stocks from the entire market, I hit a wall. The model kept recommending stocks it “learned” from its training data - the historical winners. Backtesting results looked great, but that’s because the model already knew which stocks won in the past.

I realized the problem: training data bias makes market picking unreliable.

A commenter on Reddit captured this perfectly:

“This makes more sense than trying to pick winners from the whole market where the model is biased by its training set making backtesting problematic.”

So I pivoted to a different approach: using AI as a trusted advisor for my existing portfolio and watchlist, not as a market scanner.

Why Portfolio Advisory Beats Market Picking

Let me show the difference with a diagram.

Market Picking Problem:

Market picker problem diagram
Traditional AI Market Picker:
┌──────────────────┐
│ Scan 4000+ │ ← Too many candidates
│ stocks │
└──────────────────┘
┌──────────────────┐
│ Training │ ← Model biased by historical
│ data bias │ winners it "learned" from
└──────────────────┘
┌──────────────────┐
│ Backtesting │ ← Results unreliable due
│ unreliable │ to look-ahead bias
└──────────────────┘

When I ask an AI to scan thousands of stocks, it applies patterns it learned from past winners. But those patterns may not work today - the model is essentially cheating by knowing what already happened.

Portfolio Advisor Approach:

Portfolio advisor approach diagram
AI Portfolio Advisor:
┌──────────────────┐
│ Your 20-50 │ ← Focused universe
│ watchlist │
└──────────────────┘
┌──────────────────┐
│ Your context │ ← Your risk tolerance,
│ & goals │ timeline, strategy
└──────────────────┘
┌──────────────────┐
│ Daily debate │ ← Fresh analysis,
│ & reasoning │ no stale picks
└──────────────────┘

This approach works because:

  1. Smaller universe - 20-50 positions vs 4000+ stocks means better signal-to-noise ratio
  2. No pattern matching - I’m not asking the model to predict based on historical winners
  3. My context - The advisor learns my portfolio context, not generic market patterns
  4. Actionable - Recommendations on positions I actually own or watch
  5. My moat - My unique portfolio context is the differentiator, not the AI model

The Architecture

I designed a daily debate system where multiple AI agents analyze each position and challenge each other’s views.

Daily debate system architecture
┌─────────────────────────────┐
│ Data Ingestion Layer │
│ - Portfolio positions │
│ - Watchlist items │
│ - Market data feeds │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Analysis Agents Layer │
│ - Fundamental agent │
│ - Technical agent │
│ - Sentiment agent │
│ - News/events agent │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Debate Orchestrator │
│ - Cross-examine views │
│ - Challenge assumptions │
│ - Synthesize consensus │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Recommendation Engine │
│ - Buy/Sell/Hold votes │
│ - Confidence scoring │
│ - Risk assessment │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Human Decision Layer │
│ - Review recommendations │
│ - Override with context │
│ - Final approval │
└─────────────────────────────┘

Each agent has a specific role:

  • Fundamental Analyst: Earnings, cash flow, valuation metrics, competitive position
  • Technical Analyst: Price action, support/resistance, momentum, volume
  • Sentiment Analyst: Social media, analyst ratings, institutional flows
  • Devil’s Advocate: Challenges bullish consensus, surfaces risks
  • Portfolio Context: Evaluates position within my portfolio (concentration, correlation)

The devil’s advocate is critical - without it, all agents might agree because they see the same bullish signals. I need someone to ask “what could go wrong?”

The Core Implementation

Let me show the key data structures and the debate orchestrator.

portfolio_advisor.py
from dataclasses import dataclass
from typing import List, Dict, Optional
from datetime import datetime
from enum import Enum
class RecommendationType(Enum):
STRONG_BUY = "strong_buy"
BUY = "buy"
HOLD = "hold"
SELL = "sell"
STRONG_SELL = "strong_sell"
@dataclass
class Position:
symbol: str
quantity: float
cost_basis: float
current_price: float
portfolio_weight: float
sector: str
added_to_watchlist: datetime
@dataclass
class WatchlistItem:
symbol: str
thesis: str # Why I'm watching this
target_price: Optional[float]
added_date: datetime
priority: int # 1-5, 1 being highest
@dataclass
class AgentVote:
agent_name: str
recommendation: RecommendationType
confidence: float # 0-1
reasoning: str
key_factors: List[str]
@dataclass
class DailyRecommendation:
symbol: str
votes: List[AgentVote]
consensus: RecommendationType
consensus_confidence: float
debate_summary: str
action_items: List[str]
timestamp: datetime

These structures capture everything I need: my positions, my watchlist thesis, and how each agent voted.

Now the main orchestrator that runs the daily debate:

debate_orchestrator.py
class PortfolioAdvisor:
"""
AI-powered portfolio advisor that debates recommendations
on existing positions and watchlist.
"""
def __init__(self, portfolio: List[Position], watchlist: List[WatchlistItem]):
self.portfolio = portfolio
self.watchlist = watchlist
self.agents = self._initialize_agents()
self.debate_history = []
def _initialize_agents(self) -> Dict:
"""Initialize specialized analysis agents."""
return {
"fundamental": FundamentalAnalystAgent(),
"technical": TechnicalAnalystAgent(),
"sentiment": SentimentAnalystAgent(),
"devils_advocate": DevilsAdvocateAgent(),
"portfolio_context": PortfolioContextAgent(self.portfolio)
}
def run_daily_debate(self) -> List[DailyRecommendation]:
"""
Run daily debate for all positions and watchlist items.
Returns recommendations with reasoning.
"""
recommendations = []
# Analyze current positions
for position in self.portfolio:
rec = self._debate_position(position)
recommendations.append(rec)
# Analyze watchlist (potential buys)
for item in self.watchlist:
rec = self._debate_watchlist_item(item)
recommendations.append(rec)
# Store for learning
self.debate_history.append({
"date": datetime.now().isoformat(),
"recommendations": recommendations
})
return recommendations
def _debate_position(self, position: Position) -> DailyRecommendation:
"""Run multi-agent debate on a portfolio position."""
votes = []
# Each agent analyzes independently
for agent_name, agent in self.agents.items():
vote = agent.analyze(position)
votes.append(vote)
# Agents challenge each other
debate_log = self._run_debate_rounds(votes, position)
# Synthesize consensus
consensus = self._calculate_consensus(votes)
return DailyRecommendation(
symbol=position.symbol,
votes=votes,
consensus=consensus["recommendation"],
consensus_confidence=consensus["confidence"],
debate_summary=debate_log["summary"],
action_items=self._generate_action_items(consensus, position)
)

The key insight here: I’m not asking the AI to scan markets. I’m asking it to debate my specific positions.

How the Debate Works

The debate has three rounds to prevent groupthink:

Debate rounds diagram
Round 1: Initial Views
┌─────────────────┐
│ Fundamental: │ "BUY - strong earnings growth"
│ Technical: │ "HOLD - near resistance"
│ Sentiment: │ "BUY - positive momentum"
└─────────────────┘
Round 2: Challenge Phase
┌─────────────────┐
│ Devil's Advocate│ "What if earnings are inflated?"
│ │ "Resistance could break..."
└─────────────────┘
Round 3: Rebuttals & Consensus
┌─────────────────┐
│ Synthesis │ "HOLD with caution - wait for
│ │ earnings confirmation"
└─────────────────┘

Here’s the challenge logic:

debate_rounds.py
def _run_debate_rounds(self, votes: List[AgentVote], position: Position) -> Dict:
"""
Facilitate structured debate between agents.
Devil's advocate challenges bullish consensus.
"""
debate_log = {"rounds": [], "summary": ""}
# Round 1: Present initial views
debate_log["rounds"].append({
"round": 1,
"type": "initial_views",
"content": [v.__dict__ for v in votes]
})
# Round 2: Challenge phase
challenges = []
for vote in votes:
if vote.recommendation in [RecommendationType.BUY, RecommendationType.STRONG_BUY]:
# Devil's advocate challenges bullish views
challenge = self.agents["devils_advocate"].challenge(
target_agent=vote.agent_name,
position=position,
original_reasoning=vote.reasoning
)
challenges.append(challenge)
debate_log["rounds"].append({
"round": 2,
"type": "challenges",
"content": challenges
})
# Round 3: Rebuttals and consensus
rebuttals = self._collect_rebuttals(challenges, votes)
debate_log["rounds"].append({
"round": 3,
"type": "rebuttals",
"content": rebuttals
})
# Generate summary
debate_log["summary"] = self._synthesize_debate_summary(debate_log)
return debate_log

The consensus calculation weights each agent by their historical accuracy:

consensus.py
def _calculate_consensus(self, votes: List[AgentVote]) -> Dict:
"""
Calculate weighted consensus from agent votes.
Weights based on agent's historical accuracy.
"""
# Convert recommendations to numeric scores
score_map = {
RecommendationType.STRONG_BUY: 2,
RecommendationType.BUY: 1,
RecommendationType.HOLD: 0,
RecommendationType.SELL: -1,
RecommendationType.STRONG_SELL: -2
}
weighted_sum = 0
total_weight = 0
for vote in votes:
weight = self._get_agent_accuracy(vote.agent_name)
score = score_map[vote.recommendation]
weighted_sum += score * weight * vote.confidence
total_weight += weight
consensus_score = weighted_sum / total_weight if total_weight > 0 else 0
# Map back to recommendation
if consensus_score >= 1.5:
rec = RecommendationType.STRONG_BUY
elif consensus_score >= 0.5:
rec = RecommendationType.BUY
elif consensus_score >= -0.5:
rec = RecommendationType.HOLD
elif consensus_score >= -1.5:
rec = RecommendationType.SELL
else:
rec = RecommendationType.STRONG_SELL
return {
"recommendation": rec,
"confidence": abs(consensus_score) / 2
}

Note: I use > instead of > in the comparison to avoid MDX parsing issues.

Daily Automation

I run this every morning before market open:

daily_pipeline.py
import schedule
import time
from datetime import datetime
class DailyAdvisorPipeline:
"""Orchestrates daily portfolio analysis and debate."""
def __init__(self, portfolio: List[Position], watchlist: List[WatchlistItem]):
self.advisor = PortfolioAdvisor(portfolio, watchlist)
self.notification_service = NotificationService()
def run_morning_analysis(self):
"""Run analysis before market open."""
print(f"[{datetime.now()}] Starting morning analysis...")
# Run debate for all positions
recommendations = self.advisor.run_daily_debate()
# Prioritize actionable items
action_items = self._prioritize_actions(recommendations)
# Generate daily report
report = self._generate_report(recommendations, action_items)
# Send notification
self.notification_service.send(
subject="Daily Portfolio Advisor Report",
body=report,
priority="high" if action_items else "normal"
)
print(f"[{datetime.now()}] Analysis complete. {len(action_items)} action items.")
return recommendations
def _prioritize_actions(self, recommendations: List[DailyRecommendation]) -> List[Dict]:
"""Sort recommendations by urgency and confidence."""
action_items = []
for rec in recommendations:
# Strong recommendations with high confidence
if rec.consensus in [RecommendationType.STRONG_BUY, RecommendationType.STRONG_SELL]:
if rec.consensus_confidence > 0.7:
action_items.append({
"symbol": rec.symbol,
"action": rec.consensus.value,
"confidence": rec.consensus_confidence,
"reasoning": rec.debate_summary
})
return sorted(action_items, key=lambda x: x["confidence"], reverse=True)
# Schedule configuration
pipeline = DailyAdvisorPipeline(my_portfolio, my_watchlist)
# Run before market open (9:00 AM ET)
schedule.every().day.at("09:00").do(pipeline.run_morning_analysis)
# Main loop
while True:
schedule.run_pending()
time.sleep(60)

Every day I get a report like:

Daily report example
Subject: Daily Portfolio Advisor Report
Priority: HIGH
Action Items:
1. AAPL - STRONG_SELL (confidence: 0.82)
Reasoning: Fundamental concerns about iPhone growth,
technical breakdown below support, devil's advocate
raised margin compression risk.
2. NVDA - STRONG_BUY (confidence: 0.78)
Reasoning: Earnings beat expectations, sentiment
positive, but watch for regulatory concerns.
Hold Recommendations:
- MSFT, GOOGL, AMZN

Addressing the Differentiation Question

A Reddit commenter asked the critical question:

“Given that this works, I can tell you why it won’t work soon: everybody has access to that, what’d be your differentiator?”

This is valid. If everyone uses the same AI models, where’s the edge?

My answer: my portfolio context is my differentiator.

Generic AI: "Buy NVDA, it's a great stock"
My AI Advisor: "Given your 15% tech exposure and risk-averse profile,
consider reducing NVDA position to maintain sector balance"

The AI knows my:

  • Risk tolerance and investment horizon
  • Existing concentration and correlation
  • Tax lot positions and cost bases
  • Historical decisions and outcomes
  • Personal constraints (ESG preferences, sector limits)

My watchlist reflects my research, my network, my domain expertise. The AI advisor helps me execute on my thesis, not generate generic picks.

Plus, the debate mechanism catches blind spots a single recommendation misses. Even if everyone uses the same base models, the orchestration and debate structure varies by implementation.

Common Pitfalls

I learned these the hard way:

PitfallProblemSolution
Over-trusting AIBlindly following advice leads to lossesAlways maintain human decision layer
Confirmation biasAgents reinforce existing viewsMandatory devil’s advocate role
Stale watchlistItems no longer relevantAutomatic pruning based on recency
Ignoring correlationAll agents see same dataFeed agents different data slices
Over-engineeringSystem too complexStart with 2-3 agents, add complexity later

The most important lesson: AI advises, I decide. Final decisions stay with me.

Implementation Roadmap

If you want to build this, here’s my suggested path:

Phase 1 (Week 1-2): Foundation

  • Set up portfolio and watchlist data structures
  • Implement basic fundamental and technical agents
  • Create simple consensus mechanism

Phase 2 (Week 3-4): Debate Layer

  • Add devil’s advocate agent
  • Implement challenge/rebuttal rounds
  • Build debate logging

Phase 3 (Week 5-6): Automation

  • Set up daily scheduling
  • Build notification system
  • Create reporting

Phase 4 (Week 7-8): Learning

  • Track recommendation outcomes
  • Implement agent weight adjustment
  • Add feedback loops

Start simple. I began with just fundamental and technical agents, then added complexity as I saw what worked.

Summary

In this post, I showed how to use AI agents as portfolio advisors instead of market pickers. The key point is that your portfolio context is your differentiator - not the AI model itself.

The approach works because:

  • Smaller universe (20-50 positions vs 4000+ stocks) improves signal-to-noise
  • No reliance on model’s historical pattern matching
  • Recommendations fit my portfolio and risk profile
  • Debate mechanism surfaces risks I might miss
  • My unique context is the moat

The Reddit commenter who worried about differentiation was half right. If you use AI as a generic stock picker, you have no edge. But when you use AI as your portfolio advisor - analyzing your positions with your context - the combination of human expertise and AI analysis creates something neither could achieve alone.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments