6 Commits

Author SHA1 Message Date
ff3aafe82a Add monthly margin breakdown by vertical to Slack summary table
- Compute monthly_verticals in run.py and send_slack_report.py using per-vertical CPL targets
- Display vertical breakdown (spend, leads, CPL, target, margin) inside the monthly table block
- Add alarmas vertical (CPL target 8€) to Baserow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 23:14:32 +02:00
6ec3342661 Add 3-day/7-day multi-window analysis for trend-based decisions
- Fetch campaign metrics for 1d/3d/7d windows at run start (2 extra API calls total)
- Pass spend_3d/leads_3d/cpl_3d and spend_7d/leads_7d/cpl_7d to Claude decide()
- DECIDE_SYSTEM: decisions based on cpl_3d (primary) vs cpl_7d (trend direction)
- PAUSE rule now uses leads_3d==0 + spend_3d >= 3x max_cpl threshold
- Adset and ad analysis now use 3-day aggregated data (get_period_*_metrics)
- Add get_period_campaign_metrics/adset/ad methods to MetaAdsClient
- Print shows 1d/3d/7d CPL and leads per campaign for easy trend reading
- UNIT_SYSTEM and AD_SYSTEM note data covers last 3 days

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 22:48:04 +02:00
8b6ec106ee Fix lead counting, call tracking, pause thresholds, and ABO budget detection
- _count_conversions: prioritize 'lead' over 'lead_grouped' to fix double-counting
  (optimizer was showing 2x actual leads on all campaigns)
- Add call_confirm/contact fallbacks for call-objective campaigns (Vodafone, Lowi)
- PAUSE at campaign level only when leads==0 AND spend >= 3x max_cpl (technical failure)
- Ad-level PAUSE threshold raised from 5€ fixed to 3x max_cpl (context-aware)
- Pass max_cpl to ad analysis so Claude uses the correct campaign target
- Skip INCREASE/REDUCE_BUDGET for ABO campaigns (no campaign-level daily budget)
- Fetch bid config before action save to enable ABO detection pre-Baserow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 21:34:48 +02:00
e5c0c9f6a6 Add ad-level pause actions via Baserow and Slack
- AD_SYSTEM prompt for per-ad PAUSE/MAINTAIN decisions (>5€ spend with 0 leads)
- pause_ad() in MetaAdsClient for individual ad pausing via Meta API
- Ad PAUSE actions saved to Baserow with "ad:{id}" campaign_id prefix
- _ad_action_blocks() in Slack notifier renders pause buttons per ad
- _table_name() strips diacritics (NFKD) for monospace column alignment
- Wider name column (45 chars) in adset/ad tables to reduce truncation
- Guard in _effect_text to suppress "+0% budget adjustment" when no change
- _execute_action handles "ad:" prefix to route pause to correct API method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:48:16 +02:00
8fb0b69896 Add Baserow persistence, Slack interactive reports, Streamlit dashboard, and full analysis pipeline
- Migrate from Airtable to Baserow: BaserowClient with snapshots, actions, creatives, logs
- Claude agents (Haiku for decisions/units, Sonnet for creatives) with cost_cap_eur vs CPL comparison
- Slack bot with colored action emojis, effect text before approval buttons, 500-char justifications
- Streamlit dashboard with date-range navigation, campaign drill-down (adsets/ads), Histórico tab
- Approval server (FastAPI + ngrok) for Slack button callbacks
- backfill.py for historical snapshot regeneration with Claude re-analysis
- Margin fix: 0-lead campaigns contribute -spend (not 0) to margin
- CTR fix: Meta returns CTR as percentage already, removed *100
- Parameter fix: pass decision parameter to Slack action for correct budget effect display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 09:30:13 +02:00
92786e94a8 Initial structure: Meta Optimizer
- meta_ads_client.py: Meta Marketing API client (facebook-business SDK)
- agent.py: Claude-powered campaign decision engine
- run.py: main orchestration script
- config.py: environment variables
- .github/workflows/daily.yml: GitHub Actions cron (8am CEST)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:22:11 +02:00