Skip to main content

Overview

Events are the heart of Opinix Trade. Each event represents a prediction market where you can trade on whether a specific outcome will occur. From cryptocurrency prices to real-world happenings, Opinix offers diverse trading opportunities.
Each event has its own isolated order book with independent price discovery based on market sentiment.

Event structure

Every event on Opinix contains structured data:
interface Event {
  id: string;              // Unique identifier
  title: string;           // Event description
  description?: string;    // Detailed context
  status: EventStatus;     // Current lifecycle state
  deadline: Date;          // Trading closes at this time
  createdAt: Date;         // When event was created
  orderBook?: OrderBook;   // Associated trading data
}

type EventStatus = "ONGOING" | "CLOSED" | "SETTLED";

Event example

{
  "id": "bitcoin-to-be-priced-at-6811470-usdt-or-more-at-0735-pm",
  "title": "Bitcoin to be priced at 68,11,470 USDT or more at 07:35 PM",
  "description": "Will Bitcoin (BTC) reach or exceed 68,11,470 USDT by 7:35 PM IST today?",
  "status": "ONGOING",
  "deadline": "2026-03-01T19:35:00Z",
  "orderBook": {
    "topPriceYes": 7.50,
    "topPriceNo": 2.50
  }
}

Event lifecycle

Events progress through distinct stages:
1

ONGOING - Active trading

Status: Trading is liveDuring this phase:
  • Users can place YES and NO orders
  • Order book actively matches trades
  • WebSocket broadcasts real-time updates
  • Prices fluctuate based on supply/demand
Duration: From creation until deadline
2

CLOSED - Trading ended

Status: Awaiting outcome verificationWhen deadline passes:
  • No new orders accepted
  • Existing orders are canceled
  • Locked funds return to available balance
  • Outcome verification begins
Duration: Minutes to hours after deadline
3

SETTLED - Outcome determined

Status: Final, payouts distributedAfter verification:
  • Outcome set to YES or NO
  • Winning positions pay ₹10 per unit
  • Losing positions expire worthless
  • Funds credited to winners’ available balance
Duration: Permanent
The transition from CLOSED to SETTLED requires manual verification by Opinix administrators to ensure accuracy.

Browsing events

Access the events list at /events to see all available markets.

Events page implementation

export default async function EventsPage() {
  const events = await getEvents();
  
  return (
    <div className="min-h-screen p-4">
      <h1 className="text-3xl font-bold mb-6">Events</h1>
      <EventList events={events} />
    </div>
  );
}

Fetching events

Events are retrieved from the database via server actions:
export async function getEvents() {
  try {
    const events = await prisma.event.findMany({
      where: {
        status: "ONGOING"  // Show only active events
      },
      include: {
        orderBook: {
          select: {
            topPriceYes: true,
            topPriceNo: true
          }
        }
      },
      orderBy: {
        deadline: 'asc'  // Soonest deadline first
      }
    });
    
    return events;
  } catch (error) {
    console.error("Error fetching events:", error);
    return [];
  }
}

Event card display

Each event appears as a card showing key information:
interface EventCardProps {
  event: Event;
}

export function EventCard({ event }: EventCardProps) {
  return (
    <Card className="hover:shadow-lg transition-shadow cursor-pointer"
          onClick={() => router.push(`/events/${event.id}`)}>
      <CardHeader>
        <CardTitle className="text-xl">
          {event.title}
        </CardTitle>
      </CardHeader>
      <CardContent>
        <div className="space-y-2">
          <div className="flex justify-between">
            <span className="text-blue-500 font-semibold">
              YES: ₹{event.orderBook?.topPriceYes || 0}
            </span>
            <span className="text-red-500 font-semibold">
              NO: ₹{event.orderBook?.topPriceNo || 0}
            </span>
          </div>
          <div className="text-sm text-gray-500">
            Closes: {new Date(event.deadline).toLocaleString()}
          </div>
          <div className="mt-2">
            <Badge variant={event.status === "ONGOING" ? "success" : "default"}>
              {event.status}
            </Badge>
          </div>
        </div>
      </CardContent>
    </Card>
  );
}

Event details page

Clicking an event takes you to its dedicated trading page at /events/[id].

Page structure

export default function EventDetailPage({ 
  params 
}: { 
  params: { id: string } 
}) {
  return (
    <div className="min-h-screen p-10">
      <TradePage eventId={params.id} />
    </div>
  );
}

Trade page components

The event detail page includes:
export default function TradePage({ eventId }: { eventId: string }) {
  const [title, setTitle] = useState("");
  
  useEffect(() => {
    async function fetchInitialData() {
      const eventData = await getEventDetails(eventId);
      setTitle(eventData.title);
    }
    fetchInitialData();
  }, [eventId]);
  
  return (
    <div className="min-h-screen p-10">
      <h1 className="text-3xl font-bold text-center mb-6 mt-1">
        {title}
      </h1>
      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        <Orderbook />
        <BidCard />
      </div>
      <ProbabilityChart />
      <Overview />
    </div>
  );
}

Orderbook

Real-time display of all pending YES and NO orders with price levels and quantities

Bid card

Interface to place your YES or NO orders with price and quantity inputs

Probability chart

Historical price movements showing how market sentiment has evolved

Overview

Detailed event description, rules, and settlement criteria

Event data fetching

Retrieve specific event details:
export async function getEventDetails(eventId: string) {
  try {
    const event = await prisma.event.findUnique({
      where: { id: eventId },
      include: {
        orderBook: {
          include: {
            yes: {
              orderBy: { price: 'desc' }  // Highest price first
            },
            no: {
              orderBy: { price: 'asc' }   // Lowest price first
            }
          }
        }
      }
    });
    
    if (!event) {
      throw new Error("Event not found");
    }
    
    return event;
  } catch (error) {
    console.error("Error fetching event details:", error);
    throw error;
  }
}

