Trong lĩnh vực Crypto, Chứng khoán hoặc Ngoại hối, tốc độ và hiệu suất đóng vai trò quan trọng, đặc biệt đối với Matching Engine – hệ thống xử lý các lệnh mua và bán. Ring Buffer là một cấu trúc dữ liệu dạng vòng, giúp cải thiện hiệu suất của sàn giao dịch bằng cách giảm chi phí sử dụng bộ nhớ và đảm bảo luồng dữ liệu ổn định. Bài viết này cung cấp hướng dẫn toàn diện về cách xây dựng Matching Engine sử dụng Ring Buffer.

Matching Engine (ME – Hệ thống Khớp lệnh)

1. Định nghĩa

Matching Engine là một trong những thành phần cốt lõi của các sàn giao dịch tài chính, bao gồm cả sàn chứng khoán và sàn giao dịch tiền mã hóa. Hệ thống thực hiện khớp các lệnh mua và bán dựa trên những quy tắc được xác định trước.

2. Cách thức hoạt động

Matching Engine hoạt động theo 5 bước chính: Tiếp nhận lệnh, lưu trữ lệnh, khớp lệnh, thực hiện giao dịch và cập nhật Sổ lệnh (Order Book).
  1. Receiving order: Matching Engine tiếp nhận các lệnh mua hoặc bán từ người dùng, bao gồm các thông tin như mã giao dịch (Symbol), giá, số lượng và loại lệnh (Market Order, Limit Order,...).
  1. Order Storage: Các lệnh được lưu trữ trong Order Book, Order bookbao gồm hai danh sách chính:
    • Buy OrdersDanh sách các lệnh mua được sắp xếp theo thứ tự giá giảm dần.
    • Sell OrdersDanh sách các lệnh bán được sắp xếp theo thứ tự giá tăng dần.
  1. Order Matching: Matching Engine kiểm tra xem các lệnh mua và bán có thể được khớp hay không. Sau đó, các lệnh được khớp theo các quy tắc phổ biến:
    • Price: Lệnh mua có thể khớp với lệnh bán nếu giá mua lớn hơn hoặc bằng giá bán.
    • Time: Nếu có nhiều lệnh cùng mức giá, lệnh được đặt trước sẽ được ưu tiên.
  1. Trade Execution: Khi một lệnh mua và một lệnh bán được khớp, giao dịch sẽ được thực hiện. Số lượng tài sản được chuyển từ người bán sang người mua, đồng thời tiền được chuyển từ người mua sang người bán.
  1. Updating the order book: Sau khi giao dịch được thực hiện, Order Book sẽ được cập nhật để phản ánh các lệnh đã khớp và số lượng còn lại của các lệnh.

Ví dụ, chúng ta có hai lệnh:

  • Buy order: 5 ETH với giá $100
  • Sell Order: 2 ETH với giá $99

Khi lệnh bán 2 ETH với giá $99 được gửi lên, Matching Engine sẽ khớp lệnh này với lệnh mua đầu tiên trong Order Book. Kết quả là một giao dịch 2 ETH với giá $99 được thực hiện.Sau khi giao dịch hoàn tất, Matching Engine sẽ lưu phần số lượng còn lại của Buy Order vào hàng đợi lệnh mua, hoặc ngược lại tùy theo trường hợp.

3. Các loại lệnh phổ biến

Một sàn giao dịch có thể hỗ trợ nhiều loại lệnh khác nhau, nhưng về cơ bản có 4 loại lệnh phổ biến như dưới đây.
  • Market Order: Lệnh mua hoặc bán được thực hiện tại mức giá tốt nhất hiện tại.
  • Limit Order: Lệnh mua hoặc bán được thực hiện tại mức giá chỉ định hoặc mức giá tốt hơn.
  • Stop Order: Lệnh được kích hoạt khi giá đạt đến mức giá đã chỉ định.
  • Stop-Limit Order: Lệnh được chuyển thành Limit Order khi giá đạt đến một mức giá cụ thể.

Tổng quan về Ring Buffer

1. Định nghĩa

