Tiếp nối hai bài viết trước về các vấn đề cần chú ý trong giao dịch theo chương trình:
Hàng khô lõi cứng - chi tiết và suy nghĩ về giao dịch tự động theo thời gian thực của hệ thống giao dịch định lượng (1. Vấn đề và khó khăn)
Hệ thống giao dịch định lượng - Chi tiết và suy nghĩ về lời đề nghị của công ty tự động (2. Mục đích của lời đề nghị của công ty)
Ở đây chúng ta sẽ tiếp tục nói về một số kỹ năng xử lý chi tiết trong thiết kế mã.
cơ sở dữ liệu
Như đã đề cập trong hai bài viết trước, bản ghi trạng thái của một tổ hợp chiến lược phức tạp hơn là rất quan trọng và yêu cầu sử dụng cơ sở dữ liệu.
Trên thực tế, hầu hết các hoạt động kinh doanh lập trình đều có thể được phân loại thành loại CRUD, tức là quy trình cốt lõi chỉ là thêm, xóa, sửa đổi và kiểm tra cơ sở dữ liệu. Mã giao dịch cũng không ngoại lệ. Hành vi giao dịch trên thị trường thứ cấp tần số trung bình và tần số thấp thực sự không khác nhiều so với khi mọi người đi mua một kho báu nào đó. Họ vừa mua vừa bán. Trọng tâm của giao dịch tự động nằm ở việc quản lý trạng thái chiến lược.
Nếu bạn chưa quen với cơ sở dữ liệu quan hệ như SQL, bạn nên trực tiếp học cách sử dụng cơ sở dữ liệu trong bộ nhớ NoSQL như Redis. Ưu điểm của nó là dễ dàng bắt đầu, có hiệu suất tuyệt vời và vốn đã đơn luồng, không cần phải xem xét các thao tác cấp thấp như khóa dữ liệu khi đọc và ghi. Tuy nhiên, nhược điểm là không có khóa chính và không thể thực hiện được các chức năng như tăng số tự động. Nếu cần, bạn phải viết mã để tự thực hiện. Tuy nhiên, tôi chưa bao giờ sử dụng chức năng này trong giao dịch tự động lâu đến vậy.
Một vấn đề không quan trọng khác là đối với cơ sở dữ liệu trong bộ nhớ, nếu lượng dữ liệu cần lưu trữ tương đối lớn thì bộ nhớ máy chủ phải lớn hơn. Tuy nhiên, đối với giao dịch định lượng nói chung, bộ nhớ 4G hoặc thậm chí 2G là đủ. không cần thiết. Lưu rất nhiều dữ liệu.
Redis thực sự mạnh mẽ trong thế giới thực. Nếu chiến lược của bạn có yêu cầu tần suất cao, nó cũng có thể triển khai mô hình đăng ký tin nhắn pub/sub, loại bỏ nhu cầu thêm nhiều mô-đun MQ bổ sung. Nếu bạn sử dụng cơ sở dữ liệu quan hệ như MySQL, như đã đề cập trước đó, miễn là bạn chưa từng sử dụng nó trong thực tế trước đây, bạn sẽ phải đầu tư thời gian và sức lực vào việc học SQL, một ngôn ngữ lập trình tương đối khó và khó. Lưu ý rằng mọi người ở đây để thực hiện giao dịch định lượng chứ không phải để học viết mã. Hãy thử sử dụng một giải pháp đơn giản hơn.
Ngoài ra, nếu bạn muốn xây dựng một trung tâm thị trường, xét đến hiệu suất, bạn cũng sẽ sử dụng cơ sở dữ liệu Redis và hợp tác với hàng đợi tin nhắn để thuận tiện. Giải pháp đơn giản và phù hợp với hầu hết các tình huống. Một máy chủ (hoặc nhiều máy chủ) có thể được sử dụng.
thiết kế mã
Về cơ bản, khi viết code thì cấu trúc dữ liệu cốt lõi là quan trọng nhất. Thiết kế cấu trúc dữ liệu không đủ hợp lý và mã sẽ được viết một cách vụng về, vì chắc chắn sẽ khiến các mô-đun khác nhau được ghép nối với nhau, khiến việc sửa đổi trở nên rất rắc rối. Nhưng để thiết kế nó đúng cách, bạn cần có một số kinh nghiệm giao dịch, tức là kinh nghiệm kinh doanh. Đây đều là về cách viết code, không có gì nhiều để nói. Chiến lược của mọi người đều khác nhau và không thể được đối xử bình đẳng. Tuy nhiên, một số nguyên tắc có lẽ giống nhau.
Ví dụ: sau khi một lệnh được gửi, không có gì đảm bảo thành công, ngay cả khi đó là lệnh thị trường, bởi vì nó có thể bị sàn giao dịch từ chối. Ví dụ: quá trình trao đổi quá bận hoặc xảy ra mất gói dữ liệu mạng tạm thời. Sau đó, tốt nhất nên thiết kế một số trạng thái trung gian tương tự như Cam kết 2 pha. Nếu thất bại, hãy tiếp tục thử (tất nhiên không quá thường xuyên, nếu không sẽ bị cấm nếu vượt quá giới hạn. Có một trình trang trí thử lại đặc biệt để thực hiện việc này). , hoặc thêm phương pháp khác để sửa trạng thái lỗi.
Trong trường hợp bình thường, ngay cả khi sàn giao dịch ngừng hoạt động, nó sẽ phục hồi trong thời gian ngắn. Nếu bạn không khôi phục thì một bên sẽ ngừng hoạt động hoàn toàn, sàn giao dịch hoặc máy chủ mã của riêng bạn. Điều này cần được theo dõi và cảnh báo. Mã tự động không thể bao trùm mọi thứ. Nếu không thể xử lý tự động, cảnh báo sẽ được đưa ra. Nếu xử lý thủ công không được xử lý kịp thời, miễn là chúng ta không tiếp tục mở các vị thế để tăng mức độ tiếp xúc thì vấn đề sẽ không quá lớn, vì đã có lệnh dừng lỗ theo thuật toán để che đáy.
Sau đây là logic mở chiến lược mà tôi đã tự mình sắp xếp:

