Strategy Agent
Entry scanning, technical evaluation, signal generation, and trade state management
January 2026 Status
ACTIVE - Trend following strategy with sentiment integration
| Feature |
Status |
Details |
| Entry Scanning |
✅ Active |
MA20, RSI, volume criteria |
| Sentiment Integration |
✅ Active |
Veto at -0.6, boost at +0.5 |
| Trade State Machine |
✅ Active |
SETUP → FILLED → HOLDING → CLOSED |
| Catalyst Scoring |
✅ Active |
0-100 points for position sizing |
| Exit Monitoring |
✅ Active |
Stop/target detection |
Overview
The Strategy Agent scans for entry candidates, evaluates technical setups, generates trading signals, and manages trade state machines.
src/agents/trading/strategy_agent.py
Responsibilities:
- Scan for entry candidates (stocks meeting criteria)
- Evaluate trend following setup (MA20, RSI, volume)
- Generate entry/exit signals with sentiment weighting
- Manage trade state machines
- Monitor open positions for exit conditions
- Score catalysts for position sizing
Strategy: Trend Following (3:1 R/R)
Entry Criteria
| Criterion |
Requirement |
Points |
| Price > MA20 |
Uptrend confirmation |
Required |
| RSI 50-70 |
Momentum without overbought |
1/3 |
| Volume > 1.5x avg |
Confirmation of interest |
1/3 |
| ADX > 25 |
Strong trend (alternative to RSI) |
1/3 |
Setup Valid: At least 2 of 3 criteria met
Exit Targets
| Exit Type |
Calculation |
Description |
| Stop Loss |
Entry × 0.95 |
5% below entry |
| Profit Target |
Entry × 1.15 |
15% above entry |
| Risk/Reward |
3:1 |
Minimum ratio |
Event Architecture
Events Published
| Event |
Description |
Data |
strategy.candidate_found |
Potential entry detected |
symbol, criteria_met |
strategy.entry_signal |
Entry criteria met |
symbol, entry_price, stop_price, target_price, sentiment_data |
strategy.exit_signal |
Exit condition met |
symbol, reason, pnl_pct |
market_data.get_quote |
Request quote data |
symbol |
market_data.get_bars |
Request historical bars |
symbol, timeframe, limit |
Events Subscribed
| Event |
Handler |
Description |
strategy.scan |
handle_strategy_scan |
Scan universe for candidates |
strategy.evaluate |
handle_strategy_evaluate |
Evaluate specific symbol |
position.opened |
handle_position_opened |
Track new position |
market_data.quote.updated |
handle_market_data_quote_updated |
Monitor for exits |
sentiment.score |
handle_sentiment_score |
Receive sentiment data |
Sentiment Integration
How Sentiment Affects Trading
| Sentiment Score |
Action |
Impact |
| < -0.6 (bearish) |
VETO entry |
Trade blocked |
| -0.6 to +0.5 |
Normal |
No adjustment |
| > +0.5 (bullish) |
BOOST size |
+25% position |
Combined Score Calculation
technical_score = (criteria_met / 3) * 100 # 0-100
sentiment_adjustment = sentiment_score * 20 * confidence # -20 to +20
combined_score = technical_score + sentiment_adjustment
Configuration
self.sentiment_veto_threshold = -0.6 # Block entries below this
self.sentiment_boost_threshold = 0.5 # Boost size above this
self.sentiment_boost_multiplier = 1.25 # 25% larger position
self.sentiment_cache_ttl_seconds = 300 # 5 minute cache
Trade State Machine
States
stateDiagram-v2
[*] --> SETUP_DETECTED: detect_setup()
SETUP_DETECTED --> ENTRY_APPROVED: approve_entry()
ENTRY_APPROVED --> ORDER_PLACED: place_order()
ORDER_PLACED --> FILLED: filled()
FILLED --> HOLDING: set_holding()
HOLDING --> EXIT_TRIGGERED: trigger_exit()
EXIT_TRIGGERED --> CLOSED: close()
CLOSED --> [*]
SETUP_DETECTED --> [*]: invalid_setup()
ENTRY_APPROVED --> [*]: order_rejected()
ORDER_PLACED --> [*]: order_cancelled()
State Transitions
| From |
To |
Trigger |
Data |
| SETUP_DETECTED |
ENTRY_APPROVED |
approve_entry() |
quantity |
| ENTRY_APPROVED |
ORDER_PLACED |
place_order() |
order_id |
| ORDER_PLACED |
FILLED |
filled() |
fill_price |
| FILLED |
HOLDING |
set_holding() |
- |
| HOLDING |
EXIT_TRIGGERED |
trigger_exit() |
reason |
| EXIT_TRIGGERED |
CLOSED |
close() |
exit_price |
Catalyst Scoring System
Score tickers based on multiple catalysts (0-100 points).
Catalyst Points
| Catalyst |
Points |
Criteria |
| FDA Approval |
25 |
Within 90 days, >50% probability |
| Earnings Beat |
25 |
30-60 days out, >10% surprise expected |
| M&A Activity |
20 |
13D filings, rumors, strategic buyers |
| Product Launch |
15 |
Announced within 60 days |
| Insider Buying |
15 |
>10% shares bought in 90 days |
| Analyst Upgrade |
15 |
Initiation, upgrade, or target raise >20% |
| Technical Breakout |
10 |
52-week high, volume surge |
Usage
score = agent.score_catalyst_stack("AAPL", {
"earnings_date": datetime(2025, 1, 30),
"earnings_surprise_est": 0.15, # 15% expected beat
"has_13d_filing": False,
"has_ma_rumors": True,
"analyst_upgrades": 2,
"current_price": 175.50,
"price_52w_high": 180.00,
"volume_ratio": 3.5
})
# score = 60 (M&A + Analyst + Technical)
Entry Requirement
- Minimum 2-3 catalysts (score > 50)
- Higher scores = larger position sizes
Error Handling
Sentiment API Failures
| Error Type |
Fallback Behavior |
Timeout |
| API timeout |
Use cached sentiment |
30s |
| API unavailable |
Default to neutral (0.0) |
3 retries |
| Parse error |
Log error, use neutral |
None |
| Rate limit |
Use cached data |
60s backoff |
def get_sentiment_with_fallback(self, symbol):
try:
sentiment = self.sentiment_api.get(symbol, timeout=30)
self.sentiment_cache[symbol] = sentiment
return sentiment
except (TimeoutError, ConnectionError):
cached = self.sentiment_cache.get(symbol)
if cached and not self._is_cache_expired(cached):
return cached
return {"score": 0.0, "confidence": 0.0} # Neutral fallback
Market Data Failures
| Error Type |
Action |
Recovery |
| Quote unavailable |
Skip symbol in scan |
Retry next cycle |
| Historical data missing |
Use available bars |
Continue with partial data |
| Price feed delay |
Use last known price |
Mark as stale |
| Metric |
Value |
Benchmark |
| Win Rate |
68% |
>60% target |
| Avg R/R |
2.8:1 |
3:1 target |
| Max Drawdown |
-12% |
<15% limit |
| Sharpe Ratio |
1.45 |
>1.0 target |
| Total Return |
+34% |
SPY: +26% |
Real-Time Monitoring
# Performance tracking
self.metrics = {
"signals_generated": 0,
"signals_executed": 0,
"sentiment_vetos": 0,
"avg_processing_time_ms": 0,
"cache_hit_rate": 0.0
}
Alerts Configuration
| Alert |
Threshold |
Action |
| Processing time > 5s |
Critical |
Notify DevOps |
| Cache hit rate < 80% |
Warning |
Check Redis |
| Sentiment API failures > 10% |
Warning |
Check API status |
Usage Examples
Initialize Agent
from src.agents.trading.strategy_agent import StrategyAgent
agent = StrategyAgent(
redis_host="172.200.3.164",
redis_port=6379,
redis_db=0,
sentiment_api_url="https://api.sentiment.corbie.ai",
cache_ttl=300
)
agent.start()
Scan Universe
agent.publish_event(
event="strategy.scan",
data={
"symbols": ["AAPL", "MSFT", "GOOGL", "NVDA", "TSLA"]
}
)
Evaluate Specific Symbol
agent.publish_event(
event="strategy.evaluate",
data={
"symbol": "AAPL",
"current_price": 175.50,
"ma20": 172.00,
"rsi": 58,
"volume": 85000000,
"avg_volume": 50000000
}
)
# If criteria met, publishes:
# strategy.entry_signal with:
# - entry_price: 175.50
# - stop_price: 166.73 (5% below)
# - target_price: 201.83 (15% above)
# - risk_reward_ratio: 3.0
# - sentiment_score, sentiment_confidence
# - position_size_multiplier
Get Active Trades
trades = agent.get_active_trades()
# Returns Dict[str, TradeStateMachine] for HOLDING/EXIT_TRIGGERED states
for symbol, trade in trades.items():
print(f"{symbol}: {trade.state.value}")
print(f" Entry: ${trade.data.entry_price:.2f}")
print(f" Stop: ${trade.data.stop_price:.2f}")
Entry Signal Flow
graph TD
A[strategy.evaluate] --> B{Price > MA20?}
B -->|No| C[Reject: Below MA]
B -->|Yes| D{RSI 50-70?}
D --> E{Volume > 1.5x?}
E --> F{2+ Criteria Met?}
F -->|No| G[Reject: Insufficient Criteria]
F -->|Yes| H{Get Sentiment}
H --> I{API Available?}
I -->|No| J[Use Cache/Neutral]
I -->|Yes| K{Score < -0.6?}
J --> K
K -->|Yes| L[VETO: Bearish Sentiment]
K -->|No| M{Score > +0.5?}
M -->|Yes| N[Apply 1.25x Multiplier]
M -->|No| O[Normal Size]
N --> P[Publish Entry Signal]
O --> P
Exit Signal Monitoring
The agent monitors positions for:
| Condition |
Action |
| Price >= Target |
Publish strategy.exit_signal with reason TARGET_HIT |
| Price <= Stop |
Publish strategy.exit_signal with reason STOP_HIT |
def handle_market_data_quote_updated(self, message):
if last_price >= target_price:
trade.trigger_exit(reason="TARGET_HIT")
self.publish_event("strategy.exit_signal", {...})
elif last_price <= stop_price:
trade.trigger_exit(reason="STOP_HIT")
self.publish_event("strategy.exit_signal", {...})
Configuration Reference
| Parameter |
Type |
Default |
Description |
redis_host |
str |
"172.200.3.164" |
Redis host for message bus |
redis_port |
int |
6379 |
Redis port |
redis_db |
int |
0 |
Redis database number |
sentiment_api_url |
str |
None |
Sentiment service endpoint |
cache_ttl |
int |
300 |
Cache TTL in seconds |
max_concurrent_evaluations |
int |
10 |
Parallel evaluation limit |
processing_timeout_ms |
int |
5000 |
Max processing time |
Strategy Parameters
| Parameter |
Default |
Description |
stop_loss_pct |
0.05 |
5% stop loss |
profit_target_pct |
0.15 |
15% profit target |
target_risk_reward |
3.0 |
Minimum R/R ratio |
rsi_min |
50 |
Minimum RSI for entry |
rsi_max |
70 |
Maximum RSI for entry |
adx_min |
25 |
Minimum ADX for entry |
volume_multiplier |
1.5 |
Volume vs average threshold |
Sentiment Parameters
| Parameter |
Default |
Description |
sentiment_veto_threshold |
-0.6 |
Block entries below this |
sentiment_boost_threshold |
0.5 |
Boost position above this |
sentiment_boost_multiplier |
1.25 |
25% position increase |
sentiment_cache_ttl_seconds |
300 |
5 minute cache |
sentiment_timeout_seconds |
30 |
API call timeout |
sentiment_retry_attempts |
3 |
Max retry attempts |
Troubleshooting
Common Issues
| Issue |
Symptoms |
Solution |
| No signals generated |
Agent running but silent |
Check market data feed |
| High sentiment API failures |
Warnings in logs |
Verify API endpoint and auth |
| Redis connection errors |
Agent crashes |
Check Redis connectivity |
| Slow processing |
Timeouts in logs |
Scale Redis or reduce scan universe |
Debug Commands
# Check agent health
agent.get_health_status()
# View sentiment cache
agent.get_sentiment_cache_stats()
# Monitor active trades
agent.get_trade_summary()
# Performance metrics
agent.get_performance_metrics()
Log Analysis
# Strategy agent logs
grep "strategy_agent" /var/log/corbie/agents.log
# Sentiment failures
grep "sentiment.*error" /var/log/corbie/agents.log
# Performance issues
grep "timeout\|slow" /var/log/corbie/agents.log
Integration Testing
With Risk Management Agent
def test_integration_risk_management():
# Strategy generates signal
strategy_agent.publish_event("strategy.entry_signal", signal_data)
# Risk management validates
assert risk_agent.last_validation_result["approved"] == True
# Integration verified
assert strategy_agent.pending_signals == 0
With Market Data Agent
def test_market_data_integration():
# Request market data
strategy_agent.request_quote("AAPL")
# Verify data received
assert market_data_agent.quote_requests > 0
assert strategy_agent.last_quote_received is not None
Logging Examples
Entry Signal
INFO Entry signal: AAPL @ $175.50 | Stop: $166.73, Target: $201.83 | R/R: 3.00:1 | Sentiment: +0.35
Sentiment Veto
WARNING SENTIMENT VETO: AAPL entry blocked due to bearish sentiment (-0.72, 85% confidence)
Sentiment Boost
INFO SENTIMENT BOOST: AAPL position size +25% due to bullish sentiment (+0.65)
Target Hit
INFO Target hit: AAPL @ $201.83 (target: $201.83)
Stop Hit
INFO Stop hit: AAPL @ $166.73 (stop: $166.73)
Error Handling
ERROR Sentiment API timeout for AAPL, using cached data (5 minutes old)
WARNING High processing time: 4.2s for NVDA evaluation (limit: 5s)
See Also
Last Updated: January 10, 2026
Platform: Trend Following 3:1 R/R | Sentiment Integration | Redis Pub/Sub