Ring Buffer (hay còn gọi là Circular Buffer) là một cấu trúc dữ liệu có kích thước cố định, được sử dụng để lưu trữ các phần tử theo nguyên tắc FIFO (First In, First Out). Khi Buffer đầy, dữ liệu mới sẽ ghi đè lên dữ liệu cũ nhất. Dưới đây là một số đặc điểm chính của Ring Buffer:
  • Kích thước cố định: Ring Buffer có kích thước cố định. Khi Buffer đầy, phần tử mới sẽ ghi đè lên phần tử cũ nhất.
  • Hai con trỏ (Two Pointers): Head Pointer trỏ đến vị trí mới nhất, còn Tail Pointer trỏ đến vị trí cũ nhất.
  • Sử dụng bộ nhớ hiệu quả (Efficient Memory Usage):: Ring Buffer không cần di chuyển dữ liệu khi ghi hoặc đọc, giúp tối ưu việc sử dụng bộ nhớ và cải thiện hiệu suất.
```bash
npm install react-native-maps or yarn add react-native-maps
```

2. Các trường hợp sử dụng phổ biến

Ring Buffer thường được sử dụng trong các hệ thống yêu cầu xử lý luồng dữ liệu liên tục, chẳng hạn như:
  • Xử lý âm thanh hoặc video.
  • Giao tiếp giữa các tiến trình.
  • Hàng đợi trong các hệ thống thời gian thực.
<application>
  <meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="YOUR_GOOGLE_MAPS_API_KEY" />
</application>

3. Ưu điểm

  • Hiệu suất cao do không cần di chuyển dữ liệu khi thêm hoặc xóa phần tử.
  • Sử dụng bộ nhớ hiệu quả nhờ kích thước cố định.

4. Nhược điểm

  • Kích thước cố định có thể trở thành hạn chế khi lượng dữ liệu thay đổi không dự đoán trước.
  • Quản lý trạng thái đầy và rỗng phức tạp hơn. 

5. Cách thức hoạt động

  • Được triển khai trên một Array, sử dụng hai con trỏ: Tail và Head.
  • Khi nhận lệnh ghi (Write), Head Pointer ghi dữ liệu vào Buffer, sau đó tăng vị trí lên 1.
  • Khi nhận lệnh đọc (Read), dữ liệu được đọc từ Buffer, sau đó Tail Pointer tăng vị trí lên 1.
  • Khi một con trỏ đến phần tử cuối cùng, nó sẽ tự động quay lại vị trí ban đầu.

Đây là đoạn lệnh:

// Does not allow overwriting
IF (TAIL + 1) === SIZE ( throw IS_FULL), TAIL = (TAIL + 1) % SIZE 

Ví dụ:
SIZE = 5 ta có:
Buffer: [ , , , , ]
HEAD = 0
TAIL = 0

  1. Mở ios/Podfile và thêm các đoạn mã sau:

// Plus 1,
Buffer: [1, , , , ]
HEAD = 0
TAIL = 1


// Plus 2. 
Buffer: [1, 2, , , ]
HEAD = 0
TAIL = 2

// Plus 3.
Buffer: [1, 2, 3, , ]
HEAD = 0
TAIL = 3

// Plus 4.
Buffer: [1, 2, 3, 4, ]
HEAD = 0
TAIL = 4

// Plus 5.
Buffer: [1, 2, 3, 4, 5]
HEAD = 0
TAIL = 5

// Plus 6.
Throw IS_FULL

// Pop 
1,2,3,4,5

// Allows overwriting
IF (TAIL + 1) === SIZE HEAD = (HEAD + 1) % SIZE  , TAIL = (TAIL + 1) % SIZE 

// Plus 1. 
Buffer: [1, , , , ]
HEAD = 0
TAIL = 1


// Plus 2. 
Buffer: [1, 2, , , ]
HEAD = 0
TAIL = 2

// Plus 3.
Buffer: [1, 2, 3, , ]
HEAD = 0
TAIL = 3

// Plus 4.
Buffer: [1, 2, 3, 4, ]
HEAD = 0
TAIL = 4

// Plus 5.
Buffer: [1, 2, 3, 4, 5]
HEAD = 0
TAIL = 5

// Plus 6.
Buffer: [6, 2, 3, 4, 5]
HEAD = 0
TAIL = 1

// Plus 6.
Buffer: [6, 7, 3, 4, 5]
HEAD = 0
TAIL = 2

// POP HEAD start = 2 
3,4,5,6,7

