Моделирование торговли на бирже с помощью Golang
Введение
В мире финансов и инвестиций торговые платформы играют важнейшую роль. Они предоставляют трейдерам безрисковую среду для отработки стратегий и тестирования алгоритмов перед выходом на реальный рынок. В этой статье мы рассмотрим, как смоделировать простую торговую платформу с помощью языка программирования Go (Golang). Мы смоделируем рыночные данные по различным акциям, дадим возможность пользователям выставлять ордера и будем наблюдать за взаимодействием между симулируемыми пользователями и акциями.
Проектирование системы
Прежде чем погружаться в код, давайте разберемся в архитектуре нашей торговой платформы. У нас будет три основных компонента:
- Акции: Они представляют собой рыночные данные по отдельным акциям, включая их символы, текущие цены, объемы торгов и глубину книги заявок.
- Портфели: Они представляют собой счета пользователей. Каждый портфель содержит денежные средства и список акций, которыми владеет пользователь.
- Симулятор рынка: Отвечает за моделирование обновления цен, объемов торгов и взаимодействия с пользователем.
Начнем с определения структур 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()
}