Моделирование торговли на бирже с помощью Golang

Введение

В мире финансов и инвестиций торговые платформы играют важнейшую роль. Они предоставляют трейдерам безрисковую среду для отработки стратегий и тестирования алгоритмов перед выходом на реальный рынок. В этой статье мы рассмотрим, как смоделировать простую торговую платформу с помощью языка программирования Go (Golang). Мы смоделируем рыночные данные по различным акциям, дадим возможность пользователям выставлять ордера и будем наблюдать за взаимодействием между симулируемыми пользователями и акциями.

Проектирование системы

Прежде чем погружаться в код, давайте разберемся в архитектуре нашей торговой платформы. У нас будет три основных компонента:

  1. Акции: Они представляют собой рыночные данные по отдельным акциям, включая их символы, текущие цены, объемы торгов и глубину книги заявок.
  2. Портфели: Они представляют собой счета пользователей. Каждый портфель содержит денежные средства и список акций, которыми владеет пользователь.
  3. Симулятор рынка: Отвечает за моделирование обновления цен, объемов торгов и взаимодействия с пользователем.

Начнем с определения структур Stock и Portfolio в Go.

// Stock represents market data for a single stock.
type Stock struct { 
    Symbol string  
    CurrentPrice float64  
    TradingVolume int  
    OrderBookDepth int  
    mu sync.Mutex // Mutex for concurrent access
}
// Portfolio represents a user's portfolio.
type Portfolio struct { 
    Cash float64  
    Stocks map[string] *Stock  
    mu sync.Mutex // Mutex for concurrent access
}

Моделирование данных

Для имитации рыночных данных мы создадим функцию, которая будет обновлять цену и объем торгов акциями через регулярные промежутки времени. Для одновременного обновления будем использовать Goroutines.

// SimulateMarket simulates market data updates for a stock.
func(s * Stock) SimulateMarket() { 
    for { 
        time.Sleep(time.Second) // Simulate updates every second.
        s.mu.Lock()  s.CurrentPrice = generateRandomPriceUpdate(s.CurrentPrice)  s.TradingVolume = generateRandomVolumeUpdate(s.TradingVolume)  s.mu.Unlock()
        fmt.Printf("Stock %s - Price: %.2f, Volume: %d\n", s.Symbol, s.CurrentPrice, s.TradingVolume) 
    }
}

Управление портфелями пользователей

Портфели пользователей играют важнейшую роль в нашей платформе симуляции торговли. Пользователи могут покупать и продавать акции в рамках своих портфелей. Для защиты одновременного доступа к портфелям мы будем использовать мьютексы.

// BuyStock simulates buying a stock for a user's portfolio.
func(p * Portfolio) BuyStock(symbol string, price float64, quantity int) error {
        p.mu.Lock()
        defer p.mu.Unlock()
            // Implement buying logic here.
    }
    // SellStock simulates selling a stock from a user's portfolio.
func(p * Portfolio) SellStock(symbol string, price float64, quantity int) error {
    p.mu.Lock()
    defer p.mu.Unlock()
        // Implement selling logic here.
}

Объединение акций и портфелей

В нашей основной функции мы будем создавать и моделировать рыночные данные по нескольким акциям и управлять несколькими портфелями пользователей. Пользователи будут одновременно выставлять заявки на покупку и продажу.

func main() {
    rand.Seed(time.Now().UnixNano())
        // Create and manage multiple stocks.
    stocks: = make(map[string] * Stock)
    symbols: = [] string {
        "AAPL", "GOOGL", "MSFT", "AMZN"
    }
    for _, symbol: = range symbols {
            stocks[symbol] = NewStock(symbol, 100.0, 10000, 10)
            go stocks[symbol].SimulateMarket()
        }
        // Create and manage multiple user portfolios.
    userPortfolios: = make(map[string] * Portfolio)
    var wg sync.WaitGroup
        // Simulate multiple users placing orders concurrently.
    usernames: = [] string {
        "user1", "user2", "user3"
    }
    for _, username: = range usernames {
        wg.Add(1)
        go func(user string) {
            defer wg.Done()
            if userPortfolios[user] == nil {
                    userPortfolios[user] = NewPortfolio(10000.0)
                }
                // Simulate buying and selling stocks for the user.
            for i := 0; i < 5; i++{
                symbol: = symbols[rand.Intn(len(symbols))]
                price: = stocks[symbol].CurrentPrice
                quantity: = rand.Intn(10) + 1 // Random quantity between 1 and 10.
                action: = rand.Intn(2) // 0 for buy, 1 for sell.
                if action == 0 {
                    err: = userPortfolios[user].BuyStock(symbol, price, quantity)
                    if err == nil {
                        fmt.Printf("%s bought %d shares of %s at %.2f\n", user, quantity, symbol, price)
                    }
                } else {
                    err: = userPortfolios[user].SellStock(symbol, price, quantity)
                    if err == nil {
                        fmt.Printf("%s sold %d shares of %s at %.2f\n", user, quantity, symbol, price)
                    }
                }
                time.Sleep(time.Second) // Sleep briefly between orders.
            }
            // Display the user's portfolio.
            fmt.Printf("%s's Portfolio:\n", user)
            fmt.Println("Cash:", userPortfolios[user].Cash)
            fmt.Println("Stocks:")
            for symbol, stock: = range userPortfolios[user].Stocks {
                fmt.Printf("%s: Price: %.2f, Volume: %d\n", symbol, stock.CurrentPrice, stock.TradingVolume)
            }
        }(username)
    }
    wg.Wait()
}

Заключение

В этой статье мы рассмотрели, как построить простую торговую платформу на языке Golang. Мы смоделировали рыночные данные по различным акциям, позволили пользователям одновременно выставлять ордера и наблюдали за взаимодействием пользователей и акций.

Приложение: Полный код Golang

package main

import (
 "fmt"
 "math/rand"
 "sync"
 "time"
)

// Stock represents market data for a single stock.
type Stock struct {
 Symbol         string
 CurrentPrice   float64
 TradingVolume  int
 OrderBookDepth int
 mu             sync.Mutex // Mutex for concurrent access
}

// Portfolio represents a user's portfolio.
type Portfolio struct {
 Cash   float64
 Stocks map[string]*Stock
 mu     sync.Mutex // Mutex for concurrent access
}

// NewStock creates a new stock with initial data.
func NewStock(symbol string, initialPrice float64, initialVolume int, orderBookDepth int) *Stock {
 return &Stock{
  Symbol:         symbol,
  CurrentPrice:   initialPrice,
  TradingVolume:  initialVolume,
  OrderBookDepth: orderBookDepth,
 }
}

// NewPortfolio creates a new portfolio for a user.
func NewPortfolio(initialCash float64) *Portfolio {
 return &Portfolio{
  Cash:   initialCash,
  Stocks: make(map[string]*Stock),
 }
}

// BuyStock simulates buying a stock for a user's portfolio.
func (p *Portfolio) BuyStock(symbol string, price float64, quantity int) error {
 p.mu.Lock()
 defer p.mu.Unlock()

 if p.Cash < price*float64(quantity) {
  return fmt.Errorf("insufficient funds")
 }

 if p.Stocks[symbol] == nil {
  p.Stocks[symbol] = NewStock(symbol, price, 0, 0)
 }

 stock := p.Stocks[symbol]
 stock.mu.Lock()
 defer stock.mu.Unlock()

 stock.CurrentPrice = price
 stock.TradingVolume += quantity
 p.Cash -= price * float64(quantity)
 return nil
}

// SellStock simulates selling a stock from a user's portfolio.
func (p *Portfolio) SellStock(symbol string, price float64, quantity int) error {
 p.mu.Lock()
 defer p.mu.Unlock()

 stock := p.Stocks[symbol]
 if stock == nil || stock.TradingVolume < quantity {
  return fmt.Errorf("insufficient stocks to sell")
 }

 stock.mu.Lock()
 defer stock.mu.Unlock()

 stock.CurrentPrice = price
 stock.TradingVolume -= quantity
 p.Cash += price * float64(quantity)

 if stock.TradingVolume == 0 {
  delete(p.Stocks, symbol)
 }
 return nil
}

// SimulateMarket simulates market data updates for a stock.
func (s *Stock) SimulateMarket() {
 for {
  time.Sleep(time.Second) // Simulate updates every second.

  s.mu.Lock()
  s.CurrentPrice = generateRandomPriceUpdate(s.CurrentPrice)
  s.TradingVolume = generateRandomVolumeUpdate(s.TradingVolume)
  s.mu.Unlock()

  fmt.Printf("Stock %s - Price: %.2f, Volume: %d\n", s.Symbol, s.CurrentPrice, s.TradingVolume)
 }
}

// generateRandomPriceUpdate simulates random price updates.
func generateRandomPriceUpdate(currentPrice float64) float64 {
 // Generate a random price change between -2% and 2% of the current price.
 priceChange := (rand.Float64() - 0.5) * 0.04 * currentPrice
 return currentPrice + priceChange
}

// generateRandomVolumeUpdate simulates random volume updates.
func generateRandomVolumeUpdate(currentVolume int) int {
 // Generate a random volume change between -10% and 10% of the current volume.
 volumeChange := rand.Intn(currentVolume/10+1) - currentVolume/20
 return currentVolume + volumeChange
}

func main() {
 rand.Seed(time.Now().UnixNano())

 // Create and manage multiple stocks.
 stocks := make(map[string]*Stock)
 symbols := []string{"AAPL", "GOOGL", "MSFT", "AMZN"}
 for _, symbol := range symbols {
  stocks[symbol] = NewStock(symbol, 100.0, 10000, 10)
  go stocks[symbol].SimulateMarket()
 }

 // Create and manage multiple user portfolios.
 userPortfolios := make(map[string]*Portfolio)
 var wg sync.WaitGroup

 // Simulate multiple users placing orders concurrently.
 usernames := []string{"user1", "user2", "user3"}
 for _, username := range usernames {
  wg.Add(1)
  go func(user string) {
   defer wg.Done()
   if userPortfolios[user] == nil {
    userPortfolios[user] = NewPortfolio(10000.0)
   }

   // Simulate buying and selling stocks for the user.
   for i := 0; i < 5; i++ {
    symbol := symbols[rand.Intn(len(symbols))]
    price := stocks[symbol].CurrentPrice
    quantity := rand.Intn(10) + 1 // Random quantity between 1 and 10.
    action := rand.Intn(2)        // 0 for buy, 1 for sell.

    if action == 0 {
     err := userPortfolios[user].BuyStock(symbol, price, quantity)
     if err == nil {
      fmt.Printf("%s bought %d shares of %s at %.2f\n", user, quantity, symbol, price)
     }
    } else {
     err := userPortfolios[user].SellStock(symbol, price, quantity)
     if err == nil {
      fmt.Printf("%s sold %d shares of %s at %.2f\n", user, quantity, symbol, price)
     }
    }

    time.Sleep(time.Second) // Sleep briefly between orders.
   }

   // Display the user's portfolio.
   fmt.Printf("%s's Portfolio:\n", user)
   fmt.Println("Cash:", userPortfolios[user].Cash)
   fmt.Println("Stocks:")
   for symbol, stock := range userPortfolios[user].Stocks {
    fmt.Printf("%s: Price: %.2f, Volume: %d\n", symbol, stock.CurrentPrice, stock.TradingVolume)
   }
  }(username)
 }

 wg.Wait()
}
+1
0
+1
0
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *