Переглянути джерело

Merge branch 'master' of http://git.skyfffire.com/skyfffire/as-market-binance-okx-spot

875428575@qq.com 2 роки тому
батько
коміт
b93add4602
8 змінених файлів з 939 додано та 113 видалено
  1. 434 2
      Cargo.lock
  2. 6 0
      Cargo.toml
  3. 17 0
      README.md
  4. 128 50
      src/as_libs.rs
  5. 122 56
      src/exchange_middle_ware.rs
  6. 182 1
      src/main.rs
  7. 50 0
      tests/binance_ws_test.rs
  8. 0 4
      tests/http_test.rs

+ 434 - 2
Cargo.lock

@@ -65,12 +65,17 @@ name = "as-market-binance-okx-spot"
 version = "0.1.0"
 dependencies = [
  "chrono",
+ "ndarray",
  "reqwest",
  "rust_decimal",
  "rust_decimal_macros",
  "serde",
  "serde_derive",
  "serde_json",
+ "time 0.2.27",
+ "tokio",
+ "tungstenite",
+ "url",
 ]
 
 [[package]]
@@ -94,6 +99,18 @@ dependencies = [
  "rustc-demangle",
 ]
 
+[[package]]
+name = "base-x"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
 [[package]]
 name = "base64"
 version = "0.21.2"
@@ -124,6 +141,15 @@ dependencies = [
  "wyz",
 ]
 
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 name = "borsh"
 version = "0.10.3"
@@ -234,11 +260,17 @@ dependencies = [
  "iana-time-zone",
  "js-sys",
  "num-traits",
- "time",
+ "time 0.1.45",
  "wasm-bindgen",
  "winapi",
 ]
 
+[[package]]
+name = "const_fn"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
+
 [[package]]
 name = "core-foundation"
 version = "0.9.3"
@@ -255,6 +287,30 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
 
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "discard"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.32"
@@ -366,6 +422,16 @@ dependencies = [
  "pin-utils",
 ]
 
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.2.10"
@@ -420,6 +486,12 @@ dependencies = [
  "ahash 0.8.3",
 ]
 
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
 [[package]]
 name = "http"
 version = "0.2.9"
@@ -534,6 +606,15 @@ dependencies = [
  "hashbrown 0.12.3",
 ]
 
+[[package]]
+name = "input_buffer"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
+dependencies = [
+ "bytes",
+]
+
 [[package]]
 name = "ipnet"
 version = "2.8.0"
@@ -573,12 +654,32 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
 
+[[package]]
+name = "lock_api"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
+[[package]]
+name = "matrixmultiply"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
 [[package]]
 name = "memchr"
 version = "2.5.0"
@@ -629,6 +730,38 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "ndarray"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "rawpointer",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.16"
@@ -638,6 +771,16 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
 [[package]]
 name = "object"
 version = "0.31.1"
@@ -653,6 +796,12 @@ version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
 [[package]]
 name = "openssl"
 version = "0.10.56"
@@ -697,6 +846,29 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
 [[package]]
 name = "percent-encoding"
 version = "2.3.0"
@@ -736,6 +908,12 @@ dependencies = [
  "toml",
 ]
 
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.66"
@@ -810,6 +988,12 @@ dependencies = [
  "getrandom",
 ]
 
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
 [[package]]
 name = "redox_syscall"
 version = "0.3.5"
