polysignal_hackaton / backend /src /markets /markets.repository.js
blackmistcode's picture
Add files using upload-large-folder tool
dfe11f8 verified
Raw
History Blame Contribute Delete
3.99 kB
/**
* 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);
},
};