Skip to main content

Welcome Contributors!

We welcome contributions to Opinix Trade! Whether you’re fixing bugs, adding features, improving documentation, or suggesting ideas, your help is appreciated.
This project is a real-time opinion trading platform where users can place bets on different events, similar to prediction markets.

Getting Started

1

Fork the repository

Fork Opinix Trade on GitHub to your account.
2

Clone your fork

git clone https://github.com/YOUR_USERNAME/opinix-trade.git
cd opinix-trade
3

Set up development environment

Follow the Development Setup guide to configure your local environment.
# Start infrastructure
docker-compose up -d

# Install dependencies
npm install

# Set up environment variables
cp packages/db/.env.example packages/db/.env
cp apps/client/.env.example apps/client/.env
cp packages/order-queue/.env.example packages/order-queue/.env

# Generate Prisma client
cd packages/db && npx prisma generate

# Run migrations
npx prisma migrate dev

# Build project
npm run build

# Start development servers
npm run dev
4

Create a branch

Create a feature branch for your changes:
git checkout -b feature/your-feature-name
Branch naming conventions:
  • feature/ - New features
  • fix/ - Bug fixes
  • docs/ - Documentation updates
  • refactor/ - Code refactoring
  • test/ - Adding tests
  • chore/ - Maintenance tasks

Development Workflow

1

Write code

Make your changes following the project’s code style.
2

Test locally

Ensure your changes work as expected:
# Start all services
npm run dev

# Test specific service
cd apps/client && npm run dev
cd apps/server && npm run dev
3

Lint your code

Run linting to check for code style issues:
npm run lint
Fix any linting errors before committing.
4

Format code

Format your code with Prettier:
npm run format

Code Style Guidelines

General Rules:
  • Use TypeScript for all new code
  • Avoid any type - use proper types
  • Use interfaces for object shapes
  • Export types when shared across files
// Good
interface Order {
  id: string;
  userId: string;
  eventId: string;
  quantity: number;
  price: number;
  side: 'YES' | 'NO';
}

async function placeOrder(order: Order): Promise<Order> {
  // Implementation
}

// Bad
async function placeOrder(order: any) {
  // Implementation
}
Variables & Functions:
  • Use camelCase
  • Descriptive names
  • Boolean variables start with is, has, should
// Good
const orderQueue = new Queue();
const isOrderValid = validateOrder(order);
const hasPermission = checkPermission(user);

// Bad
const q = new Queue();
const valid = validateOrder(order);
const perm = checkPermission(user);
Classes & Interfaces:
  • Use PascalCase
  • Interfaces don’t need I prefix
// Good
class OrderMatchingEngine {}
interface User {}

// Avoid
class order_matching_engine {}
interface IUser {}
Constants:
  • Use UPPER_SNAKE_CASE for true constants
const MAX_ORDER_SIZE = 1000;
const DEFAULT_CACHE_TTL = 60;
File naming:
  • Use kebab-case for files: order-matching.ts
  • Use PascalCase for component files: OrderBook.tsx
Project structure:
apps/
  client/          # Next.js frontend
  server/          # Express backend
packages/
  db/              # Database & Prisma
  order-queue/     # Order queue logic
  types/           # Shared types
  zod/             # Validation schemas
  logger/          # Logging utility
services/
  engine/          # Matching engine
  wss/             # WebSocket server
Always handle errors properly:
// Good - Proper error handling
try {
  const order = await placeOrder(orderData);
  logger.info('Order placed', { orderId: order.id });
  return order;
} catch (error) {
  logger.error('Failed to place order', { 
    error: error.message, 
    stack: error.stack,
    orderData 
  });
  throw new Error('Order placement failed');
}

// Bad - Silent failure
try {
  await placeOrder(orderData);
} catch (error) {
  // Silent
}
Prefer async/await over promises:
// Good
async function getUserOrders(userId: string) {
  const user = await prisma.user.findUnique({ 
    where: { id: userId } 
  });
  
  if (!user) throw new Error('User not found');
  
  return prisma.order.findMany({ 
    where: { userId } 
  });
}

