monitor-ml-aceites / scripts /load_prices.py
dyandun's picture
feat: dashboard visual tipo n8n, seed de agentes y carga de precios FRED
4e89618
Raw
History Blame Contribute Delete
3.47 kB
"""
load_prices.py
--------------
Descarga e ingesta el histórico de precios del aceite de palma desde FRED
(Federal Reserve Bank of St. Louis) — serie PPOILUSDM (USD/tonelada métrica).
Fuente: https://fred.stlouisfed.org/series/PPOILUSDM
Frecuencia: mensual | Unidad: USD/MT | Desde: 2003
Ejecutar:
python -m scripts.load_prices
python -m scripts.load_prices --years 5 (solo últimos 5 años)
"""
import argparse
import logging
from datetime import datetime, date
from dotenv import load_dotenv
load_dotenv()
import httpx
from database.supabase_client import get_supabase
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
FRED_URL = "https://fred.stlouisfed.org/graph/fredgraph.csv?id=PPOILUSDM"
def fetch_fred_prices(years: int | None = None) -> list[dict]:
"""Descarga la serie PPOILUSDM de FRED y devuelve lista de registros."""
logger.info(f"Descargando datos de FRED: {FRED_URL}")
with httpx.Client(timeout=30, follow_redirects=True) as client:
response = client.get(FRED_URL)
response.raise_for_status()
lines = response.text.strip().split("\n")
records = []
cutoff = None
if years:
cutoff = date(datetime.now().year - years, 1, 1)
for line in lines[1:]: # Skip header
parts = line.strip().split(",")
if len(parts) != 2:
continue
date_str, price_str = parts
try:
record_date = datetime.strptime(date_str.strip(), "%Y-%m-%d").date()
price = float(price_str.strip())
if price <= 0:
continue
if cutoff and record_date < cutoff:
continue
records.append({
"date": record_date.isoformat(),
"actual_price": round(price, 2),
"source": "FRED",
"currency": "USD",
"unit": "USD/MT",
})
except (ValueError, TypeError):
continue
logger.info(f"Registros obtenidos de FRED: {len(records)}")
return records
def load_to_supabase(records: list[dict]) -> int:
"""Inserta registros en price_data (upsert por date+source)."""
db = get_supabase()
inserted = 0
batch_size = 50
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
db.table("price_data").upsert(batch, on_conflict="date,source").execute()
inserted += len(batch)
logger.info(f" Insertados {inserted}/{len(records)} registros...")
return inserted
def run(years: int | None = None):
records = fetch_fred_prices(years)
if not records:
logger.warning("No se obtuvieron registros de FRED.")
return
total = load_to_supabase(records)
logger.info(f"\n✅ Carga completada: {total} registros en price_data (fuente: FRED).")
# Mostrar rango de fechas cargado
dates = [r["date"] for r in records]
logger.info(f"Rango: {min(dates)}{max(dates)}")
logger.info(f"Último precio: ${records[-1]['actual_price']} USD/MT ({records[-1]['date']})")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Carga histórico de precios de palma desde FRED")
parser.add_argument("--years", type=int, default=None,
help="Cargar solo los últimos N años (default: todo desde 2003)")
args = parser.parse_args()
run(years=args.years)