Deltalytix allows you to create a fully customizable dashboard with various widgets to visualize your trading performance.
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
Calendar View
Visual calendar showing daily P&L and trade counts. Click any day to see detailed trade information.
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 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.
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.
Enable Edit Mode
Click the Edit button in the toolbar to enter customization mode.
Select Widget Category
Choose from the widget categories:
- Calendar
- Charts
- Statistics
- Tables
- Communication
Add Widget
Click on any widget to add it to your dashboard. The system automatically finds the best placement.
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])
Desktop Sizes
Compact widget for simple metrics
Small widget for cards and simple charts
Medium widget for most visualizations
Large widget for detailed charts
Full-width widget for tables and complex views
Mobile Sizes
On mobile, widgets adapt automatically:
- Small: 12x1 (compact)
- Medium: 12x4 (standard)
- Large: 12x6 (expanded)
<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>
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
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>
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])
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>
)
}
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...
}
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
- Widget Organization: Group related widgets together
- Size Appropriately: Use larger sizes for detailed charts
- Mobile Consideration: Test your layout on mobile devices
- Performance: Limit the number of complex widgets on one screen
- Regular Saves: Changes are auto-saved, but verify they persist
Troubleshooting
- 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
- 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