SDEX Components
Barchart

Bar Chart

The "BarChart" component provides a candle chart for XLM with candle information.

Features

  • Automatically fetches previous 12 hours of orders and current information and populates the chart.
  • Automatically Updates live.
  • Hover over the candles to see the price of XLM, high, low, open, close, and volume.
  • Candles are set to 15 minute intervals.
  • Provides a visual representation of the price of XLM over the last 12 hours and currently.

Additional Resources

Plotly provides opensource graphing libraries for making interactive, publication-quality graphs online. The Plotly library is available in Python, R, and JavaScript.

Review the documentation on Plotly graphs below

C#

Install the PakanaRazorSDEX NuGet package via the NuGet Package Manager Console.

.Net8.0 or greater is required.

dotnet add package PakanaRazorSDEX --version 0.0.4
??

Stellar Horizon Dependency will install automatically

NuGet Package Implementation
<PakanaRazorSDEX.Component.BarChart/>
C#
@page "/RemoveSigner"
@rendermode InteractiveAuto
 
@using Microsoft.JSInterop
 
<div class="loading-container" id="loadingContainer">
    <div class="spinner" id="spinner"></div> Loading...
</div>
 
<div class="chart-container">
    <div id="tradingChart"></div>
</div>
 
@code {
    [Inject] IJSRuntime JS { get; set; }
 
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // Call the initializeChart function in the external JavaScript file
            await JS.InvokeVoidAsync("initializeChart");
        }
    }
}
 
<script src="https://cdn.jsdelivr.net/npm/plotly.js-dist@2.19.0/plotly.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stellar-sdk/13.0.0/stellar-sdk.min.js"></script>
<script src="./_content/PakanaRazorSDEX/js/barChart.js"></script>
<script src="./_content/PakanaRazorSDEX/js/global.js"></script>

Javascript

We've provided simple HTML and JavaScript that can be used in your project as partial-components or be customized and integrated into your existing codebase.

Stellar CDN
*Current Version as of 2024-24-12*
<script src="https://cdnjs.cloudflare.com/ajax/libs/stellar-sdk/13.0.0/stellar-sdk.min.js"></script>
JavaScript
 
<script>
    const CHART_INTERVAL = 30 * 60 * 1000; // 30-minute candles
    const REALTIME_INTERVAL = 5 * 1000; // 5 seconds update interval
    const MAX_TIMEFRAME = 12 * 60 * 60 * 1000; // Last 12 hours
 
    let historicalData = [];
 
    async function fetchHistoricalTrades() {
        let trades = [];
        let cursor = 'now';
        let keepFetching = true;
        const now = Date.now();
 
        while (keepFetching) {
            const response = await server.trades()
                .forAssetPair(xlmAsset, usdcAsset)
                .order("desc")
                .limit(200)
                .cursor(cursor)
                .call();
 
            if (response.records.length === 0) break;
 
            for (const trade of response.records) {
                const tradeTime = new Date(trade.ledger_close_time).getTime();
                if (now - tradeTime > MAX_TIMEFRAME) {
                    keepFetching = false;
                    break;
                }
                trades.push(trade);
            }
 
            cursor = response.records[response.records.length - 1].paging_token;
            await new Promise(resolve => setTimeout(resolve, 200)); // Prevent API rate limiting
        }
 
        return processTradeData(trades);
    }
 
    function processTradeData(trades) {
        const groupedData = [];
        trades.forEach(trade => {
            const time = new Date(trade.ledger_close_time);
            const price = parseFloat(trade.price.n) / parseFloat(trade.price.d);
            const amount = parseFloat(trade.base_amount);
            const timeIndex = Math.floor(time.getTime() / CHART_INTERVAL) * CHART_INTERVAL;
 
            let candle = groupedData.find(c => c.x === timeIndex);
            if (!candle) {
                candle = { x: timeIndex, o: price, h: price, l: price, c: price, volume: amount };
                groupedData.push(candle);
            } else {
                candle.h = Math.max(candle.h, price);
                candle.l = Math.min(candle.l, price);
                candle.c = price;
                candle.volume += amount;
            }
        });
 
        return groupedData.sort((a, b) => a.x - b.x);
    }
 
    function renderChart(data) {
        const ohlcData = data.map(d => ({
            x: new Date(d.x),
            open: d.o,
            high: d.h,
            low: d.l,
            close: d.c
        }));
 
        const layout = {
            title: 'XLM-USD Trading Chart [12-Hour History]',
            xaxis: { type: 'date', title: 'Time', rangeslider: { visible: false } },
            yaxis: { title: 'Price (USD)' },
            paper_bgcolor: '#1a1a1a',
            plot_bgcolor: '#1a1a1a',
            font: { color: '#fff' }
        };
 
        Plotly.newPlot('tradingChart', [{
            x: ohlcData.map(d => d.x),
            open: ohlcData.map(d => d.open),
            high: ohlcData.map(d => d.high),
            low: ohlcData.map(d => d.low),
            close: ohlcData.map(d => d.close),
            type: 'candlestick',
            increasing: {
                line: { color: 'green' },
                fillcolor: 'green' // Green fill matching the outline
            },
            decreasing: {
                line: { color: 'red' },
                fillcolor: 'red' // Red fill matching the outline
            },
        }], layout);
    }
 
    async function updateLastCandle() {
        try {
            const response = await server.trades()
                .forAssetPair(xlmAsset, usdcAsset)
                .order("desc")
                .limit(1)
                .call();
 
            const trade = response.records[0];
            const tradeTime = new Date(trade.ledger_close_time).getTime();
            const price = parseFloat(trade.price.n) / parseFloat(trade.price.d);
            const amount = parseFloat(trade.base_amount);
            const timeIndex = Math.floor(tradeTime / CHART_INTERVAL) * CHART_INTERVAL;
 
            let lastCandle = historicalData.find(c => c.x === timeIndex);
            if (!lastCandle) {
                lastCandle = { x: timeIndex, o: price, h: price, l: price, c: price, volume: amount };
                historicalData.push(lastCandle);
            } else {
                lastCandle.h = Math.max(lastCandle.h, price);
                lastCandle.l = Math.min(lastCandle.l, price);
                lastCandle.c = price;
                lastCandle.volume += amount;
            }
 
            historicalData.sort((a, b) => a.x - b.x);
            renderChart(historicalData);
        } catch (error) {
            console.error("Error updating the last candle:", error);
        }
    }
 
    // Rotate spinner with JavaScript
    function rotateSpinner() {
        const spinner = document.getElementById('spinner');
        let angle = 0;
        setInterval(() => {
            angle += 6;
            spinner.style.transform = `rotate(${angle}deg)`;
        }, 50); // Rotate 6 degrees every 50ms
    }
 
    async function initializeChart() {
 
        // Fetch historical data and render the chart
        historicalData = await fetchHistoricalTrades();
        renderChart(historicalData);
 
        // Set up real-time updates every 5 seconds
        setInterval(updateLastCandle, REALTIME_INTERVAL);
    }
 
    initializeChart();
</script>
 
 
HTML
 
<body>
<div class="chart-container">
    <div id="tradingChart"></div>
</div>
</body>