@@ -834,7 +1018,7 @@ version = "0.11.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
 dependencies = [
- "base64",
+ "base64 0.21.2",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -926,6 +1110,15 @@ version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
 [[package]]
 name = "rustix"
 version = "0.38.8"
@@ -954,6 +1147,12 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
 [[package]]
 name = "seahash"
 version = "4.1.0"
@@ -983,6 +1182,21 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
 [[package]]
 name = "serde"
 version = "1.0.183"
@@ -1023,6 +1237,43 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "sha-1"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sha1"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+dependencies = [
+ "sha1_smol",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "simdutf8"
 version = "0.1.4"
@@ -1038,6 +1289,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "smallvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
 [[package]]
 name = "socket2"
 version = "0.4.9"
@@ -1058,6 +1315,75 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "socks"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
+dependencies = [
+ "byteorder",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "standback"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "stdweb"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
+dependencies = [
+ "discard",
+ "rustc_version",
+ "stdweb-derive",
+ "stdweb-internal-macros",
+ "stdweb-internal-runtime",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "stdweb-derive"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stdweb-internal-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
+dependencies = [
+ "base-x",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stdweb-internal-runtime"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
+
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -1099,6 +1425,26 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "thiserror"
+version = "1.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9207952ae1a003f42d3d5e892dac3c6ba42aa6ac0c79a6a91a2b5cb4253e75c"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1728216d3244de4f14f14f8c15c79be1a7c67867d28d69b719690e2a19fb445"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
 [[package]]
 name = "time"
 version = "0.1.45"
@@ -1110,6 +1456,44 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "time"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
+dependencies = [
+ "const_fn",
+ "libc",
+ "standback",
+ "stdweb",
+ "time-macros",
+ "version_check",
+ "winapi",
+]
+
+[[package]]
+name = "time-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
+dependencies = [
+ "proc-macro-hack",
+ "time-macros-impl",
+]
+
+[[package]]
+name = "time-macros-impl"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "standback",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "tinyvec"
 version = "1.6.0"
@@ -1135,11 +1519,26 @@ dependencies = [
  "bytes",
  "libc",
  "mio",
+ "num_cpus",
+ "parking_lot",
  "pin-project-lite",
+ "signal-hook-registry",
  "socket2 0.5.3",
+ "tokio-macros",
  "windows-sys",
 ]
 
+[[package]]
+name = "tokio-macros"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
 [[package]]
 name = "tokio-native-tls"
 version = "0.3.1"
@@ -1205,6 +1604,33 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
 
+[[package]]
+name = "tungstenite"
+version = "0.13.0"
+source = "git+https://github.com/PrivateRookie/tungstenite-rs.git?rev=1d9289276518e5ab7e5194126d40b441d8938375#1d9289276518e5ab7e5194126d40b441d8938375"
+dependencies = [
+ "base64 0.13.1",
+ "byteorder",
+ "bytes",
+ "http",
+ "httparse",
+ "input_buffer",
+ "log",
+ "native-tls",
+ "rand",
+ "sha-1",
+ "socks",
+ "thiserror",
+ "url",
+ "utf-8",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.13"
@@ -1237,6 +1663,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
 [[package]]
 name = "uuid"
 version = "1.4.1"

+ 6 - 0
Cargo.toml

@@ -13,3 +13,9 @@ chrono = "0.4.26"
 serde = "1.0.183"
 serde_derive = "1.0.183"
 serde_json = "1.0.104"
+ndarray = "0.15"
+time = "0.2"
+tokio = {version = "1.31.0", features = ["full"]}
+
+tungstenite = {git="https://github.com/PrivateRookie/tungstenite-rs.git", rev="1d9289276518e5ab7e5194126d40b441d8938375"}
+url = "2.4.0"

+ 17 - 0
README.md

@@ -0,0 +1,17 @@
+## 项目结构解析
+### 1. main.rs
+    这个应该不用介绍,就是做市程序入口
+### 2. as_libs.rs
+    AS套利模型的相关库
+### 3. exchange_libs.rs
+    交易所对接相关库
+### 4. exchange_middle_ware.rs
+    交易所中间键(方便其他模块对接)
+
+## 其他问题
+### 1. 关于库存的问题
+    我们的目的是维持0库存(库存目标通过公式优化)
+### 2. 运行方式
+    clone到本地/服务器之后,直接编译即可运行(需要配置)。
+### 3. 遇到的问题
+    各种类型不熟悉问题吧,总算是都克服了。

+ 128 - 50
src/as_libs.rs

@@ -2,33 +2,30 @@ use crate::exchange_middle_ware::{Depth, Record};
 
 // 获取买一卖一价差(spread)、中间价
 pub fn get_spread(depth: &Depth) -> (f64, f64, f64, f64) {
-    return (0f64, 0f64, 0f64, 0f64);
-}
+    // 定义一个乘法系数,主要是防止f64精度丢失问题
+    let mul = 1e6;
 
-// 方向判断函数
-pub fn get_direction(records: &Vec<Record>) -> String {
-    // // 遍历列表中的每一条记录
-    // for record in records {
-    //     println!("Time: {}, Open: {}, High: {}, Low: {}, Close: {}, Volume: {}",
-    //              record.time, record.open, record.high, record.low, record.close, record.volume);
-    // }
-
-    return if records[0].close > records[0].open {
-        "long".to_string()
-    } else {
-        "short".to_string()
-    }
+    let ask = depth.asks[0].price;
+    let bid = depth.bids[0].price;
+    let spread = (ask * mul - bid * mul) / mul;
+    let price_diff = (ask * mul + bid * mul) / (2f64 * mul);
+
+    return (spread, price_diff, ask, bid);
 }
 
 // 计算最近N根K线的收盘价的标准差
-pub fn std_n_by_records(records: &Vec<Record>, n: i8) -> Vec<f64> {
-    let mut rst = Vec::new();
+pub fn std_n_by_records(records: &Vec<Record>, n: usize) -> f64 {
+    let close_list: Vec<_> = records.iter().rev().take(n).map(|r| r.close).collect();
 
-    for i in 0..n {
-        rst.push(i as f64)
-    }
+    let count = close_list.len();
 
-    return rst;
+    let mean = close_list.iter().sum::<f64>() / (count as f64);
+    let variance = close_list.iter().map(|&value| {
+        let diff = mean - value;
+        diff * diff
+    }).sum::<f64>() / count as f64;
+
+    return variance.sqrt();
 }
 
 // 计算gamma值
@@ -78,9 +75,10 @@ pub fn calc_theta(gamma: f64, std: f64, kappa: f64, ira: f64) -> f64 {
 }
 
 // 给定ira、当前库存、最大库存、买入价格、下单数量小数位数,计算合理下单数量
-pub fn calc_order_amount(ira: f64, quantity: f64, quantity_max: f64, bid_price: f64, amount_decimal_places: usize) -> f64 {
-    let eta = -ira / quantity;
-    let order_value = (quantity_max * (-eta * quantity_max).ln()).abs();
+// 下单数量要与ira(风险系数)、quantity(库存)成反比
+pub fn calc_order_amount(ira_max: f64, ira: f64, quantity: f64, quantity_max: f64, bid_price: f64, amount_decimal_places: usize) -> f64 {
+    let eta = (ira_max / (ira + 1.0)) / (quantity * 2.0);
+    let order_value = (-quantity_max * eta).abs();
     let order_amount = order_value / bid_price;
 
     return truncate_decimal_places(order_amount, amount_decimal_places);
@@ -95,33 +93,113 @@ pub fn truncate_decimal_places(num: f64, decimal_places: usize) -> f64 {
 // 单元测试集
 #[cfg(test)]
 mod tests {
+    use crate::exchange_middle_ware::{get_binance_depth, get_binance_klines};
     use super::*;
 
+    #[tokio::test]
+    async fn test_get_spread() {
+        let depth = get_binance_depth(&"BTC_USDT".to_string(), 100i32).await;
+        let ask = depth.asks[0].price;
+        let bid = depth.bids[0].price;
+
+        println!("ask={}, bid={}", ask, bid);
+        println!("{:?}", get_spread(&depth))
+    }
+
     #[test]
-    fn test_direction() {
-        // 创建一个空的 Record 列表
-        let mut records = Vec::new();
-
-        // 向列表中添加一条记录
-        records.push(Record {
-            time: 1,
-            open: 100.0,
-            high: 200.0,
-            low: 50.0,
-            close: 150.0,
-            volume: 5000.0,
-        });
-
-        // 向列表中添加另一条记录
-        records.push(Record {
-            time: 2,
-            open: 150.0,
-            high: 250.0,
-            low: 100.0,
-            close: 200.0,
-            volume: 7000.0,
-        });
-
-        println!("{}", get_direction(&records));
+    fn test_std_n_by_records() {
+        let records = vec![
+            Record{
+            time: 1567736576000,
+            open: 1000.0,
+            high: 1500.0,
+            low: 900.9,
+            close: 1200.9,
+            volume: 1000000.0,
+        }, Record{
+            time: 1567736576000,
+            open: 1000.0,
+            high: 1500.0,
+            low: 900.9,
+            close: 1219.9,
+            volume: 1000000.0,
+        }, Record{
+            time: 1567736576000,
+            open: 1000.0,
+            high: 1500.0,
+            low: 900.9,
+            close: 1218.1,
+            volume: 1000000.0,
+        }];
+
+        println!("{}", std_n_by_records(&records, 3))
     }
-}
+
+    #[test]
+    fn test_calc_gamma() {
+        println!("{}", calc_gamma(0.1, 49.0, 23.0))
+    }
+
+    #[test]
+    fn test_calc_deviation_range() {
+        let max_ira = 10.0;
+        let ira = 0.1;
+        let spread_list = vec![
+            0.1,
+            0.2,
+            0.15,
+            0.1
+        ];
+
+        println!("{}", calc_deviation_range(max_ira, &spread_list, ira));
+    }
+
+    #[test]
+    fn test_calc_rp() {
+        let mid_price = 1992.01;
+        let quantity = 49.0;
+        let ira = 0.1;
+        let gamma = 1.9289379267775165e-06;
+        let std = 23.0;
+
+        println!("{}", calc_rp(mid_price, quantity, ira, gamma, std));
+    }
+
+    #[test]
+    fn test_calc_dk() {
+        let deviation_range = 1.9900000000000002;
+        let ira = 0.1;
+        let gamma = 1.9289379267775165e-06;
+        let std = 23.0;
+
+        println!("{}", calc_dk(deviation_range, gamma, std, ira));
+    }
+
+    #[test]
+    fn test_calc_kappa() {
+        let deviation_range = 1.9900000000000002;
+        let ira = 0.1;
+        let gamma = 1.9289379267775165e-06;
+        let dk = 0.00000019191948219432835;
+
+        println!("{}", calc_kappa(gamma, dk, deviation_range, ira));
+    }
+
+    #[test]
+    fn test_calc_order_amount() {
+        let quantity = 400.0;
+        let quantity_max = 1000.0;
+        let bid_price = 30000.0;
+        let amount_decimal_places = 4;
+
+        let start = 0.1;
+        let end = 10.0;
+        let step = 0.1;
+        let mut ira = start;
+
+        while ira <= end {
+            println!("ira={}, order_amount={}", ira, calc_order_amount(end, ira, quantity, quantity_max, bid_price, amount_decimal_places));
+            ira += step;
+        }
+    }
+}

+ 122 - 56
src/exchange_middle_ware.rs

@@ -1,22 +1,24 @@
+use std::net::ToSocketAddrs;
 // use serde::{Deserialize, Serialize};
 use serde_derive::{Deserialize, Serialize};
 use serde_json::json;
 
-use crate::exchange_libs::*;
+use crate::exchange_libs;
 
 // 深度结构体
+#[derive(Debug)]
 pub struct Depth {
     pub(crate) asks: Vec<DepthItem>,
     pub(crate) bids: Vec<DepthItem>,
-    pub(crate) time: i64,
-    pub(crate) source: Vec<Vec<f64>>,
 }
+#[derive(Debug)]
 pub struct DepthItem {
     pub(crate) price: f64,
     pub(crate) amount: f64,
 }
 
 // k线数据结构体
+#[derive(Debug)]
 pub struct Record {
     pub(crate) time: i64,      // 时间
     pub(crate) open: f64,      // 开盘价
@@ -32,52 +34,89 @@ pub struct Account{
     pub(crate) frozen_balance: f64,    // Balance挂单的冻结数量
     pub(crate) stocks: f64,            // 可用交易币数量
     pub(crate) frozen_stocks: f64,     // stocks挂单的冻结数量
-    pub(crate) info: serde_json::Value
 }
 
 // 币对市场信息结构体
 pub struct Market {
-    symbol: String,
-    base_asset: String,
-    quote_asset: String,
-    tick_size: f64,
-    amount_size: f32,       // 下单量最小变动数值
-    price_precision: f32,   // 价格精度,表示价格精确到2位小数
-    amount_precision: f32,  // 下单量精度,表示下单量精确到3位小数
-    min_qty: f32,           // 最小下单量
-    max_qty: f64,           // 最大下单量
-    min_notional: f64,      // 最小下单金额
-    max_notional: f64,      // 最大下单金额
-    ct_val: f64,            // 合约价值
+    pub(crate) symbol: String,
+    pub(crate) base_asset: String,
+    pub(crate) quote_asset: String,
+    pub(crate) tick_size: f64,
+    pub(crate) amount_size: f64,       // 下单量最小变动数值
+    pub(crate) price_precision: f64,   // 价格精度,表示价格精确到2位小数
+    pub(crate) amount_precision: f64,  // 下单量精度,表示下单量精确到3位小数
+    pub(crate) min_qty: f64,           // 最小下单量
+    pub(crate) max_qty: f64,           // 最大下单量
+    pub(crate) min_notional: f64,      // 最小下单金额
+    pub(crate) max_notional: f64,      // 最大下单金额
+    pub(crate) ct_val: f64,            // 合约价值
+}
+
+// 处理交易对格式
+pub fn get_real_symbol(symbol: &String, source:bool) -> String {
+    return if source {symbol.replace("_", "")} else {symbol.replace("_", "-")}
 }
 
 // 获取币安深度信息
-pub fn get_binance_depth(symbol: &str, limit: &str) -> Depth {
+pub async fn get_binance_depth(symbol: &String, limit: i32) -> Depth {
+    let real_symbol = &get_real_symbol(symbol, true);
+    let base_url = "https://api.binance.com/api/v3/depth?symbol=".to_string() + &real_symbol + "&limit=" + &limit.to_string();
+    let req = exchange_libs::get(base_url.to_string());
+    let mut res_data = "".to_string();
+
+    match req.await {
+        Ok(data) => {
+            res_data = data
+        }
+        Err(err) => {
+            println!("err-数据:{:?}", err);
+        }
+    }
+
+    // let real_symbol = &get_real_symbol(symbol, true);
+    // let depth_json_str = exchange_libs::binan_k(real_symbol, limit).await;
+    let depth:serde_json::Value = serde_json::from_str(&*res_data).unwrap();
+    let mut depth_asks:Vec<DepthItem> = vec![];
+    for value in depth["asks"].as_array().unwrap() {
+        let asks_item = DepthItem {
+            price: value[0].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            amount: value[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        };
+        depth_asks.push(asks_item)
+    }
+    let mut depth_bids:Vec<DepthItem> = vec![];
+    for value in depth["bids"].as_array().unwrap() {
+        let asks_item = DepthItem {
+            price: value[0].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            amount: value[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        };
+        depth_bids.push(asks_item)
+    }
+
     let result = Depth {
-        asks: vec![DepthItem {
-            price: 1.8,
-            amount: 1.8,
-        }],
-        bids: vec![DepthItem {
-            price: 1.8,
-            amount: 1.8,
-        }],
-        time: 1567736576000,
-        source: vec![vec![668.25, 336.5]],
+        asks: depth_asks,
+        bids: depth_bids,
     };
     return result
 }
 
 // 获取币安K线数据信息
-pub fn get_binance_klines(symbol:  &String,interval:  &String,_limit: &i32) -> Vec<Record> {
-    let result =vec![Record{
-        time: 1567736576000,
-        open: 1000.0,
-        high: 1500.0,
-        low: 900.9,
-        close: 1200.9,
-        volume: 1000000.0,
-    }];
+pub async fn get_binance_klines(symbol: &String, interval: &String, limit: &i32) -> Vec<Record> {
+    let real_symbol = &get_real_symbol(symbol, true);
+    let klines_json_str = exchange_libs::binan_k(real_symbol, interval, limit).await;
+    let klines: Vec<Vec<serde_json::Value>> = serde_json::from_str(&*klines_json_str).unwrap();
+    let mut result: Vec<Record> = vec![];
+    for i in 0..klines.len() {
+        let record = Record{
+            time: klines[i][0].as_i64().unwrap_or(0),
+            open: klines[i][1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            high: klines[i][2].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            low: klines[i][3].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            close: klines[i][4].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+            volume: klines[i][5].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        };
+        result.push(record);
+    }
     return result;
 }
 
@@ -87,29 +126,56 @@ pub fn get_okx_account(symbols: &Vec<String>) -> Account {
         balance: 0.0,
         frozen_balance: 0.0,
         stocks: 0.0,
-        frozen_stocks: 0.0,
-        info: json!({})
-    };
+        frozen_stocks: 0.0,    };
     return result;
 }
 
-
 // 获取okx产品交易信息
-pub fn get_okx_instruments(inst_type: &String, symbol: &Vec<String>)->Vec<Market>{
-    let new_inst_type =  if inst_type.is_empty()  { inst_type.clone() } else { "SPOT".to_string() };
-    let symbols = vec![Market{
-        symbol: "OKB/USDT".to_string(),
-        base_asset: "OKB".to_string(),
-        quote_asset: "USDT".to_string(),
-        tick_size: 0.001,
-        amount_size: 0.000001,
-        price_precision: 0.000001,
-        amount_precision: 0.01,
-        min_qty: 0.01,
-        max_qty: 999999999999.0,
-        min_notional: 0.0,
-        max_notional: 0.0,
-        ct_val: 0.0,
+pub async fn get_okx_instruments(inst_type: &String, symbol: &String) -> Vec<Market>{
+
+
+    let real_symbol = &get_real_symbol(symbol, false);
+    let def_inst_type = &"SPOT".to_string();
+    let new_inst_type =  if inst_type.is_empty()  { inst_type } else { def_inst_type };
+    let base_url = "https://www.okx.com/api/v5/public/instruments?instType=".to_string() + new_inst_type;
+    let req = exchange_libs::get(base_url.to_string());
+    let mut res_data = "".to_string();
+
+    match req.await {
+        Ok(data) => {
+            res_data = data;
+        }
+        Err(err) => {
+            println!("err-数据:{:?}", err);
+        }
+    }
+
+
+    // let real_symbol = &get_real_symbol(symbol, true);
+    // let symbols_json_str = exchange_libs::binan_k(real_symbol, interval, limit).await;
+    let symbols:serde_json::Value = serde_json::from_str(&*res_data).unwrap();
+    let mut find_symbols:&serde_json::Value = &json!({});
+    for item in symbols["data"].as_array().unwrap() {
+        if item["instId"].as_str().unwrap() == real_symbol {
+            find_symbols = item;
+        }
+    }
+    let result = vec![Market{
+        symbol: find_symbols["instId"].as_str().unwrap().parse().unwrap(),
+        base_asset: find_symbols["baseCcy"].as_str().unwrap().parse().unwrap(),
+        quote_asset: find_symbols["quoteCcy"].as_str().unwrap().parse().unwrap(),
+        tick_size: find_symbols["quoteCcy"].as_f64().unwrap_or(0.0),
+        amount_size: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        price_precision: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        amount_precision: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        min_qty: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        max_qty: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        min_notional: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        max_notional: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
+        ct_val: find_symbols["quoteCcy"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
     }];
-    return symbols;
+    return result;
 }
+
+// okx下单接口
+

+ 182 - 1
src/main.rs

@@ -1,7 +1,188 @@
+use ndarray::prelude::*;
+use chrono::{Timelike};
+use crate::as_libs::*;
+use crate::exchange_middle_ware::{Account, get_binance_depth, get_binance_klines, get_okx_account};
+use time::OffsetDateTime;
+
 mod as_libs;
 mod exchange_libs;
 mod exchange_middle_ware;
 
+struct Ira{
+    ira: f64,
+    gamma: f64,
+    mid_price: f64,
+    rp: f64,
+    theta_a_b_half: f64,
+    bid: f64,
+    bid_best: f64,
+    ask: f64,
+    ask_best: f64,
+    kappa: f64,
+    cal_theta: f64
+}
+
+#[derive(Debug)]
+struct OrderDict {
+    zj: f64,
+    buy_price: f64,
+    sell_price: f64
+}
+// 订单信息
+struct OrderInfo {
+    id: str,
+    time_num: i64
+}
+
+
+fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_params: &Vec<String>, short_interval: &String, ira_start: f64, ira_end: f64, quantity_max: f64, amount_decimal_places: usize, order_info_list: &mut Vec<OrderInfo>) -> i8{
+    // 1.获取最新spread,如何将主逻辑和spread(ws推送方式)关联起来
+    let (spread, mid_price, ask, bid) = get_spread(&get_binance_depth(&symbol, limit));
+
+    if spreadlist.len() > 300 {
+        spreadlist.remove(0);
+    }
+    spreadlist.push(spread);
+    // 2.获取最大spread????
+    let max_spread = *spreadlist.iter().max().unwrap();
+    if spreadlist.len() < 10 {
+
+    } else {
+        // 1.获取账户信息
+        let balance_info:Account = get_okx_account( &account_params);
+        // 2.获取最新k线
+        let df2 = get_binance_klines(&symbol.to_string(), &short_interval, 200);
+        // 计算最近20根K线的标准差
+        let std = std_n_by_records(&df2, 20);
+        // 3.获取标准差数组的最后一个值
+        let last_std = std.last().unwrap();
+        // 4.计算库存资产折合本位币价值
+        let mut q = (balance_info.stocks + balance_info.frozen_stocks) * mid_price;
+        if q == 0{
+            q = 10.0;
+        }
+        // 5.计算gamma值
+        let gamma = calc_gamma(max_spread, q, last_std);
+        // 6.生成等差数列
+        let ira = Array::linspace(ira_start, ira_end, 50).into_owned();
+        let mut ira_list: Vec<Ira> = Vec::new();
+        for i in ira.iter(){
+            // 7.获取dd
+            let dd = calc_deviation_range(ira_end, spreadlist, *i);
+            // 8.获取Reservation price 预定价格
+            let rp = calc_rp(mid_price, q, *i, gamma, last_std);
+            if (rp + 0.5 * dd) >= ask && (rp - 0.5 * dd) <= bid{
+                // 9.获取dk,用于计算e的dk次方
+                let dk = calc_dk(dd, gamma, last_std, *i);
+                // 10.获取kappa
+                let kappa = calc_kappa(gamma, dk, dd, *i);
+                // 11.获取theta
+                let cal_theta = calc_theta(gamma, last_std, kappa, *i);
+                let ira_info = Ira{
+                    ira: *i,
+                    gamma: gamma*i,
+                    mid_price,
+                    rp,
+                    theta_a_b_half: 0.5*dd,
+                    bid: rp - cal_theta,
+                    bid_best: bid,
+                    ask: rp + cal_theta,
+                    ask_best: ask,
+                    kappa,
+                    cal_theta
+                };
+                ira_list.push(ira_info);
+            }
+        }
+        // 12.df4要求过滤出,挂单卖出价 >= 实际卖一价的部分,相当于是更严格的挂单(防止吃单)
+        let df4 = ira_list.iter().filter(|&ira_info| ira_info.ask >= ira_info.ask_best).cloned().collect();
+        println!("df4: {}", df4);
+
+        // 13.对不同资产级别的本位币应用不同的库存处理方式(仓位等级),最后分别得出:A. 开多资金 B. 开多价格 C. 开空资金 D. 开空价格
+        let rate = 50;
+        let mut order_dict :OrderDict;
+        if rate < 40 {
+            let mut zj = calc_order_amount(ira_end, ira_list.first().unwrap().ira, q, quantity_max, ira_list.first().unwrap().bid_price, amount_decimal_places);
+
+            // 使用资金范围限制
+            if zj < 10.0 {
+               zj = 50.0;
+            } else if zj > 800.0 {
+                zj = 500.0;
+            }
+            order_dict = OrderDict{
+                zj,
+                buy_price: truncate_decimal_places(ira_list.first().unwrap().bid, 2),
+                sell_price: truncate_decimal_places(ira_list.first().unwrap().ask, 2)
+            };
+            println!("order_dict: {:?}", order_dict);
+        } else if rate <= 60{
+            let index :i8 = (ira_list.len() / 2) as i8;
+            let zj = calc_order_amount(ira_end, ira_list.get(index).ira, q, quantity_max, ira_list.get(index).bid_price, amount_decimal_places);
+            order_dict = OrderDict{
+                zj,
+                buy_price: truncate_decimal_places(ira_list.get(index).bid, 2),
+                sell_price: truncate_decimal_places(ira_list.get(index).ask, 2)
+            };
+            println!("order_dict: {:?}", order_dict);
+        } else if rate > 60 {
+            let zj = calc_order_amount(ira_end, ira_list.last().unwrap().ira, q, quantity_max, ira_list.last().unwrap().bid_price, amount_decimal_places);
+            order_dict = OrderDict{
+                zj,
+                buy_price: truncate_decimal_places(ira_list.last().unwrap().bid, 2),
+                sell_price: truncate_decimal_places(ira_list.last().unwrap().ask, 2)
+            };
+            println!("order_dict: {:?}", order_dict);
+        }
+        /**
+            TODO: 发起交易
+        **/
+
+    }
+    if order_info_list.len() > 0{
+        let now_time = OffsetDateTime::now_utc().unix_timestamp();
+        // 超300s 需取消的订单
+        let check_order:Vec<OrderInfo> = order_info_list.iter().filter(|info| info.time_num + 300 >= now_time).cloned().collect();
+        let next_list:Vec<OrderInfo> =  order_info_list.iter().filter(|info| info.time_num + 300 < now_time).cloned().collect();
+        if check_order.len() > 0 {
+            for info in check_order{
+                // 取消订单
+            }
+        }
+        order_info_list.clear();
+        order_info_list.extend(next_list);
+    }
+
+    return 1;
+}
+
 fn main() {
-    println!("Hello, world!");
+    // 获取账户信息参数
+    let account_params = vec!["213".to_string()];
+    let mut spreadlist:Vec<f64> = Vec::new();
+    // 币对-获取深度信息参数
+    let symbol:String = "btc_usdt".to_string();
+    // 限制数-获取深度信息参数
+    let limit = 12;
+    // 开始
+    let ira_start= 0.0;
+    // 结束
+    let ira_end= 20.0;
+    let short_interval = "5m".to_string();
+    // 最大库存
+    let quantity_max = 50.0;
+
+    let amount_decimal_places: usize = 2;
+    // 订单信息list
+    let mut  order_info_list:Vec<OrderInfo> = Vec::new();
+
+    while true {
+        let result = do_logic(&mut spreadlist, &symbol, limit, &account_params, &short_interval, ira_start, ira_end, quantity_max, amount_decimal_places, &mut order_info_list);
+        match result {
+            Ok(value) => println!("Result: {}", value),
+            Err(error) => println!("Error: {}", error),
+        }
+
+    }
+
 }

+ 50 - 0
tests/binance_ws_test.rs

@@ -0,0 +1,50 @@
+use tungstenite::client::connect_with_proxy;
+use tungstenite::protocol::WebSocketConfig;
+use tungstenite::Message;
+use std::net::{SocketAddr, IpAddr, Ipv4Addr};
+use url::Url;
+
+#[tokio::test]
+async fn test() {
+    let request_url = Url::parse("wss://stream.binance.com:443/ws/btcusdt@depth10@100ms").unwrap();
+    let proxy_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9080);
+    let websocket_config = Some(WebSocketConfig {
+        max_send_queue: Some(16),
+        max_message_size: Some(16 * 1024 * 1024),
+        max_frame_size: Some(16 * 1024 * 1024),
+        accept_unmasked_frames: false,
+    });
+    let max_redirects = 5;
+
+    let (mut socket, response) =
+        connect_with_proxy(request_url, proxy_address, websocket_config, max_redirects)
+            .expect("Can't connect");
+
+    // socket
+    //     .write_message(Message::Text("Hello WebSocket".into()))
+    //     .unwrap();
+
+    loop {
+        let msg = socket.read_message();
+
+        println!("有消息了");
+
+        match msg {
+            Ok(Message::Text(text)) => {
+                println!("Received text: {}", text);
+            }
+            Ok(Message::Binary(bin)) => {
+                println!("Received binary: {:?}", bin);
+            }
+            Ok(Message::Ping(_)) | Ok(Message::Pong(_)) | Ok(Message::Close(_)) => {
+                // Handle control frames if necessary.
+            }
+            Err(error) => {
+                println!("Error receiving message: {}", error);
+                break;
+            }
+        }
+    }
+
+    socket.close(None).unwrap();
+}

+ 0 - 4
tests/http_test.rs

@@ -1,4 +0,0 @@
-#[test]
-fn it_works() {
-    println!("Hello World.")
-}