Skip to content

Cosmicist/cocos-challenge

Repository files navigation

Cocos Backend Challenge

Setup

.env

Copiar .env.example a .env y modificar con los datos de conexión a la db. Si se está usando docker, los datos pueden quedar tal cual están.

Instalar dependencias

pnpm install

Docker (opcional)

Incluí una db postgres local en docker. Esta base de datos se inicializa usando el archivo database.sql. Es el mismo que fue proveído en el challenge.

Para levantarla correr:

docker compose up -d

De no usarla, se tiene que actualizar el .env con los datos de la db de neon. Sino, sale andando tal como está.

Base de datos

Usando drizzle-kit, hacer push para actualizar la db:

pnpm drizzle-kit push

Si por alguna razón, pnpm drizzle-kit push pregunta de truncar tablas, responder que no (debería ser la opcion seleccionada por defecto). De última, siempre está la posibilidad de usar las queries de database.sql con Drizzle Studio para restaurar la db fácil.

Luego reconstruir las positions para el único usuario que inicialmente tiene ordenes:

pnpm rebuild-positions 10001

# Modo dry-run para ver cual sería el resultado:
pnpm rebuild-positions 10001 --dry

rebuild-positions solo es necesario la primera vez, para compensar el estado original de la db, que no tiene positions precargadas. Si se corre más de una vez no pasa nada, es idempotente (a excepcion de la fecha de last_updated).

Development

Para levantar el dev server:

pnpm dev # O usar la task de vscode - Detalles más abajo

Por defecto levanta en el puerto 3000, pero se puede cambiar seteando la variable de entorno PORT en el .env.

VS Code Tasks

Para facilitar un poco el desarrollo, incluí 2 tasks de vscode:

  • pnpm dev levanta el dev server
  • drizzle-kit studio levanta drizzle-kit studio

Tests

Hay tests unitarios y de integración. Para correrlos:

# Correr todos los tests:
pnpm test

# O por separado:
pnpm test:unit
pnpm test:integration

Los tests de integración corren en una db in-memory usando PGlite.

REST Client

Incluí requests para probar el API usando la extension de vscode REST Client.

Están en la carpeta rest-client. Hay uno por contexto, account, market, y orders.

Importante: De cambiar el puerto del dev-server, actualizarlo en .vscode/settings.json.


Desiciónes de diseño de dominio/arquitectura

Opté por dividir el sistema en 3 bounded contexts, y un módulo compartido:

  • Portfolio
  • Trading
  • Market
  • Shared

La desición fue porque ninguna de las 3 operaciones parecían estar fuertemente relacionadas. Portfolio solo se encarga de mostrar datos de la cuenta, Market es un dominio soporte que solo provee datos, y Trading es donde las operaciones se llevan a cabo.

Intenté evitar comunicación cross-modulo por medio de Shared.

Para lograr esto, todas las cosas que se necesitan en común y la comunicación cross-module están en Shared. Como la conexión a la db, utilidades de arquitectura (bootstrapping, UoW, domain events, etc), contratos comunes, DTOs y types compartidos, etc.

Ya que tanto Trading como Portfolio necesitan datos de Market, el contrato de MarketDataProvider reside en Shared, pero la implementación está en Market.

En el caso de Trading, necesita tambíen accedar a datos de Portfolio (datos de la cuenta), de manera que el contrato de AccountDataProvider lo define Trading, pero la implementación está en Portfolio.

Cambios con respecto a la DB original

Nueva tabla positions

La idea es tener un "snapshot" acumulado de cada instrumento para evitar consultar el historial de ordenes constantemente.

Inicialmente no sería un gran problema no tenerlo, pero a medida que el historial de ordenes crece, se iría haciendo más pesado—O(n). Al tener Position, se actualiza (o crea) la correspondiente position del instrumento, teniendo así una consulta O(1) en vez O(n) por instrumento para generar la respuesta del portfolio.

Nuevo campo users.availableCash

La función de este campo sería análoga a la de la tabla positions, cumpliendo el mismo objetivo de evitar recorrer todas las ordenes para calcular el dinero disponible.

Opté por esta solución porque, según los requerimientos, "Los precios de los activos tienen que estar en pesos". Si se necesitara soportar operaciones multi-moneda, entonces habría que tratar a los instrumentos de tipo MONEDA como el resto. Pero esto implicaría tambíen la necesidad de aclarar con qué moneda se hace cada operación y repensar el snapshot de positions, porque un mismo instrumento podría ser operado en distintas monedas, entre otras cosas.

Índices nuevos

Con el objetivo de optimizar la base de datos y asegurar la consistencia de los datos, agregué los siguientes índices nuevos:

  • users
    • email: unique not null
    • accountnumber: unique not null
  • orders
    • not null a todos los campos
  • instruments
    • ticker unique not null
    • not null a todos los campos
  • marketdata
    • date: not null

Mejoras posibles

  • En el endpoint para enviar una order, idealmente se debería validar que que userId o accountNumber concuerde con el usuario que está haciendo el request, ya sea por medio de JWT o cualquier otro medio de autenticación.

    Esta validación ya se está aplicando a nivel repo, para evitar que usuarios puedan generar ordenes a otros usuarios, pero al no haber autenticación es solo un proof-of-concept.

  • Actualmente, si bien no está especificado en los requerimientos, en la funcionalidad de Venta en Corto se permite sólo si el usuario tiene el 100% de pesos disponibles para cubrir la venta. Según lo que investigué, lo ideal sería implementar una limitación usando un "factor de garantía" para que el monto a cubrir sea mayor al total de la venta, ya que la pérdida es potencialmente ilimitada. Pero esto implica otros conceptos que también están fuera de los requisitos originales.

  • Validar venta si se pasa de posición larga a corta. De momento se permite este tipo de venta (por ej.: tenía 10 acciones, quiero vender 20). Se podría incluir una validación para no permitir pasar a una posición corta y solo permitir ventas en corto si se tiene 0. Pero no estoy seguro de las reglas de negocio en este caso.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published