Blog
Development·11 min read

Hyperscalper Bouwen: Een Volledig Client-Side Crypto Trading Terminal

Technische deep-dive in het bouwen van een professionele trading terminal voor Hyperliquid DEX met zero backend, meerdere ordermethoden en realtime markt-scanners.

Jo Vinkenroye·January 13, 2026
Hyperscalper Bouwen: Een Volledig Client-Side Crypto Trading Terminal

Hyperscalper is een professionele cryptocurrency trading terminal die ik bouwde voor Hyperliquid DEX. Wat het uniek maakt is dat het volledig client-side is — geen backend servers, geen tussenpersonen. Je private keys verlaten nooit je browser.

Hyperscalper Trading Interface
Hyperscalper Trading Interface

In deze post loop ik door de belangrijkste architecturale beslissingen, de verschillende ordermethoden die ik implementeerde, en hoe de realtime markt-scanners werken.

Waarom Volledig Client-Side?

Bij het bouwen van een trading-applicatie die private keys beheert, is vertrouwen alles. Ik maakte een bewuste keuze: zero backend.

Het Vertrouwensprobleem

Traditionele tradingplatformen vereisen dat je hen je inloggegevens toevertrouwt. Zelfs met de beste beveiligingspraktijken is er altijd een server die gecompromitteerd kan worden, een database die gelekt kan worden, of een medewerker die kwaad in de zin heeft.

Met Hyperscalper is de datastroom simpel:

Browser → Hyperliquid DEX API

Dat is het. Geen relay servers, geen proxies, geen backend databases.

Hoe Private Keys Beveiligd Worden

Hoewel alles in de browser draait, had ik toch robuuste encryptie nodig voor de private key die in localStorage wordt opgeslagen. Dit is de aanpak:

// AES-GCM encryptie met PBKDF2 key derivation
Const deriveKey = async (password: string, salt: Uint8Array) => {
const keyMaterial = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(password),
'PBKDF2',
false,
['deriveBits', 'deriveKey']
);
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000, // Maakt brute-force duur
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
};

Het systeem genereert bij eerste gebruik een apparaat-specifieke sleutel, opgeslagen in localStorage. Gebruikers kunnen optioneel een wachtwoord toevoegen voor extra bescherming. Zelfs als iemand de localStorage-data steelt, hebben ze 100.000 PBKDF2-iteraties nodig om elke gok te kraken.

De Afwegingen

Volledig client-side gaan heeft consequenties:

  • Zero vertrouwen vereist — maar je kunt geen server-side bots draaien
  • Geen dataverzameling — maar ordergeschiedenis is afhankelijk van de exchange API
  • Echte decentralisatie — maar alle berekeningen gebeuren in de browser
  • Geen serverkosten — maar je kunt geen zware backtesting doen

Voor een scalping terminal waar snelheid ertoe doet, zijn deze afwegingen logisch. De latency (vertraging) tussen je browser en Hyperliquid is het enige dat telt.

Ordermethoden

Traders hebben verschillende workflows. Sommigen klikken het liefst, anderen gebruiken sneltoetsen, en weer anderen willen geautomatiseerde ladder orders. Ik implementeerde vijf verschillende manieren om orders in te voeren.

Click-on-Chart Orders

Soms wil je een order plaatsen op een specifiek prijsniveau dat je op de chart ziet. Ik implementeerde een crosshair-systeem dat dit intuïtief maakt:

Precise Order Placement
Precise Order Placement
Const handleChartClick = (params) => {
if (!crosshairActive) return;
const price = params.price;
const isBuy = price < currentPrice;
placeLimitOrderAtPrice({
symbol,
price,
isBuy,
percentage: settings.orderSizePercent
});
setCrosshairActive(false);
};

Klik onder de huidige prijs = koop limit order. Klik erboven = verkoop limit order. Simpel en intuïtief.

Cloud Ladder Orders (DCA)

De kenmerkende feature. In plaats van één enkele order te plaatsen, plaatst Cloud orders 5 limit orders gestapeld op intervallen onder (voor kooporders) of boven (voor verkooporders) de huidige prijs.

