Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate TradingView-style dark-theme candlestick charts with RSI, MACD, Bollinger Bands, and EMA/SMA using mplfinance.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
templates/waterfall.html
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="UTF-8">5<meta name="viewport" content="width=device-width, initial-scale=1.0">6<title>Waterfall Chart</title>7<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>8<style>{{BASE_STYLES}}</style>9</head>10<body>11<div class="toolbar">12<div>13<h1>{{TITLE}}</h1>14<div class="subtitle">{{SUBTITLE}}</div>15</div>16<div class="actions">17<button onclick="downloadPNG(this)">๐ฅ Download PNG</button>18<button onclick="copyToClipboard(this)">๐ Copy Image</button>19<button onclick="saveToProject(this)">๐พ Save Image</button>20</div>21</div>2223<div id="chart-area">24<div id="main-chart" style="width:100%;height:460px;"></div>25</div>2627<script>{{BASE_EXPORT_JS}}</script>28<script>29// === DATA ===30// type: 'start' | 'end' | 'increase' | 'decrease'31const items = [32{ name: 'Starting Balance', value: 1000, type: 'start' },33{ name: 'Trading Profit', value: 320, type: 'increase' },34{ name: 'Funding Income', value: 85, type: 'increase' },35{ name: 'Withdrawal', value: -200, type: 'decrease' },36{ name: 'Gas Fees', value: -45, type: 'decrease' },37{ name: 'Slippage Loss', value: -30, type: 'decrease' },38{ name: 'Airdrop', value: 120, type: 'increase' },39{ name: 'Ending Balance', value: null, type: 'end' }, // auto-calculated40];4142// === BUILD WATERFALL DATA ===43const COLOR_START = '#5470c6';44const COLOR_INCREASE = '#26a69a';45const COLOR_DECREASE = '#ef5350';46const COLOR_END = '#fac858';4748let runningTotal = 0;49const categories = [];50const baseData = []; // invisible base bar (offset)51const barData = []; // actual bar value5253items.forEach(item => {54categories.push(item.name);55if (item.type === 'start') {56runningTotal = item.value;57baseData.push(0);58barData.push({ value: item.value, itemStyle: { color: COLOR_START } });59} else if (item.type === 'end') {60const endVal = runningTotal;61baseData.push(0);62barData.push({ value: endVal, itemStyle: { color: COLOR_END } });63} else {64const change = item.value;65if (change > 0) {66baseData.push(runningTotal);67barData.push({ value: change, itemStyle: { color: COLOR_INCREASE } });68runningTotal += change;69} else {70runningTotal += change; // decrease first71baseData.push(runningTotal);72barData.push({ value: -change, itemStyle: { color: COLOR_DECREASE } });73}74}75});7677// === CHART ===78window.CHART_INSTANCES = [];79const chart = echarts.init(document.getElementById('main-chart'), 'dark');80chart.setOption({81backgroundColor: 'transparent',82tooltip: {83trigger: 'axis',84axisPointer: { type: 'shadow' },85formatter: params => {86// Show only the actual bar (series index 1)87const bar = params.find(p => p.seriesIndex === 1);88if (!bar) return '';89const item = items[bar.dataIndex];90const isDecrease = item.type === 'decrease';91const sign = isDecrease ? '-' : (item.type === 'start' || item.type === 'end') ? '' : '+';92const val = isDecrease ? -item.value : (item.type === 'end' ? bar.value : item.value);93return `<b>${bar.name}</b><br/>${sign}${val.toLocaleString()}`;94},95},96legend: {97top: 8,98data: [99{ name: 'Start/End', icon: 'rect', itemStyle: { color: COLOR_START } },100{ name: 'Increase', icon: 'rect', itemStyle: { color: COLOR_INCREASE } },101{ name: 'Decrease', icon: 'rect', itemStyle: { color: COLOR_DECREASE } },102],103},104grid: { top: 50, right: 30, bottom: 40, left: 80 },105xAxis: {106type: 'category',107data: categories,108axisLabel: { rotate: 20, color: '#c8cdd6', fontSize: 11 },109},110yAxis: {111type: 'value',112splitLine: { lineStyle: { color: '#2a2d3a' } },113},114series: [115// Invisible base (offset)116{117name: '_base',118type: 'bar',119stack: 'waterfall',120silent: true,121itemStyle: { color: 'transparent', borderColor: 'transparent' },122data: baseData,123},124// Actual bar125{126name: 'Change',127type: 'bar',128stack: 'waterfall',129data: barData,130barMaxWidth: 50,131itemStyle: { borderRadius: [4, 4, 0, 0] },132label: {133show: true,134position: 'top',135color: '#c8cdd6',136fontSize: 11,137formatter: p => {138const item = items[p.dataIndex];139if (item.type === 'start' || item.type === 'end') return p.value.toLocaleString();140return (item.value > 0 ? '+' : '') + item.value.toLocaleString();141},142},143},144],145});146147CHART_INSTANCES.push(chart);148</script>149</body>150</html>151