Về cơ bản, bằng cách vẽ một sơ đồ tương tự, mã sẽ có tổ chức hơn nhiều và số lượng lỗi tiềm ẩn sẽ giảm đi. Vì vậy, trước khi viết code, tốt nhất bạn nên vẽ sơ đồ hoặc sắp xếp trong khi viết để logic rõ ràng và bạn sẽ có ý tưởng.
Logic của việc đóng một vị thế phức tạp hơn thế này nhiều, bởi vì có nhiều điều kiện hơn để đóng một vị thế, chẳng hạn như các tín hiệu dừng lãi, dừng lỗ và thoát của các chiến lược khác nhau, sẽ kích hoạt việc đóng vị thế. Nói tóm lại, việc thoát khỏi thị trường quan trọng hơn việc tham gia thị trường và lời đề nghị thực sự sẽ rắc rối hơn một chút.
Bởi vì logic chiến lược của mỗi người chắc chắn là khác nhau nên tôi sẽ không đi sâu vào chi tiết ở đây. Điều chính là làm rõ logic chiến lược của bạn và ngăn chặn các lỗi khác nhau có thể xảy ra.
Tuy nhiên, những điều này có vẻ phức tạp và chúng chỉ là một loạt các if khác (Python thậm chí không có câu lệnh switch). Nếu các điều kiện được đáp ứng, hãy chuyển sang bước tiếp theo. Nếu không, hãy thoát ra. Không có vấn đề gì lớn.
kiểm soát thời gian
Khi thực hiện giao dịch, thời gian chắc chắn là rất quan trọng. Ở đây tôi sẽ chia sẻ thêm một số kỹ năng kiểm soát thời gian của mình.
Trong lập trình nói chung, nếu có những tác vụ cần được thực hiện thường xuyên thì các gói công việc định kỳ như apscheduler thường được sử dụng. Ví dụ: lập kế hoạch cho một nhiệm vụ đã lên lịch để phát hiện xem giá dòng k cần mở hay đóng một vị thế ngay khi đến giờ.
Loại gói này thực sự mở một luồng mới ngay lập tức tại thời điểm đã đặt (có thể chính xác đến cấp độ thứ hai), sau đó thực thi tác vụ đã chỉ định. Vấn đề là nếu thời gian thực hiện của tác vụ này quá dài thì tác vụ tiếp theo. sẽ bắt đầu lại. Sẽ có những vấn đề mặc dù có thể giải quyết được nhưng chúng lại rắc rối hơn.
Tôi thường sử dụng các tác vụ đã lên lịch như vậy khi cần xuất thông tin nhật ký thường xuyên và thực hiện tự sửa lỗi và tự kiểm tra chương trình, đây là những chức năng tần suất thấp có thể được hoàn thành nhanh chóng. Các hàm này chỉ đọc dữ liệu trong cơ sở dữ liệu và không có thao tác ghi hoặc cập nhật nào, vì vậy sẽ không hại gì khi để apscheduler mở một luồng khác để xử lý.
Tuy nhiên, logic yêu cầu truy vấn liên tục và thường xuyên về giá thị trường hoặc trạng thái chính sách địa phương thì không phù hợp với gói thời gian như vậy và cần được kiểm soát bằng vòng lặp while.
Ví dụ: nếu chiến lược sử dụng tín hiệu trong thanh của đường K, điều đó có nghĩa là tín hiệu có thể xuất hiện bất kỳ lúc nào và bạn cần liên tục kiểm tra giá cơ bản hoặc khối lượng giao dịch để xác định xem có cần thực hiện các hoạt động tương ứng hay không. Ví dụ: nếu bạn cần kiểm soát thời gian chính xác hơn vào thời điểm này, chỉ thực hiện chuỗi logic mở một vị trí 10 giây trước khi kết thúc giờ k-line. Vậy làm thế nào bạn có thể đánh giá một cách đơn giản liệu đó có phải là 10 giây cuối cùng hay không?
Tôi lấy phần còn lại của 3600 bằng cách sử dụng dấu thời gian.
(Nếu bạn không quen với dấu thời gian trên máy tính, tức là dấu thời gian, nó bắt đầu từ Giờ trung bình Greenwich 1970.1.1. Ví dụ: đó là khoảng 1691240298 giây. Không có vấn đề gì với việc máy tính chính xác với mức micro giây.)
Sau khi lấy số dư từ 3600, nếu số dư lớn hơn 3590 giây thì đó là mười giây cuối cùng. Lỗi có thể giảm xuống mức mili giây. Như đã đề cập trước đó, tốt nhất là các hoạt động giao dịch chính nên được thực hiện tuần tự trong một luồng. Bằng cách này, thời gian thực hiện có thể được kiểm soát một cách đại khái hoặc khoảng thời gian có thể bị làm mờ, điều này sẽ không có vấn đề gì.
Kiểu kiểm soát này yêu cầu cài đặt thời gian trên máy chủ của bạn không sai lệch nhiều so với thời gian tiêu chuẩn. Tuy nhiên, loại điều khiển này sẽ không chính xác lắm, nó chỉ có thể đạt đến cấp độ thứ hai. Tuy nhiên, Python không thể kiểm soát thời gian một cách chính xác và các hàm ngủ đó sẽ gây ra lỗi khoảng micro giây.
Giảm tần suất truy cập API
Nếu bạn đã áp dụng một trung tâm thị trường độc lập dựa trên websocket để chia sẻ thông tin công khai trên thị trường như dòng K giữa các chiến lược khác nhau hoặc thậm chí các mã giao dịch khác nhau thì không cần lo lắng về giới hạn tần suất API về vấn đề này.
Nhưng chúng ta cũng cần lấy thông tin tài khoản một cách kịp thời, đó là ba loại thông tin: tiền, vị thế và lệnh. Mặc dù thông tin tài khoản cũng có thể sử dụng websocket và đợi máy chủ trao đổi chủ động đẩy nó, nhưng việc sử dụng trực tiếp api còn lại sẽ đơn giản hơn vì ba lý do.
Đầu tiên, để giảm bớt gánh nặng cho thông tin tài khoản do websocket đẩy, sàn yêu cầu sàn kết nối lại hoặc cập nhật listenKey định kỳ (Binance là 60 phút). Tình trạng này rõ ràng là rắc rối hơn để duy trì. Mặc dù không có gì rắc rối lắm nhưng nếu bạn thực hiện lưu trữ API và giao dịch nhiều tài khoản cùng một lúc, bạn phải kích hoạt một chương trình riêng để đảm bảo tính chính xác theo thời gian thực của thông tin và logic trở nên phức tạp hơn rất nhiều.
Thứ hai, một số sàn giao dịch không có tính năng đẩy websocket để cập nhật thông tin tài khoản. Trong trường hợp này, logic giao dịch không phổ biến và việc chuyển mã giao dịch thực giữa các sàn giao dịch khác nhau sẽ rắc rối hơn. Tất nhiên, hầu hết mọi người sẽ không giao dịch trên nhiều sàn giao dịch và sẽ không gặp phải tình huống như vậy.
Thứ ba, nếu liên kết websocket bị hỏng hoặc gặp sự cố như cập nhật listenKey hoặc hết hạn đã đề cập trước đó, nếu chương trình không phát hiện kịp thời và xảy ra giao dịch vào thời điểm này, thì nếu bạn chỉ dựa vào websocket, nó sẽ gây ra sự so sánh Vấn đề lớn, vì thông tin cập nhật tài khoản bị bỏ lỡ sẽ không được đẩy lại. Để tránh khả năng xảy ra sự cố tiềm ẩn như vậy, bạn cũng phải dựa vào api còn lại để có được thông tin tài khoản nhất định. Vì vậy, cuối cùng chúng ta phải dựa vào api còn lại, phải không?
Tóm lại, đối với các giao dịch tần suất trung bình và thấp, tốt hơn hết bạn nên sử dụng trực tiếp api còn lại để lấy thông tin tài khoản. Đối với các nguồn dữ liệu quan trọng, tốt nhất bạn nên tuân theo SSOT (Single Source Of Truth), nếu không, nó có thể gây ra sự nhầm lẫn tiềm ẩn. Khái niệm này có thể được bạn tự tìm trên Google và rất hữu ích trong việc lập trình dựa trên việc xử lý dữ liệu.
Sau đó, nếu bạn chỉ sử dụng API REST để lấy thông tin tài khoản, bạn có thể duy trì bảng thông tin tài khoản tạm thời trong bộ nhớ mã cục bộ, đặc biệt là số tiền khả dụng. Điều này có thể làm giảm số lượt truy cập thường xuyên vào sàn giao dịch. Điều quan trọng là phải nhanh chóng. bạn có thể gửi tất cả các đơn đặt hàng cùng một lúc. Tương tự như đặt hàng theo đợt, sau đó xử lý từ từ logic đằng sau bản cập nhật, đơn hàng được đặt càng nhanh thì độ trượt đương nhiên sẽ càng nhỏ.
Ngoài ra, để xác định xem lệnh dừng lỗ thuật toán đã được khớp hay chưa, bạn có thể sử dụng giá cao nhất và thấp nhất gần đây thay vì lấy trực tiếp trạng thái của lệnh chờ xử lý, vì để xác định xem lệnh đã được khớp hay chưa, bạn cần thăm dò ý kiến liên tục, điều này tương đối thường xuyên và sẽ gây ra nhiều truy cập api không hợp lệ. Cập nhật giá K-line của websocket rất kịp thời và không chiếm giới hạn tần suất API.
Tương tự như vậy, một số lệnh thoát cũng có thể giống như thế này. Nếu tín hiệu thoát của bạn phụ thuộc vào giá trong một thanh thì tốt hơn nên sử dụng giá cao và thấp gần đây. Bởi vì đôi khi giá kích hoạt và quay trở lại ngay lập tức, nếu tần suất kiểm tra của bạn không quá cao, bạn có thể bỏ lỡ. Để đảm bảo tính nhất quán trong chiến lược giao dịch của bạn, hãy kiểm tra mức cao và mức thấp để không bỏ sót giá. Cách tiếp cận này có thêm lợi ích là giá giao dịch thực tế có thể thuận lợi hơn giá kích hoạt thực tế.
Tôi không biết hai điểm trên có dễ hiểu không. Nó có thể yêu cầu một số kinh nghiệm thực tế.
Mình xin chia sẻ một số kinh nghiệm phát triển code thực tế của mình, hy vọng nó sẽ hữu ích cho mọi người.