Khi áp dụng Ring Buffer vào Matching Engine, hệ thống có thể tối ưu quá trình xử lý các lệnh mua và bán một cách nhanh chóng và hiệu quả. Ring Buffer đảm bảo các lệnh được xử lý theo thứ tự FIFO, trong khi Order Book và thuật toán khớp lệnh đảm bảo các lệnh được khớp chính xác theo các quy tắc đã được thiết lập.

Demo: Ứng dụng Matching Engine và Ring Buffer trong giao dịch P2P

Trong bối cảnh giao dịch tài chính hiện nay, các hệ thống giao dịch P2P (Peer-to-Peer) ngày càng phổ biến nhờ tính minh bạch và hiệu quả. Để nâng cao hiệu suất và độ tin cậy của các hệ thống này, việc ứng dụng các công nghệ tiên tiến như Matching Engine (ME) và Ring Buffer là rất quan trọng.

Công nghệ sử dụng

Database: MYSQL
Backend: Java Spring boot
Front end: NextJs

Quy trình hoạt động

Data Diagram

Dưới đây là một ví dụ về dữ liệu đầu vào của người dùng.

Order type: Chọn loại lệnh bạn muốn thực hiện.
Price: Nhập mức giá bạn muốn.
Amount: Nhập số lượng Token bạn muốn mua hoặc bán.
Total: Hệ thống sẽ tự động tính tổng số USD cần thiết.

Các lớp xử lý chính

  • P2POrderService
  • MatchingEngineService
  • P2PTransactionService
  • UserWalletService
  • RingBuffer
  • UserCryptoWalletService

P2POrderService

Tiếp nhận các lệnh và đưa vào Ring Buffer để chờ Matching Engine xử lý. Service này gọi UserWalletService và UserCryptoWalletService để cập nhật số dư của người dùng.

Nếu loại giao dịch là BUY, hệ thống sẽ đóng băng số USD cần thiết trong ví của người dùng.

Ngược lại, hệ thống sẽ đóng băng tổng số Token cần bán.

MatchingEngineService

Lần lượt lấy các lệnh từ Ring Buffer theo thứ tự FCFS (First Come, First Served) và khớp với các lệnh trước đó để thực hiện giao dịch P2P.

Các giao dịch được so sánh dựa trên các điều kiện sau:


 * Buy order: Buy price >= Sell price
 * Sell order: Sell price <= Buy price
 * Cannot buy from oneself
 * Tokens must be the same 

Bạn có thể sử dụng @PostConstruct để khai báo một Ring Buffer duy nhất cho toàn bộ ứng dụng, với kích thước Buffer được cấu hình trong file application.

Cron Job sẽ gọi hàm processOrders mỗi phút.

Hoặc bạn có thể sử dụng @Scheduled như ví dụ dưới đây để thiết lập thời gian chạy tùy chỉnh.

 @Scheduled(fixedRate = {milisecond})
Hàm xử lý khớp lệnh.

Sau khi khớp lệnh thành công, MatchingEngineService sẽ gọi P2PTransactionService để lưu lịch sử giao dịch vào Database, bao gồm Buy Order, Sell Order, số lượng và giá.

Khai báo Ring Buffer

Class RingBuffer bao gồm các thuộc tính: size, head, tail, isFull.

Bằng cách áp dụng thuật toán Ring Buffer, chúng ta có thể khai báo hàm push để thêm một phần tử mới vào Buffer.


// Does not allow overwriting
IF (TAIL + 1) === SIZE ( throw IS_FULL), TAIL = (TAIL + 1) % SIZE 
Hàm pop sẽ lấy phần tử đầu tiên từ Buffer và tăng Head lên vị trí của phần tử tiếp theo.

Các bước trên là toàn bộ quy trình giúp bạn xây dựng Matching Engine sử dụng Ring Buffer. Bạn có thể áp dụng các bước này để nâng cao hiệu suất cho các sàn giao dịch Crypto, Forex hoặc Chứng khoán. Nếu bạn muốn phát triển các sàn giao dịch này hoặc một ứng dụng cần Matching Engine, hãy liên hệHãy chia sẻ thông tin chi tiết về dự án và nhận báo giá từ chúng tôi. 
Chúng tôi luôn sẵn sàng ký kết một NDA để bảo mật thông tin của bạn!