Skip to main content
Deltalytix allows you to create a fully customizable dashboard with various widgets to visualize your trading performance.

Widget System Overview

The widget system is built on a responsive grid layout that adapts to your screen size:
  • Desktop: 12-column grid with customizable widget sizes
  • Mobile: Single-column layout with optimized widget sizing
  • Drag & Drop: Rearrange widgets by dragging them to new positions
  • Resizable: Change widget sizes to fit your preferences

Available Widget Types

Calendar Widgets

Calendar View

Visual calendar showing daily P&L and trade counts. Click any day to see detailed trade information.

Chart Widgets

Equity Chart

Track your equity growth over time with support for grouped and individual account views.

P&L Chart

Daily profit and loss visualization with long/short trade breakdowns.

P&L by Hour

Average P&L for each hour of the trading day.

P&L by Weekday

Average P&L by day of the week to identify your best trading days.

P&L by Side

Compare performance between long and short positions.

Avg P&L per Contract

Net P&L per contract by instrument.

Tick Distribution

Distribution of trades by tick value.

Daily Tick Target

Track progress toward daily tick targets.

Statistics Widgets

Statistics Overview

Comprehensive overview of your trading statistics including profit/loss, performance metrics, and activity.

Cumulative P&L

Running total of your profits and losses.

Long/Short Ratio

Percentage breakdown of long vs short trades.

Trade Performance

Win rate, loss rate, and break-even statistics.

Winning Streak

Your current and longest winning streaks.

Other Widgets

Trade Review Table

Detailed table view of all trades with filtering and sorting.

Chat

AI-powered chat for analyzing your trading data.

Tag Management

Create and manage trade tags for organization.

Adding Widgets

1

Enable Edit Mode

Click the Edit button in the toolbar to enter customization mode.
2

Select Widget Category

Choose from the widget categories:
  • Calendar
  • Charts
  • Statistics
  • Tables
  • Communication
3

Add Widget

Click on any widget to add it to your dashboard. The system automatically finds the best placement.

Widget Addition Code

const addWidget = useCallback(async (
  type: WidgetType, 
  size: WidgetSize = 'medium'
) => {
  const currentLayout = layouts[activeLayout]

  // Prevent adding duplicate widget types
  if (currentLayout.some(widget => widget.type === type)) {
    toast.error('Widget already exists', {
      description: 'This widget is already on your dashboard',
    })
    return
  }

  const grid = sizeToGrid(size, activeLayout === 'mobile')
  
  // Find the best position for the new widget
  let bestX = 0
  let bestY = 0
  
  // Algorithm to find gaps in existing rows or add to bottom
  // ... position calculation logic ...
  
  const newWidget: Widget = {
    i: `widget${Date.now()}`,
    type,
    size,
    x: bestX,
    y: bestY,
    w: grid.w,
    h: grid.h
  }

  const updatedWidgets = [...currentLayout, newWidget]
  const newLayouts = {
    ...layouts,
    [activeLayout]: updatedWidgets
  }
  
  setLayouts(newLayouts)
  await saveDashboardLayout(newLayouts)
}, [layouts, activeLayout, setLayouts, saveDashboardLayout])

Widget Sizes

Desktop Sizes

tiny
3x1
Compact widget for simple metrics
small
3x4
Small widget for cards and simple charts
medium
6x4
Medium widget for most visualizations
large
6x6
Large widget for detailed charts
extra-large
12x6
Full-width widget for tables and complex views

Mobile Sizes

On mobile, widgets adapt automatically:
  • Small: 12x1 (compact)
  • Medium: 12x4 (standard)
  • Large: 12x6 (expanded)

Changing Widget Size

<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline" size="icon">
      <Maximize2 className="h-4 w-4" />
    </Button>
  </PopoverTrigger>
  <PopoverContent>
    <Button onClick={() => handleSizeChange('tiny')}>
      Tiny (3x1)
    </Button>
    <Button onClick={() => handleSizeChange('small')}>
      Small (3x4)
    </Button>
    <Button onClick={() => handleSizeChange('medium')}>
      Medium (6x4)
    </Button>
    <Button onClick={() => handleSizeChange('large')}>
      Large (6x6)
    </Button>
    <Button onClick={() => handleSizeChange('extra-large')}>
      Extra Large (12x6)
    </Button>
  </PopoverContent>
</Popover>

Customizing Widgets

Edit Mode

Enter edit mode to customize your dashboard:
const [isCustomizing, setIsCustomizing] = useState(false)

<Button onClick={() => setIsCustomizing(!isCustomizing)}>
  {isCustomizing ? 'Done' : 'Edit'}
</Button>
When in edit mode:
  • Widgets show a drag handle
  • Size and remove buttons appear
  • Click outside widgets to exit edit mode

Moving Widgets

Drag widgets to reposition them:
<ResponsiveGridLayout
  layouts={responsiveLayout}
  breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
  cols={{ lg: 12, md: 12, sm: 12, xs: 12, xxs: 12 }}
  rowHeight={isMobile ? 65 : 70}
  isDraggable={isCustomizing}
  draggableHandle=".drag-handle"
  onLayoutChange={handleLayoutChange}
>
  {/* Widgets */}
</ResponsiveGridLayout>

Removing Widgets

