AUM CAGR
Revenue CAGR
Clients CAGR
Net Flows CAGR
Growth Over Time
Sources of AUM Growth by Year
Revenue Yield & Operating Margin
Expense Structure
Client Capacity & Productivity
Strategic Initiatives
Derived Metrics (Formula-Based)
AUM/Client Efficiency โ
AUM CAGR รท Client CAGR
Net-Flow Share of AUM Growth โ
5-yr aggregate attribution
Service Cost as % of Revenue โ
(Client-service bps รท Yield bps)
AUM per Client CAGR โ
2019โ2024
Revenue per Client CAGR โ
2020โ2024
New vs. Total Clients โ
New per professional รท Clients per professional (2024)
Retention Drag โ
Client attrition (2024)
AUM Growth Attribution โ 5-Year Mix
Net-Flow Share of AUM Growth by Year
Assets per Client โ Growth Index (2019=100)
Revenue per Client โ Growth Index (2020=100)
Cost per $1M AUM (bps) โ By Function
Converted to bps: $ per $1M โ bps = amount รท 10,000.
Revenue Yield vs. Service Cost (bps)
.ga-fw{width:100%;margin:0;background:var(--bg);color:var(--ink);font:inherit}
.ga-bleed{width:100%;padding:24px clamp(16px,3vw,32px)}
.ga-head{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin:4px 0 16px;flex-wrap:wrap}
.ga-title h3{margin:0 0 4px}
.ga-badges{display:flex;gap:8px;flex-wrap:wrap}
.ga-badge{background:#f7f9fc;border:1px solid var(--border);border-radius:999px;padding:6px 10px;box-shadow:0 1px 0 rgba(0,0,0,.02)}
.ga-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:10px 0 18px}
.ga-kpi{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:14px;box-shadow:0 10px 24px rgba(0,0,0,.06)}
.ga-kpi h4{margin:0 0 6px}
.ga-kpi-val{font-weight:800;font-size:22px}
.ga-row{margin:14px 0}
.ga-row2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.ga-card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:12px 12px 10px;box-shadow:0 16px 32px rgba(16,24,40,.06)}
.ga-card h4{margin:0 0 10px}
.ga-chart{position:relative;height:340px}
/* smaller donut height */
.ga-chart.ga-donut{height:220px}
.ga-legend{display:flex;gap:16px;flex-wrap:wrap;align-items:center;margin-top:8px;font-size:12px;color:var(--muted)}
.ga-legend .sw{display:inline-block;width:12px;height:12px;border-radius:2px;margin-right:6px;vertical-align:-2px}
.ga-sep{border:none;border-top:1px solid var(--border);margin:22px 0}
.ga-der-head h3{margin:0 0 10px}
.ga-der-kpis{display:grid;grid-template-columns:repeat(7,minmax(160px,1fr));gap:12px;margin:8px 0 16px}
.ga-der-kpi{border:1px solid rgba(0,0,0,.1);border-radius:10px;padding:10px;background:rgba(0,0,0,.02)}
@media (prefers-color-scheme: dark){ .ga-der-kpi{border-color:rgba(255,255,255,.15);background:rgba(255,255,255,.04)} }
.ga-der-kpi .num{font-weight:800}
.ga-der-kpi p{margin:.25rem 0 0;opacity:.75}
.ga-der-grid{display:grid;grid-template-columns:repeat(2,minmax(320px,1fr));gap:16px}
.ga-der-card{border:1px solid rgba(0,0,0,.1);border-radius:10px;padding:12px;background:var(--card)}
@media (prefers-color-scheme: dark){ .ga-der-card{border-color:rgba(255,255,255,.15)} }
.ga-der-card h4{margin:0 0 8px}
/* Responsive */
@media (max-width: 1180px){ .ga-der-kpis{grid-template-columns:repeat(4,1fr)} }
@media (max-width: 980px){
.ga-kpis{grid-template-columns:repeat(2,1fr)}
.ga-row2{grid-template-columns:1fr}
.ga-chart{height:300px}
.ga-chart.ga-donut{height:200px}
.ga-der-grid{grid-template-columns:1fr}
}
@media (max-width: 560px){
.ga-kpis{grid-template-columns:1fr}
.ga-chart{height:260px}
.ga-chart.ga-donut{height:180px}
}
/* ========== SHARED DATA (original dashboard) ========== */
const YEARS = ['2020','2021','2022','2023','2024'];
const KPIS = { aum: 58.5, revenue: 49.6, clients: 16.2, netflows: 51.6 };
const CLIENT_GROWTH = [18, 25, 12, 7, 20];
const NET_FLOW_GROWTH = [0, 0, 25, 17, 150];
const REV_YIELD = [1.50, 1.07, 0.72, 0.57, 1.07];
const SOURCE_MIX = [
{ year:'2020', newClients:0, performance:0, attrition:0 },
{ year:'2021', newClients:0, performance:100, attrition:0 },
{ year:'2022', newClients:25, performance:25, attrition:0 },
{ year:'2023', newClients:17, performance:17, attrition:0 },
{ year:'2024', newClients:150, performance:0, attrition:0 }
];
const STD_MARGIN = 36.5;
const EXPENSES = [
{ label:'Direct (incl. Sr Mgmt comp)', val:160 },
{ label:'Rent', val:8 },
{ label:'IT', val:3.3 }
];
const CAPACITY = { clientsPerPro: 33, hoursPerClientService: 41, hoursPerClientOps: 6 };
const STRATEGY = [
{ label:'Marketing & BD', val:54 },
{ label:'Ops & Client Service', val:26 },
{ label:'Strategy & Planning', val:10 },
{ label:'Staff & Org', val:10 }
];
/* ========== KPIs ========== */
document.getElementById('kpi-aum').textContent = KPIS.aum.toFixed(1) + '%';
document.getElementById('kpi-rev').textContent = KPIS.revenue.toFixed(1) + '%';
document.getElementById('kpi-cli').textContent = KPIS.clients.toFixed(1) + '%';
document.getElementById('kpi-naf').textContent = KPIS.netflows.toFixed(1) + '%';
/* ========== Legend helper (uniform) ========== */
const putLegend = (elId, items) => {
const el = document.getElementById(elId);
if(!el) return;
el.innerHTML = items.map(i=>`
${i.label}`).join('');
};
/* ========== Palette (kept) ========== */
const C = {
a:'#4e79a7', // clients / perf
b:'#e15759', // net flows / ops
c:'#59a14f', // yield / new clients
d:'#f28e2b', // accent
e:'#d26a00', // DARKER ORANGE (Operating margin line)
f:'#2f6fae' // DEEPER BLUE (Expense bars)
};
/* ========== Original DASHBOARD (with legends for every chart) ========== */
// Growth Over Time
new Chart(document.getElementById('chGrowth'), {
type:'line',
data:{
labels:YEARS,
datasets:[
{ label:'Client growth', data:CLIENT_GROWTH, borderColor:C.a, backgroundColor:C.a, tension:0.25, pointRadius:3 },
{ label:'Net asset flows', data:NET_FLOW_GROWTH, borderColor:C.b, backgroundColor:C.b, tension:0.25, pointRadius:3, borderDash:[6,4] },
{ label:'Revenue yield', data:REV_YIELD, borderColor:C.c, backgroundColor:C.c, tension:0.25, pointRadius:3, borderDash:[2,3] }
]
},
options:{
responsive:true,
scales:{ y:{beginAtZero:true,ticks:{callback:v=>v+'%'}}, x:{ticks:{maxRotation:0,autoSkip:false}} },
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-growth', [
{label:'Client growth', color:C.a},
{label:'Net asset flows', color:C.b},
{label:'Revenue yield', color:C.c}
]);
// Sources of AUM Growth
new Chart(document.getElementById('chSources'), {
type:'bar',
data:{
labels: SOURCE_MIX.map(r=>r.year),
datasets:[
{ label:'New clients', data:SOURCE_MIX.map(r=>r.newClients), backgroundColor:C.c, stack:'g' },
{ label:'Market performance', data:SOURCE_MIX.map(r=>r.performance), backgroundColor:C.a, stack:'g' },
{ label:'Attrition', data:SOURCE_MIX.map(r=>r.attrition), backgroundColor:C.b, stack:'g' }
]
},
options:{
responsive:true,
scales:{
x:{ stacked:true, ticks:{ maxRotation:40, minRotation:40, font:{ size:14 } } },
y:{ stacked:true, beginAtZero:true, ticks:{ callback:v=>v+'%' } }
},
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-sources', [
{label:'New clients', color:C.c},
{label:'Market performance', color:C.a},
{label:'Attrition', color:C.b}
]);
// Yield + Margin
new Chart(document.getElementById('chYieldMargin'), {
type:'bar',
data:{
labels:YEARS,
datasets:[
{ type:'bar', label:'Revenue yield', data:REV_YIELD, backgroundColor:C.c, borderRadius:0, barPercentage:0.5, categoryPercentage:0.6 },
{ type:'line', label:'Operating margin', data:YEARS.map(()=>STD_MARGIN), borderColor:C.e, tension:0, pointRadius:0, borderWidth:2 }
]
},
options:{
responsive:true,
scales:{ y:{ beginAtZero:true, ticks:{ callback:v=>v+'%' } }, x:{ ticks:{ maxRotation:0, autoSkip:false } } },
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-yieldmargin', [
{label:'Revenue yield (bars)', color:C.c},
{label:'Operating margin (line)', color:C.e}
]);
// Expenses
new Chart(document.getElementById('chExpenses'), {
type:'bar',
data:{
labels: EXPENSES.map(e=>e.label),
datasets:[ { label:'% of revenue', data:EXPENSES.map(e=>e.val), backgroundColor:C.f, borderRadius:0, barPercentage:0.5, categoryPercentage:0.5 } ]
},
options:{
responsive:true,
scales:{ x:{ ticks:{ maxRotation:40, minRotation:40, font:{ size:14 } } }, y:{ beginAtZero:true, ticks:{ callback:v=>v+'%' } } },
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-expenses', [
{label:'% of revenue', color:C.f}
]);
// Capacity
new Chart(document.getElementById('chCapacity'), {
type:'bar',
data:{
labels:['Clients / Pro','Service hrs / client','Ops hrs / client'],
datasets:[{
label:'Firm',
data:[CAPACITY.clientsPerPro, CAPACITY.hoursPerClientService, CAPACITY.hoursPerClientOps],
backgroundColor:[C.a, C.c, C.b],
borderRadius:0, barPercentage:0.5, categoryPercentage:0.6
}]
},
options:{
responsive:true,
scales:{ x:{ ticks:{ maxRotation:40, minRotation:40, font:{ size:14 } } }, y:{ beginAtZero:true } },
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-capacity', [
{label:'Clients / Pro', color:C.a},
{label:'Service hrs / client', color:C.c},
{label:'Ops hrs / client', color:C.b}
]);
// Strategy
new Chart(document.getElementById('chStrategy'), {
type:'bar',
data:{
labels: STRATEGY.map(s=>s.label),
datasets:[
{ label:'% focus', data:STRATEGY.map(s=>s.val), backgroundColor:[C.c, C.a, C.e, C.d], borderRadius:0, barPercentage:0.5, categoryPercentage:0.6 }
]
},
options:{
responsive:true,
scales:{ x:{ ticks:{ maxRotation:40, minRotation:40, font:{ size:14 } } }, y:{ beginAtZero:true, ticks:{ callback:v=>v+'%' } } },
plugins:{ legend:{ display:false } }
}
});
putLegend('leg-strategy', [
{label:'Marketing & BD', color:C.c},
{label:'Ops & Client Service', color:C.a},
{label:'Strategy & Planning', color:C.e},
{label:'Staff & Org', color:C.d}
]);
/* ========== DERIVED METRICS (NEW) ========== */
const AUM_CAGR = 58.5;
const REV_CAGR = 49.6;
const CLI_CAGR = 16.2;
const REV_YIELD_BPS = 107; // 1.07%
const COST_PER_M_USD = { clientService: 2746, opsAdmin: 275, compliance: 107, bizMgmt: 131 };
const NEW_PER_PRO = 6.0;
const CLIENTS_PER_PRO = 33.0;
const RETENTION_2024 = 100;
const SRC = {
2020:{...SOURCE_MIX[0]}, 2021:{...SOURCE_MIX[1]}, 2022:{...SOURCE_MIX[2]},
2023:{...SOURCE_MIX[3]}, 2024:{...SOURCE_MIX[4]}
};
const APC = [0.06, 0.05, 0.08, 0.11, 0.13, 0.28]; // 2019โ2024
const APC_YEARS = [2019, 2020, 2021, 2022, 2023, 2024];
const RPC = [750, 640, 643, 667, 2083]; // 2020โ2024
const RPC_YEARS = [2020, 2021, 2022, 2023, 2024];
const effMult = AUM_CAGR / Math.max(1e-9, CLI_CAGR);
const totals = Object.values(SRC).reduce((a,y)=>{ a.new+=y.newClients||0; a.perf+=y.performance||0; a.total+= (y.total ?? ((y.newClients||0)+(y.performance||0))); return a;},{new:0,perf:0,total:0});
const nfShare = totals.total>0 ? (totals.new / totals.total * 100) : 0;
const perfShare = totals.total>0 ? (totals.perf / totals.total * 100) : 0;
const toBps = usdPerM => (usdPerM / 1000000) * 10000;
const BPS = {
clientService:+toBps(COST_PER_M_USD.clientService).toFixed(2),
opsAdmin:+toBps(COST_PER_M_USD.opsAdmin).toFixed(2),
compliance:+toBps(COST_PER_M_USD.compliance).toFixed(2),
bizMgmt:+toBps(COST_PER_M_USD.bizMgmt).toFixed(2)
};
const svcPctOfRev = (BPS.clientService / REV_YIELD_BPS) * 100;
const apcCagr = (Math.pow(APC[APC.length-1]/APC[0], 1/(APC.length-1)) - 1) * 100;
const rpcCagr = (Math.pow(RPC[RPC.length-1]/RPC[0], 1/(RPC.length-1)) - 1) * 100;
const newShare = (NEW_PER_PRO / CLIENTS_PER_PRO) * 100;
const churn = 100 - RETENTION_2024;
// Fill derived KPI text
const $ = s=>document.querySelector(s);
$('#kpi-eff-mult').textContent = effMult.toFixed(2) + 'ร';
$('#kpi-nf-share').textContent = nfShare.toFixed(1) + '%';
$('#kpi-svc-pct').textContent = svcPctOfRev.toFixed(1) + '%';
$('#kpi-apc-cagr').textContent = apcCagr.toFixed(1) + '%';
$('#kpi-rpc-cagr').textContent = rpcCagr.toFixed(1) + '%';
$('#kpi-new-share').textContent = newShare.toFixed(1) + '%';
$('#kpi-churn').textContent = churn.toFixed(1) + '%';
// Derived palette
const P = { green:'#27ae60', teal:'#1abc9c', blue:'#2e86de', navy:'#2c3e50', orange:'#e67e22', red:'#e74c3c', gray:'#7f8c8d', yellow:'#f1c40f', purple:'#8e44ad' };
// AUM Growth attribution โ 5-yr mix (smaller donut + legend)
const donut = new Chart(document.getElementById('ch-mix-donut'), {
type:'doughnut',
data:{ labels:['Net Flows','Market Performance'], datasets:[{ data:[nfShare, perfShare], backgroundColor:[P.green, P.blue] }] },
options:{ cutout:'65%', plugins:{ legend:{ display:false } }, maintainAspectRatio:false }
});
putLegend('leg-mix-donut', [
{label:'Net Flows', color:P.green},
{label:'Market Performance', color:P.blue}
]);
// Net-flow share by year (bar) + legend
const years = Object.keys(SRC).map(y=>+y).filter(y=> (SRC[y].total ?? ((SRC[y].newClients||0)+(SRC[y].performance||0))) > 0);
const nfByYear = years.map(y => (SRC[y].newClients / (SRC[y].total || (SRC[y].newClients+SRC[y].performance)) * 100));
new Chart(document.getElementById('ch-nf-byyear'), {
type:'bar',
data:{ labels:years, datasets:[{ label:'Net-flow share of AUM growth (%)', data:nfByYear, backgroundColor:P.teal }] },
options:{ scales:{ y:{ beginAtZero:true, ticks:{ callback:v=>v+'%' } } }, plugins:{ legend:{ display:false } } }
});
putLegend('leg-nf-byyear', [
{label:'Net-flow share of AUM growth', color:P.teal}
]);
// APC index (line) + legend
new Chart(document.getElementById('ch-apc-index'), {
type:'line',
data:{ labels:APC_YEARS, datasets:[{ label:'Assets per Client โ Index', data:APC.map((v,i,arr)=>+(v/arr[0]*100).toFixed(1)), borderColor:P.navy, fill:false, tension:0.2, pointRadius:3 }] },
options:{ scales:{ y:{ beginAtZero:false } }, plugins:{ legend:{ display:false } } }
});
putLegend('leg-apc-index', [
{label:'Assets per Client โ Index', color:P.navy}
]);
// RPC index (line) + legend
new Chart(document.getElementById('ch-rpc-index'), {
type:'line',
data:{ labels:RPC_YEARS, datasets:[{ label:'Revenue per Client โ Index', data:RPC.map((v,i,arr)=>+(v/arr[0]*100).toFixed(1)), borderColor:P.purple, fill:false, tension:0.2, pointRadius:3 }] },
options:{ scales:{ y:{ beginAtZero:false } }, plugins:{ legend:{ display:false } } }
});
putLegend('leg-rpc-index', [
{label:'Revenue per Client โ Index', color:P.purple}
]);
// Cost per $1M (bps) โ bars + legend
new Chart(document.getElementById('ch-cost-bps'), {
type:'bar',
data:{ labels:['Client Service','Ops & Admin','Compliance','Business Mgmt'], datasets:[{ label:'bps', data:[BPS.clientService,BPS.opsAdmin,BPS.compliance,BPS.bizMgmt], backgroundColor:[P.blue,P.gray,P.orange,P.red] }] },
options:{ scales:{ y:{ beginAtZero:true, title:{ display:true, text:'bps' } } }, plugins:{ legend:{ display:false } } }
});
putLegend('leg-cost-bps', [
{label:'Client Service', color:P.blue},
{label:'Ops & Admin', color:P.gray},
{label:'Compliance', color:P.orange},
{label:'Business Mgmt', color:P.red}
]);
// Yield vs Service Cost โ bars + legend
new Chart(document.getElementById('ch-yield-vs-cost'), {
type:'bar',
data:{ labels:['Revenue Yield','Client-Service Cost'], datasets:[{ label:'bps', data:[REV_YIELD_BPS,BPS.clientService], backgroundColor:[P.green,P.blue] }] },
options:{ scales:{ y:{ beginAtZero:true, title:{ display:true, text:'bps' } } }, plugins:{ legend:{ display:false } } }
});
putLegend('leg-yield-vs-cost', [
{label:'Revenue Yield', color:P.green},
{label:'Client-Service Cost', color:P.blue}
]);