Direkt zum Inhalt
NikoFischer.com

Main navigation

  • Startseite
  • Über mich
    • My Reading List
    • Recommended Youtube Channels
    • Life Rules
    • Podcast
  • 50-Tage Challenge
  • Impressum
Sprachumschalter
  • German
  • English

Pfadnavigation

  1. Startseite

Binance API Guide: Crypto Trading Bot mit Supabase Backend - Live Trading Data Storage und Monitoring

🎸
🚀 Beta läuft

PYNGUP: Rebellion gegen toxische Produktivität

Beta auf 100 Plätze begrenzt. Tasks werden zu sozialen Commitments statt einsamer To-Dos.

🚀 Beta beitreten 📖 Story lesen "€487 verschwendet"

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.

Warum Binance API + Supabase?

Binance API bietet Zugang zur weltgrößten Krypto-Börse mit:

  • Real-time Market Data
  • Order Management
  • Account Information
  • WebSocket Streams für Live-Updates

Supabase als Backend liefert:

  • PostgreSQL Database für komplexe Abfragen
  • Real-time Subscriptions
  • Edge Functions für API-Processing
  • Row Level Security für sichere Daten

Projekt Setup und Grundkonfiguration

Starte mit einem neuen Supabase-Projekt auf database.new und erstelle dir Binance API-Keys in deinem Binance Account.

Supabase Database Schema

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);

Binance API Integration mit Supabase Edge Functions

Erstelle eine neue Edge Function für die Binance API-Integration:

# Terminal
supabase functions new binance-api-handler

Binance API Wrapper Function

// 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' }
      }
    )
  }
})

Frontend-Integration: React Trading Dashboard

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>
  )
}

Real-time Price Monitoring mit WebSockets

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}%)`)
}

Trading Strategien automatisieren

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
}

Risk Management und Performance Tracking

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
  }
}

Performance Analytics Dashboard

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
  }
}

Deployment und Production Setup

Environment Variables

# .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

Deploy Edge Functions

# Terminal
supabase functions deploy binance-api-handler

Sicherheitsaspekte und Best Practices

API-Schlüssel Verschlüsselung:

  • Verwende niemals Klartext für API-Schlüssel
  • Implementiere AES-256 Verschlüsselung für sensible Daten
  • Rotiere API-Schlüssel regelmäßig

Rate Limiting:

  • Implementiere Rate Limiting für API-Aufrufe
  • Verwende Exponential Backoff bei Fehlern
  • Cache häufig abgerufene Daten

Monitoring und Alerting:

  • Überwache API-Limits und -Fehler
  • Setze Alerts für ungewöhnliche Trading-Aktivitäten
  • Logge alle kritischen Aktionen

Erweiterte Features

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.

Fazit

Die Kombination aus Binance API und Supabase bietet dir eine mächtige, skalierbare Grundlage für professionelle Trading-Anwendungen. Du hast jetzt:

  • ✅ Vollständige Binance API-Integration
  • ✅ Strukturierte Datenspeicherung in PostgreSQL
  • ✅ Real-time Trading Dashboard
  • ✅ Automatisierte Trading-Strategien
  • ✅ Risk Management System
  • ✅ Performance Analytics
  • ✅ Production-ready Deployment

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.

📚 Empfohlene Bücher für Deep Dive:

Algorithmic Trading:

  • Algorithmic Trading: Winning Strategies and Their Rationale - Eine umfassende Einführung in algorithmisches Trading
  • Quantitative Trading: How to Build Your Own Algorithmic Trading Business - Praktischer Leitfaden für den Aufbau eines Trading-Systems

Python für Finance:

  • Python for Finance: Mastering Data-Driven Finance - Finanzanalyse mit Python
  • Hands-On Machine Learning for Algorithmic Trading - ML-Strategien für Trading

Tags

  • Trading
  • Crypto
  • Supabase

Comments

Hilfe zum Textformat

Restricted HTML

  • Erlaubte HTML-Tags: <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Zeilenumbrüche und Absätze werden automatisch erzeugt.
  • Website- und E-Mail-Adressen werden automatisch in Links umgewandelt.

Related articles

Einen Crypto-Trading Bot programmieren

Über den Autor

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:

  • Website: nikofischer.com
  • LinkedIn: Nikolai Fischer
  • Podcast: Kommit mich
Ihre Anmeldung konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.
Ihre Anmeldung war erfolgreich.

Newsletter

Melden Sie sich zu unserem Newsletter an, um auf dem Laufenden zu bleiben.

Nikolai Fischer

✌ Hi, ich bin Niko
Unternehmer, Entwickler & Podcaster

Kontaktier mich:

  • E-Mail
  • Telefon
  • LinkedIn

My Reading List

  • Quantitative Trading: How to Build Your Own Algorithmic Trading Business (Wiley Trading) - Ernest P. Chan
  • Hands-On Machine Learning for Algorithmic Trading: Design and implement investment strategies based on smart algorithms that learn from data using Python - Stefan Jansen
  • Algorithmic Trading - Ernie Chan
  • Let Me Tell You a Story: Tales Along the Road to Happiness - Jorge Bucay
  • Mindset: The New Psychology of Success - Carol S. Dweck
more
RSS feed