Click the remove button (minus icon) on any widget in edit mode:
const removeWidget = useCallback(async (i: string) => {
  if (!layouts) return
  
  const updatedWidgets = layouts[activeLayout].filter(
    widget => widget.i !== i
  )
  
  const newLayouts = {
    ...layouts,
    [activeLayout]: updatedWidgets
  }
  
  setLayouts(newLayouts)
  await saveDashboardLayout(newLayouts)
}, [layouts, activeLayout, setLayouts, saveDashboardLayout])

Creating Custom Widget Components

Widget Structure

Here’s an example of the Statistics Widget implementation:
interface StatisticsWidgetProps {
  size?: 'tiny' | 'small' | 'medium' | 'large' | 'small-long' | 'extra-large'
  dayData?: CalendarEntry // Optional: for day-specific statistics
}

export default function StatisticsWidget({ 
  size = 'medium', 
  dayData 
}: StatisticsWidgetProps) {
  const dataContext = useData()
  const t = useI18n()
  const locale = useCurrentLocale()

  // Calculate statistics
  const statistics = React.useMemo(() => {
    if (dayData?.trades) {
      return calculateStatistics(dayData.trades as Trade[], [])
    }
    return dataContext.statistics
  }, [dayData, dataContext.statistics])

  const { 
    nbWin, nbLoss, nbBe, nbTrades, 
    averagePositionTime, 
    cumulativePnl, cumulativeFees,
    winningStreak,
    grossLosses,
    grossWin
  } = statistics

  return (
    <Card className="h-full flex flex-col">
      <CardHeader>
        <CardTitle>{t('statistics.title')}</CardTitle>
      </CardHeader>
      <CardContent>
        <div className="grid grid-cols-2">
          {/* Profit/Loss Section */}
          <div className="flex flex-col">
            <h3>{t('statistics.profitLoss.title')}</h3>
            <div>
              <span>Profits</span>
              <span>{formatCurrency(grossWin)}</span>
            </div>
            <div>
              <span>Losses</span>
              <span>{formatCurrency(grossLosses)}</span>
            </div>
            {/* More statistics */}
          </div>
          {/* Other sections */}
        </div>
      </CardContent>
    </Card>
  )
}

Widget Registry

Register your widget in the widget registry:
export const WIDGET_REGISTRY = {
  statisticsOverview: {
    component: StatisticsWidget,
    category: 'statistics',
    defaultSize: 'medium',
    allowedSizes: ['tiny', 'small', 'medium', 'large'],
    requiresFullWidth: false
  },
  // More widgets...
}

Responsive Widget Wrapper

Wrap your widget with the customization controls:
function WidgetWrapper({ 
  children, 
  onRemove, 
  onChangeSize, 
  isCustomizing, 
  size 
}: WidgetWrapperProps) {
  return (
    <div className="relative h-full w-full">
      <div className={cn(
        "h-full w-full", 
        isCustomizing && "group-hover:blur-[2px]"
      )}>
        {children}
      </div>
      
      {isCustomizing && (
        <>
          {/* Drag handle */}
          <div className="drag-handle cursor-grab">
            <GripVertical />
            <p>Drag to move</p>
          </div>
          
          {/* Size and remove buttons */}
          <div className="absolute top-2 right-2">
            <Button onClick={onChangeSize}>
              <Maximize2 />
            </Button>
            <Button onClick={onRemove}>
              <Minus />
            </Button>
          </div>
        </>
      )}
    </div>
  )
}

Layout Management

Saving Layouts

Layouts are automatically saved to the database:
const handleLayoutChange = useCallback(
  (layout: LayoutItem[]) => {
    if (!isCustomizing) return

    const updatedLayouts = {
      ...layouts,
      [activeLayout]: layout.map(item => {
        const existingWidget = layouts[activeLayout].find(
          w => w.i === item.i
        )
        return {
          ...existingWidget,
          x: isMobile ? 0 : item.x,
          y: item.y,
          w: isMobile ? 12 : item.w,
          h: item.h,
        }
      })
    }

    setLayouts(updatedLayouts)
    saveDashboardLayout(updatedLayouts)
  },
  [isCustomizing, layouts, activeLayout, isMobile]
)

Default Layouts

Reset to default layout:
const restoreDefaultLayout = useCallback(async () => {
  const newLayouts = {
    ...layouts,
    desktop: defaultLayouts.desktop,
    mobile: defaultLayouts.mobile
  }
  
  setLayouts(newLayouts)
  await saveDashboardLayout(newLayouts)
  
  toast.success('Layout restored', {
    description: 'Default layout has been restored'
  })
}, [layouts, setLayouts, saveDashboardLayout])

Best Practices

  1. Widget Organization: Group related widgets together
  2. Size Appropriately: Use larger sizes for detailed charts
  3. Mobile Consideration: Test your layout on mobile devices
  4. Performance: Limit the number of complex widgets on one screen
  5. Regular Saves: Changes are auto-saved, but verify they persist

Troubleshooting

Widgets Not Saving

  • Check your internet connection
  • Ensure you’re logged in
  • Try refreshing the page

Layout Issues

  • Clear browser cache
  • Reset to default layout
  • Check for overlapping widgets

Performance Issues

  • Reduce number of large chart widgets
  • Close widgets you’re not actively using
  • Consider using smaller widget sizes

Data Import

Import data to visualize in widgets

Dashboard Features

Learn about all dashboard features

Build docs developers (and LLMs) love