🎸
🚀 Beta läuft
PYNGUP: Rebellion gegen toxische Produktivität
Beta auf 100 Plätze begrenzt. Tasks werden zu sozialen Commitments statt einsamer To-Dos.
Du möchtest einen professionellen Crypto Trading Bot entwickeln, der nicht nur handelt, sondern auch alle Daten strukturiert speichert und analysiert? Die Kombination aus Binance API und Supabase als Backend ist die perfekte Lösung für skalierbare Trading-Anwendungen.
In diesem umfassenden Guide zeige ich dir, wie du die Binance API mit Supabase verbindest, um ein vollständiges Trading-System zu erstellen - von der Authentifizierung bis zum Live-Monitoring deiner Trading-Performance.
Binance API bietet Zugang zur weltgrößten Krypto-Börse mit:
Supabase als Backend liefert:
Starte mit einem neuen Supabase-Projekt auf database.new und erstelle dir Binance API-Keys in deinem Binance Account.
Führe dieses SQL in deinem Supabase SQL Editor aus:
-- Trading Accounts Table
CREATE TABLE trading_accounts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
exchange TEXT NOT NULL DEFAULT 'binance',
api_key_encrypted TEXT NOT NULL,
api_secret_encrypted TEXT NOT NULL,
is_testnet BOOLEAN DEFAULT true,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Trading Pairs Table
CREATE TABLE trading_pairs (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
symbol TEXT NOT NULL UNIQUE,
base_asset TEXT NOT NULL,
quote_asset TEXT NOT NULL,
is_active BOOLEAN DEFAULT true,
min_quantity DECIMAL,
max_quantity DECIMAL,
step_size DECIMAL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Price Data Table
CREATE TABLE price_data (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
symbol TEXT NOT NULL,
price DECIMAL NOT NULL,
volume DECIMAL,
timestamp TIMESTAMPTZ NOT NULL,
source TEXT DEFAULT 'binance_websocket',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Trading Orders Table
CREATE TABLE trading_orders (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
account_id UUID REFERENCES trading_accounts(id) ON DELETE CASCADE,
binance_order_id BIGINT,
symbol TEXT NOT NULL,
side TEXT CHECK (side IN ('BUY', 'SELL')),
type TEXT CHECK (type IN ('MARKET', 'LIMIT', 'STOP_LOSS', 'STOP_LOSS_LIMIT')),
quantity DECIMAL NOT NULL,
price DECIMAL,
stop_price DECIMAL,
status TEXT DEFAULT 'NEW',
filled_quantity DECIMAL DEFAULT 0,
commission DECIMAL DEFAULT 0,
commission_asset TEXT,
executed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Portfolio Holdings Table
CREATE TABLE portfolio_holdings (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
account_id UUID REFERENCES trading_accounts(id) ON DELETE CASCADE,
asset TEXT NOT NULL,
free_balance DECIMAL DEFAULT 0,
locked_balance DECIMAL DEFAULT 0,
total_balance DECIMAL GENERATED ALWAYS AS (free_balance + locked_balance) STORED,
avg_buy_price DECIMAL DEFAULT 0,
unrealized_pnl DECIMAL DEFAULT 0,
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, account_id, asset)
);
-- Trading Strategies Table
CREATE TABLE trading_strategies (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
strategy_config JSONB NOT NULL,
is_active BOOLEAN DEFAULT false,
profit_loss DECIMAL DEFAULT 0,
total_trades INTEGER DEFAULT 0,
win_rate DECIMAL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Create indexes for better performance
CREATE INDEX idx_price_data_symbol_timestamp ON price_data(symbol, timestamp DESC);
CREATE INDEX idx_trading_orders_user_created ON trading_orders(user_id, created_at DESC);
CREATE INDEX idx_portfolio_holdings_user_account ON portfolio_holdings(user_id, account_id);
-- Row Level Security Policies
ALTER TABLE trading_accounts ENABLE ROW LEVEL SECURITY;
ALTER TABLE trading_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE portfolio_holdings ENABLE ROW LEVEL SECURITY;
ALTER TABLE trading_strategies ENABLE ROW LEVEL SECURITY;
-- Users can only access their own data
CREATE POLICY "Users can manage their own trading accounts" ON trading_accounts
FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can manage their own orders" ON trading_orders
FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can view their own portfolio" ON portfolio_holdings
FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "Users can manage their own strategies" ON trading_strategies
FOR ALL USING (auth.uid() = user_id);
-- Price data is public (read-only)
CREATE POLICY "Anyone can read price data" ON price_data
FOR SELECT USING (true);
Erstelle eine neue Edge Function für die Binance API-Integration:
# Terminal
supabase functions new binance-api-handler
// supabase/functions/binance-api-handler/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { crypto } from "https://deno.land/std/crypto/mod.ts"
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
interface BinanceConfig {
apiKey: string
apiSecret: string
testnet?: boolean
}
class BinanceAPI {
private apiKey: string
private apiSecret: string
private baseUrl: string
constructor(config: BinanceConfig) {
this.apiKey = config.apiKey
this.apiSecret = config.apiSecret
this.baseUrl = config.testnet
? 'https://testnet.binance.vision/api'
: 'https://api.binance.com/api'
}
private async signRequest(queryString: string): Promise {
const encoder = new TextEncoder()
const keyData = encoder.encode(this.apiSecret)
const msgData = encoder.encode(queryString)
const key = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signature = await crypto.subtle.sign('HMAC', key, msgData)
return Array.from(new Uint8Array(signature))
.map(byte => byte.toString(16).padStart(2, '0'))
.join('')
}
async getAccountInfo(): Promise {
const timestamp = Date.now()
const queryString = `timestamp=${timestamp}`
const signature = await this.signRequest(queryString)
const response = await fetch(
`${this.baseUrl}/v3/account?${queryString}&signature=${signature}`,
{
headers: {
'X-MBX-APIKEY': this.apiKey
}
}
)
return response.json()
}
async getAllOrders(symbol: string, limit = 500): Promise {
const timestamp = Date.now()
const queryString = `symbol=${symbol}×tamp=${timestamp}&limit=${limit}`
const signature = await this.signRequest(queryString)
const response = await fetch(
`${this.baseUrl}/v3/allOrders?${queryString}&signature=${signature}`,
{
headers: {
'X-MBX-APIKEY': this.apiKey
}
}
)
return response.json()
}
async placeOrder(orderParams: any): Promise {
const timestamp = Date.now()
const params = { ...orderParams, timestamp }
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&')
const signature = await this.signRequest(queryString)
const response = await fetch(
`${this.baseUrl}/v3/order?${queryString}&signature=${signature}`,
{
method: 'POST',
headers: {
'X-MBX-APIKEY': this.apiKey
}
}
)
return response.json()
}
async getTickerPrices(): Promise {
const response = await fetch(`${this.baseUrl}/v3/ticker/price`)
return response.json()
}
}
serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
try {
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
throw new Error('No authorization header')
}
const { data: { user }, error: authError } = await supabase.auth.getUser(
authHeader.replace('Bearer ', '')
)
if (authError || !user) {
throw new Error('Invalid user')
}
const { action, accountId, ...params } = await req.json()
// Get trading account credentials
const { data: account, error: accountError } = await supabase
.from('trading_accounts')
.select('*')
.eq('id', accountId)
.eq('user_id', user.id)
.single()
if (accountError || !account) {
throw new Error('Trading account not found')
}
// Decrypt API credentials (implement your encryption logic)
const binanceAPI = new BinanceAPI({
apiKey: account.api_key_encrypted, // TODO: Decrypt
apiSecret: account.api_secret_encrypted, // TODO: Decrypt
testnet: account.is_testnet
})
let result
switch (action) {
case 'getAccountInfo':
result = await binanceAPI.getAccountInfo()
break
case 'getAllOrders':
result = await binanceAPI.getAllOrders(params.symbol, params.limit)
break
case 'placeOrder':
result = await binanceAPI.placeOrder(params)
// Store order in database
await supabase.from('trading_orders').insert({
user_id: user.id,
account_id: accountId,
binance_order_id: result.orderId,
symbol: params.symbol,
side: params.side,
type: params.type,
quantity: params.quantity,
price: params.price,
status: result.status
})
break
case 'getTickerPrices':
result = await binanceAPI.getTickerPrices()
// Store price data
const priceInserts = result.map((ticker: any) => ({
symbol: ticker.symbol,
price: parseFloat(ticker.price),
timestamp: new Date().toISOString()
}))
await supabase.from('price_data').insert(priceInserts)
break
default:
throw new Error('Invalid action')
}
return new Response(
JSON.stringify({ success: true, data: result }),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
}
)
} catch (error) {
return new Response(
JSON.stringify({ error: error.message }),
{
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
}
)
}
})
Erstelle eine React-Komponente für dein Trading Dashboard:
// TradingDashboard.tsx
import React, { useState, useEffect } from 'react'
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(process.env.REACT_APP_SUPABASE_URL!, process.env.REACT_APP_SUPABASE_ANON_KEY!)
interface Portfolio {
asset: string
free_balance: number
locked_balance: number
total_balance: number
unrealized_pnl: number
}
interface Order {
id: string
symbol: string
side: string
type: string
quantity: number
price: number
status: string
created_at: string
}
export const TradingDashboard: React.FC = () => {
const [portfolio, setPortfolio] = useState<Portfolio[]>([])
const [orders, setOrders] = useState<Order[]>([])
const [loading, setLoading] = useState(true)
const [selectedAccount, setSelectedAccount] = useState<string | null>(null)
useEffect(() => {
loadDashboardData()
setupRealtimeSubscriptions()
}, [selectedAccount])
const loadDashboardData = async () => {
try {
// Load portfolio holdings
const { data: portfolioData, error: portfolioError } = await supabase
.from('portfolio_holdings')
.select('*')
.eq('account_id', selectedAccount)
.order('total_balance', { ascending: false })
if (portfolioError) throw portfolioError
setPortfolio(portfolioData || [])
// Load recent orders
const { data: ordersData, error: ordersError } = await supabase
.from('trading_orders')
.select('*')
.eq('account_id', selectedAccount)
.order('created_at', { ascending: false })
.limit(50)
if (ordersError) throw ordersError
setOrders(ordersData || [])
} catch (error) {
console.error('Error loading dashboard data:', error)
} finally {
setLoading(false)
}
}
const setupRealtimeSubscriptions = () => {
// Subscribe to portfolio changes
const portfolioSubscription = supabase
.channel('portfolio-changes')
.on('postgres_changes',
{
event: '*',
schema: 'public',
table: 'portfolio_holdings',
filter: `account_id=eq.${selectedAccount}`
},
(payload) => {
console.log('Portfolio update:', payload)
loadDashboardData()
}
)
.subscribe()
// Subscribe to new orders
const ordersSubscription = supabase
.channel('orders-changes')
.on('postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'trading_orders',
filter: `account_id=eq.${selectedAccount}`
},
(payload) => {
console.log('New order:', payload)
setOrders(prev => [payload.new as Order, ...prev])
}
)
.subscribe()
return () => {
portfolioSubscription.unsubscribe()
ordersSubscription.unsubscribe()
}
}
const syncWithBinance = async () => {
try {
setLoading(true)
// Call Edge Function to sync account data
const { data, error } = await supabase.functions.invoke('binance-api-handler', {
body: {
action: 'getAccountInfo',
accountId: selectedAccount
}
})
if (error) throw error
// Update portfolio holdings
const balances = data.data.balances.filter((balance: any) =>
parseFloat(balance.free) > 0 || parseFloat(balance.locked) > 0
)
for (const balance of balances) {
await supabase.from('portfolio_holdings').upsert({
account_id: selectedAccount,
asset: balance.asset,
free_balance: parseFloat(balance.free),
locked_balance: parseFloat(balance.locked),
updated_at: new Date().toISOString()
})
}
await loadDashboardData()
} catch (error) {
console.error('Error syncing with Binance:', error)
} finally {
setLoading(false)
}
}
if (loading) {
return <div className="text-center p-8">Loading dashboard...</div>
}
return (
<div className="max-w-7xl mx-auto p-6">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Trading Dashboard</h1>
<button
onClick={syncWithBinance}
className="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded"
>
Sync with Binance
</button>
</div>
{/* Portfolio Overview */}
<div className="bg-white rounded-lg shadow mb-8 p-6">
<h2 className="text-xl font-semibold mb-4">Portfolio Holdings</h2>
<div className="overflow-x-auto">
<table className="min-w-full table-auto">
<thead>
<tr className="bg-gray-50">
<th className="px-4 py-2 text-left">Asset</th>
<th className="px-4 py-2 text-right">Free Balance</th>
<th className="px-4 py-2 text-right">Locked Balance</th>
<th className="px-4 py-2 text-right">Total Balance</th>
<th className="px-4 py-2 text-right">Unrealized P&L</th>
</tr>
</thead>
<tbody>
{portfolio.map((holding) => (
<tr key={holding.asset} className="border-t">
<td className="px-4 py-2 font-medium">{holding.asset}</td>
<td className="px-4 py-2 text-right">{holding.free_balance.toFixed(8)}</td>
<td className="px-4 py-2 text-right">{holding.locked_balance.toFixed(8)}</td>
<td className="px-4 py-2 text-right font-semibold">{holding.total_balance.toFixed(8)}</td>
<td className={`px-4 py-2 text-right font-semibold ${
holding.unrealized_pnl >= 0 ? 'text-green-600' : 'text-red-600'
}`}>
{holding.unrealized_pnl.toFixed(2)}%
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* Recent Orders */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-semibold mb-4">Recent Orders</h2>
<div className="overflow-x-auto">
<table className="min-w-full table-auto">
<thead>
<tr className="bg-gray-50">
<th className="px-4 py-2 text-left">Symbol</th>
<th className="px-4 py-2 text-left">Side</th>
<th className="px-4 py-2 text-left">Type</th>
<th className="px-4 py-2 text-right">Quantity</th>
<th className="px-4 py-2 text-right">Price</th>
<th className="px-4 py-2 text-left">Status</th>
<th className="px-4 py-2 text-left">Time</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order.id} className="border-t">
<td className="px-4 py-2 font-medium">{order.symbol}</td>
<td className={`px-4 py-2 font-semibold ${
order.side === 'BUY' ? 'text-green-600' : 'text-red-600'
}`}>
{order.side}
</td>
<td className="px-4 py-2">{order.type}</td>
<td className="px-4 py-2 text-right">{order.quantity}</td>
<td className="px-4 py-2 text-right">{order.price}</td>
<td className="px-4 py-2">
<span className={`px-2 py-1 rounded-full text-xs ${
order.status === 'FILLED' ? 'bg-green-100 text-green-800' :
order.status === 'CANCELED' ? 'bg-red-100 text-red-800' :
'bg-yellow-100 text-yellow-800'
}`}>
{order.status}
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600">
{new Date(order.created_at).toLocaleString()}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
)
}
Für Live-Kursdaten kannst du Binance WebSocket Streams verwenden:
// PriceStreamService.ts
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(process.env.REACT_APP_SUPABASE_URL!, process.env.REACT_APP_SUPABASE_ANON_KEY!)
export class PriceStreamService {
private ws: WebSocket | null = null
private symbols: string[] = []
constructor(symbols: string[]) {
this.symbols = symbols.map(s => s.toLowerCase())
this.connect()
}
private connect() {
const streams = this.symbols.map(symbol => `${symbol}@ticker`)
const wsUrl = `wss://stream.binance.com:9443/ws/${streams.join('/')}`
this.ws = new WebSocket(wsUrl)
this.ws.onopen = () => {
console.log('Connected to Binance WebSocket')
}
this.ws.onmessage = async (event) => {
try {
const data = JSON.parse(event.data)
// Store price data in Supabase
await supabase.from('price_data').insert({
symbol: data.s,
price: parseFloat(data.c),
volume: parseFloat(data.v),
timestamp: new Date(data.E).toISOString()
})
// Broadcast to subscribers
this.onPriceUpdate?.(data)
} catch (error) {
console.error('Error processing price data:', error)
}
}
this.ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
this.ws.onclose = () => {
console.log('WebSocket connection closed, reconnecting...')
setTimeout(() => this.connect(), 5000)
}
}
public onPriceUpdate?: (data: any) => void
public disconnect() {
if (this.ws) {
this.ws.close()
this.ws = null
}
}
}
// Usage
const priceStream = new PriceStreamService(['BTCUSDT', 'ETHUSDT', 'ADAUSDT'])
priceStream.onPriceUpdate = (data) => {
console.log(`${data.s}: ${data.c} (${data.P}%)`)
}
Erstelle eine einfache Trading-Strategie mit Moving Averages:
// TradingStrategy.ts
interface TradingSignal {
symbol: string
action: 'BUY' | 'SELL' | 'HOLD'
confidence: number
reason: string
}
export class MovingAverageStrategy {
private supabase: any
private symbol: string
private shortPeriod: number
private longPeriod: number
constructor(supabase: any, symbol: string, shortPeriod = 10, longPeriod = 30) {
this.supabase = supabase
this.symbol = symbol
this.shortPeriod = shortPeriod
this.longPeriod = longPeriod
}
async analyze(): Promise<TradingSignal> {
// Get recent price data
const { data: priceData, error } = await this.supabase
.from('price_data')
.select('price, timestamp')
.eq('symbol', this.symbol)
.order('timestamp', { ascending: false })
.limit(this.longPeriod)
if (error || !priceData || priceData.length < this.longPeriod) {
return {
symbol: this.symbol,
action: 'HOLD',
confidence: 0,
reason: 'Insufficient data'
}
}
// Calculate moving averages
const prices = priceData.map(d => parseFloat(d.price)).reverse()
const shortMA = this.calculateMA(prices.slice(-this.shortPeriod))
const longMA = this.calculateMA(prices.slice(-this.longPeriod))
const prevShortMA = this.calculateMA(prices.slice(-this.shortPeriod - 1, -1))
const prevLongMA = this.calculateMA(prices.slice(-this.longPeriod - 1, -1))
// Determine signal
if (shortMA > longMA && prevShortMA <= prevLongMA) {
return {
symbol: this.symbol,
action: 'BUY',
confidence: Math.min((shortMA - longMA) / longMA * 100, 100),
reason: `Short MA (${shortMA.toFixed(2)}) crossed above Long MA (${longMA.toFixed(2)})`
}
} else if (shortMA < longMA && prevShortMA >= prevLongMA) {
return {
symbol: this.symbol,
action: 'SELL',
confidence: Math.min((longMA - shortMA) / shortMA * 100, 100),
reason: `Short MA (${shortMA.toFixed(2)}) crossed below Long MA (${longMA.toFixed(2)})`
}
}
return {
symbol: this.symbol,
action: 'HOLD',
confidence: 50,
reason: `Short MA: ${shortMA.toFixed(2)}, Long MA: ${longMA.toFixed(2)} - No clear signal`
}
}
private calculateMA(prices: number[]): number {
return prices.reduce((sum, price) => sum + price, 0) / prices.length
}
}
// Usage
const strategy = new MovingAverageStrategy(supabase, 'BTCUSDT')
const signal = await strategy.analyze()
if (signal.action !== 'HOLD' && signal.confidence > 70) {
console.log(`Strong ${signal.action} signal for ${signal.symbol}: ${signal.reason}`)
// Execute trade via Binance API
}
Implementiere ein Risk Management System:
// RiskManager.ts
export class RiskManager {
private maxPositionSize: number = 0.1 // 10% of portfolio
private maxDailyLoss: number = 0.05 // 5% daily loss limit
private stopLossPercentage: number = 0.02 // 2% stop loss
async checkTradeRisk(
userId: string,
accountId: string,
symbol: string,
side: 'BUY' | 'SELL',
quantity: number,
price: number
): Promise<{ allowed: boolean, reason?: string }> {
// Check portfolio size limit
const portfolioValue = await this.getPortfolioValue(userId, accountId)
const tradeValue = quantity * price
if (tradeValue > portfolioValue * this.maxPositionSize) {
return {
allowed: false,
reason: `Trade size (${(tradeValue/portfolioValue*100).toFixed(1)}%) exceeds maximum position size (${this.maxPositionSize*100}%)`
}
}
// Check daily loss limit
const dailyPnL = await this.getDailyPnL(userId, accountId)
if (dailyPnL < -portfolioValue * this.maxDailyLoss) {
return {
allowed: false,
reason: `Daily loss limit reached (${(dailyPnL/portfolioValue*100).toFixed(1)}%)`
}
}
return { allowed: true }
}
async setStopLoss(orderId: string, currentPrice: number, side: 'BUY' | 'SELL') {
const stopPrice = side === 'BUY'
? currentPrice * (1 - this.stopLossPercentage)
: currentPrice * (1 + this.stopLossPercentage)
// Create stop loss order via Binance API
console.log(`Setting stop loss for order ${orderId} at ${stopPrice}`)
}
private async getPortfolioValue(userId: string, accountId: string): Promise<number> {
// Calculate total portfolio value in USDT
// Implementation depends on your price conversion logic
return 10000 // Placeholder
}
private async getDailyPnL(userId: string, accountId: string): Promise<number> {
// Calculate daily profit/loss
// Implementation depends on your PnL calculation logic
return -50 // Placeholder
}
}
Erstelle detaillierte Performance-Metriken:
// PerformanceAnalytics.ts
export class PerformanceAnalytics {
private supabase: any
constructor(supabase: any) {
this.supabase = supabase
}
async getPerformanceMetrics(userId: string, accountId: string, period: string = '30d') {
const endDate = new Date()
const startDate = new Date()
startDate.setDate(endDate.getDate() - parseInt(period))
// Get all trades in period
const { data: trades } = await this.supabase
.from('trading_orders')
.select('*')
.eq('user_id', userId)
.eq('account_id', accountId)
.eq('status', 'FILLED')
.gte('executed_at', startDate.toISOString())
.lte('executed_at', endDate.toISOString())
if (!trades || trades.length === 0) {
return null
}
// Calculate metrics
const totalTrades = trades.length
const winningTrades = trades.filter(t => this.isWinningTrade(t)).length
const losingTrades = totalTrades - winningTrades
const winRate = (winningTrades / totalTrades) * 100
const totalPnL = trades.reduce((sum, trade) => sum + this.calculateTradePnL(trade), 0)
const avgWin = winningTrades > 0
? trades.filter(t => this.isWinningTrade(t)).reduce((sum, t) => sum + this.calculateTradePnL(t), 0) / winningTrades
: 0
const avgLoss = losingTrades > 0
? Math.abs(trades.filter(t => !this.isWinningTrade(t)).reduce((sum, t) => sum + this.calculateTradePnL(t), 0) / losingTrades)
: 0
const profitFactor = avgLoss > 0 ? (avgWin * winningTrades) / (avgLoss * losingTrades) : 0
const sharpeRatio = this.calculateSharpeRatio(trades)
return {
period,
totalTrades,
winningTrades,
losingTrades,
winRate: parseFloat(winRate.toFixed(2)),
totalPnL: parseFloat(totalPnL.toFixed(2)),
avgWin: parseFloat(avgWin.toFixed(2)),
avgLoss: parseFloat(avgLoss.toFixed(2)),
profitFactor: parseFloat(profitFactor.toFixed(2)),
sharpeRatio: parseFloat(sharpeRatio.toFixed(2)),
bestTrade: Math.max(...trades.map(t => this.calculateTradePnL(t))),
worstTrade: Math.min(...trades.map(t => this.calculateTradePnL(t)))
}
}
private isWinningTrade(trade: any): boolean {
return this.calculateTradePnL(trade) > 0
}
private calculateTradePnL(trade: any): number {
// Simplified PnL calculation
// In practice, you'd need to track entry/exit prices more carefully
return trade.side === 'BUY' ? 10 : -5 // Placeholder
}
private calculateSharpeRatio(trades: any[]): number {
const returns = trades.map(t => this.calculateTradePnL(t))
const avgReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length
const volatility = Math.sqrt(
returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / returns.length
)
return volatility > 0 ? avgReturn / volatility : 0
}
}
# .env.local
REACT_APP_SUPABASE_URL=your-supabase-url
REACT_APP_SUPABASE_ANON_KEY=your-anon-key
# Supabase Edge Functions .env
BINANCE_API_KEY=your-binance-api-key
BINANCE_API_SECRET=your-binance-secret
SUPABASE_URL=your-supabase-url
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Terminal
supabase functions deploy binance-api-handler
API-Schlüssel Verschlüsselung:
Rate Limiting:
Monitoring und Alerting:
Backtesting System:
Mit deinen gespeicherten Preisdaten kannst du Strategien gegen historische Daten testen, bevor du sie live einsetzt.
Multi-Exchange Support:
Erweitere das System um andere Börsen wie Coinbase Pro, Kraken oder KuCoin mit ähnlichen API-Patterns.
Social Trading:
Teile Strategien mit anderen Nutzern und implementiere Copy-Trading Features.
Die Kombination aus Binance API und Supabase bietet dir eine mächtige, skalierbare Grundlage für professionelle Trading-Anwendungen. Du hast jetzt:
Starte mit dem Testnet, implementiere deine ersten Strategien und skaliere dann auf Live-Trading. Mit dieser robusten Architektur bist du bestens gerüstet für professionelles Cryptocurrency Trading.
Disclaimer: Trading mit Kryptowährungen birgt hohe Risiken. Investiere nur, was du bereit bist zu verlieren, und teste alle Strategien gründlich im Testnet.
Algorithmic Trading:
Python für Finance:
Nikolai Fischer ist Gründer von Kommune3 (seit 2007) und führender Experte für die Verbindung von Software-Entwicklung und Unternehmertum. Mit 17+ Jahren Erfahrung hat er hunderte von Projekten geleitet und erreichte #1 auf Hacker News. Als Host des Podcasts "Kommit mich" und Gründer von skillution verbindet er technische Expertise mit unternehmerischem Denken. Seine Artikel über moderne Webentwicklung und systematisches Problem-Solving haben tausende von Entwicklern beeinflusst.
Folge Niko auf:
Comments