// Avoid
function getUserOrders(userId: string) {
  return prisma.user.findUnique({ where: { id: userId } })
    .then(user => {
      if (!user) throw new Error('User not found');
      return prisma.order.findMany({ where: { userId } });
    });
}

Areas to Contribute

Core Features

  • Portfolio management improvements
  • Advanced order types (stop-loss, etc.)
  • Social features (leaderboards, etc.)
  • Analytics dashboard

Performance

  • Order matching optimization
  • Database query optimization
  • Caching improvements
  • WebSocket efficiency

Testing

  • Unit tests
  • Integration tests
  • E2E tests
  • Load testing

Documentation

  • API documentation
  • Architecture guides
  • Tutorial content
  • Code comments

UI/UX

  • Design improvements
  • Mobile responsiveness
  • Accessibility
  • Dark mode enhancements

DevOps

  • CI/CD improvements
  • Docker optimization
  • Monitoring setup
  • Deployment automation

Project Architecture

Understanding the architecture helps you contribute effectively:Flow:
  1. Client - User places order via Next.js frontend
  2. Backend (Server) - Validates and accepts order
  3. Queue - Order added to Redis queue (BullMQ)
  4. Worker (Engine) - Processes order from queue
  5. Matching Logic - Updates order book with matching
  6. WebSocket - Broadcasts updates to all clients
  7. Database - Persists state to PostgreSQL
Key Components:
  • Order Book: In-memory structure for fast matching
  • Queue: Asynchronous order processing
  • WebSocket: Real-time updates
  • Database: Source of truth for all data
See the Architecture Overview for detailed diagrams.

Monorepo Structure

client/ - Next.js frontend
  • React components
  • NextAuth authentication
  • Real-time WebSocket integration
  • TanStack Query for data fetching
server/ - Express backend API
  • REST endpoints
  • JWT authentication
  • Order management
  • Event management
db/ - Database layer
  • Prisma schema
  • Database client
  • Migrations
order-queue/ - Queue management
  • BullMQ integration
  • Redis connection
  • Worker logic
types/ - Shared TypeScript typeszod/ - Validation schemaslogger/ - Logging utility
engine/ - Order matching engine
  • Core matching algorithm
  • Order book management
  • Price calculation
wss/ - WebSocket server
  • Real-time connections
  • Message broadcasting
  • Redis pub/sub

Testing

Add tests for new features:
// Example test structure
import { describe, it, expect } from 'vitest';
import { placeOrder } from './orders';

describe('Order Placement', () => {
  it('should place a valid order', async () => {
    const order = {
      userId: 'user123',
      eventId: 'event456',
      side: 'YES',
      quantity: 10,
      price: 5.5
    };

    const result = await placeOrder(order);

    expect(result).toBeDefined();
    expect(result.id).toBeTruthy();
    expect(result.userId).toBe(order.userId);
  });

  it('should reject invalid order', async () => {
    const order = {
      userId: 'user123',
      eventId: 'event456',
      side: 'INVALID',
      quantity: -10,
      price: 0
    };

    await expect(placeOrder(order)).rejects.toThrow();
  });
});

Database Changes

When modifying the database schema:
1

Update Prisma schema

Edit packages/db/prisma/schema.prisma:
model NewModel {
  id        String   @id @default(cuid())
  field     String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
2

Create migration

cd packages/db
npx prisma migrate dev --name add_new_model
3

Generate Prisma client

npx prisma generate
4

Update application code

Use the new schema in your application code.

Getting Help

Recognition

All contributors are recognized in:
  • README.md contributors section
  • GitHub contributor graph
  • Release notes for significant contributions
We value all contributions, no matter how small. Every improvement helps make Opinix Trade better!

Next Steps