Const buyCloud = async ({ symbol, currentPrice, priceInterval, percentage }) => {
const ORDER_COUNT = 5;
const TAKE_PROFIT_PERCENT = 2;
const orders = [];
for (let I = 0; I < ORDER_COUNT; I++) {
const price = currentPrice - (priceInterval * I);
const size = totalSize / ORDER_COUNT;
orders.push({
type: 'limit',
side: 'buy',
price,
size,
reduceOnly: false
});
// Auto TP op 2% winst
orders.push({
type: 'limit',
side: 'sell',
price: price * (1 + TAKE_PROFIT_PERCENT / 100),
size,
reduceOnly: true
});
}
await Promise.all(orders.map(o => submitOrder(o)));
};

Het prijsinterval wordt berekend op basis van recente kaarshoogtes — het systeem past zich aan de huidige volatiliteit aan.

Sneltoetsen

Voor snelheidstraders is naar de muis grijpen te langzaam. Ik koppelde alle veelgebruikte acties aan hotkeys:

Keyboard Shortcuts
Keyboard Shortcuts
UseEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.metaKey || e.ctrlKey) {
switch (e.key) {
case 'b':
e.preventDefault();
inverted ? SellCloud() : buyCloud();
break;
case 's':
e.preventDefault();
inverted ? BuyCloud() : sellCloud();
break;
case 'e':
e.preventDefault();
closePosition(25); // Sluit 25%
break;
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [inverted]);

Let op de inverted check — daar kom ik later op terug.

Optimistic Updates

Niemand wil 500ms wachten op een API-respons voordat ze feedback zien. Ik implementeerde optimistic updates (optimistische bijwerkingen):

Const placeOrder = async (order) => {
// 1. Direct tonen in UI
const optimisticId = crypto.randomUUID();
addOptimisticOrder({ ...order, id: optimisticId });
showToast('Placing order...');
try {
// 2. Daadwerkelijk indienen bij de exchange
const result = await submitToHyperliquid(order);
// 3. Optimistisch vervangen door echt
removeOptimisticOrder(optimisticId);
addConfirmedOrder(result);
playSuccessSound();
} catch (error) {
// 4. Terugdraaien bij fout
removeOptimisticOrder(optimisticId);
showErrorToast(error.message);
}
};

De UI reageert in minder dan 100ms, ongeacht netwerklatency.

Markt-Scanners

Een trading terminal is slechts zo goed als zijn vermogen om kansen te vinden. Ik bouwde 8 verschillende scannertypes die in realtime draaien over alle symbolen.

Scanner Results
Scanner Results

Scanner-Architectuur

Elke scanner abonneert zich op candle data via WebSocket en voert analyse uit bij elke update:

// Gedeelde WebSocket-verbinding (singleton pattern)
Const ws = getWebSocketService();
// Abonneer op alle symbolen op meerdere timeframes
Symbols.forEach(symbol => {
['1m', '5m', '15m', '1h'].forEach(timeframe => {
ws.subscribeToCandles(symbol, timeframe, (candles) => {
runScanners(symbol, timeframe, candles);
});
});
});

Stochastic Scanner

De stochastic oscillator is uitstekend voor het vinden van oversold/overbought (oververkocht/overkocht) condities. Ik implementeerde vier varianten met verschillende periodes:

Const scanStochastic = (candles, config) => {
const variants = {
ultraFast: { period: 5, smoothK: 2, smoothD: 2 },
fast: { period: 9, smoothK: 3, smoothD: 3 },
medium: { period: 14, smoothK: 3, smoothD: 3 },
slow: { period: 21, smoothK: 5, smoothD: 5 }
};
const signals = [];
Object.entries(variants).forEach(([name, params]) => {
if (!config.variants[name].enabled) return;
const stoch = calculateStochastic(candles, params);
const current = stoch[stoch.length - 1];
const previous = stoch[stoch.length - 2];
// Bullish: K kruist boven D in oversold zone
if (current.k > current.d &&
previous.k <= previous.d &&
current.k < config.oversoldLevel) {
signals.push({
type: 'stochastic',
variant: name,
direction: 'bullish',
strength: (config.oversoldLevel - current.k) / config.oversoldLevel
});
}
});
return signals;
};

Divergentie-Detectie

Divergenties zijn krachtige signalen — wanneer de prijs een nieuw dieptepunt maakt maar de RSI een hoger dieptepunt, duidt dat vaak op een ommekeer. Dit vereiste complexere logica:

Const detectDivergence = (candles, rsiValues) => {
const pricePivots = findPivots(candles, 3);
const rsiPivots = findPivots(rsiValues, 3);
const divergences = [];
const recentPriceLows = pricePivots
.filter(p => p.type === 'low')
.slice(-5);
for (let I = 1; I < recentPriceLows.length; I++) {
const current = recentPriceLows[I];
const previous = recentPriceLows[I - 1];
// Prijs maakte een lager dieptepunt
if (current.price < previous.price) {
const currentRsi = findClosestPivot(rsiPivots, current.index);
const previousRsi = findClosestPivot(rsiPivots, previous.index);
// RSI maakte een hoger dieptepunt = bullish divergentie
if (currentRsi.value > previousRsi.value) {
divergences.push({
type: 'regular_bullish',
pricePoints: [previous, current],
rsiPoints: [previousRsi, currentRsi],
strength: calculateDivergenceStrength(...)
});
}
}
}
return divergences;
};

Prestatieoverwegingen

8 scanners draaien over 500+ symbolen op 4 timeframes zou de browserprestaties makkelijk kunnen killen. Ik gebruikte verschillende optimalisatietechnieken:

Memoization met TTL:

Const memoizedCalculate = createMemoizedFunction(
calculateStochastic,
(candles, period) => `stoch-${candles.length}-${period}`,
100, // max cache entries
60000 // vervalt na 60s
);

Debounced divergentie-detectie:

// Herbereken niet bij elke tick
Const debouncedDivergence = useDebouncedCallback(
detectDivergence,
1000 // Wacht 1s na laatste update
);

Virtual scrolling voor resultaten:

<FixedSizeList
height={600}
itemCount={signals.length}
itemSize={60}
>
{({ index, style }) => (
<SignalItem signal={signals[index]} style={style} />
)}
</FixedSizeList>

Multi-Timeframe Analyse

Een van de krachtigste features is het multi-timeframe overzicht. Hetzelfde symbool tegelijk zien op 1m, 5m, 15m en 1u helpt bij het identificeren van confluence (samenvloeiing van signalen).

Multi-Timeframe View
Multi-Timeframe View

Alle charts zijn gesynchroniseerd — wanneer je op één chart zoomt of schuift, volgen alle andere. Dit wordt bereikt via een gedeelde chart sync store:

Const useChartSyncStore = create((set) => ({
timeRange: null,
setTimeRange: (range) => set({ timeRange: range }),
}));
// In elke chart-component
UseEffect(() => {
const unsubscribe = useChartSyncStore.subscribe(
(state) => state.timeRange,
(range) => {
if (range) chart.timeScale().setVisibleRange(range);
}
);
return unsubscribe;
}, [chart]);

Support en Resistance Lijnen

Geautomatiseerde trendlijn-detectie is een van die features die simpel klinkt maar verrassend diep gaat. Ik implementeerde drie complementaire benaderingen.

Pivot-Gebaseerde Trendlijnen

De meest intuïtieve aanpak: vind pivotpunten en trek er lijnen doorheen.

Const detectPivots = (candles, strength = 3) => {
const pivots = [];
for (let I = strength; I < candles.length - strength; I++) {
const current = candles[I];
let isHigh = true;
for (let j = I - strength; j <= I + strength; j++) {
if (j !== I && candles[j].high >= current.high) {
isHigh = false;
break;
}
}
if (isHigh) {
pivots.push({ index: I, price: current.high, type: 'high' });
}
// Dezelfde logica voor dieptepunten...
}
return pivots;
};

Envelope Validatie

Een supportlijn die doorbroken wordt is niet nuttig. Ik valideer lijnen tegen alle candles:

Const findBestSupportLine = (pivots, candles) => {
let bestLine = null;
let bestScore = -Infinity;
for (let I = 0; I < pivots.length - 1; I++) {
for (let j = I + 1; j < pivots.length; j++) {
const line = createLine(pivots[I], pivots[j]);
let touches = 0;
let violations = 0;
candles.forEach((candle, idx) => {
const linePrice = getLineValueAt(line, idx);
const tolerance = linePrice * 0.002;
if (candle.low < linePrice - tolerance) {
violations++;
} else if (Math.abs(candle.low - linePrice) < tolerance) {
touches++;
}
});
const score = touches * 10 - violations * 100;
if (score > bestScore && violations === 0 && touches >= 3) {
bestScore = score;
bestLine = line;
}
}
}
return bestLine;
};

Het belangrijkste inzicht: een supportlijn met nul doorbraken is veel waardevoller dan eentje met veel raakpunten maar enkele doorbraken.

Trade-Analyse

Prestaties bijhouden is essentieel om te verbeteren als trader. Hyperscalper biedt dagelijkse en maandelijkse P&L-overzichten (winst en verlies):

Daily Overview
Daily Overview
Monthly Overview
Monthly Overview

Al deze data komt rechtstreeks van Hyperliquid's API — geen backend-opslag nodig.

De Inverted Modus

Dit is een feature geboren uit echte trading-ervaring: inverted mode (omgekeerde modus).

Als je een short-biased trader bent, is de standaard UI verwarrend. Groen betekent dat de prijs omhoog ging (slecht voor shorts), rood betekent dat het omlaag ging (goed voor shorts). Bullish signalen zijn bearish kansen.

Inverted mode draait alles om:

Const getSignalColor = (signal, inverted) => {
const isBullish = signal.direction === 'bullish';
if (inverted) {
// Bullish signaal = shortkans = toon als "goed" (groen)
return isBullish ? Colors.bearish : colors.bullish;
}
return isBullish ? Colors.bullish : colors.bearish;
};

Kaarskleuren, signaalindicatoren, zelfs de support/resistance label-semantiek draait om. Een short trader ziet dezelfde patronen als een long trader, alleen correct geïnterpreteerd voor zijn bias.

Multi-Monitor Ondersteuning

Professionele traders gebruiken vaak meerdere monitoren. Hyperscalper ondersteunt het losmaken van charts in aparte vensters:

Multi-Monitor Setup
Multi-Monitor Setup

Elk popup-venster behoudt zijn eigen WebSocket-verbinding en synchroniseert state met het hoofdvenster via BroadcastChannel:

Const channel = new BroadcastChannel('hyperscalper-sync');
// Hoofdvenster broadcast statuswijzigingen
Channel.postMessage({ type: 'POSITION_UPDATE', data: positions });
// Popup-vensters luisteren
Channel.onmessage = (event) => {
if (event.data.type === 'POSITION_UPDATE') {
setPositions(event.data.data);
}
};

State Management met Zustand

Met 20+ stores die verschillende domeinen beheren, had ik lichtgewicht state management nodig. Redux voelde als overkill. Ik koos Zustand:

Const useTradingStore = create<TradingStore>((set, get) => ({
orders: [],
positions: [],
addOrder: (order) => set((state) => ({
orders: [...state.orders, order]
})),
closePosition: async (symbol, percentage) => {
const position = get().positions.find(p => p.symbol === symbol);
if (!position) return;
const size = position.size * (percentage / 100);
await submitMarketOrder({
symbol,
side: position.side === 'long' ? 'sell' : 'buy',
size,
reduceOnly: true
});
}
}));

Elke store is gericht op één domein: trading, orders, posities, candles, scanner, instellingen, etc. Componenten abonneren zich op precies wat ze nodig hebben.

Lessen Geleerd

Het bouwen van Hyperscalper leerde me verschillende dingen:

1. Client-side kan genoeg zijn. Voor applicaties waar vertrouwen ertoe doet, is het elimineren van de backend niet alleen mogelijk — het verdient de voorkeur.

2. Optimistic updates zijn essentieel. Bij trading voelt 500ms latency als een eeuwigheid. Toon direct feedback, reconcilieer later.

3. Meerdere invoermethoden doen ertoe. Verschillende traders hebben verschillende workflows. Sommigen klikken, sommigen gebruiken sneltoetsen, sommigen willen automatisering. Ondersteun ze allemaal.

4. Prestatie-optimalisatie is verplicht. Technische analyse draaien op 500 symbolen in realtime vereist zorgvuldige aandacht voor memoization, debouncing en virtual rendering.

5. Omgekeerd denken helpt. Bouwen voor short traders dwong me om aannames te bevragen over wat „omhoog" en „goed" betekenen. Het resultaat is een flexibeler systeem.

Hyperscalper is live op hyperscalper.vercel.app als je het wilt uitproberen. De code laat zien dat professionele tradingtools volledig in de browser kunnen draaien — geen backend vereist.

Stay Updated

Get notified about new posts on automation, productivity tips, indie hacking, and web3.

No spam, ever. Unsubscribe anytime.

Comments

Related Posts