Event categories

Opinix supports multiple event types:
Price predictions for digital assetsExamples:
  • “Bitcoin to reach $100,000 by end of month”
  • “Ethereum to be priced above 4,000 USDT at 6 PM”
  • “Solana market cap to exceed $50B this week”
Characteristics:
  • High volatility
  • Frequent price updates
  • Short-term outcomes (hours to days)
  • Objective settlement criteria

Real-time event updates

Events receive live updates via WebSocket:

Client-side subscription

useEffect(() => {
  const ws = new WebSocket('ws://server-url');
  
  ws.onopen = () => {
    // Subscribe to this event's updates
    ws.send(JSON.stringify({
      eventId: eventId
    }));
  };
  
  ws.onmessage = (message) => {
    const data = JSON.parse(message.data);
    
    // Update order book
    setOrderBook({
      yes: data.orderBook.yes,
      no: data.orderBook.no,
      topPriceYes: data.orderBook.topPriceYes,
      topPriceNo: data.orderBook.topPriceNo
    });
  };
  
  ws.onclose = () => {
    console.log('Disconnected from event updates');
  };
  
  return () => ws.close();
}, [eventId]);

Server-side broadcasting

export async function updateOrderBook() {
  const onGoingEvents = await prisma.event.findMany({
    where: { status: "ONGOING" },
    include: {
      orderBook: {
        include: { yes: true, no: true }
      }
    }
  });
  
  for (const event of onGoingEvents) {
    if (!event.orderBook) continue;
    
    const orderBook = event.orderBook;
    
    // Simulate market activity (or use real data)
    orderBook.yes.forEach((order: any) => {
      if (order.price >= orderBook.topPriceYes) {
        const change = Math.floor(Math.random() * 5) - 2;
        order.quantity = Math.max(0, order.quantity + change);
      }
    });
    
    // Broadcast to all subscribed clients
    WebsocketServer.broadcast(event.id, {
      orderBook: {
        yes: orderBook.yes,
        no: orderBook.no,
        topPriceYes: orderBook.topPriceYes,
        topPriceNo: orderBook.topPriceNo
      }
    });
  }
}

Event settlement

When an event’s deadline passes, the settlement process begins:
1

Automatic closure

At the deadline, a scheduled job:
await prisma.event.update({
  where: { id: eventId },
  data: { status: "CLOSED" }
});
This stops all trading and cancels unmatched orders.
2

Outcome verification

Administrators verify the actual outcome:
  • Check official data sources
  • Confirm event occurrence
  • Document evidence
  • Set outcome in database
3

Payout calculation

System calculates payouts for all positions:
const positions = await prisma.position.findMany({
  where: { eventId, side: outcome }
});

for (const position of positions) {
  const payout = position.quantity * 10;
  
  await prisma.user.update({
    where: { id: position.userId },
    data: {
      balance: { increment: payout }
    }
  });
}
4

Event marked settled

await prisma.event.update({
  where: { id: eventId },
  data: { 
    status: "SETTLED",
    outcome: outcome  // "YES" or "NO"
  }
});
Winners receive ₹10 per unit, losers get nothing.

Event creation (Admin)

While not user-facing, here’s how events are created:
export async function createEvent({
  title,
  description,
  deadline
}: {
  title: string;
  description?: string;
  deadline: Date;
}) {
  const event = await prisma.event.create({
    data: {
      id: generateEventId(title),  // URL-friendly ID
      title,
      description,
      status: "ONGOING",
      deadline,
      orderBook: {
        create: {
          topPriceYes: 5.0,  // Initial price
          topPriceNo: 5.0,   // Initial price
          yes: { create: [] },
          no: { create: [] }
        }
      }
    }
  });
  
  return event;
}

function generateEventId(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

Searching and filtering events

Search and filter functionality is not yet implemented in the UI but can be added.
Potential filters:
interface EventFilters {
  status?: EventStatus[];
  category?: string[];
  minVolume?: number;
  maxDeadline?: Date;
  searchQuery?: string;
}

export async function getFilteredEvents(filters: EventFilters) {
  return await prisma.event.findMany({
    where: {
      status: filters.status ? { in: filters.status } : undefined,
      deadline: filters.maxDeadline ? { lte: filters.maxDeadline } : undefined,
      title: filters.searchQuery ? {
        contains: filters.searchQuery,
        mode: 'insensitive'
      } : undefined
    },
    orderBy: { deadline: 'asc' }
  });
}

Event metrics

Useful analytics for events:
Total value traded on the event:
const volume = await prisma.trade.aggregate({
  where: { eventId },
  _sum: {
    amount: true  // price * quantity
  }
});

Best practices

Read event details

Always review the event description and settlement criteria before trading.

Check deadline

Ensure you have enough time to react to market movements before the event closes.

Verify sources

For data-driven events, confirm the official data source used for settlement.

Manage exposure

Don’t over-concentrate in a single event; diversify across multiple markets.

Troubleshooting

Possible causes:
  • Invalid event ID in URL
  • Event has been deleted
  • Database connection issue
Try returning to the events list and selecting again.
Check:
  • WebSocket connection status (browser console)
  • Network connectivity
  • Server status
Refresh the page to reconnect WebSocket.
Verify:
  • Event status is ONGOING (not CLOSED or SETTLED)
  • You have sufficient balance
  • You’re signed in
Check event deadline hasn’t passed.
Settlement requires:
  • Manual verification by administrators
  • Confirmation of outcome from official sources
  • Database updates across all positions
Complex events may take hours to settle. Check back later.

Next steps