Skip to main content

Overview

Your portfolio is your trading command center. It displays all your active positions, historical trades, and calculates your real-time profit and loss across all events.
Portfolio values update automatically as market prices change, giving you instant feedback on your trading performance.

Accessing your portfolio

Navigate to /portfolio or click “Portfolio” in the main navigation to view your trading dashboard.

Portfolio structure

The portfolio is divided into key sections:

Current returns

Aggregate P&L across all active positions

Active trades

Open positions in ongoing events

Past trades

Settled positions with realized gains/losses

Current returns

At the top of your portfolio, see your total unrealized profit or loss:
interface PortfolioProps {
  currentReturns: number;
  trades: Trade[];
  onExit: (id: string) => void;
}

<div className="text-center mb-6">
  <p className="text-lg font-semibold">Current Returns</p>
  <p className={`text-3xl font-bold ${
    currentReturns >= 0 ? "text-green-500" : "text-red-500"
  }`}>
    {currentReturns >= 0 ? "+" : "-"}₹{Math.abs(currentReturns).toFixed(2)}
    {currentReturns >= 0 ? (
      <ArrowUpIcon className="inline ml-2 h-6 w-6" />
    ) : (
      <ArrowDownIcon className="inline ml-2 h-6 w-6" />
    )}
  </p>
</div>

Calculating returns

Current returns = Sum of (Current market value - Cost basis) for all active trades Example:
  • Trade 1: Bought 10 YES at ₹7.00 = ₹70 cost, now worth ₹80 (+₹10)
  • Trade 2: Bought 5 NO at ₹3.00 = ₹15 cost, now worth ₹10 (-₹5)
  • Total returns: +₹5
Returns are unrealized until you exit the position or the event settles. Market prices can change before settlement.

Active trades

This section displays all your open positions in ongoing events.

Trade data structure

interface Trade {
  id: string;                    // Unique trade identifier
  title: string;                 // Event title
  price: number;                 // Your entry price
  quantity: number;              // Number of units
  type: "YES" | "NO";           // Position type
  gainloss: number | null;      // Current unrealized P&L
  status: "ACTIVE" | "PAST";    // Trade status
}

Active trade card

Each active position shows:
<Card className="bg-gray-800 border-gray-700">
  <CardContent className="p-4">
    <div className="flex justify-between items-center">
      <div>
        <h3 className="font-semibold text-lg mb-2">
          {trade.title}
        </h3>
        <p className="text-sm text-gray-400">
          Price: ₹{trade.price.toFixed(2)} | Quantity: {trade.quantity}
        </p>
      </div>
      <div className="flex items-center space-x-4">
        <span className={`text-sm font-medium px-2 py-1 rounded ${
          trade.type === "YES"
            ? "bg-blue-500 text-white"
            : "bg-red-500 text-white"
        }`}>
          {trade.type.toUpperCase()}
        </span>
        <Button
          onClick={() => onExit(trade.id)}
          variant="destructive"
          size="sm"
          className="bg-red-600 hover:bg-gray-600"
        >
          Exit
        </Button>
      </div>
    </div>
  </CardContent>
</Card>

Trade information

The average price you paid per unit:
  • For single orders: Your order price
  • For multiple fills: Weighted average
Example:
  • Bought 5 units at ₹7.00 = ₹35
  • Bought 5 units at ₹7.50 = ₹37.50
  • Average entry: (₹35 + ₹37.50) / 10 = ₹7.25
Total units in your position:
  • Sum of all matched orders for this event and side
  • Each unit pays ₹10 if you win
Example:
  • 10 YES units × ₹10 = ₹100 potential payout
  • YES (Blue): You profit if event occurs
  • NO (Red): You profit if event doesn’t occur
You can hold both YES and NO positions on the same event (hedging).
Unrealized profit or loss based on current market price:For YES positions:
P&L = (Current YES price - Entry price) × Quantity
For NO positions:
P&L = (Current NO price - Entry price) × Quantity
Updates in real-time via WebSocket.

