> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Market Sentiment → Brand Positioning

### Scenario

Alpha Vantage's `NEWS_SENTIMENT` function returns sentiment scores for tickers and topics across financial news. This job pulls sentiment data for your company's ticker and competitors, maps the sentiment landscape, then asks Mave to recommend brand positioning adjustments — should you lean into market optimism, differentiate against pessimism, or stay neutral?

**Flow:** Alpha Vantage `GET ?function=NEWS_SENTIMENT&tickers={ticker}` → Mavera `POST /mave/chat` → Positioning recommendations

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  AV_KEY = os.environ["ALPHA_VANTAGE_KEY"]
  AV_BASE = "https://www.alphavantage.co/query"
  MV = os.environ["MAVERA_API_KEY"]
  MV_BASE = "https://app.mavera.io/api/v1"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  TICKERS = {"AAPL": "Apple", "MSFT": "Microsoft", "GOOGL": "Google"}
  OUR_TICKER = "AAPL"

  # 1. Fetch news sentiment per ticker
  sentiment_data = {}
  for ticker, name in TICKERS.items():
      r = requests.get(AV_BASE, params={
          "function": "NEWS_SENTIMENT", "tickers": ticker,
          "limit": 20, "apikey": AV_KEY,
      })
      r.raise_for_status()
      feed = r.json().get("feed", [])
      
      scores = []
      articles_summary = []
      for item in feed:
          for ts in item.get("ticker_sentiment", []):
              if ts.get("ticker") == ticker:
                  score = float(ts.get("ticker_sentiment_score", 0))
                  scores.append(score)
                  articles_summary.append(
                      f"- {item.get('title','')[:100]} (score: {score:.2f}, "
                      f"relevance: {ts.get('relevance_score','N/A')})"
                  )
      
      avg = sum(scores) / len(scores) if scores else 0
      sentiment_data[ticker] = {
          "name": name, "avg_sentiment": avg, "article_count": len(feed),
          "positive": sum(1 for s in scores if s > 0.15),
          "negative": sum(1 for s in scores if s < -0.15),
          "neutral": sum(1 for s in scores if -0.15 <= s <= 0.15),
          "articles": articles_summary[:10],
      }
      print(f"{name} ({ticker}): avg={avg:.3f}, articles={len(feed)}, "
            f"+{sentiment_data[ticker]['positive']}/-{sentiment_data[ticker]['negative']}")
      time.sleep(15)

  # 2. Build sentiment landscape
  landscape = "\n\n".join(
      f"## {d['name']} ({t})\n"
      f"Average Sentiment: {d['avg_sentiment']:.3f}\n"
      f"Distribution: {d['positive']} positive, {d['neutral']} neutral, {d['negative']} negative\n"
      f"Top Headlines:\n" + "\n".join(d['articles'][:5])
      for t, d in sentiment_data.items()
  )

  # 3. Mave positioning analysis
  analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Brand strategist specializing in market-sentiment-aligned positioning.\n\n"
          f"SENTIMENT LANDSCAPE (our ticker: {OUR_TICKER}):\n\n{landscape}\n\n"
          "Analyze:\n"
          "1. **Sentiment Map** — Where does each player sit? Who's gaining/losing favor?\n"
          "2. **Our Position** — Given our sentiment vs competitors, what positioning plays are available?\n"
          "3. **Messaging Recommendations** — 3 specific messaging angles based on the sentiment gap\n"
          "4. **Timing** — Is sentiment trending up/down? Should we act now or wait?\n"
          "5. **Risk** — What happens if sentiment reverses?\n\n"
          "Be specific. Reference actual headline themes."
  }).json()

  print(f"\n{'='*60}\nSENTIMENT-BASED POSITIONING ANALYSIS\n{'='*60}")
  print(analysis.get("content", "")[:2500])
  ```

  ```javascript JavaScript theme={"dark"}
  const AV_KEY = process.env.ALPHA_VANTAGE_KEY;
  const AV_BASE = "https://www.alphavantage.co/query";
  const MV = process.env.MAVERA_API_KEY;
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const TICKERS = { AAPL: "Apple", MSFT: "Microsoft", GOOGL: "Google" };
  const OUR_TICKER = "AAPL";

  // 1. News sentiment per ticker
  const sentimentData = {};
  for (const [ticker, name] of Object.entries(TICKERS)) {
    const r = await fetch(`${AV_BASE}?function=NEWS_SENTIMENT&tickers=${ticker}&limit=20&apikey=${AV_KEY}`);
    const feed = (await r.json()).feed || [];
    
    const scores = [];
    const articlesSummary = [];
    for (const item of feed) {
      for (const ts of item.ticker_sentiment || []) {
        if (ts.ticker === ticker) {
          const score = parseFloat(ts.ticker_sentiment_score || 0);
          scores.push(score);
          articlesSummary.push(`- ${(item.title||"").slice(0,100)} (score: ${score.toFixed(2)})`);
        }
      }
    }
    
    const avg = scores.length ? scores.reduce((a,b) => a+b, 0) / scores.length : 0;
    sentimentData[ticker] = {
      name, avg_sentiment: avg, article_count: feed.length,
      positive: scores.filter(s => s > 0.15).length,
      negative: scores.filter(s => s < -0.15).length,
      neutral: scores.filter(s => s >= -0.15 && s <= 0.15).length,
      articles: articlesSummary.slice(0, 10),
    };
    console.log(`${name} (${ticker}): avg=${avg.toFixed(3)}, articles=${feed.length}`);
    await new Promise(r => setTimeout(r, 15000));
  }

  // 2. Landscape
  let landscape = "";
  for (const [t, d] of Object.entries(sentimentData))
    landscape += `## ${d.name} (${t})\nAvg Sentiment: ${d.avg_sentiment.toFixed(3)}\nDistribution: ${d.positive}+/${d.neutral}n/${d.negative}-\n${d.articles.slice(0,5).join("\n")}\n\n`;

  // 3. Positioning analysis
  const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Brand strategist. Sentiment-aligned positioning.\n\nLANDSCAPE (our: ${OUR_TICKER}):\n\n${landscape}\n\n1. Sentiment Map\n2. Our Position\n3. Messaging Recommendations (3 angles)\n4. Timing\n5. Risk\n\nReference actual headline themes.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nSENTIMENT-BASED POSITIONING ANALYSIS`);
  console.log((analysis.content || "").slice(0, 2500));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Apple (AAPL): avg=0.187, articles=20, +12/-3
Microsoft (MSFT): avg=0.241, articles=20, +15/-2
Google (GOOGL): avg=0.098, articles=18, +8/-5

SENTIMENT-BASED POSITIONING ANALYSIS
============================================================

1. SENTIMENT MAP:
   Microsoft leads (+0.241) — driven by Azure AI and Copilot coverage.
   Apple mid-range (+0.187) — Vision Pro mixed but Services revenue strong.
   Google weakest (+0.098) — antitrust coverage dragging sentiment down.

2. OUR POSITION (AAPL):
   Healthy positive but gap vs. MSFT. Services narrative is winning;
   hardware skepticism (Vision Pro) is a drag. Opportunity: lean into
   the privacy + ecosystem story that differentiates from both.

3. MESSAGING RECOMMENDATIONS:
   a) "Privacy is the product" — exploit Google's antitrust weakness
   b) "Ecosystem, not features" — differentiate from MSFT's feature-race
   c) "Services momentum" — ride the positive revenue narrative
```

### Error Handling

<AccordionGroup>
  <Accordion title="Free tier limits">25 req/day. Each ticker = 1 call. 3 tickers = 3 calls. Add 15s delay between calls to stay under 5/min. Cache results for same-day re-runs.</Accordion>
  <Accordion title="Empty feed">Some tickers return empty feeds. Check `r.json().get("Note")` for rate-limit messages — Alpha Vantage returns 200 with a note instead of 429.</Accordion>
  <Accordion title="Sentiment scale">Scores range -1 to +1. Thresholds: above 0.35 = bullish, below -0.35 = bearish, between = neutral. Adjust thresholds for your industry.</Accordion>
</AccordionGroup>
