File size: 3,993 Bytes
dfe11f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
 * Repositorio de acceso a datos para el modelo Market.
 *
 * Responsabilidades:
 *   - findMany({ limit, offset, category, status }) → listado paginado y filtrado.
 *   - count({ category, status })                    → conteo para paginacion.
 *   - findById(id)                                   → busqueda por ID nativo.
 *   - upsert(market)                                 → crea o actualiza un mercado.
 *
 * Todas las operaciones usan Prisma ORM (queries parametrizadas, sin SQL injection).
 * El orden por defecto es descendente por volumen (volumeEur).
 */

import { prisma } from '../utils/prisma.js';

export const marketsRepository = {
  findMany({ limit, offset, category, status }) {
    const where = { status };
    if (category) where.category = category;
    // Ordenamos por liquidez (lo realmente tradeable AHORA) y luego volumen
    // como desempate. Esto evita que los whales politicos historicos copen
    // siempre la primera pagina.
    return prisma.market.findMany({
      where,
      orderBy: [{ liquidityEur: 'desc' }, { volumeEur: 'desc' }],
      take: limit,
      skip: offset,
    });
  },

  count({ category, status }) {
    const where = { status };
    if (category) where.category = category;
    return prisma.market.count({ where });
  },

  findById(id) {
    return prisma.market.findUnique({ where: { id } });
  },

  upsert(market) {
    return prisma.market.upsert({
      where: { id: market.id },
      update: market,
      create: market,
    });
  },

  /**
   * Marca como 'closed' los mercados activos que NO esten en la lista de IDs.
   * Se invoca despues de un sync para purgar mercados que ya no aparecen
   * en el fetch curado por tags (evita mostrar restos de syncs anteriores).
   *
   * @param {string[]} activeIds - IDs presentes en el ultimo sync.
   * @returns {Promise<number>} Numero de mercados marcados como cerrados.
   */
  async deactivateStale(activeIds) {
    const result = await prisma.market.updateMany({
      where: {
        id: { notIn: activeIds },
        status: 'active',
      },
      data: { status: 'closed' },
    });
    return result.count;
  },

  /**
   * Selecciona un conjunto diversificado de mercados activos, ponderado por
   * liquidez+volumen y distribuido entre categorias de alto valor accionable.
   *
   * Categorias prioritarias (peso = numero de mercados pedidos):
   *   - cripto, economía, geopolítica → mayor peso (mas alpha financiero)
   *   - política, ciencia              → peso medio
   *   - entretenimiento, deportes, general → peso bajo (relleno si sobra)
   *
   * @param {number} total - Numero total deseado (default 40).
   * @returns {Promise<Market[]>}
   */
  async findDiversified(total = 40) {
    const weights = {
      'cripto': 0.20,
      'economía': 0.18,
      'geopolítica': 0.18,
      'política': 0.14,
      'ciencia': 0.12,
      'entretenimiento': 0.08,
      'deportes': 0.05,
      'general': 0.05,
    };

    const slices = await Promise.all(
      Object.entries(weights).map(async ([category, weight]) => {
        const take = Math.max(1, Math.round(total * weight));
        // Score: ordenamos por liquidez DESC para priorizar mercados tradeables
        return prisma.market.findMany({
          where: { status: 'active', category },
          orderBy: [{ liquidityEur: 'desc' }, { volumeEur: 'desc' }],
          take,
        });
      }),
    );

    const picked = slices.flat();
    const seen = new Set(picked.map((m) => m.id));

    // Si no llegamos al total (categoria vacia), rellenamos con los mas liquidos restantes
    if (picked.length < total) {
      const remaining = total - picked.length;
      const filler = await prisma.market.findMany({
        where: { status: 'active', id: { notIn: Array.from(seen) } },
        orderBy: [{ liquidityEur: 'desc' }, { volumeEur: 'desc' }],
        take: remaining,
      });
      picked.push(...filler);
    }

    return picked.slice(0, total);
  },
};