Exiting positions

You can close positions before event settlement.

Exit flow

1

Click Exit button

In your active trades section, click “Exit” on the position you want to close.
2

System places counter-order

The system automatically places an opposing order:
  • If you hold YES: Places NO order
  • If you hold NO: Places YES order
  • At current market price
  • For your full quantity
3

Order matches

Your exit order is matched against existing orders in the order book.
4

Position closed

Once fully matched:
  • Your position is closed
  • P&L is realized
  • Funds return to available balance
  • Trade moves to “Past Trades”

Exit implementation

const handleExit = async (tradeId: string) => {
  const trade = activeTrades.find(t => t.id === tradeId);
  
  if (!trade) return;
  
  // Determine opposing side
  const exitSide = trade.type === "YES" ? "no" : "yes";
  
  // Get current market price
  const currentPrice = await getCurrentMarketPrice(
    trade.eventId,
    exitSide
  );
  
  // Place exit order
  const response = await fetch('/api/placeorder', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      market: trade.eventId,
      price: currentPrice,
      quantity: trade.quantity,
      side: exitSide,
      userId: session.user.id
    })
  });
  
  // Update UI
  if (response.ok) {
    // Move to past trades
    setPastTrades([...pastTrades, {
      ...trade,
      status: "PAST",
      gainloss: calculateRealizedPL(trade, currentPrice)
    }]);
    
    // Remove from active
    setActiveTrades(activeTrades.filter(t => t.id !== tradeId));
  }
};
Exit orders execute at market price, which may differ from the price shown when you clicked Exit if the market moved.

Past trades

This section shows your trading history with realized gains and losses.

Past trade card

<Card className="bg-gray-800 border-gray-700">
  <CardContent className="p-4">
    <div className="flex justify-between items-center">
      <div>
        <h3 className="font-semibold text-lg mb-2">
          {trade.title}
        </h3>
        <p className="text-sm text-gray-400">
          Price: ₹{trade.price.toFixed(2)} | Quantity: {trade.quantity}
        </p>
        <p className={`text-sm font-semibold ${
          trade.gainloss !== null && trade.gainloss >= 0
            ? "text-green-500"
            : "text-red-500"
        }`}>
          Gain/Loss: 
          {trade.gainloss !== null
            ? trade.gainloss >= 0 ? "+" : "-"
            : ""}
          ₹{Math.abs(trade.gainloss || 0).toFixed(2)}
        </p>
      </div>
      <div className="flex items-center space-x-4">
        <span className={`text-sm font-medium px-2 py-1 rounded ${
          trade.type === "YES"
            ? "bg-blue-500 text-white"
            : "bg-red-500 text-white"
        }`}>
          {trade.type.toUpperCase()}
        </span>
      </div>
    </div>
  </CardContent>
</Card>

Gain/Loss calculation

For settled events: If you won:
const payout = quantity * 10; // ₹10 per unit
const cost = quantity * entryPrice;
const gainLoss = payout - cost;
Example (YES winner):
  • Bought 10 YES at ₹7.00 = ₹70 cost
  • Event outcome: YES
  • Payout: 10 × ₹10 = ₹100
  • Gain: ₹100 - ₹70 = +₹30
If you lost:
const payout = 0; // Position expires worthless
const cost = quantity * entryPrice;
const gainLoss = payout - cost;
Example (NO loser):
  • Bought 5 NO at ₹3.00 = ₹15 cost
  • Event outcome: YES (you had NO)
  • Payout: ₹0
  • Loss: ₹0 - ₹15 = -₹15
For manually exited positions:
const entryValue = entryQuantity * entryPrice;
const exitValue = exitQuantity * exitPrice;
const gainLoss = exitValue - entryValue;

Filtering trades

The portfolio component filters trades by status:
const activeTrades = trades.filter(trade => trade.status === "ACTIVE");
const pastTrades = trades.filter(trade => trade.status === "PAST");

return (
  <div>
    <div className="mb-8">
      <h3 className="text-xl font-semibold mb-4">Active Trades</h3>
      {activeTrades.length > 0 ? (
        <div className="space-y-4">
          {activeTrades.map(trade => (
            <TradeCard key={trade.id} trade={trade} />
          ))}
        </div>
      ) : (
        <p className="text-gray-400 text-center">No active trades.</p>
      )}
    </div>
    
    <div>
      <h3 className="text-xl font-semibold mb-4">Past Trades</h3>
      {pastTrades.length > 0 ? (
        <div className="space-y-4">
          {pastTrades.map(trade => (
            <TradeCard key={trade.id} trade={trade} />
          ))}
        </div>
      ) : (
        <p className="text-gray-400 text-center">No past trades.</p>
      )}
    </div>
  </div>
);

Fetching trade data

Trades are fetched from the database using Prisma:
export async function getTrades(userId: string) {
  try {
    const trades = await prisma.trade.findMany({
      where: { userId },
      include: {
        event: {
          select: {
            title: true,
            status: true
          }
        }
      },
      orderBy: { createdAt: 'desc' }
    });
    
    return trades.map(trade => ({
      id: trade.id,
      title: trade.event.title,
      price: trade.price,
      quantity: trade.quantity,
      type: trade.side.toUpperCase() as "YES" | "NO",
      gainloss: calculatePL(trade),
      status: trade.event.status === "ONGOING" ? "ACTIVE" : "PAST"
    }));
  } catch (error) {
    console.error("Error fetching trades:", error);
    return [];
  }
}

Real-time updates

Portfolio values update automatically via WebSocket:
useEffect(() => {
  const ws = new WebSocket('ws://server-url');
  
  // Subscribe to price updates for all active events
  activeTrades.forEach(trade => {
    ws.send(JSON.stringify({
      eventId: trade.eventId
    }));
  });
  
  ws.onmessage = (message) => {
    const data = JSON.parse(message.data);
    
    // Update trade P&L based on new prices
    setActiveTrades(prevTrades => 
      prevTrades.map(trade => {
        if (trade.eventId === data.eventId) {
          const newPrice = trade.type === "YES" 
            ? data.orderBook.topPriceYes 
            : data.orderBook.topPriceNo;
          
          return {
            ...trade,
            gainloss: (newPrice - trade.price) * trade.quantity
          };
        }
        return trade;
      })
    );
  };
  
  return () => ws.close();
}, [activeTrades]);

Portfolio analytics

Calculate your historical win percentage:
const winRate = (winningTrades / totalTrades) * 100;

const winningTrades = pastTrades.filter(
  trade => trade.gainloss > 0
).length;

const totalTrades = pastTrades.length;

Best practices

Review regularly

Check your portfolio daily to stay informed about your positions and market movements.

Set exit targets

Decide profit targets and stop-losses before placing trades to maintain discipline.

Diversify

Spread risk across multiple events rather than concentrating in one market.

Track patterns

Analyze your past trades to identify winning strategies and avoid repeating mistakes.

Troubleshooting

Possible reasons:
  • Order hasn’t matched yet (check order book)
  • Database sync delay (refresh after 30 seconds)
  • Order was canceled
Check your wallet for locked balance to verify if order is pending.
Verify:
  • Entry price is average across all fills
  • Current market price is from most recent WebSocket update
  • Quantity includes all matched portions
Hard refresh the page to re-sync data.
Common causes:
  • Insufficient liquidity at current price
  • Event has closed (trading ended)
  • WebSocket disconnected (reconnect)
Try refreshing the page and attempting again.
This happens when:
  • Event settlement is still pending
  • Database hasn’t updated yet
  • Manual intervention needed for outcome
Gains are credited once event status changes to SETTLED.

Next steps