From 0c4e8ada55d7ab26b46ff234e8907738db859b91 Mon Sep 17 00:00:00 2001 From: Sik Yoon Date: Sat, 15 Jul 2023 19:41:36 +0900 Subject: [PATCH] Fork from tradingbot_sandbox --- .gitignore | 1 + Cargo.lock | 2803 +++++++ Cargo.toml | 24 + src/coex.rs | 4 + src/coex/assets_managing_team.rs | 427 + src/coex/exchange_team.rs | 1173 +++ src/coex/order_team.rs | 2689 +++++++ src/coex/strategy_team.rs | 6841 +++++++++++++++++ src/coin_health_check_team.rs | 3 + src/coin_health_check_team/monitors.rs | 598 ++ src/coin_health_check_team/request_candles.rs | 788 ++ src/coin_health_check_team/request_others.rs | 458 ++ src/database_control.rs | 758 ++ src/decimal_funcs.rs | 38 + src/initialization.rs | 1442 ++++ src/lib.rs | 34 + src/main.rs | 2363 ++++++ src/server_health_check_team.rs | 146 + src/signal_association.rs | 5 + src/signal_association/coinmarketcap.rs | 609 ++ src/signal_association/dollar_index.rs | 208 + src/signal_association/exchange_rate.rs | 607 ++ src/signal_association/future_ratio.rs | 208 + src/signal_association/signal_decision.rs | 103 + src/time_checking_team.rs | 144 + src/value_estimation_team.rs | 2 + src/value_estimation_team/datapoints.rs | 1 + .../datapoints/price_data.rs | 477 ++ src/value_estimation_team/indicators.rs | 9 + .../indicators/bollingerband.rs | 139 + src/value_estimation_team/indicators/ema.rs | 84 + .../indicators/heatmap_volume.rs | 166 + src/value_estimation_team/indicators/macd.rs | 83 + src/value_estimation_team/indicators/rsi.rs | 151 + src/value_estimation_team/indicators/sma.rs | 71 + .../indicators/stoch_rsi.rs | 131 + .../indicators/supertrend.rs | 238 + src/value_estimation_team/indicators/tema.rs | 87 + 38 files changed, 24113 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/coex.rs create mode 100644 src/coex/assets_managing_team.rs create mode 100644 src/coex/exchange_team.rs create mode 100644 src/coex/order_team.rs create mode 100644 src/coex/strategy_team.rs create mode 100644 src/coin_health_check_team.rs create mode 100644 src/coin_health_check_team/monitors.rs create mode 100644 src/coin_health_check_team/request_candles.rs create mode 100644 src/coin_health_check_team/request_others.rs create mode 100644 src/database_control.rs create mode 100644 src/decimal_funcs.rs create mode 100644 src/initialization.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/server_health_check_team.rs create mode 100644 src/signal_association.rs create mode 100644 src/signal_association/coinmarketcap.rs create mode 100644 src/signal_association/dollar_index.rs create mode 100644 src/signal_association/exchange_rate.rs create mode 100644 src/signal_association/future_ratio.rs create mode 100644 src/signal_association/signal_decision.rs create mode 100644 src/time_checking_team.rs create mode 100644 src/value_estimation_team.rs create mode 100644 src/value_estimation_team/datapoints.rs create mode 100644 src/value_estimation_team/datapoints/price_data.rs create mode 100644 src/value_estimation_team/indicators.rs create mode 100644 src/value_estimation_team/indicators/bollingerband.rs create mode 100644 src/value_estimation_team/indicators/ema.rs create mode 100644 src/value_estimation_team/indicators/heatmap_volume.rs create mode 100644 src/value_estimation_team/indicators/macd.rs create mode 100644 src/value_estimation_team/indicators/rsi.rs create mode 100644 src/value_estimation_team/indicators/sma.rs create mode 100644 src/value_estimation_team/indicators/stoch_rsi.rs create mode 100644 src/value_estimation_team/indicators/supertrend.rs create mode 100644 src/value_estimation_team/indicators/tema.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cdf245e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2803 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-trait" +version = "0.1.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", + "crypto-bigint", + "pem-rfc7468", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "font-kit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" +dependencies = [ + "bitflags", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +dependencies = [ + "hashbrown 0.14.0", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac-sha256" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdc571e566521512579aab40bf807c5066e1765fb36857f16ed7595c13567c6" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[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-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ord_subset" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ce14664caf5b27f5656ff727defd68ae1eb75ef3c4d95259361df1eb376bef" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[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 0.9.8", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[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 0.3.5", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" +dependencies = [ + "der", + "pkcs8", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-bitmap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cebbe1f70205299abc69e8b295035bb52a6a70ee35474ad10011f0a4efb8543" +dependencies = [ + "gif", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rend" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" +dependencies = [ + "byteorder", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "smallvec", + "subtle", + "zeroize", +] + +[[package]] +name = "rust_decimal" +version = "1.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0446843641c69436765a35a5a77088e28c2e6a12da93e84aa3ab1cd4aa5a042" +dependencies = [ + "arrayvec", + "borsh", + "bytecheck", + "byteorder", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rust_decimal_macros" +version = "1.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca5c398d85f83b9a44de754a2048625a8c5eafcf070da7b8f116b685e2f6608" +dependencies = [ + "quote", + "rust_decimal", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_json" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +dependencies = [ + "indexmap 2.0.0", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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 = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" +dependencies = [ + "ahash 0.7.6", + "atoi", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "digest", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "generic-array", + "hashlink", + "hex", + "indexmap 1.9.3", + "itoa", + "libc", + "log", + "memchr", + "num-bigint", + "once_cell", + "paste", + "percent-encoding", + "rand", + "rsa", + "rust_decimal", + "sha1", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "tokio-stream", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +dependencies = [ + "dotenvy", + "either", + "heck", + "once_cell", + "proc-macro2", + "quote", + "sha2", + "sqlx-core", + "sqlx-rt", + "syn 1.0.109", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +dependencies = [ + "native-tls", + "once_cell", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "stringmatch" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c0faab770316c3838f895fc2dfc3a8707ef4da48676f1014e1061ebd583b40" +dependencies = [ + "regex", +] + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys", +] + +[[package]] +name = "thirtyfour" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a3dc39b4cd4f6512fa5c663a697c40d297263b7db0a179bd2ad80cb0e92a5f" +dependencies = [ + "async-trait", + "base64 0.13.1", + "chrono", + "displaydoc", + "futures", + "log", + "parking_lot 0.11.2", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "stringmatch", + "thiserror", + "tokio", + "urlparse", +] + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "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.26", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tradingbot" +version = "0.10.0" +dependencies = [ + "chrono", + "csv", + "futures", + "hex", + "hmac-sha256", + "ord_subset", + "plotters", + "rand", + "reqwest", + "rust_decimal", + "rust_decimal_macros", + "serde", + "serde_json", + "sqlx", + "thirtyfour", + "tokio", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "ttf-parser" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlparse" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517" + +[[package]] +name = "uuid" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.26", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..708e024 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "tradingbot" +version = "0.10.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +reqwest = { version = "0.11.4", features = ["json"]} +serde = { version ="1.0.126", features = ["derive"] } +serde_json = "1.0.64" +hmac-sha256 = "0.1.7" +tokio = { version = "1.28.2", features = ["full"] } +futures = "0.3.17" +chrono = "0.4.19" +hex = "0.4.3" +rand = { version = "0.8.4", features = ["getrandom"]} +sqlx = { version = "0.6.3", features = ["runtime-tokio-native-tls", "mysql", "macros", "migrate", "decimal", "chrono"] } +csv = "1.1.6" +thirtyfour = { version = "0.28.1", features = ["tokio-runtime"]} +ord_subset = "3.1.1" +rust_decimal = "1.29.1" +rust_decimal_macros = "1.29.1" +plotters = "0.3.1" diff --git a/src/coex.rs b/src/coex.rs new file mode 100644 index 0000000..9a9b0a1 --- /dev/null +++ b/src/coex.rs @@ -0,0 +1,4 @@ +pub mod assets_managing_team; +pub mod exchange_team; +pub mod order_team; +pub mod strategy_team; diff --git a/src/coex/assets_managing_team.rs b/src/coex/assets_managing_team.rs new file mode 100644 index 0000000..0a27cc5 --- /dev/null +++ b/src/coex/assets_managing_team.rs @@ -0,0 +1,427 @@ +// assets_managing_team manages [asset_manage_announcement] and [wallet] ([wallet_testnet] as well) + +use crate::coex::exchange_team::*; +use crate::database_control::*; +use crate::decimal_funcs::*; +use crate::RunningMode::*; +use crate::RUNNING_MODE; +use reqwest::Client; +use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy}; +use rust_decimal_macros::dec; +use serde_json::{Result, Value}; +use sqlx::FromRow; + +#[derive(Debug, FromRow)] +pub struct AssetInfo { + pub initial_usdt: Decimal, + pub current_total_usdt: Decimal, + pub profit: Decimal, + pub realtime_expected_total_usdt: f64, + pub realtime_profit: f64, + pub available_usdt: Decimal, + pub is_tradable: u8, + pub unit_trade_usdt: Decimal, +} + +#[derive(Debug, FromRow)] +pub struct WalletInfo { + pub asset: String, + pub free: Decimal, + pub locked: Decimal, +} + +// set unit_trade_usdt +pub async fn set_unit_usdt() { + let asset_info = select_asset_manage_announcement().await; + let mut set_unit_trade_usdt = Decimal::new(0, 8); + + // define protect_rate and unit_trade_usdt as high as total_usdt_amount + if dec!(0.0) <= asset_info.current_total_usdt && asset_info.current_total_usdt < dec!(500.0) { + set_unit_trade_usdt = dec!(100.0); // $100 for each trade + // set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.8)); // 80% of total usdt + } else if dec!(500.0) <= asset_info.current_total_usdt + && asset_info.current_total_usdt < dec!(2000.0) + { + // set_unit_trade_usdt = dec!(150.0); // $150 for each trade + set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.1)); // 10% of total usdt + } else if dec!(2000.0) <= asset_info.current_total_usdt + && asset_info.current_total_usdt < dec!(5000.0) + { + set_unit_trade_usdt = dec!(200.0); // $200 for each trade + } else if dec!(5000.0) <= asset_info.current_total_usdt + && asset_info.current_total_usdt < dec!(10000.0) + { + set_unit_trade_usdt = dec!(300.0); // $300 for each trade + } else { + set_unit_trade_usdt = dec!(500.0); // $500 for each trade + } + + // update fields in [asset_manage_announcement] table + let update_table_name = String::from("asset_manage_announcement"); + let update_values = vec![( + String::from("unit_trade_usdt"), + set_unit_trade_usdt.to_string(), + )]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); +} + +// add extra usdt to initial usdt +pub async fn add_extra_usdt(extra_usdt: Decimal) { + // update fields in [asset_manage_announcement] table + let update_table_name = String::from("asset_manage_announcement"); + let mut value_build = String::from("initial_usdt + "); + value_build.push_str( + extra_usdt + .round_dp_with_strategy(8, RoundingStrategy::ToZero) + .to_string() + .as_str(), + ); + + let update_values = vec![(String::from("initial_usdt"), value_build)]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + + let mut value_build = String::from("current_total_usdt + "); + value_build.push_str( + extra_usdt + .round_dp_with_strategy(8, RoundingStrategy::ToZero) + .to_string() + .as_str(), + ); + + let update_values = vec![(String::from("current_total_usdt"), value_build)]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + + add_available_usdt(extra_usdt.round_dp_with_strategy(8, RoundingStrategy::ToZero)).await; + + // add additional_usdt into [wallet_simul] + let mut update_table_name = String::new(); + if RUNNING_MODE == SIMUL { + let update_table_name = String::from("wallet"); + let mut value_build = String::from("free + "); + value_build.push_str(extra_usdt.to_string().as_str()); + let update_values = vec![(String::from("free"), value_build)]; + let update_condition = vec![(String::from("asset"), String::from("USDT"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + println!("add_extra_usdt 완료"); +} + +// update current_total_usdt and profit of asset +pub async fn update_current_total_usdt() { + let mut update_values: Vec<(String, String)> = Vec::new(); + if RUNNING_MODE == REAL || RUNNING_MODE == TEST { + let free_usdt = fetch_free_usdt().await; + let initial_usdt = select_asset_manage_announcement().await.initial_usdt; + // println!("update_current_total_usdt실행 free_usdt: {}, initial_usdt: {}", free_usdt, initial_usdt); + let mut profit: Decimal; + if initial_usdt.is_zero() { + profit = dec!(0.0); + } else { + profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1)); + } + + update_values = vec![ + (String::from("current_total_usdt"), free_usdt.to_string()), + (String::from("profit"), profit.to_string()), + ( + String::from("realtime_expected_total_usdt"), + 0.0.to_string(), + ), + (String::from("realtime_profit"), 0.0.to_string()), + ]; + } else { + let asset_info = select_asset_manage_announcement().await; + let mut profit: Decimal; + if asset_info.initial_usdt.is_zero() { + profit = dec!(0.0); + } else { + profit = decimal_sub( + decimal_div(asset_info.current_total_usdt, asset_info.initial_usdt), + dec!(1), + ); + } + + update_values = vec![ + ( + String::from("current_total_usdt"), + asset_info.available_usdt.to_string(), + ), + (String::from("profit"), profit.to_string()), + ( + String::from("realtime_expected_total_usdt"), + 0.0.to_string(), + ), + (String::from("realtime_profit"), 0.0.to_string()), + ]; + } + let update_table_name = String::from("asset_manage_announcement"); + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + set_unit_usdt().await; +} + +// monitor whether [buy_ordered_coin_list] is empty to update current_total_usdt and available_usdt in [asset_manage_announcement] +pub async fn monitoring_asset_usdt(previous_result: &mut bool, client: &Client) -> Result<()> { + let table_name = String::from("buy_ordered_coin_list"); + let condition = None; + let buy_list_result = exists_record(&table_name, &condition).await; + + let table_name = String::from("sell_ordered_coin_list"); + let condition = None; + let sell_list_result = exists_record(&table_name, &condition).await; + + if buy_list_result == false && sell_list_result == false { + if buy_list_result == false + && sell_list_result == false + && *previous_result == true + && (RUNNING_MODE == TEST || RUNNING_MODE == REAL) + { + let mut result = crate::coex::order_team::account_information(&client).await; // (/api, Weight(IP) 10) + loop { + if result.is_ok() { + break; + } else { + println!("retry account_information()"); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + result = crate::coex::order_team::account_information(&client).await; + // (/api, Weight(IP) 10) + } + } + update_current_total_usdt().await; + set_available_usdt().await; + } else if buy_list_result == false + && sell_list_result == false + && *previous_result == true + && (RUNNING_MODE == SIMUL) + { + update_current_total_usdt().await; + set_is_tradable().await; + } + *previous_result = false; + } else { + update_realtime_asset().await; + *previous_result = true; + } + Ok(()) +} + +// update realtime_expected_total_usdt and realtime_profit +pub async fn update_realtime_asset() { + let asset_info = select_asset_manage_announcement().await; + let scoreboard_list = select_scoreboard().await; + let scoreboard_short = scoreboard_list.first().unwrap(); + let scoreboard_long = scoreboard_list.last().unwrap(); + + if scoreboard_short.total_number_of_coin != 0 || scoreboard_long.total_number_of_coin != 0 { + // calculate expect_realtime_asset + let available_usdt = asset_info.available_usdt.to_f64().unwrap(); + let total_used_usdt = scoreboard_short.total_used_usdt + scoreboard_long.total_used_usdt; + let current_pos_profit_usdt = + scoreboard_short.total_pos_profit_usdt + scoreboard_long.total_pos_profit_usdt; + let current_neg_profit_usdt = + scoreboard_short.total_neg_profit_usdt + scoreboard_long.total_neg_profit_usdt; + let expect_realtime_expected_total_usdt = + available_usdt + total_used_usdt + current_pos_profit_usdt + current_neg_profit_usdt; + + // calculate realtime_profit + let mut realtime_profit = 0.0; + let current_total_usdt = asset_info.current_total_usdt; + if current_total_usdt.is_zero() { + realtime_profit = 0.0; + } else { + realtime_profit = ((expect_realtime_expected_total_usdt + / current_total_usdt.to_f64().unwrap()) + - 1.0) + * 100.0; + } + + // update record + let update_table_name = String::from("asset_manage_announcement"); + let update_values = vec![ + ( + String::from("realtime_expected_total_usdt"), + expect_realtime_expected_total_usdt.to_string(), + ), + (String::from("realtime_profit"), realtime_profit.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + } +} + +// set available_usdt +pub async fn set_available_usdt() { + let update_table_name = String::from("asset_manage_announcement"); + // fetch current_total_usdt + let mut asset_info = select_asset_manage_announcement().await; + + let available_usdt = asset_info.current_total_usdt; + let unit_trade_usdt = asset_info.unit_trade_usdt; + + // set available_usdt with current_total_usdt and update is_tradable + let update_value = { + if available_usdt > unit_trade_usdt { + vec![ + (String::from("available_usdt"), available_usdt.to_string()), + (String::from("is_tradable"), (String::from("1"))), + ] + } else { + vec![ + (String::from("available_usdt"), available_usdt.to_string()), + (String::from("is_tradable"), (String::from("0"))), + ] + } + }; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); +} + +// fetch available_usdt +pub async fn fetch_available_usdt() -> Decimal { + let update_table_name = String::from("asset_manage_announcement"); + let mut asset_info = select_asset_manage_announcement().await; + asset_info.available_usdt +} + +// add available_usdt +pub async fn add_available_usdt(add_usdt: Decimal) { + let update_table_name = String::from("asset_manage_announcement"); + let mut value_build = String::from("available_usdt + "); + value_build.push_str(add_usdt.to_string().as_str()); + + let update_values = vec![(String::from("available_usdt"), value_build)]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); +} + +// subtract available_usdt +pub async fn sub_available_usdt(sub_usdt: Decimal) { + let update_table_name = String::from("asset_manage_announcement"); + let mut value_build = String::from("available_usdt - "); + value_build.push_str(sub_usdt.to_string().as_str()); + + let update_values = vec![(String::from("available_usdt"), value_build)]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); +} + +// set is_tradable as available_usdt +pub async fn set_is_tradable() { + let update_table_name = String::from("asset_manage_announcement"); + let mut asset_info = select_asset_manage_announcement().await; + let unit_trade_usdt = asset_info.unit_trade_usdt; + let available_usdt = asset_info.available_usdt; + + let update_value = { + if available_usdt > unit_trade_usdt { + vec![(String::from("is_tradable"), (String::from("1")))] + } else { + vec![(String::from("is_tradable"), (String::from("0")))] + } + }; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); +} + +// check it's tradable or not +pub async fn check_is_tradable() -> bool { + let asset_info = select_asset_manage_announcement().await; + let is_tradable = asset_info.is_tradable; + + if asset_info.is_tradable == 1 { + true + } else { + false + } +} + +pub async fn fetch_unit_trade_usdt() -> Decimal { + let asset_info = select_asset_manage_announcement().await; + asset_info.unit_trade_usdt +} + +pub async fn select_asset_manage_announcement() -> AssetInfo { + let mut asset_info = AssetInfo { + initial_usdt: Decimal::new(0, 8), + current_total_usdt: Decimal::new(0, 8), + profit: Decimal::new(0, 8), + realtime_expected_total_usdt: 0.0, + realtime_profit: 0.0, + available_usdt: Decimal::new(0, 8), + is_tradable: 0, + unit_trade_usdt: Decimal::new(0, 8), + }; + + let table_name = String::from("asset_manage_announcement"); + let columns = String::from("*"); + let condition = None; + + let select_result = select_record(&table_name, &columns, &condition, &asset_info) + .await + .unwrap(); + let result_vec = select_result.first().unwrap(); + + asset_info.initial_usdt = result_vec.initial_usdt; + asset_info.current_total_usdt = result_vec.current_total_usdt; + asset_info.profit = result_vec.profit; + asset_info.realtime_expected_total_usdt = result_vec.realtime_expected_total_usdt; + asset_info.realtime_profit = result_vec.realtime_profit; + asset_info.available_usdt = result_vec.available_usdt; + asset_info.is_tradable = result_vec.is_tradable; + asset_info.unit_trade_usdt = result_vec.unit_trade_usdt; + + asset_info +} + +// fetch free usdt from wallet +pub async fn fetch_free_usdt() -> Decimal { + let wallet_info = WalletInfo { + asset: String::new(), + free: Decimal::new(0, 8), + locked: Decimal::new(0, 8), + }; + let select_table_name = { + if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL { + String::from("wallet") + } else { + String::from("wallet_testnet") + } + }; + + let select_columns_name = String::from("*"); + let condition = Some(String::from("WHERE asset = 'USDT'")); + + let mut select_result = select_record( + &select_table_name, + &select_columns_name, + &condition, + &wallet_info, + ) + .await + .unwrap(); + select_result.first().unwrap().free +} diff --git a/src/coex/exchange_team.rs b/src/coex/exchange_team.rs new file mode 100644 index 0000000..dc84886 --- /dev/null +++ b/src/coex/exchange_team.rs @@ -0,0 +1,1173 @@ +use crate::coex::assets_managing_team::*; +use crate::coex::order_team::*; +use crate::coex::strategy_team::AllData; +use crate::coin_health_check_team::request_others::{CoinPriceData, ExchangeInfo, TradeFee}; +use crate::database_control::*; +use crate::decimal_funcs::{decimal, decimal_add, decimal_div, decimal_mul, decimal_sub}; +use crate::signal_association::signal_decision::*; +use reqwest::Client; +use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; +use rust_decimal_macros::dec; +use sqlx::FromRow; + +#[derive(Debug, FromRow)] +struct IsTradableInfo { + is_tradable: u8, +} + +#[derive(Debug, FromRow)] +struct PreSuggestedCoin { + id: u64, + symbol: String, + close_time: i64, + suggested_price: Decimal, + current_price: Decimal, + registered_server_epoch: i64, + profit_percent: f64, + minimum_profit_percent: f64, + maximum_profit_percent: f64, + registerer: u16, +} + +#[derive(Debug, FromRow)] +pub struct SuggestedCoin { + pub id: u64, + pub symbol: String, + pub suggested_price: Decimal, + pub close_time: i64, + pub registered_server_epoch: i64, + pub registerer: u16, + pub is_long: u8, + pub already_buy: u8, +} + +#[derive(Debug, FromRow)] +pub struct ServerEpoch { + server_epoch: u64, +} + +#[derive(Debug, FromRow)] +struct UnitTradeUsdt { + unit_trade_usdt: Decimal, +} + +#[derive(Debug, FromRow)] +struct CurrentPrice { + price: Decimal, +} + +#[derive(Debug, FromRow, Clone)] +pub struct ScoreboardList { + pub id: u64, + pub total_number_of_coin: u32, + pub pos_profit_number: u32, + pub neg_profit_number: u32, + pub total_used_usdt: f64, + pub pos_used_usdt: f64, + pub neg_used_usdt: f64, + pub total_pos_profit_usdt: f64, + pub total_neg_profit_usdt: f64, + pub maximum_total_pos_profit_usdt: f64, + pub avg_neg_profit_percent: f64, + pub pos_liquidation_signal: u32, + pub neg_liquidation_signal: u32, +} + +#[derive(Debug, FromRow)] +pub struct EvaluationList { + id: u64, + strategist: u16, + invested_usdt: Decimal, + usdt_profit: Decimal, + profit_percent: f64, +} + +#[derive(Debug, FromRow)] +pub struct IndexList { + pub server_epoch: u64, + pub total_24h_change_profit_index: f64, + pub usdt_24h_change_profit_index: f64, + pub total_price_down_dist_index: f64, +} + +#[derive(Debug, FromRow)] +pub struct FilteredIndexList { + pub server_epoch: u64, + pub total_24h_change_profit_index: f64, + pub usdt_24h_change_profit_index: f64, + pub total_price_down_dist_index: f64, + pub total_price_down_dist_index_maximum: f64, + pub total_price_down_dist_index_flag: i32, +} + +#[derive(Debug, FromRow, Clone)] +pub struct MarketCapIndex { + pub market_cap_index: f64, + pub minimum: f64, + pub maximum: f64, + pub transition_point: f64, + pub liquidation_signal: i8, + pub negative_buy_signal: i8, + pub transition_buy_signal: i8, +} + +// buy coin +pub async fn buy_coin( + client: &Client, + coin_price_vec: &Vec, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // check conditions for buying + set_is_tradable().await; + let mut is_tradable = check_is_tradable().await; + let suggested_coin_list_table_name = String::from("suggested_coin_list"); + let mut suggested_coin = select_suggested_coin().await; + let mut delete_condition = String::from("WHERE "); + let mut is_exist_delete_symbol: bool = false; + let marketcap_vec = select_marketcap().await; + + if is_tradable == true && !suggested_coin.is_empty() && !marketcap_vec.is_empty() { + // && (signal_decision.first().unwrap().decision == "KEEP" || signal_decision.first().unwrap().decision == "UP") { + let unit_trade_usdt = fetch_unit_trade_usdt().await; + + let server_epoch = server_epoch().await; + let mut filtered_suggested_coin_vec: Vec<&SuggestedCoin> = Vec::new(); + + // filtering symbols to buy + suggested_coin.reverse(); + for element in &suggested_coin { + let price_index = coin_price_vec + .iter() + .position(|x| *x.symbol == element.symbol); + + if price_index.is_some() + && element.already_buy == 0 + && server_epoch - element.registered_server_epoch <= 600_000 + // 600_000 (600 secs = 10 mins) + // if price_index.is_some() && element.already_buy == 0 && marketcap_vec.first().unwrap().market_cap_index <= -10.0 && server_epoch - element.registered_server_epoch <= 600_000 // 600_000 (600 secs = 10 mins) + { + // && (market_cap_info.first().unwrap().market_cap_index <= 10.0) { + + // let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap(); + // if current_price <= element.suggested_price || ((decimal_sub(decimal_div(decimal_sub(current_price, element.suggested_price), element.suggested_price), dec!(1))) <= dec!(0.005) && current_price > element.suggested_price) { + // filtered_suggested_coin_vec.push(element); + // } + filtered_suggested_coin_vec.push(element); + delete_condition.push_str("id = "); + delete_condition.push_str(element.id.to_string().as_str()); + delete_condition.push_str(" OR "); + is_exist_delete_symbol = false; + } else if server_epoch - element.registered_server_epoch >= 1_800_000 { + delete_condition.push_str("id = "); + delete_condition.push_str(element.id.to_string().as_str()); + delete_condition.push_str(" OR "); + is_exist_delete_symbol = true; + } + // if price_index.is_some() && (element.registerer != 1 || element.registerer != 5 || element.registerer != 6) && server_epoch - element.registered_server_epoch <= 7_200_000 // 7_200_000 (120 mins) + // // && market_cap_info.first().unwrap().negative_buy_signal == 1 { + // && (market_cap_info.first().unwrap().market_cap_index <= 10.0) && (market_cap_info.first().unwrap().liquidation_signal != 1) { + + // let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap(); + // if current_price < element.suggested_price || ((current_price - element.suggested_price)/element.suggested_price <= dec!(0.005) && current_price > element.suggested_price) { + // filtered_suggested_coin_vec.push(element); + // } + // delete_condition.push_str("id = "); + // delete_condition.push_str(element.id.to_string().as_str()); + // delete_condition.push_str(" OR "); + // } else if price_index.is_some() && (element.registerer == 1 || element.registerer == 5 || element.registerer == 6 ) && server_epoch - element.registered_server_epoch <= 10_000 // 10000 (10 secs) + // && ((market_cap_info.first().unwrap().market_cap_index >= 0.0 && market_cap_info.first().unwrap().liquidation_signal == 0) || market_cap_info.first().unwrap().negative_buy_signal == 1) { + // let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap(); + // if current_price <= element.suggested_price { + // filtered_suggested_coin_vec.push(element); + // } + + // delete_condition.push_str("id = "); + // delete_condition.push_str(element.id.to_string().as_str()); + // delete_condition.push_str(" OR "); + // } + } + + if !filtered_suggested_coin_vec.is_empty() { + let available_usdt = fetch_available_usdt().await; + let vec_len = Decimal::new(filtered_suggested_coin_vec.len() as i64, 0); + let weighted_unit_usdt = + (((available_usdt / vec_len).trunc() / unit_trade_usdt).trunc() * unit_trade_usdt) + .trunc(); + let mut used_usdt = Decimal::new(0, 8); + let mut base_qty_ordered = Decimal::new(0, 8); + let mut base_qty_fee_adjusted = Decimal::new(0, 8); + let mut expected_pure_profit_percent = 0.0; + let insert_table_name = String::from("buy_ordered_coin_list"); + + for element in &filtered_suggested_coin_vec { + if is_tradable == true { + let lot_step_size_result = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let tick_size_result = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let base_commission_precision_result = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let trade_fee_result = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == element.symbol); + + if lot_step_size_result.is_some() + && tick_size_result.is_some() + && base_commission_precision_result.is_some() + && trade_fee_result.is_some() + { + let lot_step_size = lot_step_size_result.unwrap().stepsize; + let tick_size = tick_size_result.unwrap().ticksize; + let base_commission_precision = base_commission_precision_result + .unwrap() + .base_commission_precision; + let trade_fee = trade_fee_result.unwrap().takercommission; + + // buy the suggested coin and transfer it into [buy_ordered_coin_list] + + // let price_index = coin_price_vec.iter().position(|x| *x.symbol == element.symbol); + // let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap(); + // let order_price = current_price; + let order_price = element.suggested_price; + if filtered_suggested_coin_vec.len() >= 10 + && weighted_unit_usdt > unit_trade_usdt + { + // adjust regulared_unit_usdt if available number of trade are over 5. + base_qty_ordered = decimal_div(weighted_unit_usdt, order_price) + .round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + base_qty_fee_adjusted = + decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy( + base_commission_precision, + RoundingStrategy::ToZero, + ); + used_usdt = decimal_mul(base_qty_ordered, order_price) + .round_dp_with_strategy( + tick_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + expected_pure_profit_percent = decimal_sub( + decimal_div( + decimal_mul(base_qty_fee_adjusted, order_price) + .round_dp_with_strategy( + tick_size.normalize().scale(), + RoundingStrategy::ToZero, + ), + used_usdt, + ), + dec!(1), + ) + .to_f64() + .unwrap() + * 100.0; + if (element.registerer == 5 || element.registerer == 6) + && expected_pure_profit_percent < -0.5 + { + } else { + // order the symbol based on base_qty_ordered and current_price + limit_order_buy( + element, + exchange_info_vec, + trade_fee, + TimeInForce::Gtc, + order_price, + base_qty_ordered, + used_usdt, + &base_qty_fee_adjusted.to_string(), + &client, + ) + .await; + } + } else { + base_qty_ordered = decimal_div(unit_trade_usdt, order_price) + .round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + base_qty_fee_adjusted = + decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy( + base_commission_precision, + RoundingStrategy::ToZero, + ); + used_usdt = decimal_mul(base_qty_ordered, order_price) + .round_dp_with_strategy( + tick_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + expected_pure_profit_percent = decimal_sub( + decimal_div( + decimal_mul(base_qty_fee_adjusted, order_price) + .round_dp_with_strategy( + tick_size.normalize().scale(), + RoundingStrategy::ToZero, + ), + used_usdt, + ), + dec!(1), + ) + .to_f64() + .unwrap() + * 100.0; + if (element.registerer == 5 || element.registerer == 6) + && expected_pure_profit_percent < -0.5 + { + } else { + // order the symbol based on base_qty_ordered and current_price + limit_order_buy( + element, + exchange_info_vec, + trade_fee, + TimeInForce::Gtc, + order_price, + base_qty_ordered, + used_usdt, + &base_qty_fee_adjusted.to_string(), + &client, + ) + .await; + } + } + + set_is_tradable().await; + is_tradable = check_is_tradable().await; + + let update_table_name = String::from("suggested_coin_list"); + let update_condition = vec![(String::from("id"), element.id.to_string())]; + let update_values: Vec<(String, String)> = + vec![(String::from("already_buy"), 1.to_string())]; + update_record2(&update_table_name, &update_values, &update_condition).await; + } + } else { + break; + } + } + } + // delete the coin record in [suggested_coin_list] + if is_exist_delete_symbol == true { + delete_condition.pop(); + delete_condition.pop(); + delete_condition.pop(); + delete_condition.pop(); + delete_condition.push(';'); + delete_record(&suggested_coin_list_table_name, &delete_condition) + .await + .unwrap(); + } + } else { + let server_epoch = server_epoch().await; + let mut filtered_suggested_coin_vec: Vec<&SuggestedCoin> = Vec::new(); + + // filtering symbols to buy + for element in &suggested_coin { + if ((element.registerer != 5 || element.registerer != 6) + && server_epoch - element.registered_server_epoch > 65_000) + || ((element.registerer == 5 || element.registerer == 6) + && server_epoch - element.registered_server_epoch >= 65_000) + { + // 10000 (10 secs), 600_000 (10 mins), 7_200_000 (120 mins) + filtered_suggested_coin_vec.push(element); + delete_condition.push_str("id = "); + delete_condition.push_str(element.id.to_string().as_str()); + delete_condition.push_str(" OR "); + is_exist_delete_symbol = true; + } + } + + // delete the coin record in [suggested_coin_list] + if is_exist_delete_symbol == true { + delete_condition.pop(); + delete_condition.pop(); + delete_condition.pop(); + delete_condition.pop(); + delete_condition.push(';'); + delete_record(&suggested_coin_list_table_name, &delete_condition) + .await + .unwrap(); + } + } + Ok(()) +} + +// buy coin for test +pub async fn buy_coin_for_test( + client: &Client, + coin_price_vec: &Vec, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + let mut suggested_coin = select_suggested_coin().await; + let mut delete_condition = String::from("WHERE "); + + let unit_trade_usdt = fetch_unit_trade_usdt().await; + + let server_epoch = server_epoch().await; + let mut filtered_suggested_coin_vec: Vec<&SuggestedCoin> = Vec::new(); + + for element in &suggested_coin { + filtered_suggested_coin_vec.push(element); + } + + let available_usdt = fetch_available_usdt().await; + let vec_len = Decimal::new(filtered_suggested_coin_vec.len() as i64, 0); + let weighted_unit_usdt = + (((available_usdt / vec_len).trunc() / unit_trade_usdt).trunc() * unit_trade_usdt).trunc(); + let mut used_usdt = Decimal::new(0, 8); + let mut base_qty_ordered = Decimal::new(0, 8); + let mut base_qty_fee_adjusted = Decimal::new(0, 8); + let mut expected_pure_profit_percent = 0.0; + + let insert_table_name = String::from("buy_ordered_coin_list"); + for element in &filtered_suggested_coin_vec { + let lot_step_size = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol) + .unwrap() + .stepsize; + let tick_size = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol) + .unwrap() + .ticksize; + let base_commission_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol) + .unwrap() + .base_commission_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == element.symbol) + .unwrap() + .takercommission; + + // buy the suggested coin and transfer it into [buy_ordered_coin_list] + // let price_index = coin_price_vec.iter().position(|x| *x.symbol == element.symbol); + // let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap(); + // let order_price = current_price; + let order_price = element.suggested_price; + + base_qty_ordered = decimal_div(unit_trade_usdt, order_price) + .round_dp_with_strategy(lot_step_size.normalize().scale(), RoundingStrategy::ToZero); + base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy(base_commission_precision, RoundingStrategy::ToZero); + used_usdt = decimal_mul(base_qty_ordered, order_price) + .round_dp_with_strategy(tick_size.normalize().scale(), RoundingStrategy::ToZero); + expected_pure_profit_percent = decimal_sub( + decimal_div( + decimal_mul(base_qty_fee_adjusted, order_price).round_dp_with_strategy( + tick_size.normalize().scale(), + RoundingStrategy::ToZero, + ), + used_usdt, + ), + dec!(1), + ) + .to_f64() + .unwrap() + * 100.0; + + // order the symbol based on base_qty_ordered and current_price + limit_order_buy( + element, + exchange_info_vec, + trade_fee, + TimeInForce::Gtc, + order_price, + base_qty_ordered, + used_usdt, + &base_qty_fee_adjusted.to_string(), + &client, + ) + .await; + println!(" buy coin 완료"); + } + + Ok(()) +} +// monitoring the price change of pre-suggested coins before suggesting them and move them when conditions are agree +pub async fn monitoring_pre_suggested_coins( + coin_price_vec: &Vec, +) -> Result<(), Box> { + let select_table_name = String::from("pre_suggested_coin_list"); + let pre_suggested_coin_list = select_pre_suggested_coin_list().await; + + if !pre_suggested_coin_list.is_empty() { + // update current_price, profit_percent, minimum_profit_percent, maximum_profit_percent + let mut price = Decimal::new(0, 8); + let mut profit_percent = 0.0; + let mut minimum_profit_percent = 0.0; + let mut maximum_profit_percent = 0.0; + + let mut update_elements: Vec<(String, String)> = Vec::new(); + let mut update_condition: Vec<(String, String)> = Vec::new(); + + for element in pre_suggested_coin_list { + let price_index = coin_price_vec + .iter() + .position(|x| *x.symbol == element.symbol); + if price_index.is_some() { + price = rust_decimal::prelude::FromPrimitive::from_f64( + coin_price_vec[price_index.unwrap()].current_price, + ) + .unwrap(); + profit_percent = ((price - element.suggested_price) / element.suggested_price) + .to_f64() + .unwrap() + * 100.0; + maximum_profit_percent = element.maximum_profit_percent; + minimum_profit_percent = element.minimum_profit_percent; + + if profit_percent > 0.0 { + minimum_profit_percent = 0.0; + if profit_percent > maximum_profit_percent { + maximum_profit_percent = profit_percent; + } + } else if profit_percent < 0.0 { + maximum_profit_percent = 0.0; + if profit_percent < minimum_profit_percent { + minimum_profit_percent = profit_percent; + } + } else { + minimum_profit_percent = 0.0; + maximum_profit_percent = 0.0; + } + + update_elements.clear(); + update_condition.clear(); + + update_elements.push((String::from("current_price"), price.to_string())); + update_elements.push((String::from("profit_percent"), profit_percent.to_string())); + update_elements.push(( + String::from("minimum_profit_percent"), + minimum_profit_percent.to_string(), + )); + update_elements.push(( + String::from("maximum_profit_percent"), + maximum_profit_percent.to_string(), + )); + + update_condition.push((String::from("id"), element.id.to_string())); + + // update for each symbol listed in [pre_suggested_coin_list] + update_record3(&select_table_name, &update_elements, &update_condition).await; + } + } + + // move coins into [suggested_coin_list] if conditions are agree + let pre_suggested_coin_list = select_pre_suggested_coin_list().await; + let insert_table_name = String::from("suggested_coin_list"); + let mut insert_columns = vec![ + "symbol", + "suggested_price", + "close_time", + "registered_server_epoch", + "registerer", + "is_long", + "already_buy", + ]; + + let mut insert_values: Vec = Vec::new(); + let mut delete_condition = String::new(); + + for element in pre_suggested_coin_list { + // insertion and delete + if (element.minimum_profit_percent > -0.01 + && element.minimum_profit_percent < 0.0 + && element.profit_percent >= element.minimum_profit_percent * 0.5) + || (element.minimum_profit_percent > -0.02 + && element.minimum_profit_percent <= -0.01 + && element.profit_percent >= element.minimum_profit_percent * 0.7) + || (element.minimum_profit_percent > -0.03 + && element.minimum_profit_percent <= -0.02 + && element.profit_percent >= element.minimum_profit_percent * 0.83) + || (element.minimum_profit_percent > -0.04 + && element.minimum_profit_percent <= -0.03 + && element.profit_percent >= element.minimum_profit_percent * 0.95) + || (element.minimum_profit_percent <= -0.04 + && element.profit_percent >= element.minimum_profit_percent * 0.98) + || (element.maximum_profit_percent >= 0.0) + { + insert_values.clear(); + insert_values.push(element.symbol.clone()); // symbol + insert_values.push(element.current_price.to_string()); // suggested_price + insert_values.push(element.close_time.to_string()); // close_time + insert_values.push(server_epoch().await.to_string()); // registered_server_epoch + insert_values.push(element.registerer.to_string()); // registerer + insert_values.push(0.to_string()); // is_long + insert_values.push(0.to_string()); // already_buy + + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + delete_condition.clear(); + delete_condition = String::from("WHERE id="); + delete_condition.push_str(element.id.to_string().as_str()); + delete_record(&select_table_name, &delete_condition).await; + } + } + } + + Ok(()) +} + +pub async fn monitoring_scoreboard( + all_data: &AllData, +) -> Result<(), Box> { + let update_table_name = String::from("scoreboard"); + let mut update_values: Vec<(String, String)> = Vec::new(); // for short + let mut update_values2: Vec<(String, String)> = Vec::new(); // for long + let mut update_condition: Vec<(String, String)> = Vec::new(); + let mut update_condition2: Vec<(String, String)> = Vec::new(); + + let filled_buy_orders = select_filled_buy_orders().await; + + if filled_buy_orders.is_empty() { + // initialization of table + update_values.push((String::from("total_number_of_coin"), 0.to_string())); + update_values.push((String::from("pos_profit_number"), 0.to_string())); + update_values.push((String::from("neg_profit_number"), 0.to_string())); + update_values.push((String::from("total_used_usdt"), 0.0.to_string())); + update_values.push((String::from("pos_used_usdt"), 0.0.to_string())); + update_values.push((String::from("neg_used_usdt"), 0.0.to_string())); + update_values.push((String::from("total_pos_profit_usdt"), 0.0.to_string())); + update_values.push((String::from("total_neg_profit_usdt"), 0.0.to_string())); + update_values.push(( + String::from("maximum_total_pos_profit_usdt"), + 0.0.to_string(), + )); + update_values.push((String::from("avg_neg_profit_percent"), 0.0.to_string())); + update_values.push((String::from("pos_liquidation_signal"), 0.to_string())); + update_values.push((String::from("neg_liquidation_signal"), 0.to_string())); + + update_values2.push((String::from("total_number_of_coin"), 0.to_string())); + update_values2.push((String::from("pos_profit_number"), 0.to_string())); + update_values2.push((String::from("neg_profit_number"), 0.to_string())); + update_values2.push((String::from("total_used_usdt"), 0.0.to_string())); + update_values2.push((String::from("pos_used_usdt"), 0.0.to_string())); + update_values2.push((String::from("neg_used_usdt"), 0.0.to_string())); + update_values2.push((String::from("total_pos_profit_usdt"), 0.0.to_string())); + update_values2.push((String::from("total_neg_profit_usdt"), 0.0.to_string())); + update_values2.push(( + String::from("maximum_total_pos_profit_usdt"), + 0.0.to_string(), + )); + update_values2.push((String::from("avg_neg_profit_percent"), 0.0.to_string())); + update_values2.push((String::from("pos_liquidation_signal"), 0.to_string())); + update_values2.push((String::from("neg_liquidation_signal"), 0.to_string())); + } else { + let scoreboard_list = select_scoreboard().await; + let mut count_short_coins = 0; + let mut count_long_coins = 0; + let server_epoch = server_epoch().await; + let mut timer_check = false; + + for element in &filled_buy_orders { + if element.used_usdt >= dec!(10.0) { + if element.is_long == 0 { + count_short_coins += 1; + } else { + count_long_coins += 1; + } + } + } + // variables for id 1 (for short) + let mut pos_profit_number1 = 0; + let mut neg_profit_number1 = 0; + let mut total_used_usdt1 = 0.0; + let mut pos_used_usdt1 = 0.0; + let mut neg_used_usdt1 = 0.0; + let mut total_pos_profit_usdt1 = 0.0; + let mut total_neg_profit_usdt1 = 0.0; + let mut sum_neg_profit_percent1 = 0.0; + let mut avg_neg_profit_percent1 = 0.0; + let mut sum_pos_profit_percent1 = 0.0; + // variables for id 2 (for long) + // let mut pos_profit_number2 = 0; + // let mut neg_profit_number2 = 0; + // let mut total_used_usdt2 = 0.0; + // let mut pos_used_usdt2 = 0.0; + // let mut neg_used_usdt2 = 0.0; + // let mut current_total_pos_profit_usdt2 = 0.0; + // let mut current_total_neg_profit_usdt2 = 0.0; + // let mut sum_neg_profit_percent2 = 0.0; + // let mut avg_neg_profit_percent2 = 0.0; + + for element in filled_buy_orders { + if element.used_usdt >= dec!(10.0) { + if element.is_long == 0 { + if element.pure_profit_percent > 0.0 { + pos_profit_number1 += 1; + total_pos_profit_usdt1 += element.expected_usdt_profit; + pos_used_usdt1 += element.used_usdt.to_f64().unwrap(); + sum_pos_profit_percent1 += element.pure_profit_percent; + } else { + neg_profit_number1 += 1; + total_neg_profit_usdt1 += element.expected_usdt_profit; + neg_used_usdt1 += element.used_usdt.to_f64().unwrap(); + sum_neg_profit_percent1 += element.pure_profit_percent; + } + total_used_usdt1 += element.used_usdt.to_f64().unwrap(); + } + + if server_epoch - element.transact_time > 180_000 { + timer_check = true; + } + } + + // else { + // if element.pure_profit_percent > 0.0 { + // pos_profit_number2 += 1; + // current_total_pos_profit_usdt2 = element.expected_usdt_profit + current_total_pos_profit_usdt2; + // pos_used_usdt2 = pos_used_usdt2 + element.used_usdt.to_f64().unwrap(); + // } else { + // neg_profit_number2 += 1; + // current_total_neg_profit_usdt2 = element.expected_usdt_profit + current_total_neg_profit_usdt2; + // neg_used_usdt2 = neg_used_usdt2 + element.used_usdt.to_f64().unwrap(); + // sum_neg_profit_percent2 = element.pure_profit_percent + sum_neg_profit_percent2; + // } + // total_used_usdt2 += element.used_usdt.to_f64().unwrap(); + // } + } + + // set update values for id 1 (for short) + update_values.push(( + String::from("total_number_of_coin"), + count_short_coins.to_string(), + )); + update_values.push(( + String::from("pos_profit_number"), + pos_profit_number1.to_string(), + )); + update_values.push(( + String::from("neg_profit_number"), + neg_profit_number1.to_string(), + )); + update_values.push(( + String::from("total_used_usdt"), + total_used_usdt1.to_string(), + )); + update_values.push((String::from("pos_used_usdt"), pos_used_usdt1.to_string())); + update_values.push((String::from("neg_used_usdt"), neg_used_usdt1.to_string())); + update_values.push(( + String::from("total_pos_profit_usdt"), + total_pos_profit_usdt1.to_string(), + )); + update_values.push(( + String::from("total_neg_profit_usdt"), + total_neg_profit_usdt1.to_string(), + )); + + if total_pos_profit_usdt1 + > scoreboard_list + .first() + .unwrap() + .maximum_total_pos_profit_usdt + { + update_values.push(( + String::from("maximum_total_pos_profit_usdt"), + total_pos_profit_usdt1.to_string(), + )); + } else if pos_profit_number1 == 0 { + update_values.push(( + String::from("maximum_total_pos_profit_usdt"), + 0.0.to_string(), + )); + } + + if neg_profit_number1 != 0 { + avg_neg_profit_percent1 = sum_neg_profit_percent1 / (neg_profit_number1 as f64); + update_values.push(( + String::from("avg_neg_profit_percent"), + avg_neg_profit_percent1.to_string(), + )); + } else { + update_values.push((String::from("avg_neg_profit_percent"), 0.0.to_string())); + } + + // calculate average amplitude of BTCUSDT to set the limit of liquidations + let mut base_profit_percent = 0.0; + let mut target_profit_percent = 0.0; + + let opclo_1m_option = all_data + .rt_price_1m_vec + .iter() + .position(|x| *x.0 == String::from("BTCUSDT")); + if opclo_1m_option.is_some() { + let mut opclo_1m_vec = all_data.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + let opclo_sample_length: usize = 5; // 10 candle samsples + opclo_1m_vec.pop(); + opclo_1m_vec.reverse(); + opclo_1m_vec.truncate(opclo_sample_length); + opclo_1m_vec.reverse(); + + let mut sum_amplitude_candles = 0.0; + let mut sum_ratio_amp_body = 0.0; + for element in &opclo_1m_vec { + sum_amplitude_candles += ((element.high_price / element.low_price) - 1.0) * 100.0; + sum_ratio_amp_body += (element.close_price - element.open_price).abs() + / (element.high_price - element.low_price); + } + let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit + let average_ratio_amp_body = sum_ratio_amp_body / opclo_sample_length as f64; + + let mut amplitude_variance = 0.0; + for element in &opclo_1m_vec { + amplitude_variance += ((((element.high_price / element.low_price) - 1.0) * 100.0) + - average_amplitude) + .powi(2); + } + amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64; + let standard_deviation_amplitude = amplitude_variance.sqrt(); + + base_profit_percent = (average_amplitude * average_ratio_amp_body) + + (standard_deviation_amplitude * (1.0 - average_ratio_amp_body)); + + if pos_profit_number1 != 0 { + target_profit_percent = (pos_profit_number1 as f64 * base_profit_percent) + + ((base_profit_percent / 2.0) + * ((pos_profit_number1 as f64).powi(2) - pos_profit_number1 as f64)); + } + + // if (count_short_coins != 0) && (pos_profit_number1 >= 2) && + // ((sum_pos_profit_percent1 >= target_profit_percent && pos_profit_number1 >= neg_profit_number1) + // || (sum_pos_profit_percent1 >= target_profit_percent * 0.8 && neg_profit_number1 != 0)) { + // println!("pos1 liq. target: {:.3}, sum_pos: {:.3}, pos_num: {}, neg_num: {}", target_profit_percent, sum_pos_profit_percent1, pos_profit_number1, neg_profit_number1); + // // update_values.push((String::from("pos_liquidation_signal"), 1.to_string())); + // } else if ((neg_profit_number1 >= 1 && total_pos_profit_usdt1 > 0.0) && (sum_pos_profit_percent1 / (neg_profit_number1 + 1) as f64 <= sum_neg_profit_percent1.abs() )) { + // println!("pos2 liq. target: {:.3}, sum_pos: {:.3}, pos_num: {}, neg_num: {}", target_profit_percent, sum_pos_profit_percent1, pos_profit_number1, neg_profit_number1); + // // update_values.push((String::from("pos_liquidation_signal"), 1.to_string())); + // } else if ((neg_profit_number1 >= 1) && (avg_neg_profit_percent1 <= base_profit_percent * -1.0 / (neg_profit_number1 as f64))) + // // || (timer_check == true && count_short_coins != 0 && sum_pos_profit_percent1 + sum_neg_profit_percent1 < base_profit_percent * -0.1) + // { + // println!("neg liq. avg_neg:{:.3}, base:{:.3}, pos_num: {}, neg_num: {}", avg_neg_profit_percent1, base_profit_percent * -1.0 / (neg_profit_number1 as f64), pos_profit_number1, neg_profit_number1); + // update_values.push((String::from("neg_liquidation_signal"), 1.to_string())); + // } else if scoreboard_list.first().unwrap().pos_liquidation_signal == 1 && count_short_coins == 0 { + // update_values.push((String::from("pos_liquidation_signal"), 0.to_string())); + // } else if scoreboard_list.first().unwrap().neg_liquidation_signal == 1 && count_short_coins == 0 { + // update_values.push((String::from("neg_liquidation_signal"), 0.to_string())); + // } + } + + // // set update values for id 2 (for long) + // update_values2.push((String::from("total_number_of_coin"), count_long_coins.to_string())); + // update_values2.push((String::from("current_pos_profit_number"), pos_profit_number2.to_string())); + // update_values2.push((String::from("current_neg_profit_number"), neg_profit_number2.to_string())); + + // if scoreboard_list.last().unwrap().current_pos_profit_number != scoreboard_list.last().unwrap().previous_pos_profit_number { + // update_values2.push((String::from("previous_pos_profit_number"), pos_profit_number2.to_string().to_string())); + // update_values2.push((String::from("maximum_total_pos_profit_usdt"), current_total_pos_profit_usdt2.to_string())); + // } + // if scoreboard_list.last().unwrap().current_neg_profit_number != scoreboard_list.last().unwrap().previous_neg_profit_number { + // update_values2.push((String::from("previous_neg_profit_number"), neg_profit_number2.to_string().to_string())); + // } + + // update_values2.push((String::from("total_used_usdt"), total_used_usdt2.to_string())); + // update_values2.push((String::from("pos_used_usdt"), pos_used_usdt2.to_string())); + // update_values2.push((String::from("neg_used_usdt"), neg_used_usdt2.to_string())); + // update_values2.push((String::from("current_total_pos_profit_usdt"), current_total_pos_profit_usdt2.to_string())); + // update_values2.push((String::from("current_total_neg_profit_usdt"), current_total_neg_profit_usdt2.to_string())); + + // if current_total_pos_profit_usdt2 > scoreboard_list.last().unwrap().maximum_total_pos_profit_usdt { + // update_values2.push((String::from("maximum_total_pos_profit_usdt"), current_total_pos_profit_usdt2.to_string())); + // } + + // if neg_profit_number2 != 0 { + // avg_neg_profit_percent2 = sum_neg_profit_percent2 / (neg_profit_number2 as f64); + // } + // update_values2.push((String::from("avg_neg_profit_percent"), avg_neg_profit_percent2.to_string())); + + // if (pos_used_usdt2 >= neg_used_usdt2) && + // (current_total_pos_profit_usdt2 <= scoreboard_list.last().unwrap().maximum_total_pos_profit_usdt * 0.85) && + // (scoreboard_list.last().unwrap().maximum_total_pos_profit_usdt > pos_used_usdt2 * 0.005) && + // (current_total_pos_profit_usdt2 > current_total_neg_profit_usdt2.abs() * 2.0) { + // update_values2.push((String::from("pos_liquidation_signal"), 1.to_string())); + // } else if scoreboard_list.last().unwrap().pos_liquidation_signal == 1 && count_short_coins == 0 { + // update_values2.push((String::from("pos_liquidation_signal"), 0.to_string())); + // } else if (pos_used_usdt2 < neg_used_usdt2) && (neg_profit_number2 > 1) && (scoreboard_list.last().unwrap().avg_neg_profit_percent <= -0.5) && (market_cap_list.negative_buy_signal == 0) { + // update_values2.push((String::from("neg_liquidation_signal"), 1.to_string())); + // } else if scoreboard_list.last().unwrap().neg_liquidation_signal == 1 && count_short_coins == 0 { + // update_values2.push((String::from("neg_liquidation_signal"), 0.to_string())); + // } + } + + update_condition.push((String::from("id"), 1.to_string())); + // update_condition2.push((String::from("id"), 2.to_string())); + update_record3(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + // update_record3(&update_table_name, &update_values2, &update_condition2).await.unwrap(); + + Ok(()) +} + +// update profit_percent in [achievement_evaluation] +pub async fn update_profit_percent() { + let select_table_name = String::from("achievement_evaluation"); + let select_columns = String::from("*"); + let select_condition = None; + let evaluation_list = EvaluationList { + id: 0, + strategist: 0, + invested_usdt: Decimal::new(0, 8), + usdt_profit: Decimal::new(0, 8), + profit_percent: 0.0, + }; + let strategists = select_record( + &select_table_name, + &select_columns, + &select_condition, + &evaluation_list, + ) + .await + .unwrap(); + + for element in strategists { + let mut profit_percent = 0.0; + let mut update_value: Vec<(String, String)> = Vec::new(); + let mut update_condition: Vec<(String, String)> = Vec::new(); + + if !element.invested_usdt.is_zero() { + profit_percent = (decimal_sub( + decimal_div(element.usdt_profit, element.invested_usdt), + dec!(1), + )) + .to_f64() + .unwrap() + * 100.0; + if profit_percent == f64::NAN { + profit_percent = 0.0; + } + } + + update_value.clear(); + update_value.push((String::from("profit_percent"), profit_percent.to_string())); + update_condition.clear(); + update_condition.push((String::from("id"), element.id.to_string())); + update_record3(&select_table_name, &update_value, &update_condition) + .await + .unwrap(); + } +} + +// bring the list of pre-suggested coins from [pre_suggested_coin_list] +async fn select_pre_suggested_coin_list() -> Vec { + let select_table_name = String::from("pre_suggested_coin_list"); + let select_columns = String::from("*"); + let select_condition = None; + let select_data_structure = PreSuggestedCoin { + id: 0, + symbol: String::new(), + close_time: 0, + suggested_price: Decimal::new(0, 8), + current_price: Decimal::new(0, 8), + registered_server_epoch: 0, + profit_percent: 0.0, + minimum_profit_percent: 0.0, + maximum_profit_percent: 0.0, + registerer: 0, + }; + let pre_suggested_coin_list = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await + .unwrap(); // unwrap부분을 오류전파로 고치기 + pre_suggested_coin_list +} + +// select a suggested coin having the lowest id in [suggested_coin_list] +async fn select_suggested_coin() -> Vec { + let table_name = String::from("suggested_coin_list"); + let columns = String::from("*"); + let condition = None; + let mut suggested_coin = SuggestedCoin { + id: 0, + symbol: String::new(), + suggested_price: Decimal::new(0, 8), + close_time: 0, + registered_server_epoch: 0, + registerer: 0, + is_long: 0, + already_buy: 0, + }; + + let select_result = try_select_record(&table_name, &columns, &condition, &suggested_coin) + .await + .unwrap(); + + select_result +} + +pub async fn server_epoch() -> i64 { + let table_name = String::from("time"); + let columns = String::from("*"); + let condition = None; + let mut time_info = ServerEpoch { server_epoch: 0 }; + + let select_result = select_record(&table_name, &columns, &condition, &time_info) + .await + .unwrap(); + + select_result.first().unwrap().server_epoch as i64 +} + +async fn select_buy_ordered_coins() -> Vec { + let table_name = String::from("buy_ordered_coin_list"); + let columns = String::from("*"); + let condition = None; + let mut ordered_coin = BuyOrderedCoinList { + id: 0, + symbol: String::new(), + order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + expected_get_usdt: 0.0, + expected_usdt_profit: 0.0, + buy_price: Decimal::new(0, 8), + current_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + base_qty_fee_adjusted: Decimal::new(0, 8), + pure_profit_percent: 0.0, + minimum_profit_percent: 0.0, + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + + let select_result = select_record(&table_name, &columns, &condition, &ordered_coin) + .await + .unwrap(); + select_result +} + +pub async fn select_indices() -> IndexList { + let mut index_list = IndexList { + server_epoch: 0, + total_24h_change_profit_index: 0.0, + usdt_24h_change_profit_index: 0.0, + total_price_down_dist_index: 0.0, + }; + let select_table_name = String::from("indices"); + let select_columns_name = String::from("*"); + let select_condition = None; + let mut select_result = select_record( + &select_table_name, + &select_columns_name, + &select_condition, + &index_list, + ) + .await + .unwrap(); + let result_vec = select_result.first().unwrap(); + index_list.server_epoch = result_vec.server_epoch; + index_list.total_24h_change_profit_index = result_vec.total_24h_change_profit_index; + index_list.usdt_24h_change_profit_index = result_vec.usdt_24h_change_profit_index; + index_list.total_price_down_dist_index = result_vec.total_price_down_dist_index; + + index_list +} + +pub async fn select_filtered_indices() -> FilteredIndexList { + let mut index_list = FilteredIndexList { + server_epoch: 0, + total_24h_change_profit_index: 0.0, + usdt_24h_change_profit_index: 0.0, + total_price_down_dist_index: 0.0, + total_price_down_dist_index_maximum: 0.0, + total_price_down_dist_index_flag: 0, + }; + let select_table_name = String::from("filtered_indices"); + let select_columns_name = String::from("*"); + let select_condition = None; + let mut select_result = select_record( + &select_table_name, + &select_columns_name, + &select_condition, + &index_list, + ) + .await + .unwrap(); + let result_vec = select_result.first().unwrap(); + index_list.server_epoch = result_vec.server_epoch; + index_list.total_24h_change_profit_index = result_vec.total_24h_change_profit_index; + index_list.usdt_24h_change_profit_index = result_vec.usdt_24h_change_profit_index; + index_list.total_price_down_dist_index = result_vec.total_price_down_dist_index; + index_list.total_price_down_dist_index_maximum = result_vec.total_price_down_dist_index_maximum; + index_list.total_price_down_dist_index_flag = result_vec.total_price_down_dist_index_flag; + + index_list +} + +pub async fn select_marketcap() -> Vec { + let mut market_cap_list = MarketCapIndex { + market_cap_index: 0.0, + minimum: 0.0, + maximum: 0.0, + transition_point: 0.0, + liquidation_signal: 0, + negative_buy_signal: 0, + transition_buy_signal: 0, + }; + let select_table_name = String::from("market_cap_index"); + let select_columns_name = String::from("*"); + let select_condition = None; + let mut select_result = select_record( + &select_table_name, + &select_columns_name, + &select_condition, + &market_cap_list, + ) + .await + .unwrap(); + select_result + // let result_vec = select_result.first().unwrap(); + // market_cap_list.market_cap_index = result_vec.market_cap_index; + // market_cap_list.minimum = result_vec.minimum; + // market_cap_list.maximum = result_vec.maximum; + // market_cap_list.transition_point = result_vec.transition_point; + // market_cap_list.liquidation_signal = result_vec.liquidation_signal; + // market_cap_list.negative_buy_signal = result_vec.negative_buy_signal; + // market_cap_list.transition_buy_signal = result_vec.transition_buy_signal; + + // market_cap_list +} + +pub async fn select_scoreboard() -> Vec { + let select_table_name = String::from("scoreboard"); + let select_columns = String::from("*"); + let select_condition = None; + let mut select_data_structure = ScoreboardList { + id: 0, + total_number_of_coin: 0, + pos_profit_number: 0, + neg_profit_number: 0, + total_used_usdt: 0.0, + pos_used_usdt: 0.0, + neg_used_usdt: 0.0, + total_pos_profit_usdt: 0.0, + total_neg_profit_usdt: 0.0, + maximum_total_pos_profit_usdt: 0.0, + avg_neg_profit_percent: 0.0, + pos_liquidation_signal: 0, + neg_liquidation_signal: 0, + }; + let scoreboard_list = select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await + .unwrap(); + + scoreboard_list +} diff --git a/src/coex/order_team.rs b/src/coex/order_team.rs new file mode 100644 index 0000000..a779d3c --- /dev/null +++ b/src/coex/order_team.rs @@ -0,0 +1,2689 @@ +// use consts +use crate::RunningMode::*; +use crate::API_KEY; +use crate::API_KEY_TESTNET; +use crate::RUNNING_MODE; +use crate::SECRET_KEY; +use crate::SECRET_KEY_TESTNET; +use crate::URL; +use crate::URL_TEST; + +// use crates +use crate::coex::assets_managing_team::*; +use crate::coex::exchange_team::*; +use crate::coex::strategy_team::AllData; +use crate::coex::strategy_team::TimeData; +use crate::coin_health_check_team::request_others::{CoinPriceData, ExchangeInfo, TradeFee}; +use crate::database_control::*; +use crate::decimal_funcs::*; +use crate::signal_association::signal_decision::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::ema::EmaData; +use crate::value_estimation_team::indicators::sma::SmaData; +use crate::value_estimation_team::indicators::stoch_rsi::{StochRsiDData, StochRsiKData}; +use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData}; +use futures::future::try_join_all; +use hex::ToHex; +use hmac_sha256::HMAC; +use reqwest::{Client, ClientBuilder}; +use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; +use rust_decimal_macros::dec; +use serde_json::Value; +use sqlx::FromRow; +use tokio::time::*; + +pub enum OrderSide { + Buy, + Sell, +} +pub enum OrderType { + Limit, + Market, + LimitMaker, +} +pub enum TimeInForce { + Gtc, + Ioc, + Fok, +} + +#[derive(Debug, FromRow, Clone)] +pub struct BuyOrderedCoinList { + pub id: u64, + pub symbol: String, + pub order_id: u64, + pub transact_time: i64, + pub close_time: i64, + pub status: String, + pub used_usdt: Decimal, + pub expected_get_usdt: f64, + pub expected_usdt_profit: f64, + pub buy_price: Decimal, + pub current_price: Decimal, + pub base_qty_ordered: Decimal, + pub base_qty_fee_adjusted: Decimal, + pub pure_profit_percent: f64, + pub minimum_profit_percent: f64, + pub maximum_profit_percent: f64, + pub registerer: u16, + pub is_long: u8, +} + +#[derive(Debug, FromRow)] +pub struct SellOrderedCoinList { + pub id: u64, + pub symbol: String, + pub buy_order_id: u64, + pub sell_order_id: u64, + pub transact_time: i64, + pub close_time: i64, + pub status: String, + pub used_usdt: Decimal, + pub get_usdt: Decimal, + pub get_usdt_fee_adjusted: Decimal, + pub buy_price: Decimal, + pub sell_price: Decimal, + pub base_qty_ordered: Decimal, + pub pure_profit_percent: Decimal, + pub maximum_profit_percent: f64, + pub registerer: u16, + pub is_long: u8, +} + +#[derive(Debug, FromRow)] +pub struct SignalDecisionInfo { + pub decision: String, +} + +pub async fn limit_order_buy_test( + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol=BTCUSDT"); + url_build.push_str("&side=BUY"); + url_build.push_str("&timeInForce=GTC"); + + url_build.push_str("&type=LIMIT"); + url_build.push_str("&quantity=0.3"); + url_build.push_str("&price=20451.37"); + + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + println!("url: {}", url); + let res = client + .post(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await + .unwrap(); + println!("response: {:?}", res); + let body = res.text_with_charset("utf-8").await.unwrap(); + println!("limit_order_buy실행 body: {}", body); + // deserialize JSON and then insert record into table + let v = serde_json::from_str::(body.as_str()); + + // match v { + // Ok(T) => { + // if T.get("code").is_some() { // when request failed + + // } else { // when request succeed + // insert_value_container.push(T.get("symbol").unwrap().as_str().unwrap().to_string()); // symbol + // insert_value_container.push(T.get("orderId").unwrap().as_u64().unwrap().to_string()); // order_id + // insert_value_container.push(server_epoch.to_string()); // transact_time + // insert_value_container.push(element.close_time.to_string()); // close_time + // insert_value_container.push(T.get("status").unwrap().as_str().unwrap().to_string()); // status + + // if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + // let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(T.get("origQty").unwrap().as_str().unwrap()).unwrap(); + // let base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)); + + // let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str(T.get("cummulativeQuoteQty").unwrap().as_str().unwrap()).unwrap(); + // let base_asset_precision = exchange_info_vec.iter().find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol).unwrap().base_asset_precision; + // let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered).round_dp_with_strategy(base_asset_precision, RoundingStrategy::ToZero); + + // insert_value_container.push(cummulative_quote_qty.to_string()); // used_usdt + // insert_value_container.push(0.0.to_string()); // expected_get_usdt + // insert_value_container.push(0.0.to_string()); // expected_usdt_profit + // insert_value_container.push(buy_price.to_string()); // buy_price + // insert_value_container.push(0.0.to_string()); // current_price + // insert_value_container.push(base_qty_ordered.to_string()); // base_qty_ordered + // insert_value_container.push(base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted + + // // reflect available_usdt in [asset_manage_announcement] + // // sub_available_usdt(cummulative_quote_qty).await; + // sub_available_usdt(simul_used_usdt).await; + // println!(" buy coin 완료"); + // } else { + // insert_value_container.push(simul_used_usdt.to_string()); // used_usdt + // insert_value_container.push(0.0.to_string()); // expected_get_usdt + // insert_value_container.push(0.0.to_string()); // expected_usdt_profit + // insert_value_container.push(0.0.to_string()); // buy_price + // insert_value_container.push(0.0.to_string()); // current_price + // insert_value_container.push(T.get("origQty").unwrap().as_str().unwrap().to_string()); // base_qty_ordered + // insert_value_container.push(0.0.to_string()); // base_qty_fee_adjusted + + // // reflect available_usdt in [asset_manage_announcement] + // sub_available_usdt(simul_used_usdt).await; + // } + + // insert_value_container.push(0.0.to_string()); // pure_profit_percent + // insert_value_container.push(0.0.to_string()); // minimum_profit_percent + // insert_value_container.push(0.0.to_string()); // maximum_profit_percent + // insert_value_container.push(element.registerer.to_string());// registerer + // insert_value_container.push(0.to_string()); // is_long + + // insert_values.push(insert_value_container.clone()); + // insert_records(&insert_table_name, &insert_columns, &insert_values).await; + // } + // }, + // Err(e) => { + // println!("order failed!: {}", body); + // } + // } + + Ok(()) +} + +pub async fn limit_order_buy( + element: &SuggestedCoin, + exchange_info_vec: &Vec, + trade_fee: Decimal, + tif: TimeInForce, + order_price: Decimal, + order_quantity: Decimal, + simul_used_usdt: Decimal, + simul_base_qty_fee_adjusted: &String, + client: &Client, +) -> Result<(), Box> { + let insert_table_name = String::from("buy_ordered_coin_list"); + let insert_columns = vec![ + "symbol", + "order_id", + "transact_time", + "close_time", + "status", + "used_usdt", + "expected_get_usdt", + "expected_usdt_profit", + "buy_price", + "current_price", + "base_qty_ordered", + "base_qty_fee_adjusted", + "pure_profit_percent", + "minimum_profit_percent", + "maximum_profit_percent", + "registerer", + "is_long", + ]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + let server_epoch = server_epoch().await; + if RUNNING_MODE == SIMUL { + insert_value_container.push(element.symbol.clone()); // symbol + insert_value_container.push(0.to_string()); // order_id + insert_value_container.push(server_epoch.to_string()); // transact_time + insert_value_container.push(element.close_time.to_string()); // close_time + insert_value_container.push(String::from("SIMUL")); // status + insert_value_container.push(simul_used_usdt.to_string()); // used_usdt + insert_value_container.push(0.0.to_string()); // expected_get_usdt + insert_value_container.push(0.0.to_string()); // expected_usdt_profit + insert_value_container.push(order_price.to_string()); // buy_price + insert_value_container.push(0.0.to_string()); // current_price + insert_value_container.push(order_quantity.to_string()); // base_qty_ordered + insert_value_container.push(simul_base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted + insert_value_container.push(0.0.to_string()); // pure_profit_percent + insert_value_container.push(0.0.to_string()); // minimum_profit_percent + insert_value_container.push(0.0.to_string()); // maximum_profit_percent + insert_value_container.push(element.registerer.to_string()); // registerer + insert_value_container.push(0.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + + // reflect available_usdt in [asset_manage_announcement] + sub_available_usdt(simul_used_usdt).await; + } else { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&element.symbol); + url_build.push_str("&side=BUY"); + + match tif { + TimeInForce::Gtc => { + url_build.push_str("&timeInForce=GTC"); + } + TimeInForce::Ioc => { + url_build.push_str("&timeInForce=IOC"); + } + TimeInForce::Fok => { + url_build.push_str("&timeInForce=FOK"); + } + } + url_build.push_str("&type=LIMIT"); + url_build.push_str("&quantity="); + url_build.push_str(order_quantity.to_string().as_str()); + url_build.push_str("&price="); + url_build.push_str(order_price.to_string().as_str()); + + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .post(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await + .unwrap(); + + let body = res.text_with_charset("utf-8").await.unwrap(); + println!("limit_order_buy실행 body: {}", body); + // deserialize JSON and then insert record into table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("code").is_some() { // when request failed + } else { + // when request succeed + insert_value_container + .push(T.get("symbol").unwrap().as_str().unwrap().to_string()); // symbol + insert_value_container + .push(T.get("orderId").unwrap().as_u64().unwrap().to_string()); // order_id + insert_value_container.push(server_epoch.to_string()); // transact_time + insert_value_container.push(element.close_time.to_string()); // close_time + insert_value_container + .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status + + if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( + T.get("origQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_qty_fee_adjusted = + decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)); + + let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol) + .unwrap() + .base_asset_precision; + let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered) + .round_dp_with_strategy(base_asset_precision, RoundingStrategy::ToZero); + + insert_value_container.push(cummulative_quote_qty.to_string()); // used_usdt + insert_value_container.push(0.0.to_string()); // expected_get_usdt + insert_value_container.push(0.0.to_string()); // expected_usdt_profit + insert_value_container.push(buy_price.to_string()); // buy_price + insert_value_container.push(0.0.to_string()); // current_price + insert_value_container.push(base_qty_ordered.to_string()); // base_qty_ordered + insert_value_container.push(base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted + + // reflect available_usdt in [asset_manage_announcement] + // sub_available_usdt(cummulative_quote_qty).await; + sub_available_usdt(simul_used_usdt).await; + println!(" buy coin 완료"); + } else { + insert_value_container.push(simul_used_usdt.to_string()); // used_usdt + insert_value_container.push(0.0.to_string()); // expected_get_usdt + insert_value_container.push(0.0.to_string()); // expected_usdt_profit + insert_value_container.push(0.0.to_string()); // buy_price + insert_value_container.push(0.0.to_string()); // current_price + insert_value_container + .push(T.get("origQty").unwrap().as_str().unwrap().to_string()); // base_qty_ordered + insert_value_container.push(0.0.to_string()); // base_qty_fee_adjusted + + // reflect available_usdt in [asset_manage_announcement] + sub_available_usdt(simul_used_usdt).await; + } + + insert_value_container.push(0.0.to_string()); // pure_profit_percent + insert_value_container.push(0.0.to_string()); // minimum_profit_percent + insert_value_container.push(0.0.to_string()); // maximum_profit_percent + insert_value_container.push(element.registerer.to_string()); // registerer + insert_value_container.push(0.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + } + } + Err(e) => { + println!("order failed!: {}", body); + } + } + } + Ok(()) +} + +pub async fn monitoring_open_buy_order( + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + let open_buy_orders = select_open_buy_orders().await; + + if !open_buy_orders.is_empty() { + let server_epoch = server_epoch().await; + let orders_outdated = open_buy_orders + .iter() + .filter(|&element| server_epoch - element.transact_time >= 600_000) + .collect::>(); // wait up to 600 secs + let orders_to_be_queried = open_buy_orders + .iter() + .filter(|&element| server_epoch - element.transact_time < 600_000) + .collect::>(); + + // cancel orders outdated over 3mins and delete the records in [buy_ordered_coin_list] + if !orders_outdated.is_empty() { + for element in orders_outdated { + cancel_buy_order(element, &client, exchange_info_vec, trade_fee_vec).await; + sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min + } + } + + if !orders_to_be_queried.is_empty() { + for element in orders_to_be_queried { + query_buy_order(element, &client, exchange_info_vec, trade_fee_vec).await; + sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min + } + } + } + + Ok(()) +} + +pub async fn monitoring_filled_buy_order( + all_data: &AllData, + coin_price_vec: &Vec, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // let index_list = select_marketcap().await; + // let scoreboard_list = select_scoreboard().await; + // let signal_decision = select_signal_decision().await; + + let filled_buy_orders = select_filled_buy_orders().await; + if !filled_buy_orders.is_empty() { + // 심볼들을 2개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다. + // update real-time current price to each record through chunks + let chunks = filled_buy_orders.chunks(2); + let mut task_vec = Vec::new(); + + for chunk in chunks { + let chunk_vec = chunk.to_vec(); + let interval_clone = interval.clone(); + let coin_price_vec_c = coin_price_vec.clone(); + let exchange_info_vec_c = exchange_info_vec.clone(); + let trade_fee_vec_c = trade_fee_vec.clone(); + task_vec.push(tokio::spawn(async move { + update_repeat_task( + chunk_vec, + &coin_price_vec_c, + &exchange_info_vec_c, + &trade_fee_vec_c, + ) + .await; + })); + } + + let result = try_join_all(task_vec).await; + + // sell coin if its sell condition is satisfied + let filled_buy_orders = select_filled_buy_orders().await; + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(5000)) + .build() + .unwrap(); + let instant = Instant::now(); + let server_epoch = server_epoch().await; + + let mut sell_order_count = 0; + for element in filled_buy_orders { + if element.used_usdt >= dec!(10.0) { + // ignore coins having 10 usdt below because of not traded + let sell_percent_for_uptrend = |z: f64| 0.94 * z - 0.5; + + let lot_step_size_option = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let quote_commission_precision_option = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + + let opclo_30m_option = all_data + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == element.symbol); + + if lot_step_size_option.is_some() + && quote_commission_precision_option.is_some() + && opclo_30m_option.is_some() + { + let lot_step_size = lot_step_size_option.unwrap().stepsize; + let quote_commission_precision = quote_commission_precision_option + .unwrap() + .quote_commission_precision; + let base_qty_to_be_ordered = + element.base_qty_fee_adjusted.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + + let mut opclo_30m_vec = all_data.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + let opclo_sample_length: usize = 50; // 50 candle samsples + opclo_30m_vec.pop(); + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(opclo_sample_length); + opclo_30m_vec.reverse(); + + let mut sum_amplitude_candles = 0.0; + let mut sum_ratio_amp_body = 0.0; + for element in &opclo_30m_vec { + sum_amplitude_candles += + ((element.high_price / element.low_price) - 1.0) * 100.0; + sum_ratio_amp_body += (element.close_price - element.open_price).abs() + / (element.high_price - element.low_price); + } + let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit + let average_ratio_amp_body = sum_ratio_amp_body / opclo_sample_length as f64; + + let mut amplitude_variance = 0.0; + for element in &opclo_30m_vec { + amplitude_variance += ((((element.high_price / element.low_price) - 1.0) + * 100.0) + - average_amplitude) + .powi(2); + } + amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64; + let standard_deviation_amplitude = amplitude_variance.sqrt(); + + // let target_profit_percent = average_amplitude + (standard_deviation_amplitude * (average_ratio_amp_body)); + let target_profit_percent = + (average_amplitude / 2.0) + (standard_deviation_amplitude * 2.0); // 2.0 sigma (recommand: 0.5 ~ 2.0(patient & greedy)) + + // if scoreboard_list.first().unwrap().pos_liquidation_signal == 1 + // || scoreboard_list.first().unwrap().neg_liquidation_signal == 1 + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + if element.is_long == 0 || element.is_long == 1 { + // if ema10_1m_vec[ema10_result.unwrap()].ema_value > ema3_1m_vec[2].ema_value { + // if stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value + // || (element.pure_profit_percent <= target_profit_percent * -1.0) + // { + // println!("Sold normal. target_profit: {:.3}", target_profit_percent * -1.0); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } else + + if element.pure_profit_percent >= 0.0 { + let mut is_sell = false; + if element.maximum_profit_percent >= target_profit_percent * 2.0 + && element.pure_profit_percent <= target_profit_percent * 1.5 + { + println!( + "Sold 150% target_profit_percent: {:.3}", + target_profit_percent * 1.5 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.1 + && element.pure_profit_percent <= target_profit_percent * 1.6 + { + println!( + "Sold 160% target_profit_percent: {:.3}", + target_profit_percent * 1.6 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.2 + && element.pure_profit_percent <= target_profit_percent * 1.7 + { + println!( + "Sold 170% target_profit_percent: {:.3}", + target_profit_percent * 1.7 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.3 + && element.pure_profit_percent <= target_profit_percent * 1.8 + { + println!( + "Sold 180% target_profit_percent: {:.3}", + target_profit_percent * 1.8 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.4 + && element.pure_profit_percent <= target_profit_percent * 1.9 + { + println!( + "Sold 190% target_profit_percent: {:.3}", + target_profit_percent * 1.9 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.5 + && element.pure_profit_percent <= target_profit_percent * 2.0 + { + println!( + "Sold 200% target_profit_percent: {:.3}", + target_profit_percent * 2.0 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.6 + && element.pure_profit_percent <= target_profit_percent * 2.1 + { + println!( + "Sold 210% target_profit_percent: {:.3}", + target_profit_percent * 2.1 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.7 + && element.pure_profit_percent <= target_profit_percent * 2.2 + { + println!( + "Sold 220% target_profit_percent: {:.3}", + target_profit_percent * 2.2 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.8 + && element.pure_profit_percent <= target_profit_percent * 2.3 + { + println!( + "Sold 230% target_profit_percent: {:.3}", + target_profit_percent * 2.3 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 2.9 + && element.pure_profit_percent <= target_profit_percent * 2.4 + { + println!( + "Sold 240% target_profit_percent: {:.3}", + target_profit_percent * 2.4 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.0 + && element.pure_profit_percent <= target_profit_percent * 2.5 + { + println!( + "Sold 250% target_profit_percent: {:.3}", + target_profit_percent * 2.5 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.1 + && element.pure_profit_percent <= target_profit_percent * 2.6 + { + println!( + "Sold 260% target_profit_percent: {:.3}", + target_profit_percent * 2.6 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.2 + && element.pure_profit_percent <= target_profit_percent * 2.7 + { + println!( + "Sold 270% target_profit_percent: {:.3}", + target_profit_percent * 2.7 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.3 + && element.pure_profit_percent <= target_profit_percent * 2.8 + { + println!( + "Sold 280% target_profit_percent: {:.3}", + target_profit_percent * 2.8 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.4 + && element.pure_profit_percent <= target_profit_percent * 2.9 + { + println!( + "Sold 290% target_profit_percent: {:.3}", + target_profit_percent * 2.9 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.5 + && element.pure_profit_percent <= target_profit_percent * 3.0 + { + println!( + "Sold 300% target_profit_percent: {:.3}", + target_profit_percent * 3.0 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.6 + && element.pure_profit_percent <= target_profit_percent * 3.1 + { + println!( + "Sold 310% target_profit_percent: {:.3}", + target_profit_percent * 3.1 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.7 + && element.pure_profit_percent <= target_profit_percent * 3.2 + { + println!( + "Sold 320% target_profit_percent: {:.3}", + target_profit_percent * 3.2 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.8 + && element.pure_profit_percent <= target_profit_percent * 3.3 + { + println!( + "Sold 330% target_profit_percent: {:.3}", + target_profit_percent * 3.3 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 3.9 + && element.pure_profit_percent <= target_profit_percent * 3.4 + { + println!( + "Sold 340% target_profit_percent: {:.3}", + target_profit_percent * 3.4 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.0 + && element.pure_profit_percent <= target_profit_percent * 3.5 + { + println!( + "Sold 350% target_profit_percent: {:.3}", + target_profit_percent * 3.5 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.1 + && element.pure_profit_percent <= target_profit_percent * 3.6 + { + println!( + "Sold 360% target_profit_percent: {:.3}", + target_profit_percent * 3.6 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.2 + && element.pure_profit_percent <= target_profit_percent * 3.7 + { + println!( + "Sold 370% target_profit_percent: {:.3}", + target_profit_percent * 3.7 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.3 + && element.pure_profit_percent <= target_profit_percent * 3.8 + { + println!( + "Sold 380% target_profit_percent: {:.3}", + target_profit_percent * 3.8 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.4 + && element.pure_profit_percent <= target_profit_percent * 3.9 + { + println!( + "Sold 390% target_profit_percent: {:.3}", + target_profit_percent * 3.9 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.5 + && element.pure_profit_percent <= target_profit_percent * 4.0 + { + println!( + "Sold 400% target_profit_percent: {:.3}", + target_profit_percent * 4.0 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.6 + && element.pure_profit_percent <= target_profit_percent * 4.1 + { + println!( + "Sold 410% target_profit_percent: {:.3}", + target_profit_percent * 4.1 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.7 + && element.pure_profit_percent <= target_profit_percent * 4.2 + { + println!( + "Sold 420% target_profit_percent: {:.3}", + target_profit_percent * 4.2 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.8 + && element.pure_profit_percent <= target_profit_percent * 4.3 + { + println!( + "Sold 430% target_profit_percent: {:.3}", + target_profit_percent * 4.3 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 4.9 + && element.pure_profit_percent <= target_profit_percent * 4.4 + { + println!( + "Sold 440% target_profit_percent: {:.3}", + target_profit_percent * 4.4 + ); + is_sell = true; + } else if element.maximum_profit_percent >= target_profit_percent * 5.0 + && element.pure_profit_percent <= target_profit_percent * 4.5 + { + println!( + "Sold 450% target_profit_percent: {:.3}", + target_profit_percent * 4.5 + ); + is_sell = true; + } else if element.pure_profit_percent >= target_profit_percent * 5.0 { + println!( + "Sold 500% target_profit_percent: {:.3}", + target_profit_percent * 5.0 + ); + is_sell = true; + } + + // if element.maximum_profit_percent >= target_profit_percent * 1.0 { + // println!("Sold target_profit_percent: {}", target_profit_percent * 1.0); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } else if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("UP") && supertrend_vec[latest_supertrend_result.unwrap()].band_value > element.current_price.to_f64().unwrap() && supertrend_vec[latest_supertrend_result.unwrap()].band_value > element.buy_price.to_f64().unwrap() { + // println!("Sold (Profit Adaptive Stoploss): {}", supertrend_vec[latest_supertrend_result.unwrap()].band_value); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } else if supertrend_vec[latest_supertrend_result.unwrap()].signal.is_some() { + // if supertrend_vec[latest_supertrend_result.unwrap()].signal.as_ref().unwrap().contains("SELL") { + // println!("Sold supertrend Sell signal"); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + + // else if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("UP") && supertrend_vec[latest_supertrend_result.unwrap()].band_value < element.buy_price.to_f64().unwrap() && stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value <= stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value - 3.0 + // { + // println!("Sold Stoch RSI"); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // else if sma10_1m_vec[sma10_result.unwrap()].sma_value > ema10_1m_vec[ema10_result.unwrap()].ema_value + // { + // println!("+Sold EMA10 < SMA10 reversal, profit: {:.3}", element.pure_profit_percent); + // sell_order_count += 1; + // is_sell = true; + // } + // else if sma10_1m_vec[sma10_result.unwrap()-2].sma_value > sma10_1m_vec[sma10_result.unwrap()].sma_value && sma10_1m_vec[sma10_result.unwrap()-1].sma_value > sma10_1m_vec[sma10_result.unwrap()].sma_value { + // println!("Sold SMA"); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + + if is_sell == true { + // let mut sell_price_ahead: Decimal = Decimal::new(14, 8); + // sell_price_ahead = decimal_mul(decimal_add(decimal_mul(decimal_mul(rust_decimal::Decimal::from_f64(element.pure_profit_percent).unwrap(), dec!(0.01)), dec!(0.97)), dec!(1)), element.buy_price).round_dp_with_strategy(2, RoundingStrategy::ToZero); + limit_order_sell( + &element, + element.current_price, + base_qty_to_be_ordered, + &client, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + // limit_order_sell(&element, sell_price_ahead, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + } + } else { + if element.pure_profit_percent < (target_profit_percent * -1.0) - 0.2 { + // -0.2 means about double trade fees. + limit_order_sell( + &element, + element.current_price, + base_qty_to_be_ordered, + &client, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + } + // if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("DOWN") && element.pure_profit_percent <= (target_profit_percent * -0.7) - 0.2 { // -0.2 means tradefee (buying+selling) + // // if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("DOWN") && element.pure_profit_percent < target_profit_percent * -0.5 { + // println!("Downtrend Sold Loss, -70% target_profit_percent: {:.3}", target_profit_percent * -0.7); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } else if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("UP") && element.pure_profit_percent < (target_profit_percent * -1.0) - 0.2 { + // println!("Uptrend Sold Loss, -100% target_profit_percent: {:.3}", target_profit_percent * -1.0); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } else if supertrend_vec[latest_supertrend_result.unwrap()].area.contains("UP") && supertrend_vec[latest_supertrend_result.unwrap()].band_value > element.current_price.to_f64().unwrap() { + // println!("Sold UP trend, (Loss Adaptive Stoploss) band_value: {:.3}", supertrend_vec[latest_supertrend_result.unwrap()].band_value); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // else if supertrend_vec[latest_supertrend_result.unwrap()].signal.is_some() { + // if supertrend_vec[latest_supertrend_result.unwrap()].signal.as_ref().unwrap().contains("SELL") { + // println!("Sold supertrend Sell signal"); + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } + // else if sma10_1m_vec[sma10_result.unwrap()-2].sma_value > sma10_1m_vec[sma10_result.unwrap()].sma_value && sma10_1m_vec[sma10_result.unwrap()-1].sma_value > sma10_1m_vec[sma10_result.unwrap()].sma_value { + // println!("Sold SMA"); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // else if sma10_1m_vec[sma10_result.unwrap()].sma_value > ema10_1m_vec[ema10_result.unwrap()].ema_value { + // println!("-Sold EMA10 < SMA10, profit: {:.3}", element.pure_profit_percent); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // else if sma3_1m_vec[sma3_result.unwrap()].sma_value > ema3_1m_vec.last().unwrap().ema_value { + // println!("-Sold EMA3 < SMA3, profit: {:.3}", element.pure_profit_percent); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // else if stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value <= stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value - 3.0 + // { + // println!("Sold Stoch RSI"); + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + } + + // if ema10_1m_vec[ema10_result.unwrap()].ema_value > ema3_1m_vec[2].ema_value { + // // if (10_000 <= server_epoch - element.transact_time) + // // || (element.pure_profit_percent <= -0.002) + // // || (element.pure_profit_percent >= 0.002) + // // || (ema3_1m_vec[0].ema_value < ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } + // else { + // // if (element.maximum_profit_percent >= 0.02 && element.pure_profit_percent < element.maximum_profit_percent * 0.5) + // // || (element.pure_profit_percent <= -0.03) + // // || (ema3_1m_vec[0].ema_value > ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // // || (stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value - 3.0) + // // || stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_k_vec[stoch_rsi30_k_prev_result.unwrap()].stoch_rsi_k_value - 5.0 + // // { + // // sell_order_count += 1; + // // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // // } + + // if average_stoch_rsi30_1m_k < 85.0 { + // if stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0 { + // if + // // element.pure_profit_percent >= target_profit_percent + // (element.pure_profit_percent <= 0.0 && element.minimum_profit_percent <= target_profit_percent * -2.0 && element.pure_profit_percent >= element.minimum_profit_percent * 0.5) + // ||(element.maximum_profit_percent >= target_profit_percent && element.pure_profit_percent < element.maximum_profit_percent * 0.95) + // // || (60_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 90_000 && element.pure_profit_percent >= target_profit_percent * 0.7) + // // || (90_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 120_000 && element.pure_profit_percent >= target_profit_percent * 0.5) + // // || (120_000 < server_epoch - element.transact_time && server_epoch - element.transact_time < 150_000 && ((element.pure_profit_percent < 0.0 && element.pure_profit_percent > element.minimum_profit_percent / 2.0) || (element.pure_profit_percent >= 0.001))) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("DOWN")) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("UP")) + // // || (ema3_1m_vec[0].ema_value > ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // || (stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value - 3.0) + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } else { + // if + // // element.pure_profit_percent >= target_profit_percent * 0.5 + // (element.pure_profit_percent <= 0.0 && element.minimum_profit_percent <= target_profit_percent * -2.0 && element.pure_profit_percent >= element.minimum_profit_percent * 0.5) + // || (element.maximum_profit_percent >= target_profit_percent * 0.5 && element.pure_profit_percent < element.maximum_profit_percent * 0.95) + // // || (30_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 60_000 && element.pure_profit_percent >= target_profit_percent * 0.5 * 0.5) + // // || (60_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 90_000 && element.pure_profit_percent >= target_profit_percent * 0.5 * 0.25) + // // || (90_000 < server_epoch - element.transact_time && server_epoch - element.transact_time < 120_000 && ((element.pure_profit_percent < 0.0 && element.pure_profit_percent > element.minimum_profit_percent / 2.0) || (element.pure_profit_percent >= 0.001))) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("DOWN")) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("UP")) + // // || (ema3_1m_vec[0].ema_value > ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // || (stoch_rsi30_1m_k_vec[stoch_rsi30_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[stoch_rsi30_d_result.unwrap()].stoch_rsi_d_value - 3.0) + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } + // } else { + // if stoch_rsi10_1m_k_vec[stoch_rsi10_k_result.unwrap()].stoch_rsi_k_value < 80.0 { + // if + // // element.pure_profit_percent >= target_profit_percent + // (element.pure_profit_percent <= 0.0 && element.minimum_profit_percent <= target_profit_percent * -2.0 && element.pure_profit_percent >= element.minimum_profit_percent * 0.5) + // || (element.maximum_profit_percent >= target_profit_percent && element.pure_profit_percent < element.maximum_profit_percent * 0.95) + // // || (60_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 90_000 && element.pure_profit_percent >= target_profit_percent * 0.7) + // // || (90_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 120_000 && element.pure_profit_percent >= target_profit_percent * 0.5) + // // || (120_000 < server_epoch - element.transact_time && server_epoch - element.transact_time < 150_000 && ((element.pure_profit_percent < 0.0 && element.pure_profit_percent > element.minimum_profit_percent / 2.0) || (element.pure_profit_percent >= 0.001))) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("DOWN")) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("UP")) + // // || (ema3_1m_vec[0].ema_value > ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // || (stoch_rsi10_1m_k_vec[stoch_rsi10_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi10_1m_d_vec[stoch_rsi10_d_result.unwrap()].stoch_rsi_d_value - 3.0) + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } else { + // if + // // element.pure_profit_percent >= target_profit_percent * 0.5 + // (element.pure_profit_percent <= 0.0 && element.minimum_profit_percent <= target_profit_percent * -2.0 && element.pure_profit_percent >= element.minimum_profit_percent * 0.5) + // || (element.maximum_profit_percent >= target_profit_percent * 0.5 && element.pure_profit_percent < element.maximum_profit_percent * 0.95) + // // || (30_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 60_000 && element.pure_profit_percent >= target_profit_percent * 0.5 * 0.5) + // // || (60_000 <= server_epoch - element.transact_time && server_epoch - element.transact_time < 90_000 && element.pure_profit_percent >= target_profit_percent * 0.5 * 0.25) + // // || (90_000 < server_epoch - element.transact_time && server_epoch - element.transact_time < 120_000 && ((element.pure_profit_percent < 0.0 && element.pure_profit_percent > element.minimum_profit_percent / 2.0) || (element.pure_profit_percent >= 0.001))) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("DOWN")) + // // || (element.pure_profit_percent <= average_amplitude_candles * -0.8 && opclo_1m_vec[opclo_sample_length-1].candle_type.contains("UP")) + // // || (ema3_1m_vec[0].ema_value > ema3_1m_vec[1].ema_value && ema3_1m_vec[1].ema_value > ema3_1m_vec[2].ema_value) + // || (stoch_rsi10_1m_k_vec[stoch_rsi10_k_result.unwrap()].stoch_rsi_k_value < stoch_rsi10_1m_d_vec[stoch_rsi10_d_result.unwrap()].stoch_rsi_d_value - 3.0) + // { + // sell_order_count += 1; + // limit_order_sell(&element, element.current_price, base_qty_to_be_ordered, &client, &exchange_info_vec, &trade_fee_vec).await; + // } + // } + // } + // // || (index_list.first().unwrap().liquidation_signal == 1 && index_list.first().unwrap().market_cap_index > 0.0) + + // } + } + } + } + + if sell_order_count == 25 { + // to avoid Limit of order: LIMIT 50 for 10 secs + sleep(Duration::from_secs(10)).await; + sell_order_count = 0; + } + } + } + + Ok(()) +} + +async fn update_repeat_task( + buy_ordered_coin_vec: Vec, + coin_price_vec: &Vec, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + let update_table_name = String::from("buy_ordered_coin_list"); + let mut update_values: Vec<(String, String)> = Vec::new(); + let mut update_condition: Vec<(String, String)> = Vec::new(); + let mut price = Decimal::new(0, 8); + let mut profit_percent = 0.0; + let server_epoch = server_epoch().await; + + let update_colums = vec![ + "current_price", + "expected_get_usdt", + "expected_usdt_profit", + "pure_profit_percent", + "minimum_profit_percent", + "maximum_profit_percent", + "is_long", + ]; + let mut update_record_build: Vec = Vec::new(); + let mut update_record: Vec> = Vec::new(); + + // update current_price, expected__get_usdt, expected_usdt_profit, pure_profit_percent, minimum_profit_percent, maximum_profit_percent + for element in buy_ordered_coin_vec { + // build update values + update_record_build.clear(); + let price_index_option = coin_price_vec + .iter() + .position(|x| *x.symbol == element.symbol); + let lot_step_size_result = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let quote_commission_precision_result = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol); + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == element.symbol) + .unwrap() + .takercommission; + if price_index_option.is_some() + && lot_step_size_result.is_some() + && quote_commission_precision_result.is_some() + { + price = rust_decimal::prelude::FromPrimitive::from_f64( + coin_price_vec[price_index_option.unwrap()].current_price, + ) + .unwrap(); + // to get quote_commission_precision + let lot_step_size = lot_step_size_result.unwrap().stepsize; + let quote_commission_precision = quote_commission_precision_result + .unwrap() + .quote_commission_precision; + let base_qty_to_be_ordered = element.base_qty_fee_adjusted.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + let expected_get_usdt = decimal_mul( + decimal_mul(base_qty_to_be_ordered, price) + .round_dp_with_strategy(quote_commission_precision, RoundingStrategy::ToZero), + decimal_sub(dec!(1), trade_fee), + ); + let pure_profit_percent = + ((expected_get_usdt.to_f64().unwrap() / element.used_usdt.to_f64().unwrap()) - 1.0) + * 100.0; + + update_record_build.push(element.id.to_string()); // id + update_record_build.push(price.to_string()); // current_price + update_record_build.push(expected_get_usdt.to_string()); //expected_get_usdt + update_record_build.push(decimal_sub(expected_get_usdt, element.used_usdt).to_string()); // expected_usdt_profit + update_record_build.push(pure_profit_percent.to_string()); // pure_profit_percent + + if element.minimum_profit_percent > pure_profit_percent { + update_record_build.push(pure_profit_percent.to_string()); // minimum_profit_percent + } else if pure_profit_percent >= 0.0 { + update_record_build.push(0.0.to_string()); // minimum_profit_percent + } else { + update_record_build.push(element.minimum_profit_percent.to_string()); + // minimum_profit_percent + } + + if element.maximum_profit_percent < pure_profit_percent { + update_record_build.push(pure_profit_percent.to_string()); // maximum_profit_percent + } else if pure_profit_percent <= 0.0 { + update_record_build.push(0.0.to_string()); // maximum_profit_percent + } else { + update_record_build.push(element.maximum_profit_percent.to_string()); + // maximum_profit_percent + } + + if server_epoch - element.transact_time >= 86_400_000 { + // turn is_long from 0 to 1 for orders whose transact time is over than a day (86,400,000 millis = a day) + update_record_build.push(1.to_string()); + } else { + update_record_build.push(0.to_string()); + } + update_record.push(update_record_build.clone()); + } + } + + update_records(&update_table_name, &update_record, &update_colums).await; + + Ok(()) +} + +pub async fn limit_order_sell( + buy_ordered_coin: &BuyOrderedCoinList, + sell_base_price: Decimal, + sell_base_quantity: Decimal, + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + let insert_table_name = String::from("sell_ordered_coin_list"); + let insert_columns = vec![ + "symbol", + "buy_order_id", + "sell_order_id", + "transact_time", + "close_time", + "status", + "used_usdt", + "get_usdt", + "get_usdt_fee_adjusted", + "buy_price", + "sell_price", + "base_qty_ordered", + "pure_profit_percent", + "maximum_profit_percent", + "registerer", + "is_long", + ]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + let server_epoch = server_epoch().await; + if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" { + let quote_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == buy_ordered_coin.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == buy_ordered_coin.symbol) + .unwrap() + .takercommission; + let get_usdt = decimal_mul(sell_base_quantity, sell_base_price) + .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); + let get_usdt_fee_adjusted = decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); + + insert_value_container.push(buy_ordered_coin.symbol.to_string()); // symbol + insert_value_container.push(0.to_string()); // buy_order_id + insert_value_container.push(0.to_string()); // sell_order_id + insert_value_container.push(server_epoch.to_string()); // transact_time + insert_value_container.push(buy_ordered_coin.close_time.to_string()); // close_time + insert_value_container.push(String::from("SIMUL")); // status + insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt + insert_value_container.push(get_usdt.to_string()); // get_usdt + insert_value_container.push(get_usdt_fee_adjusted.to_string()); // get_usdt_fee_adjusted + insert_value_container.push(buy_ordered_coin.buy_price.to_string()); // buy_price + insert_value_container.push(sell_base_price.to_string()); // sell_price + insert_value_container.push(sell_base_quantity.to_string()); // base_qty_ordered + + let pure_profit_percent = decimal_mul( + decimal_sub( + decimal_div(get_usdt_fee_adjusted, buy_ordered_coin.used_usdt), + dec!(1), + ), + dec!(100), + ); + insert_value_container.push(pure_profit_percent.to_string()); // pure_profit_percent + insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer + insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + + // delete record in buy_ordered_coin_list + let delete_table_name = String::from("buy_ordered_coin_list"); + let mut delete_condition = String::from("WHERE id = "); + delete_condition.push_str(buy_ordered_coin.id.to_string().as_str()); + delete_record(&delete_table_name, &delete_condition).await; + } else if RUNNING_MODE == REAL || RUNNING_MODE == TEST { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&buy_ordered_coin.symbol); + url_build.push_str("&side=SELL"); + url_build.push_str("&timeInForce=GTC"); + + url_build.push_str("&type=LIMIT"); + url_build.push_str("&quantity="); + url_build.push_str(sell_base_quantity.to_string().as_str()); + url_build.push_str("&price="); + url_build.push_str(sell_base_price.to_string().as_str()); + + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .post(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("limit_order_sell실행 body: {}", body); + // deserialize JSON and then insert record into table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("code").is_some() { + // when request failed + sleep(Duration::from_secs(1)).await; + } else { + // when request succeed + insert_value_container + .push(T.get("symbol").unwrap().as_str().unwrap().to_string()); // symbol + insert_value_container.push(buy_ordered_coin.order_id.to_string()); // buy_order_id + insert_value_container + .push(T.get("orderId").unwrap().as_u64().unwrap().to_string()); // sell_order_id + insert_value_container.push(server_epoch.to_string()); // transact_time + insert_value_container.push(buy_ordered_coin.close_time.to_string()); // close_time + insert_value_container + .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status + insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt + + let quote_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == buy_ordered_coin.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == buy_ordered_coin.symbol) + .unwrap() + .takercommission; + let get_usdt = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let get_usdt_fee_adjusted = + decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + let ordered_base_qty = rust_decimal::prelude::FromStr::from_str( + T.get("origQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + + if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + insert_value_container.push(get_usdt.to_string()); // get_usdt + insert_value_container.push(get_usdt_fee_adjusted.to_string()); + // get_usdt_fee_adjusted + } else { + insert_value_container.push(0.0.to_string()); // get_usdt + insert_value_container.push(0.0.to_string()); // get_usdt_fee_adjusted + } + + insert_value_container.push(buy_ordered_coin.buy_price.to_string()); // buy_price + + if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + let sell_price = decimal_div(get_usdt, ordered_base_qty) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + insert_value_container.push(sell_price.to_string()); // sell_price + } else { + insert_value_container.push(0.0.to_string()); // sell_price + } + + insert_value_container.push(ordered_base_qty.to_string()); // base_qty_ordered + + if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + let pure_profit_percent = decimal_mul( + decimal_sub( + decimal_div(get_usdt_fee_adjusted, buy_ordered_coin.used_usdt), + dec!(1), + ), + dec!(100), + ); + insert_value_container + .push(buy_ordered_coin.pure_profit_percent.to_string()); + // pure_profit_percent + } else { + insert_value_container.push(0.0.to_string()); // pure_profit_percent + } + insert_value_container + .push(buy_ordered_coin.maximum_profit_percent.to_string()); // maximum_profit_percent + insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer + insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + + // delete record in buy_ordered_coin_list + let delete_table_name = String::from("buy_ordered_coin_list"); + let mut delete_condition = String::from("WHERE id = "); + delete_condition.push_str(buy_ordered_coin.id.to_string().as_str()); + delete_record(&delete_table_name, &delete_condition).await; + } + } + Err(e) => { + println!("sell order failed!: {}", body); + } + } + } + Ok(()) +} + +pub async fn monitoring_open_sell_order( + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + let open_sell_orders = select_open_sell_orders().await; + + if !open_sell_orders.is_empty() { + let server_epoch = server_epoch().await; + let orders_outdated = open_sell_orders + .iter() + .filter(|&element| server_epoch - element.transact_time >= 5_000) + .collect::>(); + let orders_to_be_queried = open_sell_orders + .iter() + .filter(|&element| server_epoch - element.transact_time < 5_000) + .collect::>(); + + // cancel orders outdated over 30secs, delete its records in [sell_ordered_coin_list], and move them into [buy_ordered_coin_list] + if !orders_outdated.is_empty() { + for element in orders_outdated { + cancel_sell_order(element, &client, exchange_info_vec, trade_fee_vec).await; + sleep(Duration::from_millis(200)).await; // Use max 30 LIMIT/min + } + } + + if !orders_to_be_queried.is_empty() { + for element in orders_to_be_queried { + query_sell_order(element, &client, exchange_info_vec, trade_fee_vec).await; + sleep(Duration::from_millis(300)).await; // Use max 30 LIMIT/min + } + } + } + + Ok(()) +} + +pub async fn monitoring_filled_sell_order( + client: &Client, +) -> Result<(), Box> { + let filled_sell_orders = select_filled_sell_orders().await; + + // Move record whose status is "FILLED" or "SIMUL" into [sell_history] and [achievement_evaluation] + if !filled_sell_orders.is_empty() { + let insert_table_name = String::from("sell_history"); + let insert_columns = vec![ + "symbol", + "soldtime", + "used_usdt", + "get_usdt", + "buy_price", + "sell_price", + "base_qty", + "pure_profit_percent", + "pure_profit_usdt", + "registerer", + ]; + let mut insert_value_container: Vec> = Vec::new(); + let mut insert_value_build: Vec = Vec::new(); + + let update_table_name = String::from("achievement_evaluation"); + let server_epoch = server_epoch().await; + let mut total_get_usdt = Decimal::new(0, 8); + + for element in filled_sell_orders { + // build insert value + let pure_profit_usdt = decimal_sub(element.get_usdt_fee_adjusted, element.used_usdt); + insert_value_build.clear(); + insert_value_build.push(element.symbol.clone()); // symbol + insert_value_build.push(server_epoch.to_string()); // soldtime + insert_value_build.push(element.used_usdt.to_string()); // used_usdt + insert_value_build.push(element.get_usdt_fee_adjusted.to_string()); // get_usdt + insert_value_build.push(element.buy_price.to_string()); // buy_price + insert_value_build.push(element.sell_price.to_string()); // sell_price + insert_value_build.push(element.base_qty_ordered.to_string()); // base_qty + insert_value_build.push( + decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100)) + .to_string(), + ); // pure_profit_percent + insert_value_build.push(pure_profit_usdt.to_string()); // pure_profit_usdt + insert_value_build.push(element.registerer.to_string()); // registerer + insert_value_container.push(insert_value_build.clone()); + + // update [achievement_evaluation] + let mut value_build1 = String::from("invested_usdt + "); + let mut invested_usdt = Decimal::new(0, 8); + invested_usdt = decimal_add(invested_usdt, element.used_usdt); + value_build1.push_str(invested_usdt.to_string().as_str()); + let update_value1 = vec![(String::from("invested_usdt"), value_build1)]; + let update_condition = + vec![(String::from("strategist"), element.registerer.to_string())]; + update_record3(&update_table_name, &update_value1, &update_condition) + .await + .unwrap(); + + if element.get_usdt_fee_adjusted.is_sign_negative() { + let mut value_build2 = String::from("usdt_profit - "); + value_build2.push_str(element.get_usdt_fee_adjusted.abs().to_string().as_str()); + let update_value2 = vec![(String::from("usdt_profit"), value_build2)]; + update_record3(&update_table_name, &update_value2, &update_condition) + .await + .unwrap(); + } else { + let mut value_build2 = String::from("usdt_profit + "); + value_build2.push_str(element.get_usdt_fee_adjusted.to_string().as_str()); + let update_value2 = vec![(String::from("usdt_profit"), value_build2)]; + update_record3(&update_table_name, &update_value2, &update_condition) + .await + .unwrap(); + } + + // add available_usdt + add_available_usdt(element.get_usdt_fee_adjusted).await; + + // delete in [sell_ordered_coin_list] + let delete_table_name = String::from("sell_ordered_coin_list"); + let mut delete_condition = String::from("WHERE id = "); + delete_condition.push_str(element.id.to_string().as_str()); + delete_record(&delete_table_name, &delete_condition).await; + + // total_get_usdt = decimal_add(total_get_usdt, element.get_usdt_fee_adjusted); + println!(" sell_coin 완료"); + } + + // update profit_percent in [achievement_evaluation] + update_profit_percent().await; + + // insert in [sell_history] + insert_records(&insert_table_name, &insert_columns, &insert_value_container).await; + + // add available_usdt + // if total_get_usdt.is_sign_positive() { + // add_available_usdt(total_get_usdt).await; + // } else { + // sub_available_usdt(total_get_usdt.abs()).await; + // } + } + + Ok(()) +} + +pub async fn market_order( + symbol: &String, + side: OrderSide, + quantity: u64, + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + hmac_signature(&mut url).await; + + // add parameters into URL + url.push_str("&symbol="); + url.push_str(symbol.as_str()); + match side { + OrderSide::Buy => { + url.push_str("&side=BUY"); + } + OrderSide::Sell => { + url.push_str("&side=SELL"); + } + } + + url.push_str("&type=MARKET"); + url.push_str("&quantity="); + url.push_str(quantity.to_string().as_str()); + + let res = client + .post(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + Ok(()) +} + +// Cancel an NEW or PARTIALLY FILLED order. (/api, Weight(IP) 1) +pub async fn cancel_buy_order( + order: &BuyOrderedCoinList, + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&order.symbol); + url_build.push_str("&orderId="); + url_build.push_str(order.order_id.to_string().as_str()); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .delete(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("cancel_buy_order실행 body: {}", body); + // deserialize JSON and then insert record into table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("status").is_some() { + // case that the order is canceled successfully + if T.get("status").unwrap().as_str().unwrap() == "CANCELED" { + let table_name = String::from("buy_ordered_coin_list"); + let cummulative_quote_qty: Decimal = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + + if cummulative_quote_qty.is_zero() { + // NOT Filled case + // delete record in [buy_ordered_coin_list] + let mut condition_build = String::from("WHERE order_id = "); + condition_build.push_str(order.order_id.to_string().as_str()); + condition_build.push_str(" AND symbol = \'"); + condition_build.push_str(&order.symbol); + condition_build.push('\''); + delete_record(&table_name, &condition_build).await; + + add_available_usdt(order.used_usdt).await; + + let update_table_name = String::from("suggested_coin_list"); + let update_condition = vec![ + (String::from("symbol"), order.symbol.clone()), + (String::from("close_time"), order.close_time.to_string()), + ]; + let update_values: Vec<(String, String)> = + vec![(String::from("already_buy"), 0.to_string())]; + update_record2(&update_table_name, &update_values, &update_condition).await; + } else { + // Patially Filled case + // update values in [buy_ordered_coin_list] + let mut status_value_build = String::from("\'"); + status_value_build.push_str("FILLED"); + status_value_build.push('\''); + + // calculate values to be updated + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == order.symbol) + .unwrap() + .takercommission; + + let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( + T.get("executedQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_qty_fee_adjusted = + decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)); + + let base_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == order.symbol) + .unwrap() + .base_asset_precision; + let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered) + .round_dp_with_strategy(base_asset_precision, RoundingStrategy::ToZero); + + let update_values = vec![ + (String::from("status"), status_value_build), // status + (String::from("used_usdt"), cummulative_quote_qty.to_string()), // used_usdt + (String::from("buy_price"), buy_price.to_string()), // buy_price + ( + String::from("base_qty_ordered"), + base_qty_ordered.to_string(), + ), // base_qty_ordered + ( + String::from("base_qty_fee_adjusted"), + base_qty_fee_adjusted.to_string(), + ), // base_qty_fee_adjusted + ]; + let update_condition = vec![ + (String::from("order_id"), order.order_id.to_string()), + (String::from("symbol"), order.symbol.clone()), + ]; + update_record3(&table_name, &update_values, &update_condition) + .await + .unwrap(); + + // update available_usdt + if order.used_usdt > cummulative_quote_qty { + add_available_usdt(decimal_sub(order.used_usdt, cummulative_quote_qty)); + } else { + sub_available_usdt(decimal_sub(cummulative_quote_qty, order.used_usdt)); + } + println!(" partially buy coin 완료"); + } + } + } else if T.get("code").is_some() { + // case that the order isn't canceled because the order completes while canceling + // update record in ordered_coin_list + query_buy_order(order, &client, exchange_info_vec, trade_fee_vec).await; + } + } + Err(e) => { + query_buy_order(order, &client, exchange_info_vec, trade_fee_vec).await; + // println!("cancel order buy failed!: {}", body); + } + } + + Ok(()) +} + +// Cancel an NEW or PARTIALLY FILLED order. (/api, Weight(IP) 1) +pub async fn cancel_sell_order( + order: &SellOrderedCoinList, + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&order.symbol); + url_build.push_str("&orderId="); + url_build.push_str(order.sell_order_id.to_string().as_str()); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .delete(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("cancel_sell_order실행 body: {}", body); + // deserialize JSON and then insert record into table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("status").is_some() { + // case that the order is canceled successfully + if T.get("status").unwrap().as_str().unwrap() == "CANCELED" { + let insert_table_name = String::from("buy_ordered_coin_list"); + let insert_columns = vec![ + "symbol", + "order_id", + "transact_time", + "close_time", + "status", + "used_usdt", + "expected_get_usdt", + "expected_usdt_profit", + "buy_price", + "current_price", + "base_qty_ordered", + "base_qty_fee_adjusted", + "pure_profit_percent", + "minimum_profit_percent", + "maximum_profit_percent", + "registerer", + "is_long", + ]; + let base_qty_executed: Decimal = rust_decimal::prelude::FromStr::from_str( + T.get("executedQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_qty_ordered: Decimal = rust_decimal::prelude::FromStr::from_str( + T.get("origQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + + if base_qty_executed.is_zero() { + // Not FILLED + // delete record in [sell_ordered_coin_list] + let table_name = String::from("sell_ordered_coin_list"); + let mut condition_build = String::from("WHERE sell_order_id = "); + condition_build.push_str(order.sell_order_id.to_string().as_str()); + condition_build.push_str(" AND symbol = \'"); + condition_build.push_str(&order.symbol); + condition_build.push('\''); + delete_record(&table_name, &condition_build).await; + + // insert record in [buy_ordered_coin_list] + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + let server_epoch = server_epoch().await; + insert_value_container.push(order.symbol.clone()); // symbol + insert_value_container.push(order.buy_order_id.to_string()); // order_id + insert_value_container.push(order.transact_time.to_string()); // transact_time + insert_value_container.push(order.close_time.to_string()); // close_time + insert_value_container.push(String::from("FILLED")); // status + insert_value_container.push(order.used_usdt.to_string()); // used_usdt + insert_value_container.push(0.0.to_string()); // expected_get_usdt + insert_value_container.push(0.0.to_string()); // expected_usdt_profit + insert_value_container.push(order.buy_price.to_string()); // buy_price + insert_value_container.push(0.0.to_string()); // current_price + insert_value_container.push(order.base_qty_ordered.to_string()); // base_qty_ordered + insert_value_container.push(order.base_qty_ordered.to_string()); // base_qty_fee_adjusted + insert_value_container.push(0.0.to_string()); // pure_profit_percent + insert_value_container.push(0.0.to_string()); // minimum_profit_percent + insert_value_container.push(order.maximum_profit_percent.to_string()); // maximum_profit_percent + insert_value_container.push(order.registerer.to_string()); // registerer + insert_value_container.push(order.is_long.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + } else { + if base_qty_executed == base_qty_ordered { + // FILLED case + // update status FILLED + let quote_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == order.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == order.symbol) + .unwrap() + .takercommission; + + let get_usdt = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let get_usdt_fee_adjusted = + decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + let sell_price = decimal_div(get_usdt, base_qty_executed) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + let pure_profit_percent = decimal_mul( + decimal_sub( + decimal_div(get_usdt_fee_adjusted, order.used_usdt), + dec!(1), + ), + dec!(100), + ); + + let table_name = String::from("sell_ordered_coin_list"); + let mut value_build = String::from("\'"); + value_build.push_str("FILLED"); + value_build.push('\''); + + let update_values = vec![ + (String::from("status"), value_build), + (String::from("get_usdt"), get_usdt.to_string()), + ( + String::from("get_usdt_fee_adjusted"), + get_usdt_fee_adjusted.to_string(), + ), + (String::from("sell_price"), sell_price.to_string()), + ( + String::from("pure_profit_percent"), + pure_profit_percent.to_string(), + ), + ]; + let update_condition = vec![(String::from("id"), order.id.to_string())]; + update_record3(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + // PARTIALLY FILLED case + // reflect partially filled information and update status with FILLED + let quote_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == order.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == order.symbol) + .unwrap() + .takercommission; + let used_usdt = decimal_mul(base_qty_executed, order.buy_price); + let get_usdt = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let get_usdt_fee_adjusted = + decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + let rest_used_usdt = decimal_sub(order.used_usdt, used_usdt); + let sell_price = decimal_div(get_usdt, base_qty_executed) + .round_dp_with_strategy( + quote_asset_precision, + RoundingStrategy::ToZero, + ); + let pure_profit_percent = decimal_mul( + decimal_sub(decimal_div(get_usdt_fee_adjusted, get_usdt), dec!(1)), + dec!(100), + ); + + let table_name = String::from("sell_ordered_coin_list"); + let mut value_build = String::from("\'"); + value_build.push_str("FILLED"); + value_build.push('\''); + + let update_values = vec![ + (String::from("status"), value_build), + (String::from("used_usdt"), used_usdt.to_string()), + (String::from("get_usdt"), get_usdt.to_string()), + ( + String::from("get_usdt_fee_adjusted"), + get_usdt_fee_adjusted.to_string(), + ), + ( + String::from("base_qty_ordered"), + base_qty_executed.to_string(), + ), + (String::from("sell_price"), sell_price.to_string()), + ( + String::from("pure_profit_percent"), + pure_profit_percent.to_string(), + ), + ]; + let update_condition = vec![(String::from("id"), order.id.to_string())]; + update_record3(&table_name, &update_values, &update_condition) + .await + .unwrap(); + + // insert record in [buy_ordered_coin_list] + let rest_base_qty = decimal_sub(base_qty_ordered, base_qty_executed); + let rest_base_qty_fee_adjusted = + decimal_mul(rest_base_qty, decimal_sub(dec!(1), trade_fee)); + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + let server_epoch = server_epoch().await; + insert_value_container.push(order.symbol.clone()); // symbol + insert_value_container.push(order.buy_order_id.to_string()); // order_id + insert_value_container.push(server_epoch.to_string()); // transact_time + insert_value_container.push(order.close_time.to_string()); // close_time + insert_value_container.push(String::from("FILLED")); // status + insert_value_container.push(rest_used_usdt.to_string()); // used_usdt + insert_value_container.push(0.0.to_string()); // expected_get_usdt + insert_value_container.push(0.0.to_string()); // expected_usdt_profit + insert_value_container.push(order.buy_price.to_string()); // buy_price + insert_value_container.push(0.0.to_string()); // current_price + insert_value_container.push(rest_base_qty.to_string()); // base_qty_ordered + insert_value_container.push(rest_base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted + insert_value_container.push(0.0.to_string()); // pure_profit_percent + insert_value_container.push(0.0.to_string()); // minimum_profit_percent + insert_value_container.push(0.0.to_string()); // maximum_profit_percent + insert_value_container.push(order.registerer.to_string()); // registerer + insert_value_container.push(order.is_long.to_string()); // is_long + + insert_values.push(insert_value_container.clone()); + insert_records(&insert_table_name, &insert_columns, &insert_values) + .await; + } + } + } else { + query_sell_order(&order, &client, exchange_info_vec, trade_fee_vec).await; + } + } else if T.get("code").is_some() { + // case that the order isn't canceled because the order completes while canceling + // update record in ordered_coin_list + query_sell_order(&order, &client, exchange_info_vec, trade_fee_vec).await; + } + } + Err(e) => { + println!("cancel sell order failed!: {}", body); + } + } + Ok(()) +} + +// cancel all open orders on a symbol (/api, Weight(IP) 1) +pub async fn cancel_all_order( + symbol: &String, + client: &Client, +) -> Result<(), Box> { + // building URL + let mut url = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + } + + let endpoint_url = "/api/v3/openOrders?"; + url.push_str(endpoint_url); + + // add parameters into URL + url.push_str("&symbol="); + url.push_str(symbol.as_str()); + url.push_str("×tamp="); + url.push_str(get_timestamp().await.as_str()); + + let res = client.delete(&url).send().await?; + + Ok(()) +} + +// Get all account orders; active, canceled, or filled. on a symbol (/api, Weight(IP) 10) +pub async fn all_orders( + symbol: &String, + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/allOrders?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&symbol); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("{}", body); + Ok(()) +} + +// query order and update record in buy_ordered_coin_list (/api, Weight(IP) 2) +pub async fn query_buy_order( + order: &BuyOrderedCoinList, + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&order.symbol); + url_build.push_str("&orderId="); + url_build.push_str(order.order_id.to_string().as_str()); + + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("query_buy_order: {}", body); + // deserialize JSON and then update record in table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("status").is_some() { + if T.get("status").unwrap().as_str().unwrap() == "FILLED" + || T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED" + { + let update_table_name = String::from("suggested_coin_list"); + let update_condition = vec![ + (String::from("symbol"), order.symbol.clone()), + (String::from("close_time"), order.close_time.to_string()), + ]; + let update_values: Vec<(String, String)> = + vec![(String::from("already_buy"), 1.to_string())]; + update_record2(&update_table_name, &update_values, &update_condition).await; + + // update values in [buy_ordered_coin_list] + let table_name = String::from("buy_ordered_coin_list"); + let mut value_build = String::from("\'"); + value_build.push_str(T.get("status").unwrap().as_str().unwrap()); + value_build.push('\''); + + // calculate values to be updated + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == order.symbol) + .unwrap() + .takercommission; + + let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( + T.get("executedQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_qty_fee_adjusted = + decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)); + + let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let base_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == order.symbol) + .unwrap() + .base_asset_precision; + let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered) + .round_dp_with_strategy(base_asset_precision, RoundingStrategy::ToZero); + + let update_values = vec![ + (String::from("status"), value_build), // status + (String::from("used_usdt"), cummulative_quote_qty.to_string()), // used_usdt + (String::from("buy_price"), buy_price.to_string()), // buy_price + ( + String::from("base_qty_ordered"), + base_qty_ordered.to_string(), + ), // base_qty_ordered + ( + String::from("base_qty_fee_adjusted"), + base_qty_fee_adjusted.to_string(), + ), // base_qty_fee_adjusted + ]; + let update_condition = vec![ + (String::from("order_id"), order.order_id.to_string()), + (String::from("symbol"), order.symbol.clone()), + ]; + update_record3(&table_name, &update_values, &update_condition) + .await + .unwrap(); + + if T.get("status").unwrap().as_str().unwrap() == "FILLED" { + println!(" buy coin 완료"); + // update available_usdt + if order.used_usdt > cummulative_quote_qty { + add_available_usdt(decimal_sub(order.used_usdt, cummulative_quote_qty)); + } else { + sub_available_usdt(decimal_sub(cummulative_quote_qty, order.used_usdt)); + } + } + } else if T.get("status").unwrap().as_str().unwrap() == "CANCELED" { + let update_table_name = String::from("suggested_coin_list"); + let update_condition = vec![ + (String::from("symbol"), order.symbol.clone()), + (String::from("close_time"), order.close_time.to_string()), + ]; + let update_values: Vec<(String, String)> = + vec![(String::from("already_buy"), 0.to_string())]; + } + } + } + Err(e) => { + println!("query order failed!: {}", body); + } + } + Ok(()) +} + +// query order and update record in sell_ordered_coin_list (/api, Weight(IP) 2) +pub async fn query_sell_order( + order: &SellOrderedCoinList, + client: &Client, + exchange_info_vec: &Vec, + trade_fee_vec: &Vec, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/order?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + + // add parameters into URL + url_build.push_str("&symbol="); + url_build.push_str(&order.symbol); + url_build.push_str("&orderId="); + url_build.push_str(order.sell_order_id.to_string().as_str()); + + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + // println!("query_sell_order실행 body: {}", body); + // deserialize JSON and then update record in table + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + if T.get("status").unwrap().as_str().unwrap() == "FILLED" + || T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED" + { + let quote_asset_precision = exchange_info_vec + .iter() + .find(|ExchangeInfo| ExchangeInfo.symbol == order.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_vec + .iter() + .find(|TradeFee| TradeFee.symbol == order.symbol) + .unwrap() + .takercommission; + let get_usdt = rust_decimal::prelude::FromStr::from_str( + T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let get_usdt_fee_adjusted = decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee)) + .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); + let ordered_base_qty = rust_decimal::prelude::FromStr::from_str( + T.get("executedQty").unwrap().as_str().unwrap(), + ) + .unwrap(); + let sell_price = decimal_div(get_usdt, ordered_base_qty) + .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); + let pure_profit_percent = decimal_mul( + decimal_sub(decimal_div(get_usdt_fee_adjusted, order.used_usdt), dec!(1)), + dec!(100), + ); + + let table_name = String::from("sell_ordered_coin_list"); + let mut value_build = String::from("\'"); + value_build.push_str(T.get("status").unwrap().as_str().unwrap()); + value_build.push('\''); + + let update_values = vec![ + (String::from("status"), value_build), + (String::from("get_usdt"), get_usdt.to_string()), + ( + String::from("get_usdt_fee_adjusted"), + get_usdt_fee_adjusted.to_string(), + ), + (String::from("sell_price"), sell_price.to_string()), + ( + String::from("pure_profit_percent"), + pure_profit_percent.to_string(), + ), + ]; + let update_condition = vec![(String::from("id"), order.id.to_string())]; + update_record3(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } + Err(e) => { + println!("query sell order failed!: {}", body); + } + } + Ok(()) +} + +// request current open orders (/api, Weight(IP) 40) +pub async fn current_open_orders( + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/openOrders?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + + // println!("msg: {}", body); + Ok(()) +} + +// request all information and status of current wallet. (/sapi, Weight(IP) 10) +// NOT FOR TESTNET +pub async fn all_coins_information( + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + + url.push_str(URL); + api_key = API_KEY.to_string(); + + let endpoint_url = "/sapi/v1/capital/config/getall?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await?; + + // deserialize JSON + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + let mut into_vec = T.as_array(); + if into_vec == None { + return Err("Err")?; + } + + let insert_table_name = String::from("wallet"); + let insert_columns = vec!["symbol", "free", "locked"]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + + for element in into_vec.unwrap() { + if element.is_object() { + insert_value_container.clear(); + insert_value_container + .push(element.get("coin").unwrap().as_str().unwrap().to_string()); + insert_value_container + .push(element.get("free").unwrap().as_str().unwrap().to_string()); + insert_value_container + .push(element.get("locked").unwrap().as_str().unwrap().to_string()); + insert_values.push(insert_value_container.clone()); + } + } + delete_all_rows(&insert_table_name).await; + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + } + Err(e) => { + println!("all coins information failed!: {}", body); + } + } + Ok(()) +} + +// request balances of all rest coins in the account. (/api, Weight(IP) 10) +pub async fn account_information( + client: &Client, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + let mut api_key = String::new(); + if RUNNING_MODE == TEST { + url.push_str(URL_TEST); + api_key = API_KEY_TESTNET.to_string(); + } else if RUNNING_MODE == REAL { + url.push_str(URL); + api_key = API_KEY.to_string(); + } + + let endpoint_url = "/api/v3/account?"; + url.push_str(endpoint_url); + + let mut url_build = String::new(); + hmac_signature(&mut url_build).await; + url.push_str(&url_build); + + let res = client + .get(&url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + let body = res.text_with_charset("utf-8").await.unwrap(); + + // deserialize JSON + let v = serde_json::from_str::(body.as_str()); + + match v { + Ok(T) => { + let option_result = T.get("balances"); + if option_result.is_none() { + return Err("Err")?; + } + let mut into_vec = option_result.unwrap().as_array(); + if into_vec == None { + return Err("Err")?; + } + + // insert contents into table + let mut insert_table_name = String::new(); + if RUNNING_MODE == TEST { + insert_table_name = String::from("wallet_testnet"); + } else if RUNNING_MODE == REAL { + insert_table_name = String::from("wallet"); + } + let insert_columns = vec!["asset", "free", "locked"]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + + for element in into_vec.unwrap() { + if element.is_object() { + insert_value_container.clear(); + insert_value_container + .push(element.get("asset").unwrap().as_str().unwrap().to_string()); + insert_value_container + .push(element.get("free").unwrap().as_str().unwrap().to_string()); + insert_value_container + .push(element.get("locked").unwrap().as_str().unwrap().to_string()); + insert_values.push(insert_value_container.clone()); + } + } + + delete_all_rows(&insert_table_name).await; + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + } + Err(e) => { + println!("account information failed!: {}", body); + } + } + Ok(()) +} + +async fn hmac_signature(query: &mut String) { + // fetch time information from [time] table + let table_name = String::from("time"); + let columns = String::from("*"); + let condition = None; + let mut time_info = TimeData { + server_epoch: 0, + local_epoch: 0, + epoch_difference: 0, + server_ymdhs: String::new(), + local_ymdhs: String::new(), + last_server_epoch: 0, + last_server_ymdhs: String::new(), + }; + + let select_result = select_record(&table_name, &columns, &condition, &time_info) + .await + .unwrap(); + let difference_epoch = select_result.first().unwrap().epoch_difference; + let server_epoch = select_result.first().unwrap().server_epoch; + + // build query message + // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤 + // 이를 timestamp로 사용한다. + let mut timestamp; + if difference_epoch >= 0 { + timestamp = (server_epoch as u128 + difference_epoch as u128).to_string(); + } else if difference_epoch < 0 { + timestamp = (server_epoch as u128 + (difference_epoch * -1) as u128).to_string(); + } else { + timestamp = server_epoch.to_string(); + } + let recv_window_size = "30000".to_string(); // default: 5,000ms, Max: 60,000ms + + let mut query_build = String::from("×tamp="); + query_build.push_str(×tamp); + query_build.push_str("&recvWindow="); + query_build.push_str(&recv_window_size); + + let mut secret_key = String::new(); + if RUNNING_MODE == TEST { + secret_key.push_str(SECRET_KEY_TESTNET); + } else if RUNNING_MODE == REAL { + secret_key.push_str(SECRET_KEY); + } + + query.push_str(&query_build); + let signature = HMAC::mac(&query.as_bytes(), secret_key.as_bytes()); + + query.push_str("&signature="); + query.push_str(signature.encode_hex::().as_str()); +} + +async fn get_timestamp() -> String { + // fetch time information from [time] table + let table_name = String::from("time"); + let columns = String::from("*"); + let condition = None; + let mut time_info = TimeData { + server_epoch: 0, + local_epoch: 0, + epoch_difference: 0, + server_ymdhs: String::new(), + local_ymdhs: String::new(), + last_server_epoch: 0, + last_server_ymdhs: String::new(), + }; + + let select_result = select_record(&table_name, &columns, &condition, &time_info) + .await + .unwrap(); + let difference_epoch = select_result.first().unwrap().epoch_difference; + let local_epoch = select_result.first().unwrap().local_epoch; + + // build query message + // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤 + // 이를 timestamp로 사용한다. + let mut timestamp; + if difference_epoch >= 0 { + timestamp = (local_epoch as u128 - difference_epoch as u128).to_string(); + } else if difference_epoch < 0 { + timestamp = (local_epoch as u128 - (difference_epoch * -1) as u128).to_string(); + } else { + timestamp = local_epoch.to_string(); + } + + timestamp +} + +pub async fn select_filled_buy_orders() -> Vec { + let select_table_name = String::from("buy_ordered_coin_list"); + let select_columns = String::from("*"); + let select_condition = Some(String::from("WHERE status = 'FILLED' or status = 'SIMUL'")); + let data_struct = BuyOrderedCoinList { + id: 0, + symbol: String::new(), + order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + expected_get_usdt: 0.0, + expected_usdt_profit: 0.0, + buy_price: Decimal::new(0, 8), + current_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + base_qty_fee_adjusted: Decimal::new(0, 8), + pure_profit_percent: 0.0, + minimum_profit_percent: 0.0, + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + + let select_result = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &data_struct, + ) + .await + .unwrap(); + + select_result +} + +// select open buy orders (NEW, Partially Filled) +async fn select_open_buy_orders() -> Vec { + let select_table_name = String::from("buy_ordered_coin_list"); + let select_columns = String::from("*"); + let select_condition = Some(String::from( + "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'", + )); + let data_struct = BuyOrderedCoinList { + id: 0, + symbol: String::new(), + order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + expected_get_usdt: 0.0, + expected_usdt_profit: 0.0, + buy_price: Decimal::new(0, 8), + current_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + base_qty_fee_adjusted: Decimal::new(0, 8), + pure_profit_percent: 0.0, + minimum_profit_percent: 0.0, + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + let select_result = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &data_struct, + ) + .await + .unwrap(); + + select_result +} + +pub async fn select_filled_sell_orders() -> Vec { + let select_table_name = String::from("sell_ordered_coin_list"); + let select_columns = String::from("*"); + let select_condition = Some(String::from("WHERE status = 'FILLED' or status = 'SIMUL'")); + let data_struct = SellOrderedCoinList { + id: 0, + symbol: String::new(), + buy_order_id: 0, + sell_order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + get_usdt: Decimal::new(0, 8), + get_usdt_fee_adjusted: Decimal::new(0, 8), + buy_price: Decimal::new(0, 8), + sell_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + pure_profit_percent: Decimal::new(0, 8), + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + + let select_result = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &data_struct, + ) + .await + .unwrap(); + + select_result +} + +// select open sell orders (NEW, Partially Filled) +async fn select_open_sell_orders() -> Vec { + let select_table_name = String::from("sell_ordered_coin_list"); + let select_columns = String::from("*"); + let select_condition = Some(String::from( + "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'", + )); + let data_struct = SellOrderedCoinList { + id: 0, + symbol: String::new(), + buy_order_id: 0, + sell_order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + get_usdt: Decimal::new(0, 8), + get_usdt_fee_adjusted: Decimal::new(0, 8), + buy_price: Decimal::new(0, 8), + sell_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + pure_profit_percent: Decimal::new(0, 8), + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + + let select_result = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &data_struct, + ) + .await + .unwrap(); + + select_result +} diff --git a/src/coex/strategy_team.rs b/src/coex/strategy_team.rs new file mode 100644 index 0000000..0279d09 --- /dev/null +++ b/src/coex/strategy_team.rs @@ -0,0 +1,6841 @@ +use crate::coex::exchange_team::*; +use crate::coex::order_team::*; +use crate::coin_health_check_team::request_others::CoinPriceData; +use crate::database_control::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::bollingerband::BollingerBandData; +use crate::value_estimation_team::indicators::ema::EmaData; +use crate::value_estimation_team::indicators::heatmap_volume::{ + heatmap_volume, HeatMapLevel, HeatmapVolumeData, +}; +use crate::value_estimation_team::indicators::macd::{ema_macd, EmaMacd}; +use crate::value_estimation_team::indicators::rsi::RsiData; +use crate::value_estimation_team::indicators::sma::SmaData; +use crate::value_estimation_team::indicators::stoch_rsi::{StochRsiDData, StochRsiKData}; +use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData}; +use csv::{DeserializeRecordsIter, StringRecord}; +use rust_decimal::prelude::ToPrimitive; +use rust_decimal::Decimal; +use serde::Deserialize; +use sqlx::FromRow; +use std::cmp::Ordering; +use tokio::time::{sleep, Duration, Instant}; + +use crate::signal_association::signal_decision::*; + +#[derive(PartialEq)] +pub enum MA { + Sma, + Ema, + Tema, +} + +pub struct AllData { + pub price_vec: Vec, + pub valid_symbol_vec: Vec, + + pub rt_price_1m_vec: Vec<(String, Vec)>, + pub rt_price_30m_vec: Vec<(String, Vec)>, + pub rt_price_1d_vec: Vec<(String, Vec)>, + pub rt_price_1w_vec: Vec<(String, Vec)>, + pub rt_price_1mon_vec: Vec<(String, Vec)>, + + pub sma3_1m_data: Vec<(String, Vec)>, + pub sma3_30m_data: Vec<(String, Vec)>, + pub sma3_1d_data: Vec<(String, Vec)>, + pub sma3_1w_data: Vec<(String, Vec)>, + pub sma3_1mon_data: Vec<(String, Vec)>, + + pub sma10_1m_data: Vec<(String, Vec)>, + pub sma10_30m_data: Vec<(String, Vec)>, + pub sma10_1d_data: Vec<(String, Vec)>, + pub sma10_1w_data: Vec<(String, Vec)>, + pub sma10_1mon_data: Vec<(String, Vec)>, + + pub sma30_1m_data: Vec<(String, Vec)>, + pub sma30_30m_data: Vec<(String, Vec)>, + pub sma30_1d_data: Vec<(String, Vec)>, + pub sma30_1w_data: Vec<(String, Vec)>, + pub sma30_1mon_data: Vec<(String, Vec)>, + + pub ema3_1m_data: Vec<(String, Vec)>, + pub ema3_30m_data: Vec<(String, Vec)>, + pub ema3_1d_data: Vec<(String, Vec)>, + pub ema3_1w_data: Vec<(String, Vec)>, + pub ema3_1mon_data: Vec<(String, Vec)>, + + pub ema10_1m_data: Vec<(String, Vec)>, + pub ema10_30m_data: Vec<(String, Vec)>, + pub ema10_1d_data: Vec<(String, Vec)>, + pub ema10_1w_data: Vec<(String, Vec)>, + pub ema10_1mon_data: Vec<(String, Vec)>, + + pub ema30_1m_data: Vec<(String, Vec)>, + pub ema30_30m_data: Vec<(String, Vec)>, + pub ema30_1d_data: Vec<(String, Vec)>, + pub ema30_1w_data: Vec<(String, Vec)>, + pub ema30_1mon_data: Vec<(String, Vec)>, + + pub rsi3_1m_data: Vec<(String, Vec)>, + pub rsi3_30m_data: Vec<(String, Vec)>, + pub rsi3_1d_data: Vec<(String, Vec)>, + pub rsi3_1w_data: Vec<(String, Vec)>, + pub rsi3_1mon_data: Vec<(String, Vec)>, + + pub rsi10_1m_data: Vec<(String, Vec)>, + pub rsi10_30m_data: Vec<(String, Vec)>, + pub rsi10_1d_data: Vec<(String, Vec)>, + pub rsi10_1w_data: Vec<(String, Vec)>, + pub rsi10_1mon_data: Vec<(String, Vec)>, + + pub rsi30_1m_data: Vec<(String, Vec)>, + pub rsi30_30m_data: Vec<(String, Vec)>, + pub rsi30_1d_data: Vec<(String, Vec)>, + pub rsi30_1w_data: Vec<(String, Vec)>, + pub rsi30_1mon_data: Vec<(String, Vec)>, + + pub stoch_rsi3_1m_3_k_data: Vec<(String, Vec)>, + pub stoch_rsi3_1m_3_d_data: Vec<(String, Vec)>, + pub stoch_rsi30_1m_30_k_data: Vec<(String, Vec)>, + pub stoch_rsi30_1m_30_d_data: Vec<(String, Vec)>, + + pub bb3_1m_data: Vec<(String, Vec)>, + pub bb3_30m_data: Vec<(String, Vec)>, + pub bb3_1d_data: Vec<(String, Vec)>, + pub bb3_1w_data: Vec<(String, Vec)>, + pub bb3_1mon_data: Vec<(String, Vec)>, + + pub bb10_1m_data: Vec<(String, Vec)>, + pub bb10_30m_data: Vec<(String, Vec)>, + pub bb10_1d_data: Vec<(String, Vec)>, + pub bb10_1w_data: Vec<(String, Vec)>, + pub bb10_1mon_data: Vec<(String, Vec)>, + + pub bb30_1m_data: Vec<(String, Vec)>, + pub bb30_30m_data: Vec<(String, Vec)>, + pub bb30_1d_data: Vec<(String, Vec)>, + pub bb30_1w_data: Vec<(String, Vec)>, + pub bb30_1mon_data: Vec<(String, Vec)>, +} +impl AllData { + pub fn new() -> AllData { + let a = AllData { + price_vec: Vec::new(), + valid_symbol_vec: Vec::new(), + rt_price_1m_vec: Vec::new(), + rt_price_30m_vec: Vec::new(), + rt_price_1d_vec: Vec::new(), + rt_price_1w_vec: Vec::new(), + rt_price_1mon_vec: Vec::new(), + + sma3_1m_data: Vec::new(), + sma3_30m_data: Vec::new(), + sma3_1d_data: Vec::new(), + sma3_1w_data: Vec::new(), + sma3_1mon_data: Vec::new(), + + sma10_1m_data: Vec::new(), + sma10_30m_data: Vec::new(), + sma10_1d_data: Vec::new(), + sma10_1w_data: Vec::new(), + sma10_1mon_data: Vec::new(), + + sma30_1m_data: Vec::new(), + sma30_30m_data: Vec::new(), + sma30_1d_data: Vec::new(), + sma30_1w_data: Vec::new(), + sma30_1mon_data: Vec::new(), + + ema3_1m_data: Vec::new(), + ema3_30m_data: Vec::new(), + ema3_1d_data: Vec::new(), + ema3_1w_data: Vec::new(), + ema3_1mon_data: Vec::new(), + + ema10_1m_data: Vec::new(), + ema10_30m_data: Vec::new(), + ema10_1d_data: Vec::new(), + ema10_1w_data: Vec::new(), + ema10_1mon_data: Vec::new(), + + ema30_1m_data: Vec::new(), + ema30_30m_data: Vec::new(), + ema30_1d_data: Vec::new(), + ema30_1w_data: Vec::new(), + ema30_1mon_data: Vec::new(), + + rsi3_1m_data: Vec::new(), + rsi3_30m_data: Vec::new(), + rsi3_1d_data: Vec::new(), + rsi3_1w_data: Vec::new(), + rsi3_1mon_data: Vec::new(), + + rsi10_1m_data: Vec::new(), + rsi10_30m_data: Vec::new(), + rsi10_1d_data: Vec::new(), + rsi10_1w_data: Vec::new(), + rsi10_1mon_data: Vec::new(), + + rsi30_1m_data: Vec::new(), + rsi30_30m_data: Vec::new(), + rsi30_1d_data: Vec::new(), + rsi30_1w_data: Vec::new(), + rsi30_1mon_data: Vec::new(), + + stoch_rsi3_1m_3_k_data: Vec::new(), + stoch_rsi3_1m_3_d_data: Vec::new(), + stoch_rsi30_1m_30_k_data: Vec::new(), + stoch_rsi30_1m_30_d_data: Vec::new(), + + bb3_1m_data: Vec::new(), + bb3_30m_data: Vec::new(), + bb3_1d_data: Vec::new(), + bb3_1w_data: Vec::new(), + bb3_1mon_data: Vec::new(), + + bb10_1m_data: Vec::new(), + bb10_30m_data: Vec::new(), + bb10_1d_data: Vec::new(), + bb10_1w_data: Vec::new(), + bb10_1mon_data: Vec::new(), + + bb30_1m_data: Vec::new(), + bb30_30m_data: Vec::new(), + bb30_1d_data: Vec::new(), + bb30_1w_data: Vec::new(), + bb30_1mon_data: Vec::new(), + }; + a + } +} + +#[derive(Debug, Deserialize, Clone)] +struct OpcloData { + price: f64, + server_epoch: u64, + usdt_volume: f64, + up_down: String, +} + +#[derive(Debug, Deserialize, Clone)] +struct PriceData { + opclo_price: f64, + open_price: f64, + close_price: f64, + high_price: f64, + low_price: f64, + close_time: u64, + quote_asset_volume: f64, + candle_type: String, +} + +#[derive(Debug, FromRow)] +pub struct TimeData { + pub server_epoch: u64, + pub local_epoch: u64, + pub epoch_difference: i64, + pub server_ymdhs: String, + pub local_ymdhs: String, + pub last_server_epoch: u64, + pub last_server_ymdhs: String, +} + +#[derive(Debug, FromRow)] +struct ServerEpoch { + server_epoch: u64, +} + +#[derive(Debug, FromRow)] +struct Record { + symbol: String, + suggested_price: Decimal, + close_time: u64, + registered_server_epoch: u64, + registerer: u16, + is_long: u8, +} + +pub async fn execute_strategists( + all_data: &AllData, +) -> Result<(), Box> { + // strategist_001(all_data).await; + // strategist_002(all_data).await; + // strategist_003(all_data).await; + // strategist_004(all_data).await; + // strategist_005(all_data).await; + // strategist_006(all_data).await; + // strategist_007().await; + // strategist_008().await; + // strategist_009().await; + + // strategist_010().await; + // strategist_011().await; + // strategist_012().await; + // strategist_013(all_data).await; + // strategist_014(all_data).await; + // strategist_015(all_data).await; + // strategist_016(all_data).await; + + execute_strategist_for_test(all_data).await; + // execute_strategist_for_test1(all_data).await; + // execute_strategist_for_test2(all_data).await; + Ok(()) +} + +// pub async fn execute_strategist_for_test(alldata: &AllData) -> Result<(), Box> { +// let inspect_table_name_1 = String::from("buy_ordered_coin_list"); +// let inspect_table_name_2 = String::from("sell_ordered_coin_list"); +// let inspect_table_name_3 = String::from("pre_suggested_coin_list"); +// let inspect_table_name_4 = String::from("suggested_coin_list"); + +// let market_cap_index = select_marketcap().await; + +// let mut filtered_symbols: Vec = Vec::new(); +// // filtered_symbols.push(String::from("BNBUSDT")); +// filtered_symbols.push(String::from("BTCUSDT")); +// // filtered_symbols.push(String::from("ETHUSDT")); +// // filtered_symbols.push(String::from("LTCUSDT")); +// // filtered_symbols.push(String::from("XRPUSDT")); +// // filtered_symbols.push(String::from("TRXUSDT")); +// let current_price = get_current_price(&filtered_symbols.first().unwrap(), &alldata.price_vec).await.unwrap(); + +// let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + +// let mut rsi3_1m_vec: Vec = Vec::new(); +// let mut rsi30_1m_vec: Vec = Vec::new(); +// let mut stoch_rsi3_1m_k_vec: Vec = Vec::new(); +// let mut stoch_rsi3_1m_d_vec: Vec = Vec::new(); +// let mut stoch_rsi30_1m_k_vec: Vec = Vec::new(); +// let mut stoch_rsi30_1m_d_vec: Vec = Vec::new(); +// let mut opclo_1m_vec: Vec = Vec::new(); +// let mut sma10_1m_vec: Vec = Vec::new(); +// let mut ema3_1m_vec: Vec = Vec::new(); +// let mut ema10_1m_vec: Vec = Vec::new(); +// let mut ema30_1m_vec: Vec = Vec::new(); +// let mut cnt = 0; + +// for symbol in filtered_symbols { +// let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); +// let rsi30_1m_option = alldata.rsi30_1m_data.iter().position(|x| *x.0 == *symbol); +// let stoch_rsi3_1m_k_option = alldata.stoch_rsi3_1m_3_k_data.iter().position(|x| *x.0 == *symbol); +// let stoch_rsi3_1m_d_option = alldata.stoch_rsi3_1m_3_d_data.iter().position(|x| *x.0 == *symbol); +// let stoch_rsi30_1m_k_option = alldata.stoch_rsi30_1m_30_k_data.iter().position(|x| *x.0 == *symbol); +// let stoch_rsi30_1m_d_option = alldata.stoch_rsi30_1m_30_d_data.iter().position(|x| *x.0 == *symbol); +// let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); +// let sma10_1m_option = alldata.sma10_1m_data.iter().position(|x| *x.0 == *symbol); +// let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); +// let ema10_1m_option = alldata.ema10_1m_data.iter().position(|x| *x.0 == *symbol); +// let ema30_1m_option = alldata.ema30_1m_data.iter().position(|x| *x.0 == *symbol); + +// if rsi3_1m_option.is_some() && rsi30_1m_option.is_some() && opclo_1m_option.is_some() && sma10_1m_option.is_some() && ema3_1m_option.is_some() && ema10_1m_option.is_some() && ema30_1m_option.is_some() && stoch_rsi3_1m_k_option.is_some() && stoch_rsi3_1m_d_option.is_some() && stoch_rsi30_1m_k_option.is_some() && stoch_rsi30_1m_d_option.is_some() { +// rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); +// rsi30_1m_vec = alldata.rsi30_1m_data[rsi30_1m_option.unwrap()].1.clone(); +// stoch_rsi3_1m_k_vec = alldata.stoch_rsi3_1m_3_k_data[stoch_rsi3_1m_k_option.unwrap()].1.clone(); +// stoch_rsi3_1m_d_vec = alldata.stoch_rsi3_1m_3_d_data[stoch_rsi3_1m_d_option.unwrap()].1.clone(); +// stoch_rsi30_1m_k_vec = alldata.stoch_rsi30_1m_30_k_data[stoch_rsi30_1m_k_option.unwrap()].1.clone(); +// stoch_rsi30_1m_d_vec = alldata.stoch_rsi30_1m_30_d_data[stoch_rsi30_1m_d_option.unwrap()].1.clone(); +// opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); +// sma10_1m_vec = alldata.sma10_1m_data[sma10_1m_option.unwrap()].1.clone(); +// ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); +// ema10_1m_vec = alldata.ema10_1m_data[ema10_1m_option.unwrap()].1.clone(); +// ema30_1m_vec = alldata.ema30_1m_data[ema30_1m_option.unwrap()].1.clone(); + +// if rsi3_1m_vec.len() >= 3 && opclo_1m_vec.len() >= 3 && sma10_1m_vec.len() >= 3 && ema3_1m_vec.len() >= 30 && ema10_1m_vec.len() >= 3 && ema30_1m_vec.len() >= 30 && stoch_rsi3_1m_k_vec.len() >= 5 && stoch_rsi3_1m_d_vec.len() >= 5 && stoch_rsi30_1m_k_vec.len() >= 5 && stoch_rsi30_1m_d_vec.len() >= 5 { +// opclo_1m_vec.reverse(); +// opclo_1m_vec.truncate(3); +// opclo_1m_vec.reverse(); +// stoch_rsi3_1m_k_vec.reverse(); +// stoch_rsi3_1m_k_vec.truncate(5); +// stoch_rsi3_1m_k_vec.reverse(); +// stoch_rsi3_1m_d_vec.reverse(); +// stoch_rsi3_1m_d_vec.truncate(5); +// stoch_rsi3_1m_d_vec.reverse(); +// stoch_rsi30_1m_k_vec.reverse(); +// stoch_rsi30_1m_k_vec.truncate(5); +// stoch_rsi30_1m_k_vec.reverse(); +// stoch_rsi30_1m_d_vec.reverse(); +// stoch_rsi30_1m_d_vec.truncate(5); +// stoch_rsi30_1m_d_vec.reverse(); +// let supertrend_vec = supertrend(&symbol, &alldata.rt_price_1m_vec, 60, 1.0, true).await; +// let macd_vec = ema_macd(&ema10_1m_vec, &ema30_1m_vec, 30).await?; +// let macd_search_result = macd_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&EmaMacd{macd_value, close_time}|close_time); + +// if macd_search_result.is_ok() { +// if macd_vec[macd_search_result.unwrap()].macd_value > 0.0 { +// //check duplicate records +// let mut exists_condition_build = String::from("symbol=\'"); +// exists_condition_build.push_str(symbol.as_str()); +// exists_condition_build.push_str("\' AND close_time="); +// exists_condition_build.push_str(opclo_1m_vec.last().unwrap().close_time.to_string().as_str()); +// let exists_condition = Some(exists_condition_build); + +// let inspect_result_1 = exists_record(&inspect_table_name_1, &exists_condition).await; +// let inspect_result_2 = exists_record(&inspect_table_name_2, &exists_condition).await; +// let inspect_result_3 = exists_record(&inspect_table_name_3, &exists_condition).await; +// let inspect_result_4 = exists_record(&inspect_table_name_4, &exists_condition).await; + +// if inspect_result_1 == false && inspect_result_2 == false && inspect_result_3 == false && inspect_result_4 == false { +// let mut sum_stoch_rsi30_1m_k = 0.0; +// for element in &stoch_rsi30_1m_k_vec { +// sum_stoch_rsi30_1m_k += element.stoch_rsi_k_value; +// } +// let average_stoch_rsi30_1m_k = sum_stoch_rsi30_1m_k / 5.0; + +// let ema3_3_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&EmaData{ema_value, close_time}|close_time); +// let ema3_2_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); +// let ema3_1_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&EmaData{ema_value, close_time}|close_time); +// let ema10_result = ema10_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&EmaData{ema_value, close_time}|close_time); +// let sma10_result = sma10_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&SmaData{sma_value, close_time}|close_time); +// if ema3_3_result.is_ok() && ema3_2_result.is_ok() && ema3_1_result.is_ok() && ema10_result.is_ok() && sma10_result.is_ok() { +// if sma10_1m_vec[sma10_result.unwrap()].sma_value < ema3_1m_vec[ema3_3_result.unwrap()].ema_value { +// let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); +// if k_result.is_ok() && k_prev_result.is_ok() { +// if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value +// && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value > 5.0 +// && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value < 80.0 +// && stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 96.0 +// && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0 { +// if current_price <= opclo_1m_vec[1].opclo_price * ((((opclo_1m_vec[1].high_price / opclo_1m_vec[1].low_price) - 1.0) / 2.0) + 1.0) { +// // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// } +// } +// } +// } + +// // if ema10_1m_vec[ema10_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value { +// // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema10_1m_vec[ema10_result.unwrap()].ema_value { +// // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema3_1m_vec[ema3_2_result.unwrap()].ema_value +// // && ema3_1m_vec[ema3_2_result.unwrap()].ema_value > ema3_1m_vec[ema3_1_result.unwrap()].ema_value +// // { +// // if average_stoch_rsi30_1m_k < 80.0 { +// // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let k_prev2_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); +// // if k_result.is_ok() && d_result.is_ok() && k_prev_result.is_ok() && k_prev2_result.is_ok() { +// // if +// // (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) +// // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) > 3.0 +// // && stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 96.0 +// // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev2_result.unwrap()].stoch_rsi_k_value) +// // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) < 25.0 +// // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) > 3.0 +// // // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0) +// // // (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value > 5.0) && +// // // ((stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value <= 80.0) +// // // || (((opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0) && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value))) +// // { +// // // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0 { +// // // if (opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 +// // // { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // // } else { +// // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // if opclo_1m_vec[1].candle_type == "DOWN" && opclo_1m_vec[1].opclo_price >= opclo_1m_vec[2].close_price { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } else if opclo_1m_vec[1].candle_type == "UP" { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // } +// // // else { +// // // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); +// // // if result.is_ok() { +// // // if rsi3_1m_vec[result.unwrap()].rsi_value < 20.0 && cnt >= 2 && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // // } +// // // } + +// // // } else { +// // // if cnt >= 2 { + +// // // let result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); +// // // match result { +// // // Ok(T) => { +// // // if ema3_1m_vec[T].ema_value > opclo_1m_vec[1].opclo_price { +// // // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); +// // // match result { +// // // Ok(T) => { +// // // if rsi3_1m_vec[T].rsi_value < 15.0 { +// // // let k_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // // let d_result = stoch_rsi10_1m_d_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + +// // // if k_result.is_ok() && d_result.is_ok() { +// // // if stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value <= stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value { +// // // if current_price < opclo_1m_vec[1].opclo_price { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // // } +// // // } +// // // } +// // // }, +// // // Err(E) => {} +// // // } +// // // } +// // // }, +// // // Err(E) => {} +// // // } +// // // } +// // // } +// // } +// // } else { +// // let k_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let k_prev_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let k_prev2_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let d_result = stoch_rsi10_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); +// // if k_result.is_ok() && d_result.is_ok() && k_prev_result.is_ok() && k_prev2_result.is_ok() { +// // if (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) +// // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) > 3.0 +// // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev2_result.unwrap()].stoch_rsi_k_value +// // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value +// // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value < 30.0 +// // && stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 95.0 +// // { +// // // if stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0 { +// // // if (opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 +// // // { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // // } else { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } +// // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // } +// // } +// // } +// // } +// // } +// // // else { +// // // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); +// // // if k_result.is_ok() && k_prev_result.is_ok() { +// // // if (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) +// // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) > 5.0 +// // // { +// // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // } + +// // // // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value < ema3_1m_vec[ema3_2_result.unwrap()].ema_value { +// // // // if opclo_1m_vec[1].candle_type == "DOWN" && opclo_1m_vec[1].open_price > ema3_1m_vec[ema3_2_result.unwrap()].ema_value +// // // // && opclo_1m_vec[2].open_price < ema3_1m_vec[ema3_3_result.unwrap()].ema_value +// // // // && opclo_1m_vec[2].high_price < ema3_1m_vec[ema3_3_result.unwrap()].ema_value +// // // // && opclo_1m_vec[2].candle_type == "UP" { +// // // // if (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) +// // // // { +// // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // // } +// // // // } +// // // // } else if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema3_1m_vec[ema3_2_result.unwrap()].ema_value +// // // // { +// // // // if opclo_1m_vec[1].candle_type == "UP" +// // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) { +// // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // // // } +// // // // } +// // // } +// // // } +// // } +// } +// } +// } +// // else if macd_vec[macd_search_result.unwrap()].macd_value > macd_vec[macd_search_result.unwrap()-1].macd_value && macd_vec[macd_search_result.unwrap()].macd_value > -3.0{ +// // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); +// // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); +// // if k_result.is_ok() && k_prev_result.is_ok() && d_result.is_ok() { +// // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value +// // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value +// // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0{ +// // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // } +// // } +// // } +// } + +// } +// } +// } + +// // let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) +// // if market_cap_index.first().unwrap().negative_buy_signal == 1 || (market_cap_index.first().unwrap().liquidation_signal == 0 && market_cap_index.first().unwrap().market_cap_index <= 20.0 && market_cap_index.first().unwrap().market_cap_index >= 0.0) { +// // let mut rsi3_1m_vec: Vec = Vec::new(); +// // let mut opclo_1m_vec: Vec = Vec::new(); +// // let mut ema3_1m_vec: Vec = Vec::new(); +// // let mut cnt = 0; +// // for symbol in filtered_symbols { +// // let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); +// // let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); +// // let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + +// // if rsi3_1m_option.is_some() && opclo_1m_option.is_some() && ema3_1m_option.is_some() { +// // rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); +// // opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); +// // ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + +// // if rsi3_1m_vec.len() >= 3 && opclo_1m_vec.len() >= 3 && ema3_1m_vec.len() >= 3 { +// // opclo_1m_vec.reverse(); +// // opclo_1m_vec.truncate(3); +// // opclo_1m_vec.reverse(); + +// // if opclo_1m_vec.last().unwrap().candle_type == "UP" { +// // cnt = 0; +// // for element in &opclo_1m_vec { +// // if element.candle_type == "DOWN" { +// // cnt += 1; +// // } +// // } + +// // if cnt >= 2 { +// // let result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if ema3_1m_vec[T].ema_value > opclo_1m_vec[1].opclo_price { +// // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if rsi3_1m_vec[T].rsi_value < 30.0 { +// // if current_price < opclo_1m_vec[1].opclo_price { +// // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } + +// // } + +// // } +// // } +// // } +// // } else if market_cap_index.first().unwrap().liquidation_signal == 1 && market_cap_index.first().unwrap().market_cap_index < - 10.0 { +// // let mut rsi3_1m_vec: Vec = Vec::new(); +// // let mut rsi10_1m_vec: Vec = Vec::new(); +// // let mut rsi30_1m_vec: Vec = Vec::new(); +// // let mut op_low_1m_vec: Vec = Vec::new(); +// // let mut ema3_1m_vec: Vec = Vec::new(); +// // let mut bb30_1m_vec: Vec = Vec::new(); +// // let mut cnt = 0; +// // for symbol in filtered_symbols { +// // let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); +// // let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); +// // let rsi30_1m_option = alldata.rsi30_1m_data.iter().position(|x| *x.0 == *symbol); +// // let op_low_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); +// // let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); +// // let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + +// // if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() && rsi30_1m_option.is_some() && op_low_1m_option.is_some() && ema3_1m_option.is_some() && bb30_1m_option.is_some() { +// // rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); +// // rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); +// // rsi30_1m_vec = alldata.rsi30_1m_data[rsi30_1m_option.unwrap()].1.clone(); +// // op_low_1m_vec = alldata.rt_price_1m_vec[op_low_1m_option.unwrap()].1.clone(); +// // ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); +// // bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + +// // if rsi3_1m_vec.len() >= 3 && rsi10_1m_vec.len() >= 3 && rsi30_1m_vec.len() >= 3 && op_low_1m_vec.len() >= 3 && ema3_1m_vec.len() >= 3 && bb30_1m_vec.len() >= 3 { +// // op_low_1m_vec.reverse(); +// // op_low_1m_vec.truncate(5); +// // op_low_1m_vec.reverse(); + +// // if op_low_1m_vec.last().unwrap().candle_type == "DOWN" { +// // cnt = 0; +// // for element in &op_low_1m_vec { +// // if element.candle_type == "DOWN" { +// // cnt += 1; +// // } +// // } + +// // if cnt >= 4 { +// // let result = ema3_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if ema3_1m_vec[T].ema_value > (op_low_1m_vec.last().unwrap().open_price + op_low_1m_vec.last().unwrap().low_price) / 2.0 { +// // let result = rsi3_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if rsi3_1m_vec[T].rsi_value < 10.0 { +// // let result = rsi10_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if rsi10_1m_vec[T].rsi_value < 15.0 { +// // let result = rsi30_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if rsi30_1m_vec[T].rsi_value < 35.0 { +// // let result = bb30_1m_vec.binary_search_by_key(&op_low_1m_vec.first().unwrap().close_time, |&BollingerBandData{sma, upperband, lowerband, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if bb30_1m_vec[T].lowerband > (op_low_1m_vec.last().unwrap().open_price + op_low_1m_vec.last().unwrap().low_price) / 2.0 { +// // filtered_2nd_symbols.push((symbol, op_low_1m_vec.last().unwrap().close_time)); +// // } +// // }, +// // Err(E) => {}, +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // }, +// // Err(E) => {} +// // } +// // } + +// // } + +// // } +// // } +// // } +// // } + +// insert_pre_suggested_coins(1, &filtered_2nd_symbols, alldata).await; + +// Ok(()) +// } + +pub async fn execute_strategist_for_test( + alldata: &AllData, +) -> Result<(), Box> { + // 1st filtering: supertrend(ATR period 10, multiplier: 1.3, 30m close price), the area should be in SELL area. + let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + let mut opclo_30m_vec: Vec = Vec::new(); + let mut supertrend_vec: Vec = Vec::new(); + for symbol in &alldata.valid_symbol_vec { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let supertrend_option_30m = + supertrend(&symbol, &alldata.rt_price_30m_vec, 10, 1.3, true).await; + + if opclo_30m_option.is_some() && supertrend_option_30m.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + supertrend_vec = supertrend_option_30m.unwrap(); + + if opclo_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 { + let supertrend_search_result = supertrend_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |SupertrendData { + band_value, + signal, + area, + close_time, + }| *close_time, + ); + if supertrend_search_result.is_ok() { + if supertrend_vec[supertrend_search_result.unwrap()] + .area + .contains("DOWN") + { + filtered_2nd_symbols + .push((symbol.clone(), opclo_30m_vec.last().unwrap().close_time)); + } + } + } + } + } + + // 2nd filtering: lookup tables if the tradepair is already there + let inspect_table_name_1 = String::from("buy_ordered_coin_list"); + let inspect_table_name_2 = String::from("sell_ordered_coin_list"); + let inspect_table_name_3 = String::from("pre_suggested_coin_list"); + let inspect_table_name_4 = String::from("suggested_coin_list"); + let mut filtered_3rd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + + for element in filtered_2nd_symbols { + let mut exists_condition_build = String::from("symbol=\'"); + exists_condition_build.push_str(element.0.as_str()); + exists_condition_build.push_str("\' AND close_time="); + exists_condition_build.push_str(element.1.to_string().as_str()); + let exists_condition = Some(exists_condition_build); + + let inspect_result_1 = exists_record(&inspect_table_name_1, &exists_condition).await; + let inspect_result_2 = exists_record(&inspect_table_name_2, &exists_condition).await; + let inspect_result_3 = exists_record(&inspect_table_name_3, &exists_condition).await; + let inspect_result_4 = exists_record(&inspect_table_name_4, &exists_condition).await; + + if inspect_result_1 == false + && inspect_result_2 == false + && inspect_result_3 == false + && inspect_result_4 == false + { + filtered_3rd_symbols.push(element); + } + } + + // 3rd filtering: BollingerBand (length 10, stddev: 3.0, 30m close price) the current price should be under the lowerband of BB. + let mut bb10_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for element in filtered_3rd_symbols { + let bb10_30m_option = alldata.bb10_30m_data.iter().position(|x| *x.0 == element.0); + + if bb10_30m_option.is_some() { + bb10_30m_vec = alldata.bb10_30m_data[bb10_30m_option.unwrap()].1.clone(); + + if bb10_30m_vec.len() >= 3 { + let bb_search_result = bb10_30m_vec.binary_search_by_key( + &element.1, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + if bb_search_result.is_ok() { + let current_price = get_current_price(&element.0, &alldata.price_vec) + .await + .unwrap(); + if bb10_30m_vec[bb_search_result.unwrap()].lowerband > current_price { + filtered_4th_symbols.push(element); + } + } + } + } + } + + // 4th filtering: RSI (length: 10, 30m close price) the current index should be lower than 30. + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut filtered_5th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for element in filtered_4th_symbols { + let rsi10_30m_option = alldata + .rsi10_30m_data + .iter() + .position(|x| *x.0 == element.0); + + if rsi10_30m_option.is_some() { + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + + if rsi10_30m_vec.len() >= 3 { + let rsi_search_result = rsi10_30m_vec.binary_search_by_key( + &element.1, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + if rsi_search_result.is_ok() { + if rsi10_30m_vec[rsi_search_result.unwrap()].rsi_value <= 30.0 { + filtered_5th_symbols.push(element); + } + } + } + } + } + + // 5th filtering: heatmap volume(MA length 10, std length 10, 30m close price), the current candle should be over than high at least. + let mut filtered_6th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for element in filtered_5th_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == element.0); + if opclo_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if opclo_30m_vec.len() >= 3 { + let heatmap_volume_option = heatmap_volume( + &element.0, + &alldata.rt_price_30m_vec, + 10, + 10, + 4.0, + 2.5, + 1.0, + -0.5, + ) + .await; + if heatmap_volume_option.is_some() { + let heatmap_volume_vec = heatmap_volume_option.unwrap(); + let heatmap_search_result = heatmap_volume_vec.binary_search_by_key( + &element.1, + |HeatmapVolumeData { + heatmap_value, + heatmap_level, + close_time, + }| *close_time, + ); + if heatmap_search_result.is_ok() { + if heatmap_volume_vec[heatmap_search_result.unwrap()].heatmap_level + == HeatMapLevel::Medium + || heatmap_volume_vec[heatmap_search_result.unwrap()].heatmap_level + == HeatMapLevel::High + || heatmap_volume_vec[heatmap_search_result.unwrap()].heatmap_level + == HeatMapLevel::ExtraHigh + { + filtered_6th_symbols.push(element); + } + } + } + } + } + } + + // 6th filtering condition: MACD + // let mut opclo_30m_vec: Vec = Vec::new(); + // let mut ema3_1d_vec: &Vec = &Vec::new(); + // let mut ema10_1d_vec: &Vec = &Vec::new(); + + // let mut filtered_7th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + // for element in filtered_6th_symbols { + // let ema3_1d_option = alldata.ema3_1d_data.iter().position(|x| *x.0 == *element.0); + // let ema10_1d_option = alldata.ema10_1d_data.iter().position(|x| *x.0 == *element.0); + + // if ema3_1d_option.is_some() && ema10_1d_option.is_some() { + // ema3_1d_vec = &alldata.ema3_1d_data[ema3_1d_option.unwrap()].1; + // ema10_1d_vec = &alldata.ema10_1d_data[ema10_1d_option.unwrap()].1; + + // if ema3_1d_vec.len() > 20 && ema10_1d_vec.len() > 20 { + // let macd_vec = ema_macd(&ema3_1d_vec, &ema10_1d_vec, 10).await?; + // // let macd_search_result = macd_vec.binary_search_by_key(&element.1, |&EmaMacd{macd_value, close_time}|close_time); + + // // if macd_search_result.is_ok() { + // if macd_vec.last().unwrap().macd_value - macd_vec[macd_vec.len() -2].macd_value >= 0.0 { + // filtered_7th_symbols.push(element); + // } + // // } + // } + // } + // } + + insert_pre_suggested_coins(1, &filtered_6th_symbols, alldata).await; + + Ok(()) +} + +pub async fn execute_strategist_for_test1( + alldata: &AllData, +) -> Result<(), Box> { + let inspect_table_name_1 = String::from("buy_ordered_coin_list"); + let inspect_table_name_2 = String::from("sell_ordered_coin_list"); + let inspect_table_name_3 = String::from("pre_suggested_coin_list"); + let inspect_table_name_4 = String::from("suggested_coin_list"); + + let market_cap_index = select_marketcap().await; + + let mut filtered_symbols: Vec = Vec::new(); + // filtered_symbols.push(String::from("BNBUSDT")); + filtered_symbols.push(String::from("BTCUSDT")); + // filtered_symbols.push(String::from("ETHUSDT")); + // filtered_symbols.push(String::from("LTCUSDT")); + // filtered_symbols.push(String::from("XRPUSDT")); + // filtered_symbols.push(String::from("TRXUSDT")); + // let current_price = get_current_price(&filtered_symbols.first().unwrap(), &alldata.price_vec).await.unwrap(); + + let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + let mut ema3_1m_vec: &Vec = &Vec::new(); + let mut ema3_30m_vec: &Vec = &Vec::new(); + let mut ema10_1m_vec: &Vec = &Vec::new(); + let mut ema30_1m_vec: &Vec = &Vec::new(); + let mut sma10_1m_vec: &Vec = &Vec::new(); + let mut sma3_30m_vec: &Vec = &Vec::new(); + let mut sma30_1m_vec: &Vec = &Vec::new(); + let mut rsi3_1m_vec: &Vec = &Vec::new(); + let mut rsi30_1m_vec: &Vec = &Vec::new(); + let mut stoch_rsi3_1m_k_vec: &Vec = &Vec::new(); + let mut stoch_rsi3_1m_d_vec: &Vec = &Vec::new(); + let mut stoch_rsi30_1m_k_vec: &Vec = &Vec::new(); + let mut stoch_rsi30_1m_d_vec: &Vec = &Vec::new(); + let mut rt_price_1m_vec: &Vec = &Vec::new(); + let mut is_filtering_passed: bool = false; + + let mut cnt = 0; + let exists_condition = Some(String::from("used_usdt >= 10.0 AND registerer = 1")); + let inspect_result_1 = exists_record(&inspect_table_name_1, &exists_condition).await; + let inspect_result_2 = exists_record(&inspect_table_name_2, &exists_condition).await; + let mut is_buy_period_30m = false; + + if inspect_result_1 == false && inspect_result_2 == false { + for symbol in filtered_symbols { + is_filtering_passed = false; + let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let ema10_1m_option = alldata.ema10_1m_data.iter().position(|x| *x.0 == *symbol); + let ema30_1m_option = alldata.ema30_1m_data.iter().position(|x| *x.0 == *symbol); + let sma10_1m_option = alldata.sma10_1m_data.iter().position(|x| *x.0 == *symbol); + let sma3_30m_option = alldata.sma3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_1m_option = alldata.sma30_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi30_1m_option = alldata.rsi30_1m_data.iter().position(|x| *x.0 == *symbol); + let stoch_rsi3_1m_k_option = alldata + .stoch_rsi3_1m_3_k_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi3_1m_d_option = alldata + .stoch_rsi3_1m_3_d_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi30_1m_k_option = alldata + .stoch_rsi30_1m_30_k_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi30_1m_d_option = alldata + .stoch_rsi30_1m_30_d_data + .iter() + .position(|x| *x.0 == *symbol); + let rt_price_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + + let supertrend_option_30m = + supertrend(&symbol, &alldata.rt_price_30m_vec, 3, 0.5, true).await; + + if supertrend_option_30m.is_some() { + let supertrend_vec = supertrend_option_30m.unwrap(); + if supertrend_vec.last().unwrap().area.contains("UP") { + is_buy_period_30m = true; + } + } + + if is_buy_period_30m == true + && ema3_1m_option.is_some() + && ema3_30m_option.is_some() + && ema10_1m_option.is_some() + && ema30_1m_option.is_some() + && sma10_1m_option.is_some() + && sma3_30m_option.is_some() + && sma30_1m_option.is_some() + && rsi3_1m_option.is_some() + && rsi30_1m_option.is_some() + && stoch_rsi3_1m_k_option.is_some() + && stoch_rsi3_1m_d_option.is_some() + && stoch_rsi30_1m_k_option.is_some() + && stoch_rsi30_1m_d_option.is_some() + && rt_price_1m_option.is_some() + { + ema3_1m_vec = &alldata.ema3_1m_data[ema3_1m_option.unwrap()].1; + ema3_30m_vec = &alldata.ema3_30m_data[ema3_30m_option.unwrap()].1; + ema10_1m_vec = &alldata.ema10_1m_data[ema10_1m_option.unwrap()].1; + ema30_1m_vec = &alldata.ema30_1m_data[ema30_1m_option.unwrap()].1; + sma10_1m_vec = &alldata.sma10_1m_data[sma10_1m_option.unwrap()].1; + sma3_30m_vec = &alldata.sma3_30m_data[sma3_30m_option.unwrap()].1; + sma30_1m_vec = &alldata.sma30_1m_data[sma30_1m_option.unwrap()].1; + rsi3_1m_vec = &alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1; + rsi30_1m_vec = &alldata.rsi30_1m_data[rsi30_1m_option.unwrap()].1; + stoch_rsi3_1m_k_vec = + &alldata.stoch_rsi3_1m_3_k_data[stoch_rsi3_1m_k_option.unwrap()].1; + stoch_rsi3_1m_d_vec = + &alldata.stoch_rsi3_1m_3_d_data[stoch_rsi3_1m_d_option.unwrap()].1; + stoch_rsi30_1m_k_vec = + &alldata.stoch_rsi30_1m_30_k_data[stoch_rsi30_1m_k_option.unwrap()].1; + stoch_rsi30_1m_d_vec = + &alldata.stoch_rsi30_1m_30_d_data[stoch_rsi30_1m_d_option.unwrap()].1; + rt_price_1m_vec = &alldata.rt_price_1m_vec[rt_price_1m_option.unwrap()].1; + + // filtering condition: data are ready + if ema3_1m_vec.len() >= 3 + && ema3_30m_vec.len() >= 3 + && ema10_1m_vec.len() >= 3 + && ema30_1m_vec.len() >= 3 + && sma10_1m_vec.len() >= 3 + && sma3_30m_vec.len() >= 3 + && sma30_1m_vec.len() >= 3 + && rsi3_1m_vec.len() >= 3 + && rsi30_1m_vec.len() >= 3 + && stoch_rsi3_1m_k_vec.len() >= 5 + && stoch_rsi3_1m_d_vec.len() >= 5 + && stoch_rsi30_1m_k_vec.len() >= 5 + && stoch_rsi30_1m_d_vec.len() >= 5 + && rt_price_1m_vec.len() >= 50 + { + is_filtering_passed = true; + } + + // // filtering condition: SuperTrend & EMA & SMA + // if is_filtering_passed == true { + // let supertrend_option = supertrend(&symbol, &alldata.rt_price_1m_vec, 10, 1.0, false).await; + // if supertrend_option.is_some() { + // let supertrend_vec = supertrend_option.unwrap(); + // let supertrend_result = supertrend_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |SupertrendData{band_value, signal, area, close_time}| *close_time); + // let ema3_result = ema3_1m_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // let sma10_result = sma10_1m_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&SmaData{sma_value, close_time}|close_time); + + // if supertrend_vec.last().unwrap().signal.is_some() && ema3_result.is_ok() && sma10_result.is_ok() { + // if (((supertrend_vec.last().unwrap().signal.as_ref().unwrap().contains("BUY") && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value) && rt_price_1m_vec.last().unwrap().close_price <= supertrend_vec[supertrend_vec.len()-2].band_value * 1.0005) + // || (supertrend_vec.last().unwrap().area.contains("UP") && ema3_1m_vec[ema3_result.unwrap()-1].ema_value < sma10_1m_vec[sma10_result.unwrap()-1].sma_value && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value)) + // && sma10_1m_vec[sma10_result.unwrap()-2].sma_value < sma10_1m_vec[sma10_result.unwrap()].sma_value { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } + + // filtering condition: SuperTrend + if is_filtering_passed == true { + let supertrend_option = + supertrend(&symbol, &alldata.rt_price_1m_vec, 30, 1.5, false).await; + if supertrend_option.is_some() { + let supertrend_vec = supertrend_option.unwrap(); + let supertrend_result = supertrend_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |SupertrendData { + band_value, + signal, + area, + close_time, + }| *close_time, + ); + + if supertrend_result.is_ok() { + if supertrend_vec[supertrend_result.unwrap()] + .signal + .as_ref() + .is_some() + { + if supertrend_vec[supertrend_result.unwrap()] + .signal + .as_ref() + .unwrap() + .contains("BUY") + && supertrend_vec[supertrend_result.unwrap() - 1].band_value + + ((rt_price_1m_vec[rt_price_1m_vec.len() - 2].high_price + - rt_price_1m_vec[rt_price_1m_vec.len() - 2].low_price) + * 0.1) + > rt_price_1m_vec.last().unwrap().close_price + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: SMA & EMA + if is_filtering_passed == true { + let sma10_1m_result = sma10_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + let sma30_1m_result = sma30_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + let ema3_1m_result = ema3_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema3_30m_result = ema3_30m_vec.binary_search_by_key( + &sma3_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema10_1m_result = ema10_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema30_1m_result = ema30_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + if sma10_1m_result.is_ok() + && sma30_1m_result.is_ok() + && ema3_1m_result.is_ok() + && ema10_1m_result.is_ok() + && ema30_1m_result.is_ok() + && ema3_30m_result.is_ok() + { + if ema3_1m_vec[ema3_1m_result.unwrap()].ema_value + >= ema10_1m_vec[ema10_1m_result.unwrap()].ema_value + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: current price limitation + // if is_filtering_passed == true { + // if rt_price_1m_vec[rt_price_1m_vec.len()-2].close_price + ((rt_price_1m_vec[rt_price_1m_vec.len()-2].high_price - rt_price_1m_vec[rt_price_1m_vec.len()-2].low_price) * 0.1) > rt_price_1m_vec.last().unwrap().close_price { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } + + // filtering condition: Heatmap Volume + // if is_filtering_passed == true { + // let heatmap_volume_option = heatmap_volume(&symbol, &alldata.rt_price_1m_vec, 30, 30, 4.0, 2.5, 1.0, -0.5).await; + // if heatmap_volume_option.is_some() { + // let heatmap_volume_vec = heatmap_volume_option.unwrap(); + // let heatmap_search_result = heatmap_volume_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |HeatmapVolumeData{heatmap_value, heatmap_level, close_time}|*close_time); + // if heatmap_search_result.is_ok() { + // let heatmap_volume_trunc_vec_option = heatmap_volume_vec.get(heatmap_search_result.unwrap()-11..heatmap_search_result.unwrap()-1); + // if heatmap_volume_trunc_vec_option.is_some() { + // let heatmap_volume_trunc_vec = heatmap_volume_trunc_vec_option.unwrap(); + // let mut heat_value_summation = 0.0; + // for element in heatmap_volume_trunc_vec { + // if element.heatmap_level == HeatMapLevel::ExtraHigh { + // heat_value_summation += 2.0; + // } else if element.heatmap_level == HeatMapLevel::High { + // heat_value_summation += 1.5; + // } else if element.heatmap_level == HeatMapLevel::Medium { + // heat_value_summation += 1.0; + // } else if element.heatmap_level == HeatMapLevel::Normal { + // heat_value_summation += 0.5; + // } else { + // heat_value_summation += 0.0; + // } + // } + // if heat_value_summation >= 2.0 { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } + // } + // } else { is_filtering_passed = false; } + // } + + // filtering condition: Stoch RSI + if is_filtering_passed == true { + // let sample_length: usize = 5; + // stoch_rsi10_1m_k_vec.reverse(); + // stoch_rsi10_1m_k_vec.truncate(5); + // stoch_rsi10_1m_k_vec.reverse(); + // stoch_rsi10_1m_d_vec.reverse(); + // stoch_rsi10_1m_d_vec.truncate(5); + // stoch_rsi10_1m_d_vec.reverse(); + let rsi3_k_result = stoch_rsi3_1m_k_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&StochRsiKData { + stoch_rsi_k_value, + close_time, + }| close_time, + ); + let rsi3_d_result = stoch_rsi3_1m_d_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&StochRsiDData { + stoch_rsi_d_value, + close_time, + }| close_time, + ); + let rsi30_k_result = stoch_rsi30_1m_k_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&StochRsiKData { + stoch_rsi_k_value, + close_time, + }| close_time, + ); + let rsi30_d_result = stoch_rsi30_1m_d_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&StochRsiDData { + stoch_rsi_d_value, + close_time, + }| close_time, + ); + if rsi30_k_result.is_ok() + && rsi30_d_result.is_ok() + && rsi3_k_result.is_ok() + && rsi3_d_result.is_ok() + { + if stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value + >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap() - 1].stoch_rsi_k_value + >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap() - 1] + .stoch_rsi_d_value + && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value + <= 50.0 + // (stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + // && stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value > stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()-1].stoch_rsi_d_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + // || + // && (stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value <= 60.0 + // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-2].stoch_rsi_k_value >= stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value + // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value <= stoch_rsi3_1m_d_vec[rsi3_d_result.unwrap()-1].stoch_rsi_d_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()-1].stoch_rsi_k_value <= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()-1].stoch_rsi_d_value) + // // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + // || (stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value == 100.0 + // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value == 100.0 + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: MACD + if is_filtering_passed == true { + let macd_vec = ema_macd(&ema3_1m_vec, &ema10_1m_vec, 10).await?; + let macd_search_result = macd_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaMacd { + macd_value, + close_time, + }| close_time, + ); + + if macd_search_result.is_ok() { + if macd_vec[macd_search_result.unwrap()].macd_value + > macd_vec[macd_search_result.unwrap() - 1].macd_value + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: Ratio between amplitude and body of candles + if is_filtering_passed == true { + let sample_length: usize = 5; // 5 candle samsples + let rt_price_1m_partial_vec_option = rt_price_1m_vec + .get(rt_price_1m_vec.len() - sample_length - 1..rt_price_1m_vec.len() - 1); + if rt_price_1m_partial_vec_option.is_some() { + let rt_price_1m_partial_vec = rt_price_1m_partial_vec_option.unwrap(); + + let mut sum_ratio_amp_body = 0.0; + for element in rt_price_1m_partial_vec { + sum_ratio_amp_body += (element.close_price - element.open_price).abs() + / (element.high_price - element.low_price); + } + let average_ratio_amp_body = sum_ratio_amp_body / sample_length as f64; + if average_ratio_amp_body > 0.3 { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // check all filterings are passed + if is_filtering_passed == true { + filtered_2nd_symbols.push((symbol, rt_price_1m_vec.last().unwrap().close_time)); + } + + // if ema3_1m_vec.len() >= 3 && ema10_1m_vec.len() >= 3 && ema30_1m_vec.len() >= 3 && sma10_1m_vec.len() >= 3 && rsi3_1m_vec.len() >= 3 && stoch_rsi10_1m_k_vec.len() >= 5 && stoch_rsi10_1m_d_vec.len() >= 5 && stoch_rsi30_1m_k_vec.len() >= 5 && stoch_rsi30_1m_d_vec.len() >= 5 && rt_price_1m_vec.len() >= 10 { + // stoch_rsi10_1m_k_vec.reverse(); + // stoch_rsi10_1m_k_vec.truncate(5); + // stoch_rsi10_1m_k_vec.reverse(); + // stoch_rsi10_1m_d_vec.reverse(); + // stoch_rsi10_1m_d_vec.truncate(5); + // stoch_rsi10_1m_d_vec.reverse(); + // stoch_rsi30_1m_k_vec.reverse(); + // stoch_rsi30_1m_k_vec.truncate(5); + // stoch_rsi30_1m_k_vec.reverse(); + // stoch_rsi30_1m_d_vec.reverse(); + // stoch_rsi30_1m_d_vec.truncate(5); + // stoch_rsi30_1m_d_vec.reverse(); + // let supertrend_option = supertrend(&symbol, &alldata.rt_price_1m_vec, 10, 1.0, false).await; + // let heatmap_volume_option = heatmap_volume(&symbol, &alldata.rt_price_1m_vec, 30, 30, 4.0, 2.5, 1.0, -0.5).await; + // let macd_vec = ema_macd(&ema10_1m_vec, &ema30_1m_vec, 30).await?; + + // if supertrend_option.is_some() && heatmap_volume_option.is_some() { + // let supertrend_vec = supertrend_option.unwrap(); + // let heatmap_volume_vec = heatmap_volume_option.unwrap(); + // let heatmap_search_result = heatmap_volume_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |HeatmapVolumeData{heatmap_value, heatmap_level, close_time}|*close_time); + // let macd_search_result = macd_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&EmaMacd{macd_value, close_time}|close_time); + // let ema3_result = ema3_1m_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // let ema10_result = ema10_1m_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // let ema30_result = ema30_1m_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // let sma10_result = sma10_1m_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&SmaData{sma_value, close_time}|close_time); + + // if supertrend_vec.last().unwrap().signal.is_some() && heatmap_search_result.is_ok() && macd_search_result.is_ok() && ema3_result.is_ok() && ema10_result.is_ok() && ema30_result.is_ok() && sma10_result.is_ok() { + // // if supertrend_vec.last().unwrap().signal.as_ref().unwrap().contains("BUY") + // // && macd_vec[macd_search_result.unwrap()].macd_value > macd_vec[macd_search_result.unwrap()-1].macd_value { + // // { + // if ((supertrend_vec.last().unwrap().signal.as_ref().unwrap().contains("BUY") && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value) && current_price <= supertrend_vec[supertrend_vec.len()-2].band_value * 1.0005) + // || (supertrend_vec.last().unwrap().area.contains("UP") && ema3_1m_vec[ema3_result.unwrap()-1].ema_value < sma10_1m_vec[sma10_result.unwrap()-1].sma_value && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value) + // // && current_price >= ema30_1m_vec[ema30_result.unwrap()].ema_value + // // && ema10_1m_vec[ema10_result.unwrap()-1].ema_value < ema10_1m_vec[ema10_result.unwrap()].ema_value + + // // && ema10_1m_vec[ema10_result.unwrap()].ema_value >= ema30_1m_vec[ema30_result.unwrap()].ema_value + // { + // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&supertrend_vec.last().unwrap().close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // if k_result.is_ok() && d_result.is_ok() { + // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value { + // let heatmap_volume_trunc_vec_option = heatmap_volume_vec.get(heatmap_search_result.unwrap()-11..heatmap_search_result.unwrap()-1); + // if heatmap_volume_trunc_vec_option.is_some() { + // let heatmap_volume_trunc_vec = heatmap_volume_trunc_vec_option.unwrap(); + // let mut heat_value_summation = 0.0; + // for element in heatmap_volume_trunc_vec { + // if element.heatmap_level == HeatMapLevel::ExtraHigh { + // heat_value_summation += 2.0; + // } else if element.heatmap_level == HeatMapLevel::High { + // heat_value_summation += 1.5; + // } else if element.heatmap_level == HeatMapLevel::Medium { + // heat_value_summation += 1.0; + // } else if element.heatmap_level == HeatMapLevel::Normal { + // heat_value_summation += 0.5; + // } else { + // heat_value_summation += 0.0; + // } + // } + // if heat_value_summation >= 3.0 { + // filtered_2nd_symbols.push((symbol, supertrend_vec.last().unwrap().close_time)); + // } + // } + // } + // } + // } + // // } + // } + // } + // // if macd_search_result.is_ok() { + // // if macd_vec[macd_search_result.unwrap()].macd_value > 0.0 { + // // //check duplicate records + // // let mut exists_condition_build = String::from("symbol=\'"); + // // exists_condition_build.push_str(symbol.as_str()); + // // exists_condition_build.push_str("\' AND close_time="); + // // exists_condition_build.push_str(opclo_1m_vec.last().unwrap().close_time.to_string().as_str()); + // // let exists_condition = Some(exists_condition_build); + + // // let inspect_result_1 = exists_record(&inspect_table_name_1, &exists_condition).await; + // // let inspect_result_2 = exists_record(&inspect_table_name_2, &exists_condition).await; + // // let inspect_result_3 = exists_record(&inspect_table_name_3, &exists_condition).await; + // // let inspect_result_4 = exists_record(&inspect_table_name_4, &exists_condition).await; + + // // if inspect_result_1 == false && inspect_result_2 == false && inspect_result_3 == false && inspect_result_4 == false { + // // let mut sum_stoch_rsi30_1m_k = 0.0; + // // for element in &stoch_rsi30_1m_k_vec { + // // sum_stoch_rsi30_1m_k += element.stoch_rsi_k_value; + // // } + // // let average_stoch_rsi30_1m_k = sum_stoch_rsi30_1m_k / 5.0; + + // // let ema3_3_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&EmaData{ema_value, close_time}|close_time); + // // let ema3_2_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); + // // let ema3_1_result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&EmaData{ema_value, close_time}|close_time); + // // let ema10_result = ema10_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&EmaData{ema_value, close_time}|close_time); + // // let sma10_result = sma10_1m_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&SmaData{sma_value, close_time}|close_time); + // // if ema3_3_result.is_ok() && ema3_2_result.is_ok() && ema3_1_result.is_ok() && ema10_result.is_ok() && sma10_result.is_ok() { + // // if sma10_1m_vec[sma10_result.unwrap()].sma_value < ema3_1m_vec[ema3_3_result.unwrap()].ema_value { + // // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // // if k_result.is_ok() && k_prev_result.is_ok() { + // // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value + // // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value > 5.0 + // // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value < 80.0 + // // && stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 96.0 + // // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0 { + // // if current_price <= opclo_1m_vec[1].opclo_price * ((((opclo_1m_vec[1].high_price / opclo_1m_vec[1].low_price) - 1.0) / 2.0) + 1.0) { + // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // } + // // } + // // } + // // } + + // // // if ema10_1m_vec[ema10_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value { + // // // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema10_1m_vec[ema10_result.unwrap()].ema_value { + // // // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema3_1m_vec[ema3_2_result.unwrap()].ema_value + // // // && ema3_1m_vec[ema3_2_result.unwrap()].ema_value > ema3_1m_vec[ema3_1_result.unwrap()].ema_value + // // // { + // // // if average_stoch_rsi30_1m_k < 80.0 { + // // // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let k_prev2_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // // // if k_result.is_ok() && d_result.is_ok() && k_prev_result.is_ok() && k_prev2_result.is_ok() { + // // // if + // // // (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) + // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) > 3.0 + // // // && stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 96.0 + // // // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev2_result.unwrap()].stoch_rsi_k_value) + // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) < 25.0 + // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) > 3.0 + // // // // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0) + // // // // (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value > 5.0) && + // // // // ((stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value <= 80.0) + // // // // || (((opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0) && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value))) + // // // { + // // // // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0 { + // // // // if (opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 + // // // // { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // // } else { + // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // if opclo_1m_vec[1].candle_type == "DOWN" && opclo_1m_vec[1].opclo_price >= opclo_1m_vec[2].close_price { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } else if opclo_1m_vec[1].candle_type == "UP" { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // } + // // // // else { + // // // // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); + // // // // if result.is_ok() { + // // // // if rsi3_1m_vec[result.unwrap()].rsi_value < 20.0 && cnt >= 2 && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // // } + // // // // } + + // // // // } else { + // // // // if cnt >= 2 { + + // // // // let result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); + // // // // match result { + // // // // Ok(T) => { + // // // // if ema3_1m_vec[T].ema_value > opclo_1m_vec[1].opclo_price { + // // // // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); + // // // // match result { + // // // // Ok(T) => { + // // // // if rsi3_1m_vec[T].rsi_value < 15.0 { + // // // // let k_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // // let d_result = stoch_rsi10_1m_d_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + + // // // // if k_result.is_ok() && d_result.is_ok() { + // // // // if stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value <= stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value { + // // // // if current_price < opclo_1m_vec[1].opclo_price { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // // } + // // // // } + // // // // } + // // // // }, + // // // // Err(E) => {} + // // // // } + // // // // } + // // // // }, + // // // // Err(E) => {} + // // // // } + // // // // } + // // // // } + // // // } + // // // } else { + // // // let k_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let k_prev_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let k_prev2_result = stoch_rsi10_1m_k_vec.binary_search_by_key(&opclo_1m_vec[0].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let d_result = stoch_rsi10_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // // // if k_result.is_ok() && d_result.is_ok() && k_prev_result.is_ok() && k_prev2_result.is_ok() { + // // // if (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) + // // // && (stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) > 3.0 + // // // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev2_result.unwrap()].stoch_rsi_k_value + // // // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi10_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value + // // // && stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi10_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value < 30.0 + // // // && stoch_rsi10_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value < 95.0 + // // // { + // // // // if stoch_rsi10_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > 80.0 { + // // // // if (opclo_1m_vec[1].open_price - opclo_1m_vec[1].close_price).abs() / (opclo_1m_vec[1].high_price - opclo_1m_vec[1].low_price) > 60.0 + // // // // { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // // } else { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // } + // // // } + // // // } + // // // } + // // // } + // // // // else { + // // // // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // // // // if k_result.is_ok() && k_prev_result.is_ok() { + // // // // if (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) + // // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value) > 5.0 + // // // // { + // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // } + + // // // // // if ema3_1m_vec[ema3_3_result.unwrap()].ema_value < ema3_1m_vec[ema3_2_result.unwrap()].ema_value { + // // // // // if opclo_1m_vec[1].candle_type == "DOWN" && opclo_1m_vec[1].open_price > ema3_1m_vec[ema3_2_result.unwrap()].ema_value + // // // // // && opclo_1m_vec[2].open_price < ema3_1m_vec[ema3_3_result.unwrap()].ema_value + // // // // // && opclo_1m_vec[2].high_price < ema3_1m_vec[ema3_3_result.unwrap()].ema_value + // // // // // && opclo_1m_vec[2].candle_type == "UP" { + // // // // // if (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value < stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) + // // // // // { + // // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // // } + // // // // // } + // // // // // } else if ema3_1m_vec[ema3_3_result.unwrap()].ema_value > ema3_1m_vec[ema3_2_result.unwrap()].ema_value + // // // // // { + // // // // // if opclo_1m_vec[1].candle_type == "UP" + // // // // // && (stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value) { + // // // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // // // } + // // // // // } + // // // // } + // // // // } + // // // } + // // } + // // } + // // } + // // // else if macd_vec[macd_search_result.unwrap()].macd_value > macd_vec[macd_search_result.unwrap()-1].macd_value && macd_vec[macd_search_result.unwrap()].macd_value > -3.0{ + // // // let k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let k_prev_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // // // let d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&opclo_1m_vec[2].close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // // // if k_result.is_ok() && k_prev_result.is_ok() && d_result.is_ok() { + // // // if stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_k_vec[k_prev_result.unwrap()].stoch_rsi_k_value + // // // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value > stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value + // // // && stoch_rsi30_1m_k_vec[k_result.unwrap()].stoch_rsi_k_value - stoch_rsi30_1m_d_vec[d_result.unwrap()].stoch_rsi_d_value > 5.0{ + // // // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // // // } + // // // } + // // // } + // // } + + // } + } + } + } + + // let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + // if market_cap_index.first().unwrap().negative_buy_signal == 1 || (market_cap_index.first().unwrap().liquidation_signal == 0 && market_cap_index.first().unwrap().market_cap_index <= 20.0 && market_cap_index.first().unwrap().market_cap_index >= 0.0) { + // let mut rsi3_1m_vec: Vec = Vec::new(); + // let mut opclo_1m_vec: Vec = Vec::new(); + // let mut ema3_1m_vec: Vec = Vec::new(); + // let mut cnt = 0; + // for symbol in filtered_symbols { + // let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + // let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + // let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + + // if rsi3_1m_option.is_some() && opclo_1m_option.is_some() && ema3_1m_option.is_some() { + // rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + // opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + // ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + + // if rsi3_1m_vec.len() >= 3 && opclo_1m_vec.len() >= 3 && ema3_1m_vec.len() >= 3 { + // opclo_1m_vec.reverse(); + // opclo_1m_vec.truncate(3); + // opclo_1m_vec.reverse(); + + // if opclo_1m_vec.last().unwrap().candle_type == "UP" { + // cnt = 0; + // for element in &opclo_1m_vec { + // if element.candle_type == "DOWN" { + // cnt += 1; + // } + // } + + // if cnt >= 2 { + // let result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&EmaData{ema_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if ema3_1m_vec[T].ema_value > opclo_1m_vec[1].opclo_price { + // let result = rsi3_1m_vec.binary_search_by_key(&opclo_1m_vec[1].close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi3_1m_vec[T].rsi_value < 30.0 { + // if current_price < opclo_1m_vec[1].opclo_price { + // filtered_2nd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); + // } + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + + // } + + // } + // } + // } + // } else if market_cap_index.first().unwrap().liquidation_signal == 1 && market_cap_index.first().unwrap().market_cap_index < - 10.0 { + // let mut rsi3_1m_vec: Vec = Vec::new(); + // let mut rsi10_1m_vec: Vec = Vec::new(); + // let mut rsi30_1m_vec: Vec = Vec::new(); + // let mut op_low_1m_vec: Vec = Vec::new(); + // let mut ema3_1m_vec: Vec = Vec::new(); + // let mut bb30_1m_vec: Vec = Vec::new(); + // let mut cnt = 0; + // for symbol in filtered_symbols { + // let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + // let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + // let rsi30_1m_option = alldata.rsi30_1m_data.iter().position(|x| *x.0 == *symbol); + // let op_low_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + // let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + // let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + + // if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() && rsi30_1m_option.is_some() && op_low_1m_option.is_some() && ema3_1m_option.is_some() && bb30_1m_option.is_some() { + // rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + // rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + // rsi30_1m_vec = alldata.rsi30_1m_data[rsi30_1m_option.unwrap()].1.clone(); + // op_low_1m_vec = alldata.rt_price_1m_vec[op_low_1m_option.unwrap()].1.clone(); + // ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + // bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + + // if rsi3_1m_vec.len() >= 3 && rsi10_1m_vec.len() >= 3 && rsi30_1m_vec.len() >= 3 && op_low_1m_vec.len() >= 3 && ema3_1m_vec.len() >= 3 && bb30_1m_vec.len() >= 3 { + // op_low_1m_vec.reverse(); + // op_low_1m_vec.truncate(5); + // op_low_1m_vec.reverse(); + + // if op_low_1m_vec.last().unwrap().candle_type == "DOWN" { + // cnt = 0; + // for element in &op_low_1m_vec { + // if element.candle_type == "DOWN" { + // cnt += 1; + // } + // } + + // if cnt >= 4 { + // let result = ema3_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if ema3_1m_vec[T].ema_value > (op_low_1m_vec.last().unwrap().open_price + op_low_1m_vec.last().unwrap().low_price) / 2.0 { + // let result = rsi3_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi3_1m_vec[T].rsi_value < 10.0 { + // let result = rsi10_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi10_1m_vec[T].rsi_value < 15.0 { + // let result = rsi30_1m_vec.binary_search_by_key(&op_low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi30_1m_vec[T].rsi_value < 35.0 { + // let result = bb30_1m_vec.binary_search_by_key(&op_low_1m_vec.first().unwrap().close_time, |&BollingerBandData{sma, upperband, lowerband, close_time}|close_time); + // match result { + // Ok(T) => { + // if bb30_1m_vec[T].lowerband > (op_low_1m_vec.last().unwrap().open_price + op_low_1m_vec.last().unwrap().low_price) / 2.0 { + // filtered_2nd_symbols.push((symbol, op_low_1m_vec.last().unwrap().close_time)); + // } + // }, + // Err(E) => {}, + // } + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + + // } + + // } + // } + // } + // } + + insert_pre_suggested_coins(1, &filtered_2nd_symbols, alldata).await; + + Ok(()) +} + +// Supertrend UP area에서 이득보기 +pub async fn execute_strategist_for_test2( + alldata: &AllData, +) -> Result<(), Box> { + let inspect_table_name_1 = String::from("buy_ordered_coin_list"); + let inspect_table_name_2 = String::from("sell_ordered_coin_list"); + let inspect_table_name_3 = String::from("pre_suggested_coin_list"); + let inspect_table_name_4 = String::from("suggested_coin_list"); + + let market_cap_index = select_marketcap().await; + + let mut filtered_symbols: Vec = Vec::new(); + + filtered_symbols.push(String::from("BTCUSDT")); + + let current_price = get_current_price(&filtered_symbols.first().unwrap(), &alldata.price_vec) + .await + .unwrap(); + + let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + let mut ema3_1m_vec: &Vec = &Vec::new(); + let mut ema3_30m_vec: &Vec = &Vec::new(); + let mut ema10_1m_vec: &Vec = &Vec::new(); + let mut ema30_1m_vec: &Vec = &Vec::new(); + let mut sma3_1m_vec: &Vec = &Vec::new(); + let mut sma10_1m_vec: &Vec = &Vec::new(); + let mut sma3_30m_vec: &Vec = &Vec::new(); + let mut sma30_1m_vec: &Vec = &Vec::new(); + let mut rsi3_1m_vec: &Vec = &Vec::new(); + let mut rsi30_1m_vec: &Vec = &Vec::new(); + let mut stoch_rsi3_1m_k_vec: &Vec = &Vec::new(); + let mut stoch_rsi3_1m_d_vec: &Vec = &Vec::new(); + let mut stoch_rsi30_1m_k_vec: &Vec = &Vec::new(); + let mut stoch_rsi30_1m_d_vec: &Vec = &Vec::new(); + let mut rt_price_1m_vec: &Vec = &Vec::new(); + let mut is_filtering_passed: bool = false; + + let mut cnt = 0; + let exists_condition = Some(String::from("used_usdt >= 10.0 AND registerer = 2")); + let inspect_result_1 = exists_record(&inspect_table_name_1, &exists_condition).await; + let inspect_result_2 = exists_record(&inspect_table_name_2, &exists_condition).await; + let inspect_result_4 = exists_record(&inspect_table_name_4, &None).await; + let mut is_buy_period_30m = false; + + if inspect_result_1 == false && inspect_result_2 == false && inspect_result_4 == false { + for symbol in filtered_symbols { + is_filtering_passed = false; + let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let ema10_1m_option = alldata.ema10_1m_data.iter().position(|x| *x.0 == *symbol); + let ema30_1m_option = alldata.ema30_1m_data.iter().position(|x| *x.0 == *symbol); + let sma3_1m_option = alldata.sma3_1m_data.iter().position(|x| *x.0 == *symbol); + let sma10_1m_option = alldata.sma10_1m_data.iter().position(|x| *x.0 == *symbol); + let sma3_30m_option = alldata.sma3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_1m_option = alldata.sma30_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi30_1m_option = alldata.rsi30_1m_data.iter().position(|x| *x.0 == *symbol); + let stoch_rsi3_1m_k_option = alldata + .stoch_rsi3_1m_3_k_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi3_1m_d_option = alldata + .stoch_rsi3_1m_3_d_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi30_1m_k_option = alldata + .stoch_rsi30_1m_30_k_data + .iter() + .position(|x| *x.0 == *symbol); + let stoch_rsi30_1m_d_option = alldata + .stoch_rsi30_1m_30_d_data + .iter() + .position(|x| *x.0 == *symbol); + let rt_price_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + + if ema3_1m_option.is_some() + && ema3_30m_option.is_some() + && ema10_1m_option.is_some() + && ema30_1m_option.is_some() + && sma3_1m_option.is_some() + && sma10_1m_option.is_some() + && sma3_30m_option.is_some() + && sma30_1m_option.is_some() + && rsi3_1m_option.is_some() + && rsi30_1m_option.is_some() + && stoch_rsi3_1m_k_option.is_some() + && stoch_rsi3_1m_d_option.is_some() + && stoch_rsi30_1m_k_option.is_some() + && stoch_rsi30_1m_d_option.is_some() + && rt_price_1m_option.is_some() + { + ema3_1m_vec = &alldata.ema3_1m_data[ema3_1m_option.unwrap()].1; + ema3_30m_vec = &alldata.ema3_30m_data[ema3_30m_option.unwrap()].1; + ema10_1m_vec = &alldata.ema10_1m_data[ema10_1m_option.unwrap()].1; + ema30_1m_vec = &alldata.ema30_1m_data[ema30_1m_option.unwrap()].1; + sma3_1m_vec = &alldata.sma3_1m_data[sma3_1m_option.unwrap()].1; + sma10_1m_vec = &alldata.sma10_1m_data[sma10_1m_option.unwrap()].1; + sma3_30m_vec = &alldata.sma3_30m_data[sma3_30m_option.unwrap()].1; + sma30_1m_vec = &alldata.sma30_1m_data[sma30_1m_option.unwrap()].1; + rsi3_1m_vec = &alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1; + rsi30_1m_vec = &alldata.rsi30_1m_data[rsi30_1m_option.unwrap()].1; + stoch_rsi3_1m_k_vec = + &alldata.stoch_rsi3_1m_3_k_data[stoch_rsi3_1m_k_option.unwrap()].1; + stoch_rsi3_1m_d_vec = + &alldata.stoch_rsi3_1m_3_d_data[stoch_rsi3_1m_d_option.unwrap()].1; + stoch_rsi30_1m_k_vec = + &alldata.stoch_rsi30_1m_30_k_data[stoch_rsi30_1m_k_option.unwrap()].1; + stoch_rsi30_1m_d_vec = + &alldata.stoch_rsi30_1m_30_d_data[stoch_rsi30_1m_d_option.unwrap()].1; + rt_price_1m_vec = &alldata.rt_price_1m_vec[rt_price_1m_option.unwrap()].1; + + // filtering condition: data are ready + if ema3_1m_vec.len() >= 3 + && ema3_30m_vec.len() >= 3 + && ema10_1m_vec.len() >= 3 + && ema30_1m_vec.len() >= 3 + && sma3_1m_vec.len() >= 3 + && sma10_1m_vec.len() >= 3 + && sma3_30m_vec.len() >= 3 + && sma30_1m_vec.len() >= 3 + && rsi3_1m_vec.len() >= 3 + && rsi30_1m_vec.len() >= 3 + && stoch_rsi3_1m_k_vec.len() >= 5 + && stoch_rsi3_1m_d_vec.len() >= 5 + && stoch_rsi30_1m_k_vec.len() >= 5 + && stoch_rsi30_1m_d_vec.len() >= 5 + && rt_price_1m_vec.len() >= 50 + { + is_filtering_passed = true; + } + + // // filtering condition: SuperTrend & EMA & SMA + // if is_filtering_passed == true { + // let supertrend_option = supertrend(&symbol, &alldata.rt_price_1m_vec, 10, 1.0, false).await; + // if supertrend_option.is_some() { + // let supertrend_vec = supertrend_option.unwrap(); + // let supertrend_result = supertrend_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |SupertrendData{band_value, signal, area, close_time}| *close_time); + // let ema3_result = ema3_1m_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); + // let sma10_result = sma10_1m_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&SmaData{sma_value, close_time}|close_time); + + // if supertrend_vec.last().unwrap().signal.is_some() && ema3_result.is_ok() && sma10_result.is_ok() { + // if (((supertrend_vec.last().unwrap().signal.as_ref().unwrap().contains("BUY") && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value) && rt_price_1m_vec.last().unwrap().close_price <= supertrend_vec[supertrend_vec.len()-2].band_value * 1.0005) + // || (supertrend_vec.last().unwrap().area.contains("UP") && ema3_1m_vec[ema3_result.unwrap()-1].ema_value < sma10_1m_vec[sma10_result.unwrap()-1].sma_value && ema3_1m_vec[ema3_result.unwrap()].ema_value > sma10_1m_vec[sma10_result.unwrap()].sma_value)) + // && sma10_1m_vec[sma10_result.unwrap()-2].sma_value < sma10_1m_vec[sma10_result.unwrap()].sma_value { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } + + // filtering condition: SuperTrend + if is_filtering_passed == true { + let supertrend_option = + supertrend(&symbol, &alldata.rt_price_1m_vec, 3, 1.0, false).await; + if supertrend_option.is_some() { + let supertrend_vec = supertrend_option.unwrap(); + let supertrend_result = supertrend_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |SupertrendData { + band_value, + signal, + area, + close_time, + }| *close_time, + ); + + if supertrend_result.is_ok() { + if supertrend_vec[supertrend_result.unwrap()] + .signal + .as_ref() + .is_some() + { + if supertrend_vec[supertrend_result.unwrap()] + .signal + .as_ref() + .unwrap() + .contains("BUY") + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: SMA & EMA + if is_filtering_passed == true { + let sma3_1m_result = sma3_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + let sma10_1m_result = sma10_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + let sma30_1m_result = sma30_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + let ema3_1m_result = ema3_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema3_30m_result = ema3_30m_vec.binary_search_by_key( + &sma3_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema10_1m_result = ema10_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + let ema30_1m_result = ema30_1m_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + if sma10_1m_result.is_ok() + && sma30_1m_result.is_ok() + && ema3_1m_result.is_ok() + && ema10_1m_result.is_ok() + && ema30_1m_result.is_ok() + && ema3_30m_result.is_ok() + { + if sma3_1m_vec[sma3_1m_result.unwrap()].sma_value + > sma3_1m_vec[sma3_1m_result.unwrap() - 1].sma_value + && sma3_1m_vec[sma3_1m_result.unwrap()].sma_value + > sma3_1m_vec[sma3_1m_result.unwrap() - 2].sma_value + && sma3_1m_vec[sma3_1m_result.unwrap() - 1].sma_value + > sma3_1m_vec[sma3_1m_result.unwrap() - 2].sma_value + && sma30_1m_vec[sma30_1m_result.unwrap()].sma_value + > rt_price_1m_vec.last().unwrap().close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 1].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 2].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 2].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 3].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 3].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 4].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 4].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 5].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 5].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 6].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 6].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 7].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 7].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 8].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 8].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 9].close_price + && sma30_1m_vec[sma30_1m_result.unwrap() - 9].sma_value + > rt_price_1m_vec[rt_price_1m_vec.len() - 10].close_price + // ema10_1m_vec[ema10_1m_result.unwrap()].ema_value > sma10_1m_vec[sma10_1m_result.unwrap()].sma_value + // ema10_1m_vec[ema10_1m_result.unwrap()-1].ema_value <= sma10_1m_vec[sma10_1m_result.unwrap()-1].sma_value + // (sma10_1m_vec[sma10_1m_result.unwrap()].sma_value - ema10_1m_vec[ema10_1m_result.unwrap()].ema_value).abs() >= (sma10_1m_vec[sma10_1m_result.unwrap()-1].sma_value - ema10_1m_vec[ema10_1m_result.unwrap()-1].ema_value).abs() + + // && ema10_1m_vec[ema10_1m_result.unwrap()-1].ema_value < ema10_1m_vec[ema10_1m_result.unwrap()].ema_value + // && ema10_1m_vec[ema10_1m_result.unwrap()-2].ema_value < ema10_1m_vec[ema10_1m_result.unwrap()-1].ema_value + + // && sma10_1m_vec[sma10_1m_result.unwrap()].sma_value > ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-1].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-2].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-3].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-4].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-5].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-2].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()-1].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-3].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()-2].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-4].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()-3].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-5].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()-4].ema_value + // && ema30_1m_vec[ema30_1m_result.unwrap()-5].ema_value < ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + // && rt_price_1m_vec.last().unwrap().close_price > ema30_1m_vec[ema30_1m_result.unwrap()].ema_value + { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: current price limitation + // if is_filtering_passed == true { + // if rt_price_1m_vec[rt_price_1m_vec.len()-2].close_price + ((rt_price_1m_vec[rt_price_1m_vec.len()-2].high_price - rt_price_1m_vec[rt_price_1m_vec.len()-2].low_price) * 0.5) > rt_price_1m_vec.last().unwrap().close_price { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } + + // filtering condition: Heatmap Volume + if is_filtering_passed == true { + let heatmap_volume_option = heatmap_volume( + &symbol, + &alldata.rt_price_1m_vec, + 30, + 30, + 4.0, + 2.5, + 1.0, + -0.5, + ) + .await; + if heatmap_volume_option.is_some() { + let heatmap_volume_vec = heatmap_volume_option.unwrap(); + let heatmap_search_result = heatmap_volume_vec.binary_search_by_key( + &rt_price_1m_vec.last().unwrap().close_time, + |HeatmapVolumeData { + heatmap_value, + heatmap_level, + close_time, + }| *close_time, + ); + if heatmap_search_result.is_ok() { + let heatmap_volume_trunc_vec_option = heatmap_volume_vec.get( + heatmap_search_result.unwrap() - 11 + ..heatmap_search_result.unwrap() - 1, + ); + if heatmap_volume_trunc_vec_option.is_some() { + let heatmap_volume_trunc_vec = + heatmap_volume_trunc_vec_option.unwrap(); + let mut heat_value_summation = 0.0; + for element in heatmap_volume_trunc_vec { + if element.heatmap_level == HeatMapLevel::ExtraHigh { + heat_value_summation += 2.0; + } else if element.heatmap_level == HeatMapLevel::High { + heat_value_summation += 1.5; + } else if element.heatmap_level == HeatMapLevel::Medium { + heat_value_summation += 1.0; + } else if element.heatmap_level == HeatMapLevel::Normal { + heat_value_summation += 0.5; + } else { + heat_value_summation += 0.0; + } + } + if heat_value_summation >= 1.5 { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } + } + } else { + is_filtering_passed = false; + } + } + + // filtering condition: Stoch RSI + // if is_filtering_passed == true { + // // let sample_length: usize = 5; + // // stoch_rsi10_1m_k_vec.reverse(); + // // stoch_rsi10_1m_k_vec.truncate(5); + // // stoch_rsi10_1m_k_vec.reverse(); + // // stoch_rsi10_1m_d_vec.reverse(); + // // stoch_rsi10_1m_d_vec.truncate(5); + // // stoch_rsi10_1m_d_vec.reverse(); + // let rsi3_k_result = stoch_rsi3_1m_k_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // let rsi3_d_result = stoch_rsi3_1m_d_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // let rsi30_k_result = stoch_rsi30_1m_k_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&StochRsiKData{stoch_rsi_k_value, close_time}|close_time); + // let rsi30_d_result = stoch_rsi30_1m_d_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&StochRsiDData{stoch_rsi_d_value, close_time}|close_time); + // if rsi30_k_result.is_ok() && rsi30_d_result.is_ok() && rsi3_k_result.is_ok() && rsi3_d_result.is_ok() { + // if + // // stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value <= 80.0 || stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value <= 80.0 + // // (stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi3_1m_d_vec[rsi3_d_result.unwrap()].stoch_rsi_d_value) + // (stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()-1].stoch_rsi_k_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + + // // (stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + // // && stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value > stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()-1].stoch_rsi_d_value + // // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + // // || + // || (stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value <= 60.0 + // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-2].stoch_rsi_k_value >= stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value + // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()-1].stoch_rsi_k_value <= stoch_rsi3_1m_d_vec[rsi3_d_result.unwrap()-1].stoch_rsi_d_value + // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()-1].stoch_rsi_k_value <= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()-1].stoch_rsi_d_value) + // // // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + // // || (stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value == 100.0 + // // && stoch_rsi3_1m_k_vec[rsi3_k_result.unwrap()].stoch_rsi_k_value == 100.0 + // // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value >= stoch_rsi30_1m_d_vec[rsi30_d_result.unwrap()].stoch_rsi_d_value + // // && stoch_rsi30_1m_k_vec[rsi30_k_result.unwrap()].stoch_rsi_k_value < 80.0) + // { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } + + // // filtering condition: MACD + // if is_filtering_passed == true { + // let macd_vec = ema_macd(&ema3_1m_vec, &ema10_1m_vec, 10).await?; + // let macd_search_result = macd_vec.binary_search_by_key(&rt_price_1m_vec.last().unwrap().close_time, |&EmaMacd{macd_value, close_time}|close_time); + + // if macd_search_result.is_ok() { + // if macd_vec[macd_search_result.unwrap()].macd_value > macd_vec[macd_search_result.unwrap()-1].macd_value { + // is_filtering_passed = true; + // } else { is_filtering_passed = false; } + // } else { is_filtering_passed = false; } + // } + + // filtering condition: Ratio between amplitude and body of candles + if is_filtering_passed == true { + let sample_length: usize = 5; // 5 candle samsples + let rt_price_1m_partial_vec_option = rt_price_1m_vec + .get(rt_price_1m_vec.len() - sample_length - 1..rt_price_1m_vec.len() - 1); + if rt_price_1m_partial_vec_option.is_some() { + let rt_price_1m_partial_vec = rt_price_1m_partial_vec_option.unwrap(); + + let mut sum_ratio_amp_body = 0.0; + for element in rt_price_1m_partial_vec { + sum_ratio_amp_body += (element.close_price - element.open_price).abs() + / (element.high_price - element.low_price); + } + let average_ratio_amp_body = sum_ratio_amp_body / sample_length as f64; + if average_ratio_amp_body > 0.3 { + is_filtering_passed = true; + } else { + is_filtering_passed = false; + } + } else { + is_filtering_passed = false; + } + } + + // check all filterings are passed + if is_filtering_passed == true { + filtered_2nd_symbols.push((symbol, rt_price_1m_vec.last().unwrap().close_time)); + } + } + } + } + + insert_pre_suggested_coins(2, &filtered_2nd_symbols, alldata).await; + Ok(()) +} +// pub async fn strategist_001 (alldata: &AllData) -> Result<(), Box> { +// // [strategist_001 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (opclo_1m price avg < opclo_30m price avg) +// // 3. 2nd filtering symbols (opclo_30m price at the previous candle < bb_sma30_30m lowerband) +// // 4. insert or update record into [strategist_001_table] +// // 5. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// // 1. retrieve all usdttrade symbols +// let signal_decision = select_signal_decision().await; + +// // 3. 1st filtering symbols: 5 samples of candle 30m has 4 DOWN candles at least && the latest candle 30m < SMA3 +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut sma3_30m_vec: Vec = Vec::new(); +// let mut filtered_1st_symbols: Vec = Vec::new(); +// let mut cnt = 0; +// for symbol in &alldata.valid_symbol_vec { + +// let opclo_30m_option = alldata.rt_price_30m_vec.iter().position(|x| *x.0 == *symbol); +// let sma3_30m_option = alldata.sma3_30m_data.iter().position(|x| *x.0 == *symbol); + +// // read 1d csv file & bollingerband sma30 csv file +// if opclo_30m_option.is_some() & sma3_30m_option.is_some() { +// opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()].1.clone(); +// sma3_30m_vec = alldata.sma3_30m_data[sma3_30m_option.unwrap()].1.clone(); + +// if opclo_30m_vec.len() >= 5 && sma3_30m_vec.len() >= 1 { +// opclo_30m_vec.reverse(); +// opclo_30m_vec.truncate(5); +// opclo_30m_vec.reverse(); + +// cnt = 0; +// for element in &opclo_30m_vec { +// if element.candle_type == "DOWN" { +// cnt += 1; +// } +// } + +// if cnt >= 4 { +// let result = sma3_30m_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&SmaData{sma_value, close_time}|close_time); +// match result { +// Ok(T) => { +// if sma3_30m_vec[T].sma_value > opclo_30m_vec.last().unwrap().opclo_price { +// filtered_1st_symbols.push(symbol.clone()); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } +// } + +// // 4. 2nd filtering symbols: 30m RSI 3 < 15, 30m RSI 10 < 25 +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for symbol in filtered_1st_symbols { + +// let rsi3_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); +// let rsi10_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); +// let opclo_30m_option = alldata.rt_price_30m_vec.iter().position(|x| *x.0 == *symbol); + +// if rsi3_option.is_some() && rsi10_option.is_some() && opclo_30m_option.is_some() { +// rsi3_vec = alldata.rsi3_30m_data[rsi3_option.unwrap()].1.clone(); +// rsi10_vec = alldata.rsi10_30m_data[rsi10_option.unwrap()].1.clone(); +// opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()].1.clone(); + +// if rsi3_vec.len() >= 1 && rsi10_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { +// let result = rsi3_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); +// match result { +// Ok(T) => { +// if rsi3_vec[T].rsi_value < 15.0 { +// let result = rsi10_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); +// match result { +// Ok(T) => { +// if rsi10_vec[T].rsi_value < 25.0 { +// filtered_2nd_symbols.push(symbol); +// } +// }, +// Err(E) => {} +// } +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// // 3. 3rd filtering symbols: 5 samples of candle 1m has 4 DOWN candles at least && the latest candle 1m < EMA3 +// let mut opclo_1m_vec: Vec = Vec::new(); +// let mut ema3_1m_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) +// let mut cnt = 0; +// for symbol in filtered_2nd_symbols { + +// let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); +// let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + +// // read 1d csv file & bollingerband sma30 csv file +// if opclo_1m_option.is_some() & ema3_1m_option.is_some() { +// opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); +// ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + +// if opclo_1m_vec.len() >= 5 && ema3_1m_vec.len() >= 1 { +// opclo_1m_vec.reverse(); +// opclo_1m_vec.truncate(5); +// opclo_1m_vec.reverse(); + +// cnt = 0; +// for element in &opclo_1m_vec { +// if element.candle_type == "DOWN" { +// cnt += 1; +// } +// } + +// if cnt >= 4 { +// let result = ema3_1m_vec.binary_search_by_key(&opclo_1m_vec.last().unwrap().close_time, |&EmaData{ema_value, close_time}|close_time); +// match result { +// Ok(T) => { +// if ema3_1m_vec[T].ema_value > opclo_1m_vec.last().unwrap().opclo_price { +// filtered_3rd_symbols.push((symbol, opclo_1m_vec.last().unwrap().close_time)); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } +// } + +// // // 2. 2nd filtering symbols: candle 1m price avg < candle 30m price avg +// // let mut opclo_1m_vec: Vec = Vec::new(); +// // let mut opclo_1m_price_avg = 0.0; +// // let mut opclo_30m_vec: Vec = Vec::new(); +// // let mut opclo_30m_price_avg = 0.0; +// // let mut filtered_2nd_symbols: Vec = Vec::new(); +// // for element in filtered_1st_symbols { +// // // initialize avgs +// // opclo_1m_price_avg = 0.0; +// // opclo_30m_price_avg = 0.0; + +// // let opclo_1m_result = read_price_into_vector("1m", &element).await; +// // let opclo_30m_result = read_price_into_vector("30m", &element).await; + +// // // read 1d csv file +// // if opclo_1m_result.is_ok() && opclo_30m_result.is_ok() { +// // let opclo_1m_option = opclo_1m_result?; +// // let opclo_30m_option = opclo_30m_result?; + +// // if opclo_1m_option.is_some() && opclo_30m_option.is_some() { +// // opclo_1m_vec = opclo_1m_option.unwrap(); +// // opclo_30m_vec = opclo_30m_option.unwrap(); + +// // if opclo_1m_vec.len() >= 10 && opclo_30m_vec.len() >= 4 { +// // opclo_1m_vec.reverse(); +// // opclo_1m_vec.split_off(10); +// // opclo_30m_vec.reverse(); +// // opclo_30m_vec.split_off(4); + +// // // calculate opclo 1m price average +// // for element in &opclo_1m_vec { +// // opclo_1m_price_avg += element.opclo_price; +// // } +// // opclo_1m_price_avg /= opclo_1m_vec.len() as f64; + +// // // calculate opclo 30m price average +// // for element in &opclo_30m_vec { +// // opclo_30m_price_avg += element.opclo_price; +// // } +// // opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // // campare both prices +// // if opclo_1m_price_avg < opclo_30m_price_avg * 0.98 { +// // filtered_2nd_symbols.push(element); +// // } +// // } +// // } +// // } +// // } + +// // // 3. 2nd filtering symbols: 85% of BB SMA10 MA price > candle 1d price yesterday avg +// // let mut opclo_1d_vec: Vec = Vec::new(); +// // let mut read_bb_vec: Vec = Vec::new(); +// // let mut filtered_2nd_symbols: Vec = Vec::new(); +// // for element in filtered_1st_symbols { + +// // let opclo_1d_result = read_price_into_vector("1d", &element).await; +// // let read_bb_result = read_bb_into_vector("sma10", "1d", &element).await; + +// // // read 1d csv file & bollingerband sma30 csv file +// // if opclo_1d_result.is_ok() & read_bb_result.is_ok() { +// // let opclo_1d_option = opclo_1d_result?; +// // let read_bb_option = read_bb_result?; +// // if opclo_1d_option.is_some() && read_bb_option.is_some() { +// // opclo_1d_vec = opclo_1d_option.unwrap(); +// // read_bb_vec = read_bb_option.unwrap(); + +// // if opclo_1d_vec.len() > 1 && read_bb_vec.len() > 1 { +// // opclo_1d_vec.pop(); +// // read_bb_vec.pop(); +// // let result = read_bb_vec.binary_search_by_key(&opclo_1d_vec.last().unwrap().close_time, |&BBData{ma_price, upperband, lowerband, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if read_bb_vec[T].ma_price > opclo_1d_vec.last().unwrap().opclo_price { +// // filtered_2nd_symbols.push(element); +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // } +// // } +// // } + +// // // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m price +// // let mut read_bb_vec: Vec = Vec::new(); +// // let mut opclo_30m_vec: Vec = Vec::new(); +// // let mut filtered_3rd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) +// // for element in filtered_2nd_symbols { + +// // // read 30m csv file & bollingerband sma30 csv file +// // let opclo_30m_result = read_price_into_vector("30m", &element).await; +// // let bb_result = read_bb_into_vector("sma30", "30m", &element).await; + +// // if opclo_30m_result.is_ok() && bb_result.is_ok() { +// // let opclo_30m_option = opclo_30m_result?; +// // let read_bb_option = bb_result?; +// // if opclo_30m_option.is_some() && read_bb_option.is_some() { +// // opclo_30m_vec = opclo_30m_option.unwrap(); +// // read_bb_vec = read_bb_option.unwrap(); + +// // // 2nd filtering symbols +// // let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&BBData{ma_price, upperband, lowerband, close_time}|close_time); +// // match result { +// // Ok(T) => { +// // if read_bb_vec[T].lowerband > opclo_30m_vec.last().unwrap().opclo_price { +// // filtered_3rd_symbols.push((element, read_bb_vec[T].close_time)); +// // } +// // }, +// // Err(E) => {} +// // } +// // } + +// // } +// // } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(1, &filtered_3rd_symbols, alldata).await; +// // println!("strategist_001 완료"); +// Ok(()) +// } + +pub async fn strategist_002( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_002 procedure] + // 1. retrieve all usdttrade symbols + // 2. 1st filtering symbols (opclo_1m price avg < opclo_30m price avg) + // 3. 2nd filtering symbols (sma30 price > opclo_30m price, check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI30 is lower than 0.3 of the middle sample) + // 4. if the two conditions are right, upload the filtered symbols into [strategist_002_table] + // 5. monitor price of symbols in [strategist_002_table], and upload them into [suggested_coin_list] when price seems to become high + let signal_decision = select_signal_decision().await; + + if signal_decision[0].decision.contains("UP") { + // 2. 1st filtering symbols: candle 1m price avg < candle 30m price avg + let mut opclo_1m_vec: Vec = Vec::new(); + let mut opclo_1m_price_avg = 0.0; + let mut opclo_30m_vec: Vec = Vec::new(); + let mut opclo_30m_price_avg = 0.0; + let mut filtered_1st_symbols: Vec = Vec::new(); + for symbol in &alldata.valid_symbol_vec { + // initialize avgs + opclo_1m_price_avg = 0.0; + opclo_30m_price_avg = 0.0; + + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + // read 1d csv file + if opclo_1m_option.is_some() && opclo_30m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if opclo_1m_vec.len() >= 10 && opclo_30m_vec.len() >= 4 { + opclo_1m_vec.reverse(); + opclo_1m_vec.split_off(10); + opclo_30m_vec.reverse(); + opclo_30m_vec.split_off(4); + + // calculate opclo 1m price average + for element in &opclo_1m_vec { + opclo_1m_price_avg += element.opclo_price; + } + opclo_1m_price_avg /= opclo_1m_vec.len() as f64; + + // calculate opclo 30m price average + for element in &opclo_30m_vec { + opclo_30m_price_avg += element.opclo_price; + } + opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + + // campare both prices + if opclo_1m_price_avg < opclo_30m_price_avg * 0.97 { + filtered_1st_symbols.push(symbol.clone()); + } + } + } + } + + // 3. 2nd filtering symbols: check if the middle sample among 5 sample from the latest price of SMA30 is the lowest price and then check if RSI30 is lower than 0.2 of the middle sample + let mut sma10_30m_vec: Vec = Vec::new(); + let mut rsi30_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_1st_symbols { + // read 30m opclo, SMA30, rsi30 csv file + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let sma10_30m_option = alldata.sma10_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi30_30m_option = alldata.rsi30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() + && sma10_30m_option.is_some() + && rsi30_30m_option.is_some() + { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + sma10_30m_vec = alldata.sma10_30m_data[sma10_30m_option.unwrap()].1.clone(); + rsi30_30m_vec = alldata.rsi30_30m_data[rsi30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 && sma10_30m_vec.len() >= 5 && rsi30_30m_vec.len() >= 5 + { + opclo_30m_vec.pop(); + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + + // 2nd filtering symbols + let result = sma10_30m_vec.binary_search_by_key( + &opclo_30m_vec.first().unwrap().close_time, + |SmaData { + sma_value, + close_time, + }| *close_time, + ); + match result { + Ok(T) => { + if sma10_30m_vec[T].sma_value > opclo_30m_vec[0].opclo_price { + let index_of_min: Option = opclo_30m_vec + .iter() + .enumerate() + .min_by(|(_, a), (_, b)| { + a.opclo_price + .partial_cmp(&b.opclo_price) + .expect("Nan was forbidden.") + }) + .map(|(index, _)| index); + + // index 1 means the middle index of 5 element in a vec, assuming the latest price is included but removed in the vec. + if index_of_min == Some(0) { + let result = rsi30_30m_vec.binary_search_by_key( + &opclo_30m_vec[0].close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi30_30m_vec[T].rsi_value < 20.0 { + filtered_2nd_symbols.push(( + symbol, + opclo_30m_vec.last().unwrap().close_time, + )); + } + } + Err(e) => {} + } + } + } + } + Err(E) => {} + } + } + } + } + // 4. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(2, &filtered_2nd_symbols, alldata).await; + } + // println!("strategist_002 완료"); + Ok(()) +} + +pub async fn strategist_003( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_003 procedure] + // 1. retrieve all usdttrade symbols + // 2. 1st filtering symbols (opclo_1m price avg < opclo_30m price avg) + // 3. 2nd filtering symbols (opclo_30m price at the previous candle < bb_sma30_30m lowerband) + // 4. 3rd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI30 is lower than 0.25 of the middle sample) + // 5. if the two conditions are right, upload the filtered symbols into [suggested_coin_list] + let signal_decision = select_signal_decision().await; + + if signal_decision[0].decision.contains("UP") { + // 1. retrieve all usdttrade symbols + + // 2. 1st filtering symbols: yesterday and today's candle 1d opclo price < EMA10 + let mut opclo_1d_vec: Vec = Vec::new(); + let mut ema10_1d_vec: Vec = Vec::new(); + let mut rsi10_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + for symbol in &alldata.valid_symbol_vec { + // read 1d csv file + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let ema10_1d_option = alldata.ema10_1d_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1d_option = alldata.rsi10_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && ema10_1d_option.is_some() && rsi10_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + ema10_1d_vec = alldata.ema10_1d_data[ema10_1d_option.unwrap()].1.clone(); + rsi10_vec = alldata.rsi10_1d_data[rsi10_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && ema10_1d_vec.len() >= 2 && rsi10_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.split_off(2); + ema10_1d_vec.reverse(); + ema10_1d_vec.split_off(2); + rsi10_vec.reverse(); + rsi10_vec.split_off(2); + + let result = ema10_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |EmaData { + ema_value, + close_time, + }| *close_time, + ); + match result { + Ok(T) => { + if ema10_1d_vec[T].ema_value > opclo_1d_vec[1].opclo_price { + let result = rsi10_vec.binary_search_by_key( + &opclo_1d_vec[1].close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_vec[T].rsi_value < 20.0 { + let result = ema10_1d_vec.binary_search_by_key(&opclo_1d_vec.first().unwrap().close_time, |EmaData{ema_value, close_time}| *close_time); + match result { + Ok(T) => { + if ema10_1d_vec[T].ema_value + > opclo_1d_vec[0].opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(e) => {} + } + } + } + Err(e) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m price + let mut opclo_30m_vec: Vec = Vec::new(); + let mut read_bb_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); // (symbol, closetime) + for symbol in filtered_1st_symbols { + // read 30m csv file + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let bb30_30m_option = alldata.bb30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && bb30_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + read_bb_vec = alldata.bb30_30m_data[bb30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 1 && read_bb_vec.len() >= 1 { + let result = read_bb_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if read_bb_vec[T].lowerband > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + + // 4. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price of SMA30 is the lowest price and then check if RSI30 is lower than 0.3 of the middle sample + let mut low_1m_vec: Vec = Vec::new(); + let mut bb30_1m_vec: Vec = Vec::new(); + let mut rsi3_vec: Vec = Vec::new(); + let mut rsi10_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_2nd_symbols { + let low_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + + if low_1m_option.is_some() + && bb30_1m_option.is_some() + && rsi3_1m_option.is_some() + && rsi10_1m_option.is_some() + { + low_1m_vec = alldata.rt_price_1m_vec[low_1m_option.unwrap()].1.clone(); + bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + rsi3_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + + if low_1m_vec.len() >= 1 + && bb30_1m_vec.len() >= 1 + && rsi3_vec.len() >= 1 + && rsi10_vec.len() >= 1 + { + let result = bb30_1m_vec.binary_search_by_key( + &low_1m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1m_vec[T].lowerband > low_1m_vec.last().unwrap().low_price { + let result = rsi3_vec.binary_search_by_key( + &low_1m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_vec[T].rsi_value < 15.0 { + let result = rsi10_vec.binary_search_by_key(&low_1m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + match result { + Ok(T) => { + if rsi10_vec[T].rsi_value < 20.0 { + filtered_3rd_symbols.push(( + symbol, + low_1m_vec.last().unwrap().close_time, + )); + } + } + Err(e) => {} + } + } + } + Err(e) => {} + } + } + } + Err(E) => {} + } + } + } + } + // 4. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(3, &filtered_3rd_symbols, alldata).await; + } + + // println!("strategist_003 완료"); + Ok(()) +} + +pub async fn strategist_004( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_004 procedure] + // 1st filtering: opclo_30m price < bb30_30m lowerband + // 2nd filtering: opclo_1m price < bb30_1m lowerband + // 3rd filtering: RSI3_1m < 10, RSI10_1m < 25 + + let signal_decision = select_signal_decision().await; + if signal_decision[0].decision.contains("UP") { + // 1st filtering: (opclo_30m price < bb30 lowerband) + let mut opclo_30m_vec: Vec = Vec::new(); + let mut bb30_30m_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + for symbol in &alldata.valid_symbol_vec { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let bb30_30m_option = alldata.bb30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && bb30_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + bb30_30m_vec = alldata.bb30_30m_data[bb30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 1 && bb30_30m_vec.len() >= 1 { + let result = bb30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_30m_vec[T].lowerband > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: (opclo_1m price < bb30_1m lowerband) + let mut opclo_1m_vec: Vec = Vec::new(); + let mut bb30_1m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + for symbol in filtered_1st_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() && bb30_1m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + + if opclo_1m_vec.len() >= 1 && bb30_1m_vec.len() >= 1 { + let result = bb30_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1m_vec[T].lowerband > opclo_1m_vec.last().unwrap().opclo_price { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + + // 3rd filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut opclo_1m_vec: Vec = Vec::new(); + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_2nd_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() && rsi3_1m_option.is_some() && rsi10_1m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + } + + if opclo_1m_vec.len() >= 1 && rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 { + let result = rsi3_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_1m_vec[T].rsi_value < 10.0 { + let result = rsi10_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_1m_vec[T].rsi_value < 25.0 { + filtered_3rd_symbols.push(( + symbol, + opclo_1m_vec.last().unwrap().close_time, + )); + } + } + Err(e) => {} + } + } + } + Err(e) => {} + } + } + } + // 4. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(4, &filtered_3rd_symbols, alldata).await; + } + // println!("strategist_004 완료"); + Ok(()) +} + +// for ultra-short +pub async fn strategist_005( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_005 procedure] + // 1st filtering: sampling the least 1m 5 tickers, check DOWN ticker at least 2, check the quote vol of the last ticker > 2X the counterpart of the previous 2 tickers. + // 2nd filtering: opclo_1m price < sma3_1m + // 3rd filtering: RSI3_1m < 10, RSI10_1m < 25 + // 4th filtering: check the latest and previous 30m candles are UPs + + let signal_decision = select_signal_decision().await; + if signal_decision[0].decision.contains("UP") { + // 1st filtering: sampling the least 1m 5 tickers, check DOWN ticker at least 2, check the quote vol of the last ticker > 2X the counterpart of the previous 2 tickers. + let mut opclo_1m_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + for symbol in &alldata.valid_symbol_vec { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + if opclo_1m_vec.len() >= 5 { + opclo_1m_vec.reverse(); + opclo_1m_vec.truncate(5); + opclo_1m_vec.reverse(); + + let mut cnt = 0; + for element in &opclo_1m_vec { + if element.candle_type == "DOWN" + && element.opclo_price > opclo_1m_vec.last().unwrap().opclo_price + { + cnt += 1; + } + } + + if cnt >= 2 && opclo_1m_vec.last().unwrap().candle_type == "DOWN" { + let mut vec_cmp = opclo_1m_vec.to_vec(); + vec_cmp.pop(); + vec_cmp.reverse(); + let mut vec_iter = vec_cmp.iter(); + + for _ in 0..2 { + let temp = vec_iter.next(); + if temp.is_some() { + if (temp.unwrap().quote_asset_volume != 0.0) + && (temp.unwrap().quote_asset_volume * 2.0 + < opclo_1m_vec.last().unwrap().quote_asset_volume) + { + filtered_1st_symbols.push(symbol.clone()); + break; + } + } + } + } + } + } + } + + // 2nd filtering: opclo_1m price < sma3_1m + let mut sma3_1m_vec: Vec = Vec::new(); + let mut opclo_1m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + for symbol in filtered_1st_symbols { + let sma3_1m_option = alldata.sma3_1m_data.iter().position(|x| *x.0 == *symbol); + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + + if sma3_1m_option.is_some() && opclo_1m_option.is_some() { + sma3_1m_vec = alldata.sma3_1m_data[sma3_1m_option.unwrap()].1.clone(); + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + + if sma3_1m_vec.len() >= 1 && opclo_1m_vec.len() >= 1 { + let result = sma3_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma3_1m_vec[T].sma_value > opclo_1m_vec.last().unwrap().opclo_price { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + + // 3rd filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut opclo_1m_vec: Vec = Vec::new(); + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + for symbol in filtered_2nd_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() && rsi3_1m_option.is_some() && rsi10_1m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + + if opclo_1m_vec.len() >= 1 && rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 { + let result = rsi3_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_1m_vec[T].rsi_value < 10.0 { + let result = rsi10_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_1m_vec[T].rsi_value < 25.0 { + filtered_3rd_symbols.push(symbol); + } + } + Err(e) => {} + } + } + } + Err(e) => {} + } + } + } + } + + // 4th filtering: check the latest and previous 30m candles are UPs + let mut opclo_30m_vec: Vec = Vec::new(); + let mut opclo_1m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_3rd_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && opclo_1m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 2 && opclo_1m_vec.len() >= 1 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(2); + opclo_30m_vec.reverse(); + + if opclo_30m_vec.first().unwrap().candle_type == "UP" + && opclo_30m_vec.last().unwrap().candle_type == "UP" + { + filtered_4th_symbols + .push((symbol, opclo_1m_vec.last().unwrap().close_time)); + } + } + } + } + + // 4. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(5, &filtered_4th_symbols, alldata).await; + } + // println!("strategist_005 완료"); + Ok(()) +} + +pub async fn strategist_006( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_006 procedure] + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 3, the latest opclo_30m < SMA3_30m + // 3rd filtering: RSI3_30m < 15, RSI10_30m < 35 + // 4th filtering: the latest 30m quote vol > 2X the previous quote vol + + // 1st filtering symbols: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + let mut opclo_1d_vec: Vec = Vec::new(); + let mut bb30_1d_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + let mut current_price: Option; + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1d_option = alldata.bb30_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && bb30_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + bb30_1d_vec = alldata.bb30_1d_data[bb30_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && bb30_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + bb30_1d_vec.reverse(); + bb30_1d_vec.truncate(2); + bb30_1d_vec.reverse(); + + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + current_price = get_current_price(&symbol, &alldata.price_vec).await; + + if current_price.is_some() { + if current_price.unwrap().is_normal() { + if (bb30_1d_vec[T].upperband + > opclo_1d_vec.first().unwrap().opclo_price) + && (opclo_1d_vec.first().unwrap().opclo_price + > current_price.unwrap()) + { + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1d_vec[T].upperband + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 3, the latest opclo_30m < SMA3_30m + let mut opclo_30m_vec: Vec = Vec::new(); + let mut sma3_30m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + let mut cnt = 0; + for symbol in filtered_1st_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let sma3_30m_option = alldata.sma3_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && sma3_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + sma3_30m_vec = alldata.sma3_30m_data[sma3_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 && sma3_30m_vec.len() >= 1 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + opclo_30m_vec.reverse(); + + cnt = 0; + for element in &opclo_30m_vec { + if element.candle_type == "DOWN" { + cnt += 1; + } + } + + if cnt >= 3 { + let result = sma3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma3_30m_vec[T].sma_value > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + } + + // 3rd filtering: RSI3_30m < 15, RSI10_30m < 35 + let mut rsi3_30m_vec: Vec = Vec::new(); + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + for symbol in filtered_2nd_symbols { + let rsi3_30m_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_30m_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_30m_option.is_some() && rsi10_30m_option.is_some() && opclo_30m_option.is_some() { + rsi3_30m_vec = alldata.rsi3_30m_data[rsi3_30m_option.unwrap()].1.clone(); + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_30m_vec.len() >= 1 && rsi10_30m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + let result = rsi3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_30m_vec[T].rsi_value < 15.0 { + let result = rsi10_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_30m_vec[T].rsi_value < 35.0 { + filtered_3rd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 4th filtering: the latest 30m quote vol > 2X the previous quote vol + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_3rd_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if opclo_30m_vec.len() >= 2 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(2); + opclo_30m_vec.reverse(); + + if 2.0 * opclo_30m_vec.first().unwrap().quote_asset_volume + < opclo_30m_vec.last().unwrap().quote_asset_volume + && opclo_30m_vec.last().unwrap().candle_type == "DOWN" + { + filtered_4th_symbols.push((symbol, opclo_30m_vec.last().unwrap().close_time)); + } + } + } + } + + // 7. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(6, &filtered_4th_symbols, alldata).await; + Ok(()) +} + +// pub async fn strategist_006 () -> Result<(), Box> { +// // [strategist_006 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (low_price_1m price avg < low_price_30m price avg) +// // 3. 2nd filtering symbols (low_price_30m price at the previous candle < bb_sma30_30m lowerband) +// // 4. insert [pre_suggested_coin_list] + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut low_price_1m_vec: Vec = Vec::new(); +// let mut low_price_1m_avg = 0.0; +// let mut low_price_30m_vec: Vec = Vec::new(); +// let mut low_price_30m_avg = 0.0; + +// // 2. 1st filtering symbols: candle 1m price avg < candle 30m price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// low_price_1m_avg = 0.0; +// low_price_30m_avg = 0.0; + +// // read 1m csv file +// low_price_1m_vec = read_low_price_into_vector("1m", &element.symbol).await?; + +// // calculate opclo 1m price average +// for element in &low_price_1m_vec { +// low_price_1m_avg += element.price; +// } +// low_price_1m_avg /= low_price_1m_vec.len() as f64; + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element.symbol).await?; + +// // calculate opclo 30m price average +// for element in &low_price_30m_vec { +// low_price_30m_avg += element.price; +// } +// low_price_30m_avg /= low_price_30m_vec.len() as f64; + +// // campare both prices +// if low_price_1m_avg < low_price_30m_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } + +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m price +// for element in filtered_1st_symbols { + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element).await?; + +// // read bollingerband sma30 csv file +// read_bb_vec = read_bb_into_vector("sma30", "30m", &element).await?; + +// // 2nd filtering symbols +// low_price_30m_vec.pop(); +// let result = read_bb_vec.binary_search_by_key(&low_price_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband > low_price_30m_vec.last().unwrap().price { +// filtered_2nd_symbols.push((element, read_bb_vec[T].server_epoch)); +// } +// }, +// Err(E) => {} +// } + +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(6, &filtered_2nd_symbols).await; + +// println!("strategist_006 완료"); +// Ok(()) +// } + +// pub async fn strategist_007 () -> Result<(), Box> { +// // [strategist_007 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (low_price_1m avg < low_price_30m price avg) +// // 3. 2nd filtering symbols (sma30 price > low_price_30m, check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI30 is lower than 0.3 of the middle sample) +// // 4. insert [pre_suggested_coin_list] + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut low_price_1m_vec: Vec = Vec::new(); +// let mut low_price_1m_avg = 0.0; +// let mut low_price_30m_vec: Vec = Vec::new(); +// let mut low_price_30m_avg = 0.0; + +// // 2. 1st filtering symbols: candle 1m price avg < candle 30m price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// low_price_1m_avg = 0.0; +// low_price_30m_avg = 0.0; + +// // read 1m csv file +// low_price_1m_vec = read_low_price_into_vector("1m", &element.symbol).await?; + +// // calculate opclo 1m price average +// for element in &low_price_1m_vec { +// low_price_1m_avg += element.price; +// } +// low_price_1m_avg /= low_price_1m_vec.len() as f64; + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element.symbol).await?; + +// // calculate opclo 30m price average +// for element in &low_price_30m_vec { +// low_price_30m_avg += element.price; +// } +// low_price_30m_avg /= low_price_30m_vec.len() as f64; + +// // campare both prices +// if low_price_1m_avg < low_price_30m_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } + +// let mut sma30_30m_vec: Vec = Vec::new(); +// let mut rsi30_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 2nd filtering symbols: check if the middle sample among 5 sample from the latest price of SMA30 is the lowest price and then check if RSI30 is lower than 0.3 of the middle sample +// for element in filtered_1st_symbols { + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element).await?; +// low_price_30m_vec.pop(); +// low_price_30m_vec.reverse(); +// low_price_30m_vec.truncate(5); + +// // read SMA30 csv file +// sma30_30m_vec = read_ma_into_vector(&MA::Sma, 30, "30m", &element).await?; + +// // read rsi30 csv file +// rsi30_vec = read_rsi_into_vector(30, "30m", &element).await?; + +// if low_price_30m_vec.len() == 5 { + +// // 2nd filtering symbols +// let result = sma30_30m_vec.binary_search_by_key(&low_price_30m_vec.first().unwrap().server_epoch, |MaData{price, server_epoch}| *server_epoch); +// match result { +// Ok(T) => { + +// if sma30_30m_vec[T].price > low_price_30m_vec[0].price { + +// let index_of_min: Option = low_price_30m_vec +// .iter() +// .enumerate() +// .min_by(|(_, a), (_, b)| a.price.partial_cmp(&b.price).expect("Nan was forbidden.")) +// .map(|(index, _)| index); + +// // index 1 means the middle index of 5 element in a vec, assuming the latest price is included but removed in the vec. +// if index_of_min == Some(1) { +// let result = rsi30_vec.binary_search_by_key(&low_price_30m_vec[1].server_epoch, |&RsiData{rsi_value, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if rsi30_vec[T].rsi_value < 0.3 { +// filtered_2nd_symbols.push((element, low_price_30m_vec.first().unwrap().server_epoch)); +// } +// }, +// Err(e) => {} +// } +// } +// } +// }, +// Err(E) => {} +// } +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(7, &filtered_2nd_symbols).await; + +// println!("strategist_007 완료"); +// Ok(()) +// } + +// pub async fn strategist_008 () -> Result<(), Box> { +// // [strategist_008 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (low_price_1m avg < low_price_30m avg) +// // 3. 2nd filtering symbols (low_price_30m at the previous candle < bb_sma30_30m lowerband) +// // 4. insert [pre_suggested_coin_list] + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut low_price_1m_vec: Vec = Vec::new(); +// let mut low_price_1m_avg = 0.0; +// let mut low_price_30m_vec: Vec = Vec::new(); +// let mut low_price_30m_avg = 0.0; + +// // 2. 1st filtering symbols: candle 1m price avg < candle 30m price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// low_price_1m_avg = 0.0; +// low_price_30m_avg = 0.0; + +// // read 1m csv file +// low_price_1m_vec = read_low_price_into_vector("1m", &element.symbol).await?; + +// // calculate opclo 1m price average +// for element in &low_price_1m_vec { +// low_price_1m_avg += element.price; +// } +// low_price_1m_avg /= low_price_1m_vec.len() as f64; + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element.symbol).await?; + +// // calculate opclo 30m price average +// for element in &low_price_30m_vec { +// low_price_30m_avg += element.price; +// } +// low_price_30m_avg /= low_price_30m_vec.len() as f64; + +// // campare both prices +// if low_price_1m_avg < low_price_30m_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } + +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m price +// for element in filtered_1st_symbols { + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element).await?; + +// // read bollingerband sma30 csv file +// read_bb_vec = read_bb_into_vector("sma30", "30m", &element).await?; + +// // 2nd filtering symbols +// low_price_30m_vec.pop(); +// let result = read_bb_vec.binary_search_by_key(&low_price_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband > low_price_30m_vec.last().unwrap().price { +// filtered_2nd_symbols.push((element, read_bb_vec[T].server_epoch)); +// } +// }, +// Err(E) => {} +// } + +// } + +// let mut sma30_30m_vec: Vec = Vec::new(); +// let mut rsi30_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 4. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price of SMA30 is the lowest price and then check if RSI30 is lower than 0.3 of the middle sample +// for element in filtered_2nd_symbols { + +// // read 30m csv file +// low_price_30m_vec = read_low_price_into_vector("30m", &element.0).await?; +// low_price_30m_vec.pop(); +// low_price_30m_vec.reverse(); +// low_price_30m_vec.truncate(5); + +// // read SMA30 csv file +// sma30_30m_vec = read_ma_into_vector(&MA::Sma, 30, "30m", &element.0).await?; + +// // read rsi30 csv file +// rsi30_vec = read_rsi_into_vector(30, "30m", &element.0).await?; + +// if low_price_30m_vec.len() == 5 { + +// // 2nd filtering symbols +// let result = sma30_30m_vec.binary_search_by_key(&low_price_30m_vec.first().unwrap().server_epoch, |MaData{price, server_epoch}| *server_epoch); +// match result { +// Ok(T) => { + +// if sma30_30m_vec[T].price > low_price_30m_vec[0].price { + +// let index_of_min: Option = low_price_30m_vec +// .iter() +// .enumerate() +// .min_by(|(_, a), (_, b)| a.price.partial_cmp(&b.price).expect("Nan was forbidden.")) +// .map(|(index, _)| index); + +// // index 1 means the middle index of 5 element in a vec, assuming the latest price is included but removed in the vec. +// if index_of_min == Some(1) { +// let result = rsi30_vec.binary_search_by_key(&low_price_30m_vec[1].server_epoch, |&RsiData{rsi_value, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if rsi30_vec[T].rsi_value < 0.25 { +// filtered_3rd_symbols.push((element.0, low_price_30m_vec.first().unwrap().server_epoch)); +// } +// }, +// Err(e) => {} +// } +// } +// } +// }, +// Err(E) => {} +// } +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(8, &filtered_3rd_symbols).await; + +// println!("strategist_008 완료"); +// Ok(()) +// } + +// pub async fn strategist_009 () -> Result<(), Box> { +// // [strategist_009 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (opclo_30m price avg < opclo_1d price avg) +// // 3. 2nd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample) +// // 4. insert or update record into [strategist_001_table] +// // 5. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut opclo_30m_price_avg = 0.0; +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_1d_price_avg = 0.0; + +// // 2. 1st filtering symbols: candle 30m price avg < candle 1d price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_30m_price_avg = 0.0; +// opclo_1d_price_avg = 0.0; + +// // read 1m csv file +// opclo_30m_vec = read_opclo_into_vector("30m", &element.symbol).await?; + +// // calculate opclo 1m price average +// for element in &opclo_30m_vec { +// opclo_30m_price_avg += element.price; +// } +// opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // read 30m csv file +// opclo_1d_vec = read_opclo_into_vector("1d", &element.symbol).await?; + +// // calculate opclo 30m price average +// for element in &opclo_1d_vec { +// opclo_1d_price_avg += element.price; +// } +// opclo_1d_price_avg /= opclo_1d_vec.len() as f64; + +// // campare both prices +// if opclo_30m_price_avg < opclo_1d_price_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } + +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 2nd filtering symbols: check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample +// for element in filtered_1st_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); +// // read rsi3 csv file +// rsi3_vec = read_rsi_into_vector(3, "30m", &element).await?; +// // read rsi10 csv file +// rsi10_vec = read_rsi_into_vector(10, "30m", &element).await?; + +// if rsi3_vec.len() >= 2 && rsi10_vec.len() >= 2 { +// if rsi3_vec.last().unwrap().rsi_value < 0.15 && rsi10_vec.last().unwrap().rsi_value < 0.30 { +// filtered_2nd_symbols.push((element, opclo_30m_vec.first().unwrap().server_epoch)); +// } +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(9, &filtered_2nd_symbols).await; + +// println!("strategist_009 완료"); +// Ok(()) +// } + +// pub async fn strategist_010 () -> Result<(), Box> { +// // [strategist_010 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (opclo_30m price avg < opclo_1d price avg) +// // 3. 2nd filtering symbols (candle 30m low price at the previous candle < bb_sma30_30m lowerband) +// // 4. 3rd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample) +// // 5. insert or update record into [strategist_001_table] +// // 6. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// let instant = Instant::now(); + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut opclo_30m_price_avg = 0.0; +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_1d_price_avg = 0.0; + +// // 2. 1st filtering symbols: candle 30m price avg < candle 1d price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_30m_price_avg = 0.0; +// opclo_1d_price_avg = 0.0; + +// // +// let opclo_30m_result = read_opclo_into_vector("30m", &element.symbol).await; +// let opclo_1d_result = read_opclo_into_vector("1d", &element.symbol).await; + +// // read opclo_30m and 1d csv file +// if opclo_30m_result.is_ok() && opclo_1d_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// opclo_1d_vec = opclo_1d_result.unwrap(); + +// if opclo_30m_vec.len() > 4 && opclo_1d_vec.len() > 7 { +// opclo_1d_vec.reverse(); +// opclo_1d_vec.split_off(7); +// opclo_30m_vec.reverse(); +// opclo_1d_vec.split_off(4); + +// // calculate opclo 1m price average +// for element in &opclo_30m_vec { +// opclo_30m_price_avg += element.price; +// } +// opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // calculate opclo 30m price average +// for element in &opclo_1d_vec { +// opclo_1d_price_avg += element.price; +// } +// opclo_1d_price_avg /= opclo_1d_vec.len() as f64; + +// // campare both prices +// if opclo_30m_price_avg < opclo_1d_price_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } +// } +// } + +// // 4. 3rd filtering symbols: yesterday 1d RSI 3 < 10, 1d RSI 10 < 15 +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for element in filtered_1st_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "1d", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "1d", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// rsi3_vec.pop(); +// rsi10_vec.pop(); +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.15 { +// filtered_2nd_symbols.push(element); +// } +// } +// } +// } + +// // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m low price +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec = Vec::new(); +// for element in filtered_2nd_symbols { + +// let opclo_30m_result = read_low_price_into_vector("30m", &element).await; +// let read_bb_result = read_bb_into_vector("sma30", "30m", &element).await; + +// // read 30m csv file && bollingerband sma30 csv file +// if opclo_30m_result.is_ok() && read_bb_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && read_bb_vec.len() > 1 { +// // 2nd filtering symbols +// opclo_30m_vec.pop(); +// let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband > opclo_30m_vec.last().unwrap().price { +// filtered_3rd_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_4th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample +// for element in filtered_3rd_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let opclo_30m_result = read_low_price_into_vector("30m", &element).await; +// let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() && rsi10_result.is_ok() && opclo_30m_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); +// opclo_30m_vec = opclo_30m_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.15 && rsi10_vec.last().unwrap().rsi_value < 0.20 { +// filtered_4th_symbols.push((element, opclo_30m_vec.last().unwrap().server_epoch)); +// } +// } + +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(10, &filtered_4th_symbols).await; + +// if instant.elapsed().as_millis() < 50 { +// sleep(Duration::from_millis(50)).await; +// } +// // println!("strategist_010 완료"); +// Ok(()) +// } + +// pub async fn strategist_011 () -> Result<(), Box> { +// // [strategist_011 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (opclo_1m price avg < opclo_30m price avg) +// // 3. 2nd filtering symbols (candle 30m low price at the previous candle < bb_sma30_30m lowerband) +// // 4. 3rd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample) +// // 5. insert or update record into [strategist_001_table] +// // 6. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// let instant = Instant::now(); + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut opclo_1m_vec: Vec = Vec::new(); +// let mut opclo_1m_price_avg = 0.0; +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut opclo_30m_price_avg = 0.0; + +// // 2. 1st filtering symbols: candle 1m price avg < candle 30m price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_1m_price_avg = 0.0; +// opclo_30m_price_avg = 0.0; + +// let opclo_1m_result = read_opclo_into_vector("1m", &element.symbol).await; +// let opclo_30m_result = read_opclo_into_vector("30m", &element.symbol).await; + +// if opclo_1m_result.is_ok() && opclo_30m_result.is_ok() { +// opclo_1m_vec = opclo_1m_result.unwrap(); +// opclo_30m_vec = opclo_30m_result.unwrap(); + +// if opclo_1m_vec.len() > 1 && opclo_30m_vec.len() > 1 { +// // calculate opclo 1m price average +// for element in &opclo_1m_vec { +// opclo_1m_price_avg += element.price; +// } +// opclo_1m_price_avg /= opclo_1m_vec.len() as f64; + +// // calculate opclo 30m price average +// for element in &opclo_30m_vec { +// opclo_30m_price_avg += element.price; +// } +// opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // campare both prices +// if opclo_1m_price_avg < opclo_30m_price_avg { +// filtered_1st_symbols.push(element.symbol); +// } +// } +// } +// } + +// // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m low price +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for element in filtered_1st_symbols { + +// let opclo_30m_result = read_low_price_into_vector("30m", &element).await; +// let read_bb_result = read_bb_into_vector("sma30", "30m", &element).await; + +// // read 30m csv file & bollingerband sma30 csv file +// if opclo_30m_result.is_ok() && read_bb_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && read_bb_vec.len() > 1 +// { +// // 2nd filtering symbols +// opclo_30m_vec.pop(); +// let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband > opclo_30m_vec.last().unwrap().price { +// filtered_2nd_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample +// for element in filtered_2nd_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; + +// if rsi3_result.is_ok() && rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.15 && rsi10_vec.last().unwrap().rsi_value < 0.20 { +// filtered_3rd_symbols.push((element, opclo_30m_vec.first().unwrap().server_epoch)); +// } +// } + +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(11, &filtered_3rd_symbols).await; + +// if instant.elapsed().as_millis() < 50 { +// sleep(Duration::from_millis(50)).await; +// } + +// // println!("strategist_011 완료"); +// Ok(()) +// } + +// pub async fn strategist_012 () -> Result<(), Box> { +// // [strategist_010 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (opclo_30m price avg < opclo_1d price avg) +// // 3. 2nd filtering symbols (candle 30m low price at the previous candle < bb_sma30_30m lowerband) +// // 4. 3rd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample) +// // 5. insert or update record into [strategist_001_table] +// // 6. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// let instant = Instant::now(); + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut opclo_30m_price_avg = 0.0; +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_1d_price_avg = 0.0; + +// // 2. 1st filtering symbols: candle 30m price avg < candle 1d price avg +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_30m_price_avg = 0.0; +// opclo_1d_price_avg = 0.0; + +// let opclo_30m_result = read_opclo_into_vector("30m", &element.symbol).await; +// let opclo_1d_result = read_opclo_into_vector("1d", &element.symbol).await; + +// // read 30m csv file & read 1d csv file +// if opclo_30m_result.is_ok() && opclo_1d_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// opclo_1d_vec = opclo_1d_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && opclo_1d_vec.len() > 1 { +// // calculate opclo 1m price average +// for element in &opclo_30m_vec { +// opclo_30m_price_avg += element.price; +// } +// opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // calculate opclo 30m price average +// for element in &opclo_1d_vec { +// opclo_1d_price_avg += element.price; +// } +// opclo_1d_price_avg /= opclo_1d_vec.len() as f64; + +// // campare both prices +// if opclo_30m_price_avg < (opclo_1d_price_avg/4.0) { +// filtered_1st_symbols.push(element.symbol); +// } +// } +// } +// } + +// let mut opclo_30m_vec: Vec = Vec::new(); +// // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m low price +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for element in filtered_1st_symbols { + +// let opclo_30m_result = read_low_price_into_vector("30m", &element).await; +// let read_bb_result = read_bb_into_vector("sma30", "30m", &element).await; + +// // read 1m csv file & bollingerband sma30 csv file +// if opclo_30m_result.is_ok() & read_bb_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && read_bb_vec.len() > 1 { +// // 2nd filtering symbols +// let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband > opclo_30m_vec.last().unwrap().price { +// filtered_2nd_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// let mut opclo_1m_vec: Vec = Vec::new(); +// let mut opclo_1m_price_avg = 0.0; +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut opclo_30m_price_avg = 0.0; +// let mut filtered_3rd_symbols: Vec = Vec::new(); +// for element in filtered_2nd_symbols { +// // initialize avgs +// opclo_1m_price_avg = 0.0; +// opclo_30m_price_avg = 0.0; + +// let opclo_1m_result = read_opclo_into_vector("1m", &element).await; +// let opclo_30m_result = read_opclo_into_vector("30m", &element).await; + +// if opclo_1m_result.is_ok() && opclo_30m_result.is_ok() { +// opclo_1m_vec = opclo_1m_result.unwrap(); +// opclo_30m_vec = opclo_30m_result.unwrap(); + +// if opclo_1m_vec.len() > 10 && opclo_30m_vec.len() > 4 { +// opclo_1m_vec.reverse(); +// opclo_1m_vec.split_off(10); +// opclo_30m_vec.reverse(); +// opclo_30m_vec.split_off(4); + +// // calculate opclo 1m price average +// for element in &opclo_1m_vec { +// opclo_1m_price_avg += element.price; +// } +// opclo_1m_price_avg /= opclo_1m_vec.len() as f64; + +// // calculate opclo 30m price average +// for element in &opclo_30m_vec { +// opclo_30m_price_avg += element.price; +// } +// opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // campare both prices +// if opclo_1m_price_avg < (opclo_30m_price_avg*0.99) { +// filtered_3rd_symbols.push(element); +// } +// } +// } +// } + +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_4th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // 3. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample +// for element in filtered_3rd_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.10 { +// filtered_4th_symbols.push((element, opclo_30m_vec.first().unwrap().server_epoch)); +// } +// } + +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(12, &filtered_4th_symbols).await; + +// if instant.elapsed().as_millis() < 50 { +// sleep(Duration::from_millis(50)).await; +// } + +// // println!("strategist_012 완료"); +// Ok(()) +// } + +// // original strategist 012 +// // pub async fn strategist_012 () -> Result<(), Box> { +// // // [strategist_010 procedure] +// // // 1. retrieve all usdttrade symbols +// // // 2. 1st filtering symbols (opclo_30m price avg < opclo_1d price avg) +// // // 3. 2nd filtering symbols (candle 30m low price at the previous candle < bb_sma30_30m lowerband) +// // // 4. 3rd filtering symbols (check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample) +// // // 5. insert or update record into [strategist_001_table] +// // // 6. if symbol's 30m price is over bb_sma30_30m lowerband, move the record from [strategist_001_table] to [suggested_coin_list] + +// // let instant = Instant::now(); + +// // // 1. retrieve all usdttrade symbols +// // #[derive(Debug, FromRow)] +// // struct UsdtTrades { +// // symbol: String, +// // } +// // let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// // let table_name = String::from("valid_usdt_trades"); +// // let column_name = String::from("symbol"); +// // let table_condition = None; +// // let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// // let mut opclo_30m_vec: Vec = Vec::new(); +// // let mut opclo_30m_price_avg = 0.0; +// // let mut opclo_1d_vec: Vec = Vec::new(); +// // let mut opclo_1d_price_avg = 0.0; + +// // // 2. 1st filtering symbols: candle 30m price avg < candle 1d price avg +// // let mut filtered_1st_symbols: Vec = Vec::new(); +// // for element in usdt_trade_symbols { +// // // initialize avgs +// // opclo_30m_price_avg = 0.0; +// // opclo_1d_price_avg = 0.0; + +// // let opclo_30m_result = read_opclo_into_vector("30m", &element.symbol).await; +// // let opclo_1d_result = read_opclo_into_vector("1d", &element.symbol).await; + +// // // read 30m csv file & read 1d csv file +// // if opclo_30m_result.is_ok() && opclo_1d_result.is_ok() { +// // opclo_30m_vec = opclo_30m_result.unwrap(); +// // opclo_1d_vec = opclo_1d_result.unwrap(); + +// // if opclo_30m_vec.len() > 1 && opclo_1d_vec.len() > 1 { +// // // calculate opclo 1m price average +// // for element in &opclo_30m_vec { +// // opclo_30m_price_avg += element.price; +// // } +// // opclo_30m_price_avg /= opclo_30m_vec.len() as f64; + +// // // calculate opclo 30m price average +// // for element in &opclo_1d_vec { +// // opclo_1d_price_avg += element.price; +// // } +// // opclo_1d_price_avg /= opclo_1d_vec.len() as f64; + +// // // campare both prices +// // if opclo_30m_price_avg < (opclo_1d_price_avg/3.0) { +// // filtered_1st_symbols.push(element.symbol); +// // } +// // } +// // } +// // } + +// // let mut opclo_1m_vec: Vec = Vec::new(); +// // // 3. 2nd filtering symbols: lower band in bollingerband of sma30 > candle 30m low price +// // let mut read_bb_vec: Vec = Vec::new(); +// // let mut filtered_2nd_symbols: Vec = Vec::new(); +// // for element in filtered_1st_symbols { + +// // let opclo_1m_result = read_low_price_into_vector("1m", &element).await; +// // let read_bb_result = read_bb_into_vector("sma30", "1m", &element).await; + +// // // read 1m csv file & bollingerband sma30 csv file +// // if opclo_1m_result.is_ok() & read_bb_result.is_ok() { +// // opclo_1m_vec = opclo_1m_result.unwrap(); +// // read_bb_vec = read_bb_result.unwrap(); + +// // if opclo_1m_vec.len() > 1 && read_bb_vec.len() > 1 { +// // // 2nd filtering symbols +// // let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// // match result { +// // Ok(T) => { +// // if read_bb_vec[T].lowerband > opclo_1m_vec.last().unwrap().price { +// // filtered_2nd_symbols.push(element); +// // } +// // }, +// // Err(E) => {} +// // } +// // } +// // } +// // } + +// // let mut rsi3_vec: Vec = Vec::new(); +// // let mut rsi10_vec: Vec = Vec::new(); +// // let mut filtered_3rd_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + +// // // 3. 3rd filtering symbols: check if the middle sample among 5 sample from the latest price is the lowest price and then check if RSI3 and RSI10 are lower than 0.15 and 0.3 respectively of the middle sample +// // for element in filtered_2nd_symbols { + +// // rsi3_vec.clear(); +// // rsi10_vec.clear(); + +// // let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// // let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; + +// // // read rsi3 csv file & rsi10 csv file +// // if rsi3_result.is_ok() & rsi10_result.is_ok() { +// // rsi3_vec = rsi3_result.unwrap(); +// // rsi10_vec = rsi10_result.unwrap(); + +// // if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// // if rsi3_vec.last().unwrap().rsi_value < 0.15 && rsi10_vec.last().unwrap().rsi_value < 0.20 { +// // filtered_3rd_symbols.push((element, opclo_30m_vec.first().unwrap().server_epoch)); +// // } +// // } + +// // } +// // } + +// // // 4. insert record into [pre_suggested_coin_list] +// // insert_pre_suggested_coins(12, &filtered_3rd_symbols).await; + +// // if instant.elapsed().as_millis() < 50 { +// // sleep(Duration::from_millis(50)).await; +// // } + +// // // println!("strategist_012 완료"); +// // Ok(()) +// // } + +// pub async fn strategist_013 () -> Result<(), Box> { +// // [strategist_013 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols (the latest opclo_1d price avg < 20% of opclo_1d price avg two days before, the latest opclo_1d price avg < opclo_1d price avg a day before) +// // 3. 2nd filtering symbols (1d RSI3 and RSI10 are lower than 0.10) +// // 4. 3rd filtering symbols (candle 1d low price < 90% of bb_sma30_1d lowerband) +// // 5. 4th filtering symbols (30m RSI3 and RSI10 are lower than 0.10 abd 0.10 respectively) +// // 6. 5th filtering symbols (candle 30m low price < 99.5% of bb_sma30_30m lowerband) + +// let instant = Instant::now(); + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// // 2. 1st filtering symbols (the latest opclo_1d price avg < 90% of opclo_1d price avg two days before) +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_1d_latest_price = 0.0; +// let mut opclo_1d_1before_price = 0.0; +// let mut opclo_1d_2before_price = 0.0; +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_1d_latest_price = 0.0; +// opclo_1d_1before_price = 0.0; +// opclo_1d_2before_price = 0.0; + +// let opclo_1d_result = read_opclo_into_vector("1d", &element.symbol).await; + +// // read 30m csv file & read 1d csv file +// if opclo_1d_result.is_ok() { +// opclo_1d_vec = opclo_1d_result.unwrap(); + +// if opclo_1d_vec.len() > 3 { +// // compare the lastest price and 20% of 2 days before price +// opclo_1d_vec.reverse(); +// let mut iter = opclo_1d_vec.iter(); +// opclo_1d_latest_price = iter.next().unwrap().price; +// opclo_1d_1before_price = iter.next().unwrap().price; +// opclo_1d_2before_price = iter.next().unwrap().price; + +// // campare both prices +// if (opclo_1d_latest_price < opclo_1d_2before_price * 0.9) && (opclo_1d_latest_price < opclo_1d_1before_price * 0.95) { +// filtered_1st_symbols.push(element.symbol); +// } +// } +// } +// } + +// // 3. 2nd filtering symbols (1d RSI3 and RSI10 are lower than 0.10) +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for element in filtered_1st_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "1d", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "1d", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.10 { +// filtered_2nd_symbols.push(element); +// } +// } + +// } +// } + +// // 4. 3rd filtering symbols (candle 1d low price < 90% of bb_sma30_1d lowerband) +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec = Vec::new(); +// for element in filtered_2nd_symbols { + +// let opclo_1d_result = read_low_price_into_vector("1d", &element).await; +// let read_bb_result = read_bb_into_vector("sma30", "1d", &element).await; + +// // read 1d csv file & bollingerband sma30 csv file +// if opclo_1d_result.is_ok() & read_bb_result.is_ok() { +// opclo_30m_vec = opclo_1d_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && read_bb_vec.len() > 1 { +// // 3rd filtering symbols +// let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband * 0.9 > opclo_30m_vec.last().unwrap().price { +// filtered_3rd_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// // 5. 4th filtering symbols (30m RSI3 and RSI10 are lower than 0.10 abd 0.10 respectively) +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_4th_symbols: Vec = Vec::new(); +// for element in filtered_3rd_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.10 { +// filtered_4th_symbols.push(element); +// } +// } + +// } +// } + +// // 6. 5th filtering symbols (candle 30m low price < 99.5% of bb_sma30_30m lowerband) +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_5th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) +// for element in filtered_4th_symbols { + +// let opclo_30m_result = read_low_price_into_vector("30m", &element).await; +// let read_bb_result = read_bb_into_vector("sma30", "30m", &element).await; + +// // read 1d csv file & bollingerband sma30 csv file +// if opclo_30m_result.is_ok() & read_bb_result.is_ok() { +// opclo_30m_vec = opclo_30m_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_30m_vec.len() > 1 && read_bb_vec.len() > 1 { +// // 3rd filtering symbols +// let result = read_bb_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].lowerband * 0.995 > opclo_30m_vec.last().unwrap().price { +// filtered_5th_symbols.push((element, opclo_30m_vec.last().unwrap().server_epoch)); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// // 4. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(13, &filtered_5th_symbols).await; + +// if instant.elapsed().as_millis() < 50 { +// sleep(Duration::from_millis(50)).await; +// } + +// // println!("strategist_013 완료"); +// Ok(()) +// } + +// pub async fn strategist_014 () -> Result<(), Box> { +// // [strategist_014 procedure] +// // 1. retrieve all usdttrade symbols +// // 2. 1st filtering symbols: candle 1d price yesterday avg < 80% of candle 1d opclo price latest 7 days avg +// // 3. 2nd filtering symbols: 85% of BB SMA10 MA price > candle 1d price yesterday avg +// // 4. 3rd filtering symbols: yesterday 1d RSI 3 < 10, 1d RSI 10 < 15 +// // 5. 4th filtering symbols: avg between yesterday 1d opclo price and yesterday low price> latest 30m opclo price +// // 6. 5th filtering symbols: 30m RSI 3 < 10, 30m RSI 10 < 15 +// // 7. insert record into [pre_suggested_coin_list] + +// let instant = Instant::now(); + +// // 1. retrieve all usdttrade symbols +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let table_condition = None; +// let mut usdt_trade_symbols = select_record(&table_name, &column_name, &table_condition, &usdt_trades).await.unwrap(); + +// // 2. 1st filtering symbols: candle 1d price yesterday avg < 80% of candle 1d opclo price latest 7 days avg +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_1d_price_7days_avg = 0.0; +// let mut filtered_1st_symbols: Vec = Vec::new(); +// for element in usdt_trade_symbols { +// // initialize avgs +// opclo_1d_price_7days_avg = 0.0; + +// let opclo_1d_result = read_opclo_into_vector("1d", &element.symbol).await; + +// // read 1d csv file +// if opclo_1d_result.is_ok() { + +// opclo_1d_vec = opclo_1d_result.unwrap(); + +// if opclo_1d_vec.len() >= 8 { +// opclo_1d_vec.pop(); +// opclo_1d_vec.reverse(); + +// opclo_1d_vec.split_off(7); + +// // calculate opclo 1d price average +// for element in &opclo_1d_vec { +// opclo_1d_price_7days_avg += element.price; +// } +// opclo_1d_price_7days_avg /= opclo_1d_vec.len() as f64; + +// // campare the yesterday opclo price with 30days avg +// if opclo_1d_vec[0].price < (opclo_1d_price_7days_avg * 0.8) { +// filtered_1st_symbols.push(element.symbol); +// } +// } +// } +// } + +// // 3. 2nd filtering symbols: 85% of BB SMA10 MA price > candle 1d price yesterday avg +// let mut read_bb_vec: Vec = Vec::new(); +// let mut filtered_2nd_symbols: Vec = Vec::new(); +// for element in filtered_1st_symbols { + +// let opclo_1d_result = read_opclo_into_vector("1d", &element).await; +// let read_bb_result = read_bb_into_vector("sma10", "1d", &element).await; + +// // read 1d csv file & bollingerband sma30 csv file +// if opclo_1d_result.is_ok() & read_bb_result.is_ok() { +// opclo_1d_vec = opclo_1d_result.unwrap(); +// read_bb_vec = read_bb_result.unwrap(); + +// if opclo_1d_vec.len() > 1 && read_bb_vec.len() > 1 { +// opclo_1d_vec.pop(); +// read_bb_vec.pop(); +// let result = read_bb_vec.binary_search_by_key(&opclo_1d_vec.last().unwrap().server_epoch, |&BBData{ma_price, upperband, lowerband, server_epoch}|server_epoch); +// match result { +// Ok(T) => { +// if read_bb_vec[T].ma_price * 0.85 > opclo_1d_vec.last().unwrap().price { +// filtered_2nd_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// // 4. 3rd filtering symbols: yesterday 1d RSI 3 < 10, 1d RSI 10 < 15 +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut filtered_3rd_symbols: Vec = Vec::new(); +// for element in filtered_2nd_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "1d", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "1d", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 { +// rsi3_vec.pop(); +// rsi10_vec.pop(); +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.15 { +// filtered_3rd_symbols.push(element); +// } +// } +// } +// } + +// // 5. 4th filtering symbols: avg between yesterday 1d opclo price and yesterday low price> latest 30m opclo price +// let mut opclo_1d_vec: Vec = Vec::new(); +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut low_1d_vec: Vec = Vec::new(); +// let mut avg = 0.0; +// let mut price_same_closetime = 0.0; +// let mut filtered_4th_symbols: Vec = Vec::new(); +// for element in filtered_3rd_symbols { +// // initialize avgs +// avg = 0.0; +// price_same_closetime = 0.0; + +// let opclo_1d_result = read_opclo_into_vector("1d", &element).await; +// let opclo_30m_result = read_opclo_into_vector("30m", &element).await; +// let low_1d_result = read_low_price_into_vector("1d", &element).await; + +// // read 1d csv file +// if opclo_1d_result.is_ok() && low_1d_result.is_ok() && opclo_30m_result.is_ok() { + +// opclo_1d_vec = opclo_1d_result.unwrap(); +// opclo_30m_vec = opclo_30m_result.unwrap(); +// low_1d_vec = low_1d_result.unwrap(); + +// if opclo_1d_vec.len() > 1 && low_1d_vec.len() > 1 && opclo_30m_vec.len() > 1 { +// opclo_1d_vec.pop(); + +// let result = low_1d_vec.binary_search_by_key(&opclo_1d_vec.last().unwrap().server_epoch, |OpcloData{price, server_epoch, usdt_volume, up_down}| *server_epoch); + +// match result { +// Ok(T) => { +// avg = (opclo_1d_vec.last().unwrap().price + low_1d_vec[T].price) / 2.0; + +// if opclo_30m_vec.last().unwrap().price < avg { +// filtered_4th_symbols.push(element); +// } +// }, +// Err(E) => {} +// } +// } +// } +// } + +// // 6. 5th filtering symbols: 30m RSI 3 < 10, 30m RSI 10 < 15 +// let mut rsi3_vec: Vec = Vec::new(); +// let mut rsi10_vec: Vec = Vec::new(); +// let mut opclo_30m_vec: Vec = Vec::new(); +// let mut filtered_5th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) +// for element in filtered_4th_symbols { + +// rsi3_vec.clear(); +// rsi10_vec.clear(); + +// let rsi3_result = read_rsi_into_vector(3, "30m", &element).await; +// let rsi10_result = read_rsi_into_vector(10, "30m", &element).await; +// let opclo_30m_result = read_opclo_into_vector("30m", &element).await; + +// // read rsi3 csv file & rsi10 csv file +// if rsi3_result.is_ok() & rsi10_result.is_ok() && opclo_30m_result.is_ok() { +// rsi3_vec = rsi3_result.unwrap(); +// rsi10_vec = rsi10_result.unwrap(); +// opclo_30m_vec = opclo_30m_result.unwrap(); + +// if rsi3_vec.len() > 1 && rsi10_vec.len() > 1 && opclo_30m_vec.len() > 1 { +// if rsi3_vec.last().unwrap().rsi_value < 0.10 && rsi10_vec.last().unwrap().rsi_value < 0.10 { +// filtered_5th_symbols.push((element, opclo_30m_vec.last().unwrap().server_epoch)); +// } +// } +// } +// } + +// // 7. insert record into [pre_suggested_coin_list] +// insert_pre_suggested_coins(14, &filtered_5th_symbols).await; + +// if instant.elapsed().as_millis() < 50 { +// sleep(Duration::from_millis(50)).await; +// } +// Ok(()) +// } + +pub async fn strategist_013( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_013 procedure] + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + // 3rd filtering: low price 30m < BB30_30m lowerband + // 4th filtering: RSI3_30m < 15, RSI10_30m < 35 + // 5th filtering: RSI3_1m < 10, RSI10_1m < 25 + // 6th filtering: opclo_1m < EMA3_1m, opclo_1m < EMA10_1m + + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + let mut opclo_1d_vec: Vec = Vec::new(); + let mut bb30_1d_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + let mut current_price: Option; + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1d_option = alldata.bb30_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && bb30_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + bb30_1d_vec = alldata.bb30_1d_data[bb30_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && bb30_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + bb30_1d_vec.reverse(); + bb30_1d_vec.truncate(2); + bb30_1d_vec.reverse(); + + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + current_price = get_current_price(&symbol, &alldata.price_vec).await; + + if current_price.is_some() { + if current_price.unwrap().is_normal() { + if (bb30_1d_vec[T].upperband + > opclo_1d_vec.first().unwrap().opclo_price) + && (opclo_1d_vec.first().unwrap().opclo_price + > current_price.unwrap()) + { + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1d_vec[T].upperband + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + let mut opclo_30m_vec: Vec = Vec::new(); + let mut ema3_30m_vec: Vec = Vec::new(); + let mut sma30_30m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + let mut cnt = 0; + for symbol in filtered_1st_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_30m_option = alldata.sma30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && ema3_30m_option.is_some() && sma30_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + ema3_30m_vec = alldata.ema3_30m_data[ema3_30m_option.unwrap()].1.clone(); + sma30_30m_vec = alldata.sma30_30m_data[sma30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 && ema3_30m_vec.len() >= 1 && sma30_30m_vec.len() >= 1 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + opclo_30m_vec.reverse(); + + cnt = 0; + for element in &opclo_30m_vec { + if element.candle_type == "DOWN" { + cnt += 1; + } + } + + if cnt >= 4 { + let result = ema3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_30m_vec[T].ema_value > opclo_30m_vec.last().unwrap().opclo_price + { + let result = sma30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma30_30m_vec[T].sma_value + > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + } + + // 3rd filtering: low price 30m < BB30_30m lowerband + let mut low_30m_vec: Vec = Vec::new(); + let mut bb30_30m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + for symbol in filtered_2nd_symbols { + let low_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let bb30_30m_option = alldata.bb30_30m_data.iter().position(|x| *x.0 == *symbol); + + if low_30m_option.is_some() && bb30_30m_option.is_some() { + low_30m_vec = alldata.rt_price_30m_vec[low_30m_option.unwrap()].1.clone(); + bb30_30m_vec = alldata.bb30_30m_data[bb30_30m_option.unwrap()].1.clone(); + + if low_30m_vec.len() >= 1 && bb30_30m_vec.len() >= 1 { + let result = bb30_30m_vec.binary_search_by_key( + &low_30m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_30m_vec[T].lowerband > low_30m_vec.last().unwrap().low_price { + filtered_3rd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + + // 4th filtering: 30m RSI 3 < 15, 30m RSI 10 < 35 + let mut rsi3_30m_vec: Vec = Vec::new(); + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec = Vec::new(); + for symbol in filtered_3rd_symbols { + let rsi3_30m_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_30m_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_30m_option.is_some() && rsi10_30m_option.is_some() && opclo_30m_option.is_some() { + rsi3_30m_vec = alldata.rsi3_30m_data[rsi3_30m_option.unwrap()].1.clone(); + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_30m_vec.len() >= 1 && rsi10_30m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + let result = rsi3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_30m_vec[T].rsi_value < 15.0 { + let result = rsi10_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_30m_vec[T].rsi_value < 35.0 { + filtered_4th_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 5th filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut filtered_5th_symbols: Vec = Vec::new(); // (symbol, closetime) + for symbol in filtered_4th_symbols { + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + + if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() { + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + + if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 { + if rsi3_1m_vec.last().unwrap().rsi_value < 10.0 + && rsi10_1m_vec.last().unwrap().rsi_value < 25.0 + { + filtered_5th_symbols.push(symbol); + } + } + } + } + + // 6th filtering: opclo_1m < EMA3_1m, opclo_1m < EMA10_1m, clo_1m < BB30_1m lowerband + let mut opclo_1m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut ema3_1m_vec: Vec = Vec::new(); + let mut ema10_1m_vec: Vec = Vec::new(); + let mut bb30_1m_vec: Vec = Vec::new(); + let mut filtered_6th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_5th_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + let ema10_1m_option = alldata.ema10_1m_data.iter().position(|x| *x.0 == *symbol); + let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() + && opclo_30m_option.is_some() + && ema3_1m_option.is_some() + && ema10_1m_option.is_some() + && bb30_1m_option.is_some() + { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + ema10_1m_vec = alldata.ema10_1m_data[ema10_1m_option.unwrap()].1.clone(); + bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + + if opclo_1m_vec.len() >= 1 + && opclo_30m_vec.len() >= 1 + && ema3_1m_vec.len() >= 1 + && ema10_1m_vec.len() >= 1 + && bb30_1m_vec.len() >= 1 + { + let result = ema3_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_1m_vec[T].ema_value > opclo_1m_vec.last().unwrap().opclo_price { + let result = ema10_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema10_1m_vec[T].ema_value + > opclo_1m_vec.last().unwrap().opclo_price + { + let result = bb30_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| { + close_time + }, + ); + match result { + Ok(T) => { + if bb30_1m_vec[T].lowerband + > opclo_1d_vec.last().unwrap().close_price + { + filtered_6th_symbols.push(( + symbol, + opclo_30m_vec.last().unwrap().close_time, + )); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + insert_pre_suggested_coins(13, &filtered_6th_symbols, alldata).await; + + Ok(()) +} + +pub async fn strategist_014( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_014 procedure] + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d, + // 2nd filtering: the latest 2 opclo_1d < SMA3_1d (apply this filtering if 5days angle of marketcap is negative and marketcap opinion is DOWN or KEEP) + // 3rd filtering: RSI3_1d < 20, RSI10_1d < 35, RSI30_1d < 45 (if 5days angle of marketcap is negative and marketcap opinion is UP, this filtering will be passed) + // 4th filtering: + // 1) check the latest candle is DOWN (common) + // 2) DOWN candles having 0.3% below change is not counted + // 3) check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + // 4) OR, check 5 samples of candle 30m has DOWN candles at least 3, the latest clo_30m < BB30_30m lowerband + // 5th filtering: RSI3_30m < 20, RSI10_30m < 35 + // 6th filtering: RSI3_1m < 10, RSI10_1m < 25 + // 7th filtering: opclo_1m < EMA3_1m, opclo_1m < EMA10_1m, clo_1m < BB30_1m lowerband + // 8th filtering: check the latest candle is DOWN and check 5 samples of candle 1m has DOWN candles at least 3 + + let signal_opinions = select_signal_association_opinion().await; + let market_cap_index = select_marketcap().await; + + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band and SMA3_1d, current price < previous opclo_1d, + let mut opclo_1d_vec: Vec = Vec::new(); + let mut bb30_1d_vec: Vec = Vec::new(); + let mut sma3_1d_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + let mut current_price: Option; + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1d_option = alldata.bb30_1d_data.iter().position(|x| *x.0 == *symbol); + let sma3_1d_option = alldata.sma3_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && bb30_1d_option.is_some() && sma3_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + bb30_1d_vec = alldata.bb30_1d_data[bb30_1d_option.unwrap()].1.clone(); + sma3_1d_vec = alldata.sma3_1d_data[sma3_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && bb30_1d_vec.len() >= 2 && sma3_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + bb30_1d_vec.reverse(); + bb30_1d_vec.truncate(2); + bb30_1d_vec.reverse(); + sma3_1d_vec.reverse(); + sma3_1d_vec.truncate(2); + sma3_1d_vec.reverse(); + + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + current_price = get_current_price(&symbol, &alldata.price_vec).await; + + if current_price.is_some() { + if current_price.unwrap().is_normal() { + if (bb30_1d_vec[T].upperband + > opclo_1d_vec.first().unwrap().opclo_price) + && (opclo_1d_vec.first().unwrap().opclo_price + > current_price.unwrap()) + { + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1d_vec[T].upperband + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: the latest 2 opclo_1d < SMA3_1d (apply this filtering if 5days angle of marketcap is negative and marketcap opinion is DOWN or KEEP) + let mut opclo_1d_vec: Vec = Vec::new(); + let mut sma3_1d_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + if (signal_opinions[0].opinion.contains("DOWN") || signal_opinions[0].opinion.contains("KEEP")) + && market_cap_index[1].market_cap_index.is_sign_negative() + { + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let sma3_1d_option = alldata.sma3_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && sma3_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + sma3_1d_vec = alldata.sma3_1d_data[sma3_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && sma3_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + sma3_1d_vec.reverse(); + sma3_1d_vec.truncate(2); + sma3_1d_vec.reverse(); + + let result = sma3_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma3_1d_vec[T].sma_value > opclo_1d_vec.first().unwrap().opclo_price + { + let result = sma3_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma3_1d_vec[T].sma_value + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + } else { + filtered_2nd_symbols = filtered_1st_symbols; + } + + // 3rd filtering: RSI3_1d < 20, RSI10_1d < 35, RSI30_1d < 45 (if 5days angle of marketcap is negative and marketcap opinion is UP, this filtering will be passed) + let mut rsi3_1d_vec: Vec = Vec::new(); + let mut rsi10_1d_vec: Vec = Vec::new(); + let mut rsi30_1d_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + if signal_opinions[0].opinion.contains("UP") + && market_cap_index[1].market_cap_index.is_sign_negative() + { + filtered_2nd_symbols = filtered_2nd_symbols; + } else { + for symbol in filtered_2nd_symbols { + let rsi3_1d_option = alldata.rsi3_1d_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1d_option = alldata.rsi10_1d_data.iter().position(|x| *x.0 == *symbol); + let rsi30_1d_option = alldata.rsi30_1d_data.iter().position(|x| *x.0 == *symbol); + + if rsi3_1d_option.is_some() && rsi10_1d_option.is_some() && rsi30_1d_option.is_some() { + rsi3_1d_vec = alldata.rsi3_1d_data[rsi3_1d_option.unwrap()].1.clone(); + rsi10_1d_vec = alldata.rsi10_1d_data[rsi10_1d_option.unwrap()].1.clone(); + rsi30_1d_vec = alldata.rsi30_1d_data[rsi30_1d_option.unwrap()].1.clone(); + + if rsi3_1d_vec.len() >= 1 && rsi10_1d_vec.len() >= 1 && rsi30_1d_vec.len() >= 1 { + if rsi3_1d_vec.last().unwrap().rsi_value < 20.0 + && rsi10_1d_vec.last().unwrap().rsi_value < 35.0 + && rsi30_1d_vec.last().unwrap().rsi_value < 45.0 + { + filtered_3rd_symbols.push(symbol); + } + } + } + } + } + + // 4th filtering: + // 1) check the latest candle is DOWN (common) + // 2) DOWN candles having 0.3% below change is not counted + // 3) check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + // 4) OR, check 5 samples of candle 30m has DOWN candles at least 3, the latest clo_30m < BB30_30m lowerband + + let mut opclo_30m_vec: Vec = Vec::new(); + let mut ema3_30m_vec: Vec = Vec::new(); + let mut sma30_30m_vec: Vec = Vec::new(); + let mut bb30_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec = Vec::new(); + let mut cnt = 0; + for symbol in filtered_3rd_symbols { + let mut is_filtering_passed: bool = false; + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_30m_option = alldata.sma30_30m_data.iter().position(|x| *x.0 == *symbol); + let bb30_30m_option = alldata.bb30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() + && ema3_30m_option.is_some() + && sma30_30m_option.is_some() + && bb30_30m_option.is_some() + { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + ema3_30m_vec = alldata.ema3_30m_data[ema3_30m_option.unwrap()].1.clone(); + sma30_30m_vec = alldata.sma30_30m_data[sma30_30m_option.unwrap()].1.clone(); + bb30_30m_vec = alldata.bb30_30m_data[bb30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 + && ema3_30m_vec.len() >= 1 + && sma30_30m_vec.len() >= 1 + && bb30_30m_vec.len() >= 1 + { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + opclo_30m_vec.reverse(); + + if opclo_30m_vec.last().unwrap().candle_type == "DOWN" { + cnt = 0; + for element in &opclo_30m_vec { + if element.candle_type == "DOWN" { + if (element.open_price / element.close_price) - 1.0 > 0.003 { + cnt += 1; + } + } + } + + if cnt >= 4 { + let result = ema3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_30m_vec[T].ema_value + > opclo_30m_vec.last().unwrap().opclo_price + { + let result = sma30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma30_30m_vec[T].sma_value + > opclo_30m_vec.last().unwrap().opclo_price + { + is_filtering_passed = true; + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } else if cnt == 3 { + let result = bb30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_30m_vec[T].lowerband + > opclo_30m_vec.last().unwrap().close_price + { + is_filtering_passed = true; + } + } + Err(E) => {} + } + } + } + } + } + if is_filtering_passed == true { + filtered_4th_symbols.push(symbol); + } + } + + // 5th filtering: RSI3_30m < 20, RSI10_30m < 35 + let mut rsi3_30m_vec: Vec = Vec::new(); + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_5th_symbols: Vec = Vec::new(); + for symbol in filtered_4th_symbols { + let rsi3_30m_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_30m_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_30m_option.is_some() && rsi10_30m_option.is_some() && opclo_30m_option.is_some() { + rsi3_30m_vec = alldata.rsi3_30m_data[rsi3_30m_option.unwrap()].1.clone(); + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_30m_vec.len() >= 1 && rsi10_30m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + let result = rsi3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_30m_vec[T].rsi_value < 20.0 { + let result = rsi10_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_30m_vec[T].rsi_value < 35.0 { + filtered_5th_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 6th filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut filtered_6th_symbols: Vec = Vec::new(); + for symbol in filtered_5th_symbols { + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + + if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() { + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + + if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 { + if rsi3_1m_vec.last().unwrap().rsi_value < 10.0 + && rsi10_1m_vec.last().unwrap().rsi_value < 25.0 + { + filtered_6th_symbols.push(symbol); + } + } + } + } + + // // 6. 5th filtering symbols: 30m current vol > 30m previous vol * 2 + // let mut opclo_30m_vec: Vec = Vec::new(); + // let mut filtered_4th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + // for element in filtered_3rd_symbols { + // let opclo_30m_result = read_price_into_vector("30m", &element).await; + + // if opclo_30m_result.is_ok() { + // let opclo_30m_option = opclo_30m_result?; + // if opclo_30m_option.is_some() { + // opclo_30m_vec = opclo_30m_option.unwrap(); + + // if opclo_30m_vec.len() >= 2 { + // opclo_30m_vec.reverse(); + // opclo_30m_vec.truncate(2); + // opclo_30m_vec.reverse(); + + // if 2.0 * opclo_30m_vec.first().unwrap().quote_asset_volume < opclo_30m_vec.last().unwrap().quote_asset_volume && opclo_30m_vec.last().unwrap().candle_type == "DOWN" { + // filtered_4th_symbols.push((element, opclo_30m_vec.last().unwrap().close_time)); + // } + // } + // } + // } + // } + + // 7th filtering: opclo_1m < EMA3_1m, opclo_1m < EMA10_1m, clo_1m < BB30_1m lowerband + let mut opclo_1m_vec: Vec = Vec::new(); + let mut ema3_1m_vec: Vec = Vec::new(); + let mut ema10_1m_vec: Vec = Vec::new(); + let mut bb30_1m_vec: Vec = Vec::new(); + let mut filtered_7th_symbols: Vec = Vec::new(); + for symbol in filtered_6th_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let ema3_1m_option = alldata.ema3_1m_data.iter().position(|x| *x.0 == *symbol); + let ema10_1m_option = alldata.ema10_1m_data.iter().position(|x| *x.0 == *symbol); + let bb30_1m_option = alldata.bb30_1m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1m_option.is_some() + && ema3_1m_option.is_some() + && ema10_1m_option.is_some() + && bb30_1m_option.is_some() + { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + ema3_1m_vec = alldata.ema3_1m_data[ema3_1m_option.unwrap()].1.clone(); + ema10_1m_vec = alldata.ema10_1m_data[ema10_1m_option.unwrap()].1.clone(); + bb30_1m_vec = alldata.bb30_1m_data[bb30_1m_option.unwrap()].1.clone(); + + if opclo_1m_vec.len() >= 1 + && ema3_1m_vec.len() >= 1 + && ema10_1m_vec.len() >= 1 + && bb30_1m_vec.len() >= 1 + { + let result = ema3_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_1m_vec[T].ema_value > opclo_1m_vec.last().unwrap().opclo_price { + let result = ema10_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema10_1m_vec[T].ema_value + > opclo_1m_vec.last().unwrap().opclo_price + { + let result = bb30_1m_vec.binary_search_by_key( + &opclo_1m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| { + close_time + }, + ); + match result { + Ok(T) => { + if bb30_1m_vec[T].lowerband + > opclo_1d_vec.last().unwrap().close_price + { + filtered_7th_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 8th filtering: + // check the latest candle is DOWN and check 5 samples of candle 30m has DOWN candles at least 3 + let mut opclo_1m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_8th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + + let mut cnt = 0; + for symbol in filtered_7th_symbols { + let opclo_1m_option = alldata.rt_price_1m_vec.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && opclo_1m_option.is_some() { + opclo_1m_vec = alldata.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if opclo_1m_vec.len() >= 5 && opclo_30m_vec.len() >= 5 { + opclo_1m_vec.reverse(); + opclo_1m_vec.truncate(5); + opclo_1m_vec.reverse(); + + if opclo_1m_vec.last().unwrap().candle_type == "DOWN" { + cnt = 0; + for element in &opclo_1m_vec { + if element.candle_type == "DOWN" { + cnt += 1; + } + } + + if cnt >= 3 { + filtered_8th_symbols + .push((symbol, opclo_30m_vec.last().unwrap().close_time)); + } + } + } + } + } + + insert_pre_suggested_coins(14, &filtered_8th_symbols, alldata).await; + + Ok(()) +} + +pub async fn strategist_015( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_015 procedure] + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + // 3rd filtering: low price 30m < BB30_30m lowerband + // 4th filtering: RSI3_30m < 15, RSI10_30m < 35 + // 5th filtering: RSI3_1m < 10, RSI10_1m < 25 + + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + let mut opclo_1d_vec: Vec = Vec::new(); + let mut bb30_1d_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + let mut current_price: Option; + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1d_option = alldata.bb30_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && bb30_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + bb30_1d_vec = alldata.bb30_1d_data[bb30_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && bb30_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + bb30_1d_vec.reverse(); + bb30_1d_vec.truncate(2); + bb30_1d_vec.reverse(); + + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + current_price = get_current_price(&symbol, &alldata.price_vec).await; + + if current_price.is_some() { + if current_price.unwrap().is_normal() { + if (bb30_1d_vec[T].upperband + > opclo_1d_vec.first().unwrap().opclo_price) + && (opclo_1d_vec.first().unwrap().opclo_price + > current_price.unwrap()) + { + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1d_vec[T].upperband + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + let mut opclo_30m_vec: Vec = Vec::new(); + let mut ema3_30m_vec: Vec = Vec::new(); + let mut sma30_30m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + let mut cnt = 0; + for symbol in filtered_1st_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_30m_option = alldata.sma30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && ema3_30m_option.is_some() && sma30_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + ema3_30m_vec = alldata.ema3_30m_data[ema3_30m_option.unwrap()].1.clone(); + sma30_30m_vec = alldata.sma30_30m_data[sma30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 && ema3_30m_vec.len() >= 1 && sma30_30m_vec.len() >= 1 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + opclo_30m_vec.reverse(); + + cnt = 0; + for element in &opclo_30m_vec { + if element.candle_type == "DOWN" { + cnt += 1; + } + } + + if cnt >= 4 { + let result = ema3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_30m_vec[T].ema_value > opclo_30m_vec.last().unwrap().opclo_price + { + let result = sma30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma30_30m_vec[T].sma_value + > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + } + + // 3rd filtering: low price 30m < BB30_30m lowerband + let mut low_30m_vec: Vec = Vec::new(); + let mut bb30_30m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + for symbol in filtered_2nd_symbols { + let low_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let bb30_30m_option = alldata.bb30_30m_data.iter().position(|x| *x.0 == *symbol); + + if low_30m_option.is_some() && bb30_30m_option.is_some() { + low_30m_vec = alldata.rt_price_30m_vec[low_30m_option.unwrap()].1.clone(); + bb30_30m_vec = alldata.bb30_30m_data[bb30_30m_option.unwrap()].1.clone(); + + if low_30m_vec.len() >= 1 && bb30_30m_vec.len() >= 1 { + let result = bb30_30m_vec.binary_search_by_key( + &low_30m_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_30m_vec[T].lowerband > low_30m_vec.last().unwrap().low_price { + filtered_3rd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + } + + // 4th filtering: 30m RSI 3 < 15, 30m RSI 10 < 35 + let mut rsi3_30m_vec: Vec = Vec::new(); + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec = Vec::new(); + for symbol in filtered_3rd_symbols { + let rsi3_30m_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_30m_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_30m_option.is_some() && rsi10_30m_option.is_some() && opclo_30m_option.is_some() { + rsi3_30m_vec = alldata.rsi3_30m_data[rsi3_30m_option.unwrap()].1.clone(); + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_30m_vec.len() >= 1 && rsi10_30m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + let result = rsi3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_30m_vec[T].rsi_value < 15.0 { + let result = rsi10_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_30m_vec[T].rsi_value < 35.0 { + filtered_4th_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 5th filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_5th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_4th_symbols { + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() && opclo_30m_option.is_some() { + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + if rsi3_1m_vec.last().unwrap().rsi_value < 10.0 + && rsi10_1m_vec.last().unwrap().rsi_value < 25.0 + { + filtered_5th_symbols.push((symbol, opclo_30m_vec.last().unwrap().close_time)); + } + } + + // if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + // let result = rsi3_1m_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi3_1m_vec[T].rsi_value < 10.0 { + // let result = rsi10_1m_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi10_1m_vec[T].rsi_value < 25.0 { + // filtered_5th_symbols.push((symbol, opclo_30m_vec.last().unwrap().close_time)); + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + } + } + // // 6. 5th filtering symbols: 30m current vol > 30m previous vol * 2 + // let mut opclo_30m_vec: Vec = Vec::new(); + // let mut filtered_5th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + // for element in filtered_4th_symbols { + // let opclo_30m_result = read_price_into_vector("30m", &element).await; + + // if opclo_30m_result.is_ok() { + // let opclo_30m_option = opclo_30m_result?; + // if opclo_30m_option.is_some() { + // opclo_30m_vec = opclo_30m_option.unwrap(); + + // if opclo_30m_vec.len() >= 2 { + // opclo_30m_vec.reverse(); + // opclo_30m_vec.truncate(2); + // opclo_30m_vec.reverse(); + + // if 2.0 * opclo_30m_vec.first().unwrap().quote_asset_volume < opclo_30m_vec.last().unwrap().quote_asset_volume && opclo_30m_vec.last().unwrap().candle_type == "DOWN" { + // filtered_5th_symbols.push((element, opclo_30m_vec.last().unwrap().close_time)); + // } + // } + // } + // } + // } + // println!("5: {:?}", filtered_5th_symbols); + // 7. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(15, &filtered_5th_symbols, alldata).await; + + Ok(()) +} + +pub async fn strategist_016( + alldata: &AllData, +) -> Result<(), Box> { + // [strategist_016 procedure] + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + // 3rd filtering: RSI3_30m < 15, RSI10_30m < 35 + // 4th filtering: RSI3_1m < 10, RSI10_1m < 25 + + // 1st filtering: the latest 2 opclo_1d < BB30_1d upper band, current price < previous opclo_1d + let mut opclo_1d_vec: Vec = Vec::new(); + let mut bb30_1d_vec: Vec = Vec::new(); + let mut filtered_1st_symbols: Vec = Vec::new(); + let mut current_price: Option; + for symbol in &alldata.valid_symbol_vec { + let opclo_1d_option = alldata.rt_price_1d_vec.iter().position(|x| *x.0 == *symbol); + let bb30_1d_option = alldata.bb30_1d_data.iter().position(|x| *x.0 == *symbol); + + if opclo_1d_option.is_some() && bb30_1d_option.is_some() { + opclo_1d_vec = alldata.rt_price_1d_vec[opclo_1d_option.unwrap()].1.clone(); + bb30_1d_vec = alldata.bb30_1d_data[bb30_1d_option.unwrap()].1.clone(); + + if opclo_1d_vec.len() >= 2 && bb30_1d_vec.len() >= 2 { + opclo_1d_vec.reverse(); + opclo_1d_vec.truncate(2); + opclo_1d_vec.reverse(); + bb30_1d_vec.reverse(); + bb30_1d_vec.truncate(2); + bb30_1d_vec.reverse(); + + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.first().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + current_price = get_current_price(&symbol, &alldata.price_vec).await; + + if current_price.is_some() { + if current_price.unwrap().is_normal() { + if (bb30_1d_vec[T].upperband + > opclo_1d_vec.first().unwrap().opclo_price) + && (opclo_1d_vec.first().unwrap().opclo_price + > current_price.unwrap()) + { + let result = bb30_1d_vec.binary_search_by_key( + &opclo_1d_vec.last().unwrap().close_time, + |&BollingerBandData { + sma, + upperband, + lowerband, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if bb30_1d_vec[T].upperband + > opclo_1d_vec.last().unwrap().opclo_price + { + filtered_1st_symbols.push(symbol.clone()); + } + } + Err(E) => {} + } + } + } + } + } + Err(E) => {} + } + } + } + } + + // 2nd filtering: check 5 samples of candle 30m has DOWN candles at least 4, the latest opclo_30m < EMA3_30m, the latest opclo_30m < SMA30_30m + let mut opclo_30m_vec: Vec = Vec::new(); + let mut ema3_30m_vec: Vec = Vec::new(); + let mut sma30_30m_vec: Vec = Vec::new(); + let mut filtered_2nd_symbols: Vec = Vec::new(); + let mut cnt = 0; + for symbol in filtered_1st_symbols { + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + let ema3_30m_option = alldata.ema3_30m_data.iter().position(|x| *x.0 == *symbol); + let sma30_30m_option = alldata.sma30_30m_data.iter().position(|x| *x.0 == *symbol); + + if opclo_30m_option.is_some() && ema3_30m_option.is_some() && sma30_30m_option.is_some() { + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + ema3_30m_vec = alldata.ema3_30m_data[ema3_30m_option.unwrap()].1.clone(); + sma30_30m_vec = alldata.sma30_30m_data[sma30_30m_option.unwrap()].1.clone(); + + if opclo_30m_vec.len() >= 5 && ema3_30m_vec.len() >= 1 && sma30_30m_vec.len() >= 1 { + opclo_30m_vec.reverse(); + opclo_30m_vec.truncate(5); + opclo_30m_vec.reverse(); + + cnt = 0; + for element in &opclo_30m_vec { + if element.candle_type == "DOWN" { + cnt += 1; + } + } + + if cnt >= 4 { + let result = ema3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if ema3_30m_vec[T].ema_value > opclo_30m_vec.last().unwrap().opclo_price + { + let result = sma30_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&SmaData { + sma_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if sma30_30m_vec[T].sma_value + > opclo_30m_vec.last().unwrap().opclo_price + { + filtered_2nd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + } + + // 3rd filtering: RSI3_30m < 15, RSI10_30m < 35 + let mut rsi3_30m_vec: Vec = Vec::new(); + let mut rsi10_30m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_3rd_symbols: Vec = Vec::new(); + for symbol in filtered_2nd_symbols { + let rsi3_30m_option = alldata.rsi3_30m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_30m_option = alldata.rsi10_30m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_30m_option.is_some() && rsi10_30m_option.is_some() && opclo_30m_option.is_some() { + rsi3_30m_vec = alldata.rsi3_30m_data[rsi3_30m_option.unwrap()].1.clone(); + rsi10_30m_vec = alldata.rsi10_30m_data[rsi10_30m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_30m_vec.len() >= 1 && rsi10_30m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + let result = rsi3_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi3_30m_vec[T].rsi_value < 15.0 { + let result = rsi10_30m_vec.binary_search_by_key( + &opclo_30m_vec.last().unwrap().close_time, + |&RsiData { + rsi_value, + close_time, + }| close_time, + ); + match result { + Ok(T) => { + if rsi10_30m_vec[T].rsi_value < 35.0 { + filtered_3rd_symbols.push(symbol); + } + } + Err(E) => {} + } + } + } + Err(E) => {} + } + } + } + } + + // 4th filtering: RSI3_1m < 10, RSI10_1m < 25 + let mut rsi3_1m_vec: Vec = Vec::new(); + let mut rsi10_1m_vec: Vec = Vec::new(); + let mut opclo_30m_vec: Vec = Vec::new(); + let mut filtered_4th_symbols: Vec<(String, i64)> = Vec::new(); // (symbol, closetime) + for symbol in filtered_3rd_symbols { + let rsi3_1m_option = alldata.rsi3_1m_data.iter().position(|x| *x.0 == *symbol); + let rsi10_1m_option = alldata.rsi10_1m_data.iter().position(|x| *x.0 == *symbol); + let opclo_30m_option = alldata + .rt_price_30m_vec + .iter() + .position(|x| *x.0 == *symbol); + + if rsi3_1m_option.is_some() && rsi10_1m_option.is_some() && opclo_30m_option.is_some() { + rsi3_1m_vec = alldata.rsi3_1m_data[rsi3_1m_option.unwrap()].1.clone(); + rsi10_1m_vec = alldata.rsi10_1m_data[rsi10_1m_option.unwrap()].1.clone(); + opclo_30m_vec = alldata.rt_price_30m_vec[opclo_30m_option.unwrap()] + .1 + .clone(); + + if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + if rsi3_1m_vec.last().unwrap().rsi_value < 10.0 + && rsi10_1m_vec.last().unwrap().rsi_value < 25.0 + { + filtered_4th_symbols.push((symbol, opclo_30m_vec.last().unwrap().close_time)); + } + } + + // if rsi3_1m_vec.len() >= 1 && rsi10_1m_vec.len() >= 1 && opclo_30m_vec.len() >= 1 { + // let result = rsi3_1m_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi3_1m_vec[T].rsi_value < 10.0 { + // let result = rsi10_1m_vec.binary_search_by_key(&opclo_30m_vec.last().unwrap().close_time, |&RsiData{rsi_value, close_time}|close_time); + // match result { + // Ok(T) => { + // if rsi10_1m_vec[T].rsi_value < 25.0 { + // filtered_5th_symbols.push((symbol, opclo_30m_vec.last().unwrap().close_time)); + // } + // }, + // Err(E) => {} + // } + // } + // }, + // Err(E) => {} + // } + // } + } + } + + // // 6. 5th filtering symbols: 30m current vol > 30m previous vol * 2 + // let mut opclo_30m_vec: Vec = Vec::new(); + // let mut filtered_4th_symbols: Vec<(String, u64)> = Vec::new(); // (symbol, closetime) + // for element in filtered_3rd_symbols { + // let opclo_30m_result = read_price_into_vector("30m", &element).await; + + // if opclo_30m_result.is_ok() { + // let opclo_30m_option = opclo_30m_result?; + // if opclo_30m_option.is_some() { + // opclo_30m_vec = opclo_30m_option.unwrap(); + + // if opclo_30m_vec.len() >= 2 { + // opclo_30m_vec.reverse(); + // opclo_30m_vec.truncate(2); + // opclo_30m_vec.reverse(); + + // if 2.0 * opclo_30m_vec.first().unwrap().quote_asset_volume < opclo_30m_vec.last().unwrap().quote_asset_volume && opclo_30m_vec.last().unwrap().candle_type == "DOWN" { + // filtered_4th_symbols.push((element, opclo_30m_vec.last().unwrap().close_time)); + // } + // } + // } + // } + // } + // println!("4: {:?}", filtered_4th_symbols); + // 7. insert record into [pre_suggested_coin_list] + insert_pre_suggested_coins(16, &filtered_4th_symbols, alldata).await; + + Ok(()) +} + +// useful functions for strategists +pub async fn get_current_price(symbol: &String, price_vec: &Vec) -> Option { + let index_result = price_vec.iter().position(|x| *x.symbol == *symbol); + match index_result { + Some(T) => Some(price_vec[T].current_price), + None => None, + } +} + +async fn insert_pre_suggested_coins( + registerer: i32, + filtered_symbols: &Vec<(String, i64)>, + alldata: &AllData, +) -> Result<(), Box> { + // Check the existance of record that is registered by this strategist + let mut dest_table_name = String::from("suggested_coin_list"); + let mut dest2_table_name = String::from("buy_ordered_coin_list"); + let mut dest3_table_name = String::from("pre_suggested_coin_list"); + let mut insert_table_name = String::from("pre_suggested_coin_list"); + let mut exists_condition_build = String::from("registerer="); + exists_condition_build.push_str(registerer.to_string().as_str()); + let mut exists_condition = Some(exists_condition_build); + let exists_result1 = exists_record(&dest_table_name, &exists_condition).await; + let exists_result2 = exists_record(&dest2_table_name, &exists_condition).await; + let exists_result3 = exists_record(&dest3_table_name, &exists_condition).await; + let insert_columns = vec![ + "symbol", + "close_time", + "suggested_price", + "current_price", + "registered_server_epoch", + "profit_percent", + "minimum_profit_percent", + "maximum_profit_percent", + "registerer", + ]; + + if exists_result1 == true && exists_result2 == true && exists_result3 == true { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + #[derive(FromRow)] + struct SelectData2 { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let mut select_columns2 = String::from("symbol, close_time"); + let select_data_structure2 = SelectData2 { + symbol: String::new(), + close_time: 0, + }; + let suggested_coin_list = try_select_record( + &dest_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + let ordered_coin_list = try_select_record( + &dest2_table_name, + &select_columns2, + &select_condition, + &select_data_structure2, + ) + .await?; + let pre_suggested_coin_list = try_select_record( + &dest3_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + for list_element in &ordered_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + for list_element in &pre_suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == true && exists_result2 == true && exists_result3 == false { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + #[derive(FromRow)] + struct SelectData2 { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let mut select_columns2 = String::from("symbol, close_time"); + let select_data_structure2 = SelectData2 { + symbol: String::new(), + close_time: 0, + }; + let suggested_coin_list = try_select_record( + &dest_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + let ordered_coin_list = try_select_record( + &dest2_table_name, + &select_columns2, + &select_condition, + &select_data_structure2, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + for list_element in &ordered_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == true && exists_result2 == false && exists_result3 == true { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let suggested_coin_list = try_select_record( + &dest_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + let pre_suggested_coin_list = try_select_record( + &dest3_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + for list_element in &pre_suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == true && exists_result2 == false && exists_result3 == false { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let suggested_coin_list = try_select_record( + &dest_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == false && exists_result2 == true && exists_result3 == true { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + #[derive(FromRow)] + struct SelectData2 { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let mut select_columns2 = String::from("symbol, close_time"); + let select_data_structure2 = SelectData2 { + symbol: String::new(), + close_time: 0, + }; + let ordered_coin_list = try_select_record( + &dest2_table_name, + &select_columns2, + &select_condition, + &select_data_structure2, + ) + .await?; + let pre_suggested_coin_list = try_select_record( + &dest3_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + + // insert record without duplicate(registerer, close_time, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &ordered_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + for list_element in &pre_suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == false && exists_result2 == true && exists_result3 == false { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + #[derive(FromRow)] + struct SelectData2 { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let mut select_columns2 = String::from("symbol, close_time"); + let select_data_structure2 = SelectData2 { + symbol: String::new(), + close_time: 0, + }; + let ordered_coin_list = try_select_record( + &dest2_table_name, + &select_columns2, + &select_condition, + &select_data_structure2, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &ordered_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else if exists_result1 == false && exists_result2 == false && exists_result3 == true { + // Bring the current list of pre-suggested coins from [pre_suggested_coin_list] and [ordered_coin_list] + #[derive(FromRow)] + struct SelectData { + symbol: String, + close_time: i64, + } + let mut select_columns = String::from("symbol, close_time"); + let mut condition_build = String::from("WHERE registerer="); + condition_build.push_str(registerer.to_string().as_str()); + let select_condition = Some(condition_build); + let select_data_structure = SelectData { + symbol: String::new(), + close_time: 0, + }; + let pre_suggested_coin_list = try_select_record( + &dest3_table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + + // insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list] + for filtered_element in filtered_symbols { + let mut is_dupe = false; // initialize + + for list_element in &pre_suggested_coin_list { + if (filtered_element.0 == list_element.symbol) + && (filtered_element.1 == list_element.close_time) + { + is_dupe = true; + break; + } + } + + if is_dupe == false { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + } else { + for filtered_element in filtered_symbols { + let current_price = get_current_price(&filtered_element.0, &alldata.price_vec) + .await + .unwrap(); + let insert_values = vec![ + filtered_element.0.clone(), // symbol + filtered_element.1.to_string(), // close_time + current_price.to_string(), // suggested_price + current_price.to_string(), // current_price + server_epoch().await.to_string(), // registered_server_epoch + 0.0.to_string(), // profit_percent + 0.0.to_string(), // minimum_profit_percent + 0.0.to_string(), // maximum_profit_percent + registerer.to_string(), // registerer + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } + + Ok(()) +} diff --git a/src/coin_health_check_team.rs b/src/coin_health_check_team.rs new file mode 100644 index 0000000..91562a8 --- /dev/null +++ b/src/coin_health_check_team.rs @@ -0,0 +1,3 @@ +pub mod monitors; +pub mod request_candles; +pub mod request_others; diff --git a/src/coin_health_check_team/monitors.rs b/src/coin_health_check_team/monitors.rs new file mode 100644 index 0000000..f5c3bb6 --- /dev/null +++ b/src/coin_health_check_team/monitors.rs @@ -0,0 +1,598 @@ +use crate::coin_health_check_team::request_candles::*; +use crate::RunningMode::*; +use crate::{database_control::*, RUNNING_MODE}; +use hex::ToHex; +use hmac_sha256::HMAC; +use reqwest::{Client, ClientBuilder, Response}; +use rust_decimal::{prelude::ToPrimitive, Decimal}; +use serde::Deserialize; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use std::borrow::{Borrow, BorrowMut}; +use std::sync::Arc; +use tokio::{join, sync::Mutex, time::*}; + +#[derive(Debug, FromRow)] +struct AllCoinProfitChangeAvgList { + server_epoch: u64, + cnt: u32, + avg_profit: f64, +} + +// filter valid USDT trades from all24hstatistics table in database +pub async fn collect_valid_usde_trade( + valid_usdt_trade_vec: &mut Vec, +) -> Result<(), Box> { + #[derive(Debug, FromRow)] + struct UsdtTrades { + symbol: String, + } + + // if RUNNING_MODE == SIMUL { // || RUNNING_MODE == REAL { + // let mut usdt_trades = UsdtTrades { symbol: String::new() }; + + // let fetch_table_name = String::from("all_24h_change"); + // let column_name = String::from("symbol"); + // let mut condition_build = String::from("WHERE symbol LIKE '%BUSD' AND symbol NOT LIKE '%DOWNUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0"); + // condition_build.push_str(" AND symbol NOT IN (SELECT symbol FROM stop_usdt_trades)"); + // // add unnessesary coins + // condition_build.push_str(" AND symbol NOT LIKE 'BUSDUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'TUSDUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'USDPUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'SUSDUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'AUDUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'EURUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'GBPUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'USDCUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'BZRXUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'USTUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'NBTUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'VGXUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'RAMPUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'TORNUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'BTTCUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'BTCSTUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'ACAUSDT'"); + // condition_build.push_str(" AND symbol NOT LIKE 'ANCUSDT'"); + // let condition = Some(condition_build); + + // let select_result = select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?; + + // let table_name = String::from("valid_usdt_trades"); + // let columns = vec!["symbol"]; + // let mut symbol_vec: Vec = Vec::new(); + // let mut value_wrapper: Vec> = Vec::new(); + // for element in select_result { + // let mut inner_vec: Vec = Vec::new(); + // inner_vec.push(element.symbol.clone()); + // value_wrapper.push(inner_vec); + // symbol_vec.push(element.symbol); + // } + // *valid_usdt_trade_vec = symbol_vec; + // delete_all_rows(&table_name).await?; + // insert_records(&table_name, &columns, &value_wrapper).await?; + // } else + if RUNNING_MODE == TEST || RUNNING_MODE == REAL || RUNNING_MODE == SIMUL { + let table_name = String::from("valid_usdt_trades"); + let columns = vec!["symbol"]; + let mut symbol_vec: Vec = Vec::new(); + let mut value_wrapper: Vec> = Vec::new(); + + value_wrapper.push(vec![String::from("BTCUSDT")]); + value_wrapper.push(vec![String::from("ETHUSDT")]); + value_wrapper.push(vec![String::from("XRPUSDT")]); + value_wrapper.push(vec![String::from("DOGEUSDT")]); + value_wrapper.push(vec![String::from("LTCUSDT")]); + value_wrapper.push(vec![String::from("TRXUSDT")]); + value_wrapper.push(vec![String::from("DOTUSDT")]); + value_wrapper.push(vec![String::from("LINKUSDT")]); + value_wrapper.push(vec![String::from("AVAXUSDT")]); + value_wrapper.push(vec![String::from("BCHUSDT")]); + value_wrapper.push(vec![String::from("SHIBUSDT")]); + value_wrapper.push(vec![String::from("APTUSDT")]); + value_wrapper.push(vec![String::from("ARBUSDT")]); + value_wrapper.push(vec![String::from("ETCUSDT")]); + value_wrapper.push(vec![String::from("XMRUSDT")]); + value_wrapper.push(vec![String::from("HBARUSDT")]); + value_wrapper.push(vec![String::from("UNIUSDT")]); + + symbol_vec.push(String::from("BTCUSDT")); + symbol_vec.push(String::from("ETHUSDT")); + symbol_vec.push(String::from("XRPUSDT")); + symbol_vec.push(String::from("DOGEUSDT")); + symbol_vec.push(String::from("LTCUSDT")); + symbol_vec.push(String::from("TRXUSDT")); + symbol_vec.push(String::from("DOTUSDT")); + symbol_vec.push(String::from("LINKUSDT")); + symbol_vec.push(String::from("AVAXUSDT")); + symbol_vec.push(String::from("BCHUSDT")); + symbol_vec.push(String::from("SHIBUSDT")); + symbol_vec.push(String::from("APTUSDT")); + symbol_vec.push(String::from("ARBUSDT")); + symbol_vec.push(String::from("ETCUSDT")); + symbol_vec.push(String::from("XMRUSDT")); + symbol_vec.push(String::from("HBARUSDT")); + symbol_vec.push(String::from("UNIUSDT")); + + *valid_usdt_trade_vec = symbol_vec; + delete_all_rows(&table_name).await?; + insert_records(&table_name, &columns, &value_wrapper).await?; + } + + // println!("valid USDT trades 완료"); + + Ok(()) +} + +// async fn detect_new_valid_usdt_trade() -> Result<(), Box>{ +// #[derive(Debug, FromRow)] +// struct UsdtTrades { +// symbol: String, +// } +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; + +// let table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("valid_usdt_trades.symbol"); +// let condition = Some(String::from("LEFT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE prev_valid_usdt_trades.symbol is null")); + +// let select_result = try_select_record(&table_name, &column_name, &condition, &usdt_trades).await.unwrap(); + +// if !select_result.is_empty() { +// let intervals = vec![String::from("1m"), String::from("30m"), String::from("1d"), String::from("1w"), String::from("1mon")]; +// let symbol_vec: Vec = select_result.into_iter().map(|element| element.symbol).collect(); +// for interval in intervals{ +// for element in &symbol_vec { +// create_candle_table(element, &interval).await.unwrap(); +// } +// request_candlestick_initial(&symbol_vec, &interval).await.unwrap(); +// } +// println!("detect_new_valid_usde_trade 완료"); +// } +// Ok(()) +// } + +async fn detect_new_valid_usdt_trade() -> Result<(), Box> { + #[derive(Debug, FromRow)] + struct UsdtTrades { + symbol: String, + } + let mut usdt_trades = UsdtTrades { + symbol: String::new(), + }; + let table_name = String::from("valid_usdt_trades"); + let column_name = String::from("valid_usdt_trades.symbol"); + let condition = Some(String::from("LEFT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE prev_valid_usdt_trades.symbol is null")); + let select_result = + try_select_record(&table_name, &column_name, &condition, &usdt_trades).await?; + if !select_result.is_empty() { + let intervals = vec![ + String::from("1m"), + String::from("30m"), + String::from("1d"), + String::from("1w"), + String::from("1mon"), + ]; + for interval in intervals { + for element in &select_result { + create_candle_table(&element.symbol, &interval).await?; + request_candlestick_initial(element.symbol.clone(), &interval).await?; + } + } + // println!("detect_new_valid_usde_trade 완료"); + } + Ok(()) +} + +async fn deceased_usdt_trade() -> Result<(), Box> { + #[derive(Debug, FromRow)] + struct UsdtTrades { + symbol: String, + } + let mut usdt_trades = UsdtTrades { + symbol: String::new(), + }; + let insert_table_name = String::from("deceased_usdt_trades"); + let table_name = String::from("valid_usdt_trades"); + let column_name = String::from("prev_valid_usdt_trades.symbol"); + let condition = Some(String::from("RIGHT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE valid_usdt_trades.symbol is null")); + + let select_result = + try_select_record(&table_name, &column_name, &condition, &usdt_trades).await?; + + if !select_result.is_empty() { + let columns = vec!["symbol"]; + let mut value_wrapper: Vec> = Vec::new(); + for element in select_result { + let mut inner_vec: Vec = Vec::new(); + inner_vec.push(element.symbol); + value_wrapper.push(inner_vec); + } + insert_records(&insert_table_name, &columns, &value_wrapper).await?; + // println!("deceased_usde_trade 완료"); + } + Ok(()) +} + +pub async fn total_24h_change_profit_index() -> Result<(), Box> +{ + #[derive(Debug, FromRow)] + struct TradesProfit { + pricechangepercent: f64, + } + let mut trades_profit = TradesProfit { + pricechangepercent: 0.0, + }; + let table_name = String::from("all_24h_change"); + let column_name = String::from("priceChangePercent"); + let condition = Some(String::from("WHERE symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0")); + + let select_result = + try_select_record(&table_name, &column_name, &condition, &trades_profit).await?; + + let mut nwa = 0.0; // Normalized Weighted Average + let mut price_down_count = 0; + let mut pdr = 0.0; // Price Down Ratio + let mut index = 0.0; + + for element in &select_result { + if element.pricechangepercent > 10.0 { + nwa += 0.0; + } else if element.pricechangepercent <= 10.0 && element.pricechangepercent > 7.0 { + nwa += 1.0; + } else if element.pricechangepercent <= 7.0 && element.pricechangepercent > 5.0 { + nwa += 2.0; + } else if element.pricechangepercent <= 5.0 && element.pricechangepercent > 3.0 { + nwa += 3.0; + } else if element.pricechangepercent <= 3.0 && element.pricechangepercent > 0.00005 { + nwa += 4.0; + } else if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 { + nwa += 5.0; + } else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 { + nwa += 6.0; + price_down_count += 1; + } else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 { + nwa += 7.0; + price_down_count += 1; + } else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 { + nwa += 8.0; + price_down_count += 1; + } else if element.pricechangepercent <= -7.0 && element.pricechangepercent >= -10.0 { + nwa += 9.0; + price_down_count += 1; + } else { + nwa += 10.0; + price_down_count += 1; + } + } + + nwa /= select_result.len() as f64; + pdr = price_down_count as f64 / select_result.len() as f64; + index = ((nwa * 100.0) + (pdr * 4000.0)) / 50.0; + + let update_table_name = String::from("indices"); + let server_epoch = server_epoch().await; + let update_values = vec![ + ( + String::from("total_24h_change_profit_index"), + index.to_string(), + ), + (String::from("server_epoch"), server_epoch.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + + update_record2(&update_table_name, &update_values, &update_condition).await?; + // println!("total_24h_change_profit_index 완료"); + Ok(()) +} + +pub async fn usdt_24h_change_profit_index() -> Result<(), Box> +{ + #[derive(Debug, FromRow)] + struct TradesProfit { + pricechangepercent: f64, + } + let mut trades_profit = TradesProfit { + pricechangepercent: 0.0, + }; + let table_name = String::from("all_24h_change"); + let column_name = String::from("priceChangePercent"); + let condition = Some(String::from("WHERE symbol LIKE '%USDT' AND symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0")); + + let select_result = + try_select_record(&table_name, &column_name, &condition, &trades_profit).await?; + + let mut nwa = 0.0; // Normalized Weighted Average + let mut price_down_count = 0; + let mut pdr = 0.0; // Price Down Ratio + let mut index = 0.0; + + for element in &select_result { + if element.pricechangepercent > 10.0 { + nwa += 0.0; + } else if element.pricechangepercent <= 10.0 && element.pricechangepercent > 7.0 { + nwa += 1.0; + } else if element.pricechangepercent <= 7.0 && element.pricechangepercent > 5.0 { + nwa += 2.0; + } else if element.pricechangepercent <= 5.0 && element.pricechangepercent > 3.0 { + nwa += 3.0; + } else if element.pricechangepercent <= 3.0 && element.pricechangepercent > 0.00005 { + nwa += 4.0; + } else if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 { + nwa += 5.0; + } else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 { + nwa += 6.0; + price_down_count += 1; + } else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 { + nwa += 7.0; + price_down_count += 1; + } else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 { + nwa += 8.0; + price_down_count += 1; + } else if element.pricechangepercent <= -7.0 && element.pricechangepercent >= -10.0 { + nwa += 9.0; + price_down_count += 1; + } else { + nwa += 10.0; + price_down_count += 1; + } + } + + nwa /= select_result.len() as f64; + pdr = price_down_count as f64 / select_result.len() as f64; + index = ((nwa * 100.0) + (pdr * 4000.0)) / 50.0; + + let update_table_name = String::from("indices"); + let server_epoch = server_epoch().await; + let update_values = vec![ + ( + String::from("usdt_24h_change_profit_index"), + index.to_string(), + ), + (String::from("server_epoch"), server_epoch.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + + update_record2(&update_table_name, &update_values, &update_condition).await?; + // println!("usdt_24h_change_profit_index 완료"); + Ok(()) +} + +pub async fn total_price_down_dist_index() -> Result<(), Box> { + #[derive(Debug, FromRow)] + struct TradesProfit { + pricechangepercent: f64, + } + let mut trades_profit = TradesProfit { + pricechangepercent: 0.0, + }; + let table_name = String::from("all_24h_change"); + let column_name = String::from("priceChangePercent"); + let condition = Some(String::from("WHERE symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0")); + + let select_result = + try_select_record(&table_name, &column_name, &condition, &trades_profit).await?; + + let mut wa = 0.0; // Weighted Average + let mut total_coin_amount = 0; + let mut index = 0.0; + + for element in &select_result { + if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 { + wa += 0.0; + total_coin_amount += 1; + } else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 { + wa += 2.0; + total_coin_amount += 1; + } else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 { + wa += 4.0; + total_coin_amount += 1; + } else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 { + wa += 6.0; + total_coin_amount += 1; + } else if element.pricechangepercent <= -7.0 && element.pricechangepercent > -10.0 { + wa += 8.0; + total_coin_amount += 1; + } else if element.pricechangepercent <= -10.0 { + wa += 10.0; + total_coin_amount += 1; + } + } + + wa /= total_coin_amount as f64; + + index = wa * 10.0; + + let update_table_name = String::from("indices"); + let server_epoch = server_epoch().await; + let update_values = vec![ + ( + String::from("total_price_down_dist_index"), + index.to_string(), + ), + (String::from("server_epoch"), server_epoch.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + + update_record2(&update_table_name, &update_values, &update_condition).await?; + + // println!("total_price_down_dist_index 완료"); + Ok(()) +} + +pub async fn monitoring_all_coin_profit_change() { + // 초기화 확인 + let select_table_name = String::from("all_coin_profit_change_avg"); + let select_columns = String::from("*"); + let select_condition = None; + let all_coin_profit_change_avg_stuct = AllCoinProfitChangeAvgList { + server_epoch: 0, + cnt: 0, + avg_profit: 0.0, + }; + let all_coin_profit_change_avg_list = select_record( + &select_table_name, + &select_columns, + &select_condition, + &all_coin_profit_change_avg_stuct, + ) + .await + .unwrap(); + let cnt = all_coin_profit_change_avg_list.first().unwrap().cnt; + let mut avg_profit = all_coin_profit_change_avg_list.first().unwrap().avg_profit; + let count_row = count_rows(&String::from("all_coin_start_price")) + .await + .unwrap(); + + let server_epoch = server_epoch().await; + let timer: u64 = 20_000; // 20_000: 20secs + + // check timer on + if server_epoch + - all_coin_profit_change_avg_list + .first() + .unwrap() + .server_epoch + > timer + { + // initialize [all_coin_start_price] table + delete_all_rows(&String::from("all_coin_start_price")) + .await + .expect("Failed to delete rows!"); + initial_all_coin_start_price().await; + + // initialize all values in [all_coin_profit_change_avg] + let update_table_name = String::from("all_coin_profit_change_avg"); + let update_values = vec![ + (String::from("server_epoch"), server_epoch.to_string()), + (String::from("cnt"), 1.to_string()), + (String::from("avg_profit"), 0.0.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record2(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + + // delete all rows when the number of rows is 200 + let history_rows_num = count_rows(&String::from("all_coin_profit_change_history")) + .await + .unwrap(); + if history_rows_num == 200 { + delete_all_rows(&String::from("all_coin_profit_change_history")) + .await + .expect("Failed to delete rows!"); + } + + // insert record into [all_coin_profit_change_history] + if all_coin_profit_change_avg_list + .first() + .unwrap() + .server_epoch + != 0 + { + let insert_table_name = String::from("all_coin_profit_change_history"); + let insert_columns = vec!["server_epoch", "avg_profit"]; + let insert_values = vec![ + server_epoch.to_string(), + all_coin_profit_change_avg_list + .first() + .unwrap() + .avg_profit + .to_string(), + ]; + insert_one_record(&insert_table_name, &insert_columns, &insert_values).await; + } + } else { + #[derive(FromRow)] + struct Avg { + avg: f64, + } + let select_data_structure = Avg { avg: 0.0 }; + let select_table_name = String::from("(SELECT sum(total) AS result FROM (SELECT (start_price-price)/start_price AS total FROM all_coin_start_price JOIN coinprices ON all_coin_start_price.symbol = coinprices.symbol) total)"); + let mut select_column = String::from("result/"); + select_column.push_str(count_row.to_string().as_str()); + select_column.push_str(" AS avg"); + let select_condition = Some(String::from("result")); + + let new_avg = select_record( + &select_table_name, + &select_column, + &select_condition, + &select_data_structure, + ) + .await + .unwrap() + .first() + .unwrap() + .avg; + + avg_profit = ((cnt as f64 - 1.0) / (cnt as f64)) * avg_profit + (new_avg / (cnt as f64)); + + let update_table_name = String::from("all_coin_profit_change_avg"); + let update_values = vec![ + (String::from("cnt"), (cnt + 1).to_string()), + (String::from("avg_profit"), avg_profit.to_string()), + ]; + let update_condition = vec![(String::from("id"), String::from("1"))]; + update_record2(&update_table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + // println!("monitoring all_coin_profit_change 완료"); +} + +async fn server_epoch() -> u64 { + #[derive(Debug, FromRow)] + struct ServerEpoch { + server_epoch: u64, + } + + let table_name = String::from("time"); + let columns = String::from("*"); + let condition = None; + let mut time_info = ServerEpoch { server_epoch: 0 }; + + let select_result = select_record(&table_name, &columns, &condition, &time_info) + .await + .unwrap(); + + select_result.first().unwrap().server_epoch +} + +pub async fn initial_all_coin_start_price() { + let table_name = String::from("all_coin_start_price"); + #[derive(Debug, FromRow)] + struct SymbolPrice { + symbol: String, + price: Decimal, + } + let fetch_table_name = String::from("valid_usdt_trades"); + let select_columns = String::from("valid_usdt_trades.symbol, coinprices.price"); + let condition = Some(String::from( + "JOIN coinprices ON valid_usdt_trades.symbol = coinprices.symbol", + )); + let data_struct = SymbolPrice { + symbol: String::new(), + price: Decimal::new(0, 8), + }; + let symbol_price_vec = + select_record(&fetch_table_name, &select_columns, &condition, &data_struct) + .await + .expect("Failed to fetch records!"); + + let insert_columns = vec!["symbol", "start_price"]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_inner_values: Vec = Vec::new(); + let mut value_string_build = String::new(); + for element in symbol_price_vec { + value_string_build.clear(); + value_string_build.push_str(element.symbol.as_str()); + value_string_build.push_str("\', \'"); + value_string_build.push_str(element.price.to_string().as_str()); + insert_inner_values.push(value_string_build.clone()); + insert_values.push(insert_inner_values.clone()); + insert_inner_values.clear(); + } + insert_records(&table_name, &insert_columns, &insert_values).await; +} diff --git a/src/coin_health_check_team/request_candles.rs b/src/coin_health_check_team/request_candles.rs new file mode 100644 index 0000000..8f65634 --- /dev/null +++ b/src/coin_health_check_team/request_candles.rs @@ -0,0 +1,788 @@ +use crate::database_control::*; +use futures::future::try_join_all; +use futures::{stream, StreamExt}; +use reqwest::{Client, ClientBuilder, Response}; +use serde::Deserialize; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use std::borrow::{Borrow, BorrowMut}; +use std::sync::Arc; +use tokio::{join, sync::Mutex, time::*}; + +#[derive(Debug, Clone)] +pub struct CandleData { + pub open_time: i64, + pub open_price: f64, + pub high_price: f64, + pub low_price: f64, + pub close_price: f64, + pub volume: f64, + pub close_time: i64, + pub quote_asset_volume: f64, + pub number_of_trades: i64, + pub taker_buy_base_asset_volume: f64, + pub taker_buy_quote_asset_volume: f64, + pub ignore_this: f64, +} + +// fetch the list of valid usdt trades +#[derive(Debug, FromRow, Clone)] +struct Symbols { + symbol: String, +} + +// request candlestick data from endpoint for each symbol (Weight(IP) 1) +// use the following intervals: 1m, 30m, 1d, 1w, 1mon (1M) +async fn request_candlestick_data( + symbol: String, + interval: &String, + client: &Client, + candle_set: &mut Vec<(String, Vec)>, +) -> Result<(), Box> { + let mut query = String::from("https://api.binance.com/api/v3/klines?"); + query.push_str("&symbol="); + query.push_str(symbol.as_str()); + query.push_str("&interval="); + if interval == "1mon" { + query.push_str("1M"); + } else { + query.push_str(interval.as_str()); + } + + let response = client.get(query).send().await?; + + let mut body = String::new(); + body = response.text_with_charset("utf-8").await?; + + if interval == "1m" || interval == "30m" { + de_candle_json(symbol, &interval, &body, candle_set).await?; + } else if interval == "1d" || interval == "1w" || interval == "1mon" { + de_candle_json2(symbol, &interval, &body, candle_set).await?; + } + + Ok(()) +} + +async fn de_candle_json( + symbol: String, + interval: &String, + body: &String, + candle_set: &mut Vec<(String, Vec)>, +) -> Result<(), Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + + let mut candle_data = CandleData { + open_time: 0, + open_price: 0.0, + high_price: 0.0, + low_price: 0.0, + close_price: 0.0, + volume: 0.0, + close_time: 0, + quote_asset_volume: 0.0, + number_of_trades: 0, + taker_buy_base_asset_volume: 0.0, + taker_buy_quote_asset_volume: 0.0, + ignore_this: 0.0, + }; + let mut candle_vec: Vec = Vec::new(); + for element in into_vec.unwrap() { + let inner_into_vec = element.as_array().unwrap(); + candle_data.open_time = element[0].as_i64().unwrap(); + candle_data.open_price = element[1].as_str().unwrap().parse::().unwrap(); + candle_data.high_price = element[2].as_str().unwrap().parse::().unwrap(); + candle_data.low_price = element[3].as_str().unwrap().parse::().unwrap(); + candle_data.close_price = element[4].as_str().unwrap().parse::().unwrap(); + candle_data.volume = element[5].as_str().unwrap().parse::().unwrap(); + candle_data.close_time = element[6].as_i64().unwrap(); + candle_data.quote_asset_volume = element[7].as_str().unwrap().parse::().unwrap(); + candle_data.number_of_trades = element[8].as_i64().unwrap(); + candle_data.taker_buy_base_asset_volume = + element[9].as_str().unwrap().parse::().unwrap(); + candle_data.taker_buy_quote_asset_volume = + element[10].as_str().unwrap().parse::().unwrap(); + candle_data.ignore_this = element[11].as_str().unwrap().parse::().unwrap(); + + candle_vec.push(candle_data.clone()); + } + + candle_set.push((symbol, candle_vec)); + // let search_result = candle_set.iter().position(|x| x.0 == symbol); + // match search_result { + // Some(T) => { + // candle_set[T].1 = candle_vec; + // }, + // None => { + // candle_set.push((symbol, candle_vec)); + // } + // } + Ok(()) +} + +async fn de_candle_json2( + symbol: String, + interval: &String, + body: &String, + candle_set: &mut Vec<(String, Vec)>, +) -> Result<(), Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + + let mut candle_data = CandleData { + open_time: 0, + open_price: 0.0, + high_price: 0.0, + low_price: 0.0, + close_price: 0.0, + volume: 0.0, + close_time: 0, + quote_asset_volume: 0.0, + number_of_trades: 0, + taker_buy_base_asset_volume: 0.0, + taker_buy_quote_asset_volume: 0.0, + ignore_this: 0.0, + }; + let mut candle_vec: Vec = Vec::new(); + for element in into_vec.unwrap() { + let inner_into_vec = element.as_array().unwrap(); + candle_data.open_time = element[0].as_i64().unwrap(); + candle_data.open_price = element[1].as_str().unwrap().parse::().unwrap(); + candle_data.high_price = element[2].as_str().unwrap().parse::().unwrap(); + candle_data.low_price = element[3].as_str().unwrap().parse::().unwrap(); + candle_data.close_price = element[4].as_str().unwrap().parse::().unwrap(); + candle_data.volume = element[5].as_str().unwrap().parse::().unwrap(); + candle_data.close_time = element[6].as_i64().unwrap(); + candle_data.quote_asset_volume = element[7].as_str().unwrap().parse::().unwrap(); + candle_data.number_of_trades = element[8].as_i64().unwrap(); + candle_data.taker_buy_base_asset_volume = + element[9].as_str().unwrap().parse::().unwrap(); + candle_data.taker_buy_quote_asset_volume = + element[10].as_str().unwrap().parse::().unwrap(); + candle_data.ignore_this = element[11].as_str().unwrap().parse::().unwrap(); + + candle_vec.push(candle_data.clone()); + } + + let search_result = candle_set.iter().position(|x| x.0 == symbol); + match search_result { + Some(T) => { + candle_set[T].1 = candle_vec; + } + None => { + candle_set.push((symbol, candle_vec)); + } + } + Ok(()) +} + +// // request candlestick data from endpoint for each symbol (Weight(IP) 1) +// // use the following intervals: 1m, 30m, 1d, 1w, 1mon(1M) +// async fn request_candlestick_data(symbol: &String, interval: &String, client: &Client) -> Result<(), Box> { +// let mut query = String::from("https://api.binance.com/api/v3/klines?"); +// query.push_str("&symbol="); +// query.push_str(symbol.as_str()); +// query.push_str("&interval="); +// if interval == "1mon" { +// query.push_str("1M"); +// } else { +// query.push_str(interval.as_str()); +// } + +// let response = client.get(query).send().await?; + +// let mut body = String::new(); +// body = response.text_with_charset("utf-8").await?; + +// de_candle_json(&symbol, &interval, &body).await?; +// // println!(" candle {} {} 완료", symbol, interval); +// Ok(()) +// } + +// // for initialization +// pub async fn request_candlestick_initial (symbol_vec: &Vec, interval: &String) -> Result<(), Box> { +// const CONCURRENT_REQUESTS: usize = 50; +// #[derive(Clone, Debug)] +// struct QuerySet { +// symbol: String, +// query_url: String, +// interval: String, +// body: String, +// } +// let client = ClientBuilder::new().connect_timeout(tokio::time::Duration::from_millis(10000)).build().unwrap(); +// let mut query_set = QuerySet{ symbol: String::new(), query_url: String::new(), interval: String::new(), body: String::new() }; +// let mut query_set_vec: Vec = Vec::new(); +// for symbol in symbol_vec { + +// let mut query_url = String::from("https://api.binance.com/api/v3/klines?"); +// query_url.push_str("&interval="); +// if interval == "1mon" { +// query_url.push_str("1M"); +// } else { +// query_url.push_str(interval.as_str()); +// } +// query_url.push_str("&symbol="); +// query_url.push_str(symbol.as_str()); +// query_set.query_url = query_url; +// query_set.symbol = symbol.clone(); +// query_set.interval = interval.clone(); +// query_set_vec.push(query_set.clone()); +// } + +// let bodies = stream::iter(query_set_vec) +// .map(|mut query_set| { +// let client = &client; +// async move { +// let mut response = client.get(query_set.query_url.clone()).send().await.unwrap(); +// let mut body = response.text_with_charset("utf-8").await; +// while let Err(e) = body { +// response = client.get(query_set.query_url.clone()).send().await.unwrap(); +// body = response.text_with_charset("utf-8").await; +// sleep(Duration::from_secs(1)).await; +// } + +// query_set.body = body.unwrap(); +// query_set +// } +// }) +// .buffer_unordered(CONCURRENT_REQUESTS); +// bodies.for_each(|query_set| async move {de_candle_json(&query_set.symbol.to_string(), &query_set.interval.to_string(), &query_set.body.to_string()).await;}).await; + +// Ok(()) +// } + +// for initialization +pub async fn request_candlestick_initial( + symbol: String, + interval: &String, +) -> Result<(), Box> { + let mut candle_set: Vec<(String, Vec)> = Vec::new(); + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(20000)) + .build() + .unwrap(); + let mut query = String::from("https://api.binance.com/api/v3/klines?"); + // query.push_str("&limit="); + // query.push_str("200"); + query.push_str("&symbol="); + query.push_str(symbol.as_str()); + query.push_str("&interval="); + if interval == "1mon" { + query.push_str("1M"); + } else { + query.push_str(interval.as_str()); + } + + let mut response = client.get(&query).send().await; + while let Err(e) = response { + response = client.get(&query).send().await; + sleep(Duration::from_secs(1)).await; + } + let mut body = response.unwrap().text_with_charset("utf-8").await; + while let Err(e) = body { + response = client.get(&query).send().await; + while let Err(e) = response { + response = client.get(&query).send().await; + sleep(Duration::from_secs(1)).await; + } + body = response.unwrap().text_with_charset("utf-8").await; + } + + de_candle_json(symbol, &interval, &body.unwrap(), &mut candle_set).await?; + Ok(()) +} + +// pub async fn request_candlestick_initial (symbol: &String, interval: &String) -> Result<(), Box> { +// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(10000)).build().unwrap(); +// let mut query = String::from("https://api.binance.com/api/v3/klines?"); +// query.push_str("&symbol="); +// query.push_str(symbol.as_str()); +// query.push_str("&interval="); +// if interval == "1mon" { +// query.push_str("1M"); +// } else { +// query.push_str(interval.as_str()); +// } +// +// let mut response = client.get(&query).send().await; +// let temp = reqwest::get("http://site.with.redirect.loop").await; +// if let Err(e) = temp{ +// e. +// } +// if let Err(e) = response { +// if e.is_timeout() { +// loop{ +// println!(">>> [timeout] retry fetching candle data: {} {}", symbol, interval); +// tokio::time::sleep(Duration::from_secs(1)).await; +// response = client.get(&query).send().await?; +// if response.is_ok() { break; } +// } +// } +// } else { +// let mut body = String::new(); +// body = response.unwrap().text_with_charset("utf-8").await.unwrap(); +// de_candle_json(&symbol, &interval, &body).await.unwrap(); +// } +// +// Ok(()) +// } + +// async fn de_candle_json(symbol: &String, interval: &String, body: &String) -> Result<(), Box> { +// let v: Value = serde_json::from_str(body.as_str())?; +// let mut into_vec = v.as_array(); +// if into_vec == None { +// return Err("Err")? +// } +// // let mut one_candle_data: &Vec = Vec::new(); + +// let columns = vec!["openTime", "openPrice", "highPrice","lowPrice", "closePrice", +// "volume", "closeTime", "quoteAssetVolume", "numberOfTrades", "takerBuyBaseAssetVolume", +// "takerBuyQuoteAssetVolume", "ignoreThis"]; +// let mut value_wrapper: Vec> = Vec::new(); + +// for element in into_vec.unwrap() { +// let inner_into_vec = element.as_array().unwrap(); + +// let mut value_vec = Vec::new(); +// for element in inner_into_vec { +// if element.is_number() { +// value_vec.push(element.as_i64().unwrap().to_string()); +// } else if element.is_string() { +// value_vec.push(element.as_str().unwrap().to_string()); +// } else { +// println!("Elements in body msg are changed. Please update parsing in de_candle_json."); +// } +// } +// value_wrapper.push(value_vec); +// } +// store_candle_db(&symbol, &interval, &columns, value_wrapper).await; +// Ok(()) +// } + +async fn store_candle_db( + symbol: &String, + interval: &String, + columns: &Vec<&str>, + value_wrapper: Vec>, +) -> Result<(), Box> { + let mut table_name = String::from("candle_"); + table_name.push_str(symbol.as_str()); + table_name.push('_'); + table_name.push_str(interval.as_str()); + + delete_all_rows(&table_name).await.unwrap(); + insert_records(&table_name, columns, &value_wrapper) + .await + .unwrap(); + + Ok(()) +} + +pub async fn create_candle_table( + symbol: &String, + interval: &String, +) -> Result<(), Box> { + let mut table_name = String::from("candle_"); + table_name.push_str(symbol.as_str()); + table_name.push('_'); + table_name.push_str(interval.as_str()); + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("openTime", "bigint", None), + ("openPrice", "double", None), + ("highPrice", "double", None), + ("lowPrice", "double", None), + ("closePrice", "double", None), + ("volume", "double", None), + ("closeTime", "bigint", None), + ("quoteAssetVolume", "double", None), + ("numberOfTrades", "int", None), + ("takerBuyBaseAssetVolume", "double", None), + ("takerBuyQuoteAssetVolume", "double", None), + ("ignoreThis", "double", None), + ]; + let initial_columns = vec![ + "openTime", + "openPrice", + "highPrice", + "lowPrice", + "closePrice", + "volume", + "closeTime", + "quoteAssetVolume", + "numberOfTrades", + "takerBuyBaseAssetVolume", + "takerBuyQuoteAssetVolume", + "ignoreThis", + ]; + let initial_values = vec![ + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0.0"), + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + new_table(&table_name, &initial_table, &table_condition) + .await + .unwrap(); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .unwrap(); + Ok(()) +} + +// for fetching 1m and 30m candle +pub async fn fetch_candle_parallel( + interval: &String, + candle_vec: &mut Vec<(String, Vec)>, +) -> Result<(), Box> { + let instant = Instant::now(); + let fetch_table_name = String::from("valid_usdt_trades"); + let column_name = String::from("symbol"); + let condition = None; + let mut symbols = Symbols { + symbol: String::new(), + }; + let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols) + .await + .unwrap(); + + // 심볼들을 20개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다. + let chunks = select_result.chunks(20); + let nbr_chunks = chunks.len(); + + let mut candle_vec_arc_wrapper: Vec)>>>> = Vec::new(); + for _ in 0..nbr_chunks { + let mut candle_vec_temp: Vec<(String, Vec)> = Vec::new(); + let mut candle_vec_arc = Arc::new(Mutex::new(candle_vec_temp)); + candle_vec_arc_wrapper.push(candle_vec_arc); + } + + let mut task_vec = Vec::new(); + + let mut index = 0; + for chunk in chunks { + let candle_arc = Arc::clone(&candle_vec_arc_wrapper[index]); + let chunk_vec = chunk.to_vec(); + let interval_clone = interval.clone(); + task_vec.push(tokio::spawn(async move { + repeat_task(interval_clone, chunk_vec, candle_arc).await; + })); + index += 1; + } + + let result = try_join_all(task_vec).await; + + match result { + Ok(T) => { + let mut candle_buffer: Vec<(String, Vec)> = Vec::new(); + for element in candle_vec_arc_wrapper { + let a = element.lock().await.clone(); + for element in a { + candle_buffer.push(element); + } + } + + *candle_vec = candle_buffer; + // println!(" candle {} 완료 elapsed:{:.2}s", interval.as_str(), instant.elapsed().as_secs_f32()); + } + Err(E) => { + panic!("Failed to fetch candle data!") + } + } + + Ok(()) +} + +async fn repeat_task( + interval: String, + symbol_vec: Vec, + my_count: Arc)>>>, +) -> Result<(), Box> { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(1200)) + .build() + .unwrap(); + + for element in symbol_vec { + let mut candle_set_lock = my_count.lock().await; + request_candlestick_data(element.symbol, &interval, &client, &mut candle_set_lock).await; + + sleep(Duration::from_millis(200)).await; + } + Ok(()) +} + +// for fetching 1d, 1w, and 1mon candle +pub async fn fetch_candle_delay( + interval: &String, + candle_vec: &mut Vec<(String, Vec)>, +) -> Result<(), Box> { + let instant_func = Instant::now(); + let server_epoch = server_epoch().await; + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(1200)) + .build() + .unwrap(); + + // to decide keeping retry or not as the current server status from database + #[derive(Debug, FromRow, Clone)] + struct ServerStatus { + server_on: bool, + } + + let serverhealth_table_name = String::from("serverhealth"); + let serverhealth_column_name = String::from("server_on"); + let serverhealth_condition = None; + let mut serverhealth = ServerStatus { server_on: true }; + + // fetch the list of valid usdt trades + #[derive(Debug, FromRow)] + struct Symbols { + symbol: String, + } + + let fetch_table_name = String::from("valid_usdt_trades"); + let column_name = String::from("symbol"); + let condition = None; + let mut symbols = Symbols { + symbol: String::new(), + }; + + let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols) + .await + .unwrap(); + + let mut time_wait = 0; + if interval == "1d" { + time_wait = 10_000; + } else if interval == "1w" { + time_wait = 20_000; + } else { + time_wait = 30_000; + } + + let mut server_on_result = select_record( + &serverhealth_table_name, + &serverhealth_column_name, + &serverhealth_condition, + &serverhealth, + ) + .await + .unwrap(); + serverhealth.server_on = server_on_result[0].server_on; + + for element in select_result { + let instant = Instant::now(); + server_on_result = select_record( + &serverhealth_table_name, + &serverhealth_column_name, + &serverhealth_condition, + &serverhealth, + ) + .await + .unwrap(); + serverhealth.server_on = server_on_result[0].server_on; + + { + request_candlestick_data(element.symbol, &interval, &client, candle_vec).await; + } + + // sleep for 10secs for 1d, 20secs for 1w, 30secs for 1mon + if time_wait > instant.elapsed().as_millis() { + sleep(Duration::from_millis( + (time_wait - instant.elapsed().as_millis()) as u64, + )) + .await; + } + } + // println!(" candle {} 완료 elapsed:{:.2}s", interval, instant_func.elapsed().as_secs()); + Ok(()) +} + +// pub async fn fetch_candle_1w() -> Result<(), Box> { +// let server_epoch = server_epoch().await; +// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(1200)).build().unwrap(); + +// // to decide keeping retry or not as the current server status from database +// #[derive(Debug, FromRow, Clone)] +// struct ServerStatus { +// server_on: bool, +// } + +// let serverhealth_table_name = String::from("serverhealth"); +// let serverhealth_column_name = String::from("server_on"); +// let serverhealth_condition = None; +// let mut serverhealth = ServerStatus { server_on: true }; + +// // fetch the list of valid usdt trades +// #[derive(Debug, FromRow)] +// struct Symbols { +// symbol: String, +// } + +// let fetch_table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let condition = None; +// let mut symbols = Symbols { symbol: String::new() }; + +// let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols).await.unwrap(); + +// let interval = String::from("1w"); +// let column_name = String::from("closeTime"); +// let condition = Some(String::from("ORDER BY id DESC LIMIT 1")); + +// #[derive(Debug, FromRow)] +// struct CloseTime { +// closetime: i64, +// } + +// let mut server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; +// let instant_1w = Instant::now(); + +// for element in select_result { +// let instant = Instant::now(); +// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; + +// if serverhealth.server_on == true { +// let mut closetime = CloseTime { closetime: 0 }; +// let mut table_name = String::from("candle_"); +// table_name.push_str(element.symbol.as_str()); +// table_name.push_str("_1w"); + +// let mut exists_result = exists_table(&table_name).await; +// if exists_result == false { create_candle_table(&element.symbol, &interval).await.unwrap(); } + +// let mut select_result = select_record(&table_name, &column_name, &condition, &closetime).await.unwrap(); + +// if (select_result[0].closetime as u64) < server_epoch { +// while let Err(e) = request_candlestick_data(&element.symbol, &interval, &client).await +// { +// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; +// if serverhealth.server_on == false { break; } +// println!(">>> retry to fetch candlestick {} {} data from endpoint.", &element.symbol, &interval); +// sleep(Duration::from_millis(500)).await; +// } +// } +// } +// // sleep as much as the loop recurs per 20 seconds if all operation finished within 20 seconds. +// if 20_000_000_000 > instant.elapsed().as_nanos() { +// sleep(Duration::from_nanos((20_000_000_000 - instant.elapsed().as_nanos()) as u64)).await; +// } +// } +// println!(" candle 1w 완료 elapsed:{:.2}s", instant_1w.elapsed().as_secs()); +// Ok(()) +// } + +// pub async fn fetch_candle_1mon() -> Result<(), Box> { +// let server_epoch = server_epoch().await; +// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(1200)).build().unwrap(); + +// // to decide keeping retry or not as the current server status from database +// #[derive(Debug, FromRow, Clone)] +// struct ServerStatus { +// server_on: bool, +// } + +// let serverhealth_table_name = String::from("serverhealth"); +// let serverhealth_column_name = String::from("server_on"); +// let serverhealth_condition = None; +// let mut serverhealth = ServerStatus { server_on: true }; + +// // fetch the list of valid usdt trades +// #[derive(Debug, FromRow)] +// struct Symbols { +// symbol: String, +// } + +// let fetch_table_name = String::from("valid_usdt_trades"); +// let column_name = String::from("symbol"); +// let condition = None; +// let mut symbols = Symbols { symbol: String::new() }; + +// let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols).await.unwrap(); + +// let interval = String::from("1mon"); +// let column_name = String::from("closeTime"); +// let condition = Some(String::from("ORDER BY id DESC LIMIT 1")); + +// #[derive(Debug, FromRow)] +// struct CloseTime { +// closetime: i64, +// } + +// let mut server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; +// let instant_1mon = Instant::now(); + +// for element in select_result { +// let instant = Instant::now(); +// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; + +// if serverhealth.server_on == true { +// let mut closetime = CloseTime { closetime: 0 }; +// let mut table_name = String::from("candle_"); +// table_name.push_str(element.symbol.as_str()); +// table_name.push_str("_1mon"); + +// let mut exists_result = exists_table(&table_name).await; +// if exists_result == false { create_candle_table(&element.symbol, &interval).await.unwrap(); } + +// let mut select_result = select_record(&table_name, &column_name, &condition, &closetime).await.unwrap(); + +// if (select_result[0].closetime as u64) < server_epoch { +// while let Err(e) = request_candlestick_data(&element.symbol, &interval, &client).await +// { +// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap(); +// serverhealth.server_on = server_on_result[0].server_on; +// if serverhealth.server_on == false { break; } +// println!(">>> retry to fetch candlestick {} {} data from endpoint.", &element.symbol, &interval); +// sleep(Duration::from_millis(500)).await; +// } +// } +// } +// // sleep as much as the loop recurs per 30 seconds if all operation finished within 30 seconds. +// if 30_000_000_000 > instant.elapsed().as_nanos() { +// sleep(Duration::from_nanos((30_000_000_000 - instant.elapsed().as_nanos()) as u64)).await; +// } +// } +// println!(" candle 1mon 완료 elapsed:{:.2}s", instant_1mon.elapsed().as_secs()); +// Ok(()) +// } + +async fn server_epoch() -> u64 { + #[derive(Debug, FromRow)] + struct ServerEpoch { + server_epoch: u64, + } + + let table_name = String::from("time"); + let columns = String::from("*"); + let condition = None; + let mut time_info = ServerEpoch { server_epoch: 0 }; + + let select_result = select_record(&table_name, &columns, &condition, &time_info) + .await + .unwrap(); + + select_result.first().unwrap().server_epoch +} diff --git a/src/coin_health_check_team/request_others.rs b/src/coin_health_check_team/request_others.rs new file mode 100644 index 0000000..26f01ab --- /dev/null +++ b/src/coin_health_check_team/request_others.rs @@ -0,0 +1,458 @@ +use crate::database_control::*; +use hex::ToHex; +use hmac_sha256::HMAC; +use reqwest::{Client, ClientBuilder, Response}; +use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; +use serde::Deserialize; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use std::borrow::{Borrow, BorrowMut}; +use std::sync::Arc; +use tokio::{join, sync::Mutex, time::*}; + +#[derive(Debug, Clone)] +pub struct TradeFee { + pub symbol: String, + pub makercommission: Decimal, + pub takercommission: Decimal, +} + +impl TradeFee { + fn new() -> TradeFee { + let a = TradeFee { + symbol: String::new(), + makercommission: Decimal::new(0, 8), + takercommission: Decimal::new(0, 8), + }; + a + } +} + +#[derive(Debug, FromRow, Clone)] +pub struct ExchangeInfo { + pub symbol: String, + pub stepsize: Decimal, + pub ticksize: Decimal, + pub base_asset_precision: u32, + pub base_commission_precision: u32, + pub quote_asset_precision: u32, + pub quote_commission_precision: u32, +} + +impl ExchangeInfo { + fn new() -> ExchangeInfo { + let a = ExchangeInfo { + symbol: String::new(), + stepsize: Decimal::new(0, 8), + ticksize: Decimal::new(0, 8), + base_asset_precision: 0, + base_commission_precision: 0, + quote_asset_precision: 0, + quote_commission_precision: 0, + }; + a + } +} + +#[derive(Clone, Debug)] +pub struct CoinPriceData { + pub symbol: String, + pub current_price: f64, +} + +impl CoinPriceData { + fn new() -> CoinPriceData { + let a = CoinPriceData { + symbol: String::new(), + current_price: 0.0, + }; + a + } +} +// request all coin price (/api, Weight(IP) 2) +// request_all_coin_price -> de_all_coin_price_json -> store_coin_price_db +// pub async fn request_all_coin_price(client: &Client, price_vec: &Arc>>) -> Result<(), Box> { +pub async fn request_all_coin_price( + client: &Client, + price_vec: &mut Vec, +) -> Result<(), Box> { + let url = "https://api.binance.com/api/v3/ticker/price"; + let mut response = client.get(url).send().await?; + + let mut body = response.text_with_charset("utf-8").await?; + + de_all_coin_price_json(&body, price_vec).await?; + + Ok(()) +} + +async fn de_all_coin_price_json( + body: &String, + price_vec: &mut Vec, +) -> Result<(), Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + let mut object_map = &serde_json::map::Map::new(); + + let mut de_price_vec: Vec = Vec::new(); + let mut data_temp = CoinPriceData::new(); + + for element in into_vec.unwrap() { + object_map = element.as_object().unwrap(); + let mut object_map_iter = object_map.iter(); + + for element in object_map_iter { + match element.0.as_str() { + "symbol" => data_temp.symbol = element.1.as_str().unwrap().to_string(), + "price" => { + data_temp.current_price = element.1.as_str().unwrap().parse::().unwrap() + } + _ => { + println!("Elements in body msg are changed. Please update both your coinprices table and vectors."); + } + } + } + de_price_vec.push(data_temp.clone()); + } + *price_vec = de_price_vec; + Ok(()) +} + +// request trade fee (/sapi, Weight(IP) 1) +// request_trade_fee -> deserialization_trade_fee_json -> save_db_trade_fee +pub async fn request_trade_fee( + api_key: &str, + secret_key: &str, + local_epoch: u128, + difference_epoch: i64, + client: &Client, + tradefee_vec: &mut Vec, +) -> Result<(), Box> { + let mut base_url = String::from("https://api.binance.com/sapi/v1/asset/tradeFee?"); + + // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤 + // 이를 timestamp로 사용한다. + let mut timestamp; + if difference_epoch >= 0 { + timestamp = (local_epoch - difference_epoch as u128).to_string(); + } else if difference_epoch < 0 { + timestamp = (local_epoch - (difference_epoch * -1) as u128).to_string(); + } else { + timestamp = local_epoch.to_string(); + } + let recv_window_size = "20000".to_string(); // default: 5,000ms, Max: 60,000ms + + let mut query_string = String::from("×tamp="); + query_string.push_str(×tamp); + query_string.push_str("&recvWindow="); + query_string.push_str(&recv_window_size); + + let signature = HMAC::mac(&query_string.as_bytes(), secret_key.as_bytes()); + + query_string.push_str("&signature="); + base_url.push_str(&query_string); + base_url.push_str(signature.encode_hex::().as_str()); + + let mut response = client + .get(&base_url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + + while !response.status().is_success() { + sleep(Duration::from_secs(5)).await; + response = client + .get(&base_url) + .header("X-MBX-APIKEY", api_key) + .send() + .await?; + } + + let mut body = response.text_with_charset("utf-8").await; + + match body { + Ok(T) => { + de_trade_fee_json(&T, tradefee_vec).await?; + // println!("tradefee 완료"); + } + Err(E) => { + println!("request_trade_fee body failed!: {:?}", E); + } + } + + Ok(()) +} + +async fn de_trade_fee_json( + body: &String, + tradefee_vec: &mut Vec, +) -> Result<(), Box> { + let v: Value = serde_json::from_str(body.as_str())?; + + let into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + let mut object_map = &serde_json::map::Map::new(); + let mut de_tradefee_vec: Vec = Vec::new(); + let mut tradefee_data = TradeFee::new(); + + for element in into_vec.unwrap() { + object_map = element.as_object().unwrap(); + + let mut object_map_iter = object_map.iter(); + + for element in object_map_iter { + match element.0.as_str() { + "symbol" => tradefee_data.symbol = element.1.as_str().unwrap().to_string(), + "makerCommission" => { + tradefee_data.makercommission = + rust_decimal::prelude::FromStr::from_str(element.1.as_str().unwrap()) + .unwrap() + } + "takerCommission" => { + tradefee_data.takercommission = + rust_decimal::prelude::FromStr::from_str(element.1.as_str().unwrap()) + .unwrap() + } + _ => { + println!("Elements in body msg are changed. Please update both your tradefees table and vectors."); + } + } + } + de_tradefee_vec.push(tradefee_data.clone()); + } + tradefee_vec.clear(); + *tradefee_vec = de_tradefee_vec; + Ok(()) +} + +// request 24hr Ticker Price Change Statistics (Weight(IP) 1 for a single symbol, 40 when the symbol parameter is omitted) +// request_24hr_ticker_price_change_statistics -> deserialization_24hr_ticker_price_change_statistics_json -> +pub async fn request_24hr_ticker_price_change_statistics( + client: &Client, +) -> Result<(), Box> { + let mut response = client + .get("https://api.binance.com/api/v3/ticker/24hr") + .send() + .await; + + match response { + Ok(T) => { + let mut body = T.text_with_charset("utf-8").await; + match body { + Ok(T) => { + de_24h_change_json(&T).await?; + // println!("24h change 완료"); + } + Err(E) => { + println!( + "request_24hr_ticker_price_change_statistics body failed!: {:?}", + E + ); + } + } + } + Err(E) => { + println!( + "request_24hr_ticker_price_change_statistics response failed!: {:?}", + E + ); + } + } + Ok(()) +} + +async fn de_24h_change_json(body: &String) -> Result<(), Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + let mut object_map = &serde_json::map::Map::new(); + + let columns = vec![ + "symbol", + "priceChange", + "priceChangePercent", + "weightedAvgPrice", + "prevClosePrice", + "lastPrice", + "lastQty", + "bidPrice", + "bidQty", + "askPrice", + "askQty", + "openPrice", + "highPrice", + "lowPrice", + "volume", + "quoteVolume", + "openTime", + "closeTime", + "firstId", + "lastId", + "count", + ]; + let mut value_wrapper: Vec> = Vec::new(); + + for element in into_vec.unwrap() { + object_map = element.as_object().unwrap(); + + let mut value_vec = vec![String::new(); 21]; + + let mut object_map_iter = object_map.iter(); + + for element in object_map_iter { + match element.0.as_str() { + "symbol" => value_vec[0] = element.1.as_str().unwrap().to_string(), + "priceChange" => value_vec[1] = element.1.as_str().unwrap().to_string(), + "priceChangePercent" => value_vec[2] = element.1.as_str().unwrap().to_string(), + "weightedAvgPrice" => value_vec[3] = element.1.as_str().unwrap().to_string(), + "prevClosePrice" => value_vec[4] = element.1.as_str().unwrap().to_string(), + "lastPrice" => value_vec[5] = element.1.as_str().unwrap().to_string(), + "lastQty" => value_vec[6] = element.1.as_str().unwrap().to_string(), + "bidPrice" => value_vec[7] = element.1.as_str().unwrap().to_string(), + "bidQty" => value_vec[8] = element.1.as_str().unwrap().to_string(), + "askPrice" => value_vec[9] = element.1.as_str().unwrap().to_string(), + "askQty" => value_vec[10] = element.1.as_str().unwrap().to_string(), + "openPrice" => value_vec[11] = element.1.as_str().unwrap().to_string(), + "highPrice" => value_vec[12] = element.1.as_str().unwrap().to_string(), + "lowPrice" => value_vec[13] = element.1.as_str().unwrap().to_string(), + "volume" => value_vec[14] = element.1.as_str().unwrap().to_string(), + "quoteVolume" => value_vec[15] = element.1.as_str().unwrap().to_string(), + "openTime" => value_vec[16] = element.1.as_i64().unwrap().to_string(), + "closeTime" => value_vec[17] = element.1.as_i64().unwrap().to_string(), + "firstId" => value_vec[18] = element.1.as_i64().unwrap().to_string(), + "lastId" => value_vec[19] = element.1.as_i64().unwrap().to_string(), + "count" => value_vec[20] = element.1.as_i64().unwrap().to_string(), + _ => { + println!("Elements in body msg are changed. Please update both your 24h_change table and vectors."); + } + } + } + value_wrapper.push(value_vec); + } + + store_24h_change_db(columns, value_wrapper).await?; + Ok(()) +} + +async fn store_24h_change_db( + columns: Vec<&str>, + value_wrapper: Vec>, +) -> Result<(), Box> { + let table_name = String::from("all_24h_change"); + delete_all_rows(&table_name).await?; + insert_records(&table_name, &columns, &value_wrapper).await?; + Ok(()) +} + +// request exchange information. (/api, Weight(IP) 10) +pub async fn request_exchange_infomation( + client: &Client, + exchange_info_data: &mut Vec, +) -> Result<(), Box> { + // building URL and API-keys + let mut url = String::new(); + + url.push_str("https://api.binance.com"); + + let endpoint_url = "/api/v3/exchangeInfo"; + url.push_str(endpoint_url); + + let mut res = client.get(&url).send().await?; + + while !res.status().is_success() { + sleep(Duration::from_secs(5)).await; + res = client.get(&url).send().await?; + } + + let body = res.text_with_charset("utf-8").await?; + + // deserialize JSON + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_object(); + if into_vec == None { + return Err("Err")?; + } + + let mut data_temp = ExchangeInfo::new(); + let mut data_temp_vec: Vec = Vec::new(); + + for element in into_vec.unwrap() { + if element.0.contains("symbols") { + for element in element.1.as_array().unwrap() { + if element.is_object() { + if element + .get("symbol") + .unwrap() + .as_str() + .unwrap() + .ends_with("USDT") + { + data_temp.symbol = + (element.get("symbol").unwrap().as_str().unwrap().to_string()); + data_temp.base_asset_precision = + (element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32; + data_temp.base_commission_precision = (element + .get("baseCommissionPrecision") + .unwrap() + .as_u64() + .unwrap()) + as u32; + data_temp.quote_asset_precision = (element + .get("quoteAssetPrecision") + .unwrap() + .as_u64() + .unwrap()) + as u32; + data_temp.quote_commission_precision = (element + .get("quoteCommissionPrecision") + .unwrap() + .as_u64() + .unwrap()) + as u32; + for element in element.get("filters").unwrap().as_array().unwrap() { + if element + .as_object() + .unwrap() + .get("filterType") + .unwrap() + .as_str() + .unwrap() + .starts_with("LOT_SIZE") + { + data_temp.stepsize = rust_decimal::prelude::FromStr::from_str( + element.get("stepSize").unwrap().as_str().unwrap(), + ) + .unwrap(); + } else if element + .as_object() + .unwrap() + .get("filterType") + .unwrap() + .as_str() + .unwrap() + .starts_with("PRICE_FILTER") + { + data_temp.ticksize = rust_decimal::prelude::FromStr::from_str( + element.get("tickSize").unwrap().as_str().unwrap(), + ) + .unwrap(); + } + } + data_temp_vec.push(data_temp.clone()); + } + } + } + } + } + + *exchange_info_data = data_temp_vec; + + Ok(()) +} diff --git a/src/database_control.rs b/src/database_control.rs new file mode 100644 index 0000000..e38fa76 --- /dev/null +++ b/src/database_control.rs @@ -0,0 +1,758 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::DB_URL; +use sqlx::{mysql::*, Connection, Error, Executor, FromRow, Row}; +use std::any::Any; +use tokio::time::{sleep, Duration}; + +// check specific table in database. If the table exists it returns true, or false. +pub async fn exists_table(table_name: &String) -> bool { + let mut query_base = String::from("SELECT 1 FROM information_schema.tables WHERE table_schema = 'tradingbot' AND table_name = '"); + query_base.push_str(table_name.as_str()); + query_base.push('\''); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let exists_table = sqlx::query(&query_base).fetch_all(&mut conn).await.unwrap(); + + !exists_table.is_empty() +} + +// check specific record in table. If the record exists it returns true, or false. +pub async fn exists_record(table_name: &String, condition: &Option) -> bool { + #[derive(Debug, FromRow)] + struct Success { + success: i32, + } + + let mut query_base = String::from("SELECT EXISTS (SELECT * FROM "); + query_base.push_str(table_name.as_str()); + match condition { + Some(T) => { + query_base.push_str(" WHERE "); + query_base.push_str(T.as_str()); + } + None => {} + } + query_base.push_str(" LIMIT 1) as success;"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + let mut exists_record = sqlx::query_as::<_, Success>(&query_base) + .fetch_one(&mut conn) + .await; + + while exists_record.is_err() { + sleep(Duration::from_millis(200)).await; + exists_record = sqlx::query_as::<_, Success>(&query_base) + .fetch_one(&mut conn) + .await; + } + + let result = exists_record.unwrap(); + if result.success == 0 { + false + } else { + true + } +} + +// make a new table. If the job succeeds it returns true, or false. +// columns_vec: Vec<(column name, type, option)> +pub async fn new_table( + table_name: &String, + columns_vec: &Vec<(&str, &str, Option<&str>)>, + table_condition: &Option<&str>, +) -> Result<(), Box> { + // query building. + let mut query = String::from("CREATE TABLE IF NOT EXISTS "); + query.push_str(table_name); + query.push('('); + + for element in columns_vec.iter() { + query.push_str(element.0); + query.push(' '); + query.push_str(element.1); + query.push(' '); + + match element.2 { + None => {} + _ => { + if (element.2.unwrap().contains("UNSIGNED")) { + query.push_str("UNSIGNED "); + } + if (element.2.unwrap().contains("UN")) { + query.push_str("UNSIGNED "); + } + if (element.2.unwrap().contains("PK")) { + query.push_str("PRIMARY KEY "); + } + if (element.2.unwrap().contains("NOTNULL")) { + query.push_str("NOT NULL"); + } + if (element.2.unwrap().contains("AI")) { + query.push_str("AUTO_INCREMENT "); + } + } + } + query.push(','); + } + query.pop(); + query.push_str(")"); + + match table_condition { + Some(T) => { + query.push(' '); + query.push_str(T); + } + None => {} + } + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let mut query_result = sqlx::query(&query).execute(&mut conn).await; + while let Err(e) = query_result { + sleep(Duration::from_millis(200)).await; + query_result = sqlx::query(&query).execute(&mut conn).await; + } + Ok(()) +} + +// drop a table. If the job succeeds it returns true, or false. +pub async fn drop_table(table_name: &String) -> Result { + let mut query = String::from("DROP TABLE `tradingbot`.`"); + query.push_str(table_name); + query.push_str("`;"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + query_result +} + +// copy data from source table to destination table +// both table structures must be same +pub async fn copy_table_data( + source_table: &String, + destination_table: &String, +) -> Result<(), Box> { + // query building. + let mut query = String::from("INSERT INTO "); + query.push_str(destination_table.as_str()); + query.push_str(" (SELECT * FROM "); + query.push_str(source_table.as_str()); + query.push_str(");"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let mut query_result = sqlx::query(&query).execute(&mut conn).await; + + while let Err(e) = query_result { + query_result = sqlx::query(&query).execute(&mut conn).await; + sleep(Duration::from_millis(200)).await; + } + Ok(()) +} + +// insert a record into specific table +// e.g. column: ["name", "class", "age"], +// values: [["Kim", "blue", "7"] +pub async fn insert_one_record( + table_name: &String, + columns: &Vec<&str>, + values: &Vec, +) -> Result { + // query building. + let mut query = String::from("INSERT INTO "); + query.push_str(table_name); + query.push('('); + + for element in (*columns).clone() { + query.push_str(element); + query.push_str(","); + } + query.pop(); + query.push_str(") "); + + query.push_str("VALUES("); + for element in (*values).clone() { + query.push('\"'); + if element == "true" { + query.push('1'); + } else if element == "false" { + query.push('0'); + } else { + query.push_str(element.as_str()); + } + query.push('\"'); + query.push_str(","); + } + query.pop(); + query.push_str(");"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +pub async fn copy_record( + src_table: &String, + dest_table: &String, + columns: &str, + condition: &Option, +) -> Result { + // query building. + let mut query = String::from("INSERT INTO "); + query.push_str(dest_table.as_str()); + query.push_str(" ("); + query.push_str(columns); + query.push_str(") SELECT "); + query.push_str(columns); + query.push_str(" FROM "); + query.push_str(src_table.as_str()); + + match condition { + None => {} + Some(condition) => { + query.push(' '); + query.push_str(condition.as_str()); + query.push(';'); + } + } + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// insert several records into specific table at once +// the length of columns and each inner vector in values should be same. +// e.g. column: ["name", "class", "age"], +// values: [["Kim", "blue", "7"], ["Lee", "red", "9"], ...] +pub async fn insert_records( + table_name: &String, + columns: &Vec<&str>, + values: &Vec>, +) -> Result<(), Box> { + // query building. + let mut query = String::from("INSERT INTO "); + query.push_str(table_name); + query.push('('); + + for element in (*columns).clone() { + query.push_str(element); + query.push_str(","); + } + query.pop(); + query.push_str(") "); + + query.push_str("VALUES "); + for element in (*values).clone() { + query.push('('); + for element in element.into_iter() { + query.push('\''); + if element == "true" { + query.push('1'); + } else if element == "false" { + query.push('0'); + } else { + query.push_str(element.as_str()); + } + query.push_str("\',"); + } + query.pop(); + query.push_str("),"); + } + query.pop(); + query.push_str(";"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let mut query_result = sqlx::query(&query).execute(&mut conn).await; + + while let Err(e) = query_result { + query_result = sqlx::query(&query).execute(&mut conn).await; + sleep(Duration::from_millis(200)).await; + } + Ok(()) +} + +// update a record in table +// record: [column name, value], condition: (column name, value) +pub async fn update_record( + table_name: &String, + record: &Vec<(&str, &str)>, + condition: &Vec<(&str, &str)>, +) -> Result { + // query building. + let mut query = String::from("UPDATE "); + query.push_str(table_name); + query.push_str(" SET "); + + for element in record.iter() { + query.push_str(element.0); + query.push('='); + if element.1 == "true" { + query.push('1'); + } else if element.1 == "false" { + query.push('0'); + } else { + query.push('\"'); + query.push_str(element.1); + query.push('\"'); + } + query.push_str(","); + } + query.pop(); + query.push_str(" WHERE "); + for element in condition { + query.push_str(element.0); + query.push_str("=\""); + query.push_str(element.1); + query.push_str("\" AND "); + } + query.pop(); + query.pop(); + query.pop(); + query.pop(); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// update a record in table +// record: [column name, value], condition: (column name, value) +pub async fn update_record2( + table_name: &String, + record: &Vec<(String, String)>, + condition: &Vec<(String, String)>, +) -> Result { + // query building. + let mut query = String::from("UPDATE "); + query.push_str(table_name); + query.push_str(" SET "); + + for element in record.iter() { + query.push_str(element.0.as_str()); + query.push('='); + if element.1.as_str() == "true" { + query.push('1'); + } else if element.1.as_str() == "false" { + query.push('0'); + } else { + query.push('\"'); + query.push_str(element.1.as_str()); + query.push('\"'); + } + query.push_str(","); + } + query.pop(); + query.push_str(" WHERE "); + for element in condition { + query.push_str(element.0.as_str()); + query.push_str("=\""); + query.push_str(element.1.as_str()); + query.push_str("\" AND "); + } + query.pop(); + query.pop(); + query.pop(); + query.pop(); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// update a record in table +// record: (column name, value), condition: (column name, value) +pub async fn update_record3( + table_name: &String, + record: &Vec<(String, String)>, + condition: &Vec<(String, String)>, +) -> Result { + // query building. + let mut query = String::from("UPDATE "); + query.push_str(table_name); + query.push_str(" SET "); + + for element in record.iter() { + query.push_str(element.0.as_str()); + query.push('='); + if element.1.as_str() == "true" { + query.push('1'); + } else if element.1.as_str() == "false" { + query.push('0'); + } else { + query.push_str(element.1.as_str()); + } + query.push_str(","); + } + query.pop(); + query.push_str(" WHERE "); + for element in condition { + query.push_str(element.0.as_str()); + query.push_str("=\""); + query.push_str(element.1.as_str()); + query.push_str("\" AND "); + } + query.pop(); + query.pop(); + query.pop(); + query.pop(); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// update many records in a row +// record: [[id1, val1_1, val2_1], [id2, val1_2, val2_1], ...] (the first element in each record must be id) +// columns: ["col_name1", "col_name2"] (columns except id) +pub async fn update_records( + table_name: &String, + records: &Vec>, + columns: &Vec<&str>, +) -> Result { + // query building. + let mut query = String::from("UPDATE "); + query.push_str(table_name); + query.push_str(" s JOIN("); + + let mut first_flag: bool = true; + let col_len = columns.len(); + let mut record_count: usize = 0; + let mut col_count: usize = 0; + let mut new_scores_vec: Vec = Vec::new(); + let mut new_score_name = String::from("new_score"); + + for _ in 0..col_len { + new_scores_vec.push(new_score_name.clone()); + new_scores_vec[record_count].push_str(record_count.to_string().as_str()); + record_count += 1; + } + + record_count = 0; + for record in records { + query.push_str("SELECT "); + if first_flag == true { + for element in record { + if record_count == 0 { + query.push_str(&element); + query.push_str(" as id, "); + record_count += 1; + } else { + query.push_str(&element); + query.push_str(" as "); + query.push_str(new_scores_vec[col_count].clone().as_str()); + query.push_str(", "); + record_count += 1; + col_count += 1; + } + } + query.pop(); + query.pop(); + query.push(' '); + first_flag = false; + } else { + for _ in 0..7 { + query.pop(); + } + query.push_str("UNION ALL SELECT "); + for element in record { + if element.as_str() == "true" { + query.push('1'); + } else if element.as_str() == "false" { + query.push('0'); + } else { + query.push_str(element.as_str()); + } + query.push_str(","); + } + query.pop(); + query.push(' '); + } + } + query.push_str(") vals ON s.id = vals.id SET "); + + record_count = 0; + for element in new_scores_vec { + query.push_str(columns[record_count]); + query.push_str(" = "); + query.push_str(&element); + query.push_str(", "); + record_count += 1; + } + query.pop(); + query.pop(); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// delete a record in table +// condition: (column name, value) +pub async fn delete_record( + table_name: &String, + condition: &String, +) -> Result { + // query building. + let mut query = String::from("DELETE FROM "); + query.push_str(table_name); + query.push(' '); + query.push_str(condition.as_str()); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query(&query).execute(&mut conn).await; + + query_result +} + +// count total row in table +pub async fn count_rows(table_name: &String) -> Result { + #[derive(FromRow)] + struct Count { + cnt: i32, + } + + // query building. + let mut query = String::from("SELECT COUNT(*) as cnt FROM "); + query.push_str(table_name.as_str()); + query.push(';'); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let query_result = sqlx::query_as::<_, Count>(&query) + .fetch_one(&mut conn) + .await; + + let result = match query_result { + Ok(T) => Ok(T.cnt), + Err(E) => return Err(E), + }; + + result +} + +// delete all rows in table +pub async fn delete_all_rows( + table_name: &String, +) -> Result<(), Box> { + let mut query = String::from("TRUNCATE TABLE "); + query.push_str(table_name); + query.push_str(";"); + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let mut query_result = sqlx::query(&query).execute(&mut conn).await; + + while let Err(e) = query_result { + query_result = sqlx::query(&query).execute(&mut conn).await; + sleep(Duration::from_millis(200)).await; + } + Ok(()) +} + +// select record from table +pub async fn select_record( + table_name: &String, + column: &String, + condition: &Option, + data_struct: &T, +) -> Result, Box> +where + T: for<'r> FromRow<'r, MySqlRow> + Send + Unpin, +{ + let mut query = String::from("SELECT "); + query.push_str(column.to_lowercase().as_str()); + query.push_str(" FROM "); + query.push_str(table_name.to_lowercase().as_str()); + + match condition { + None => {} + Some(condition) => { + query.push(' '); + query.push_str(condition.as_str()); + query.push(';'); + } + } + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + // let mut query_result: Vec = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await?; + let mut query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await; + let mut query_result_vec: Vec = Vec::new(); + + loop { + match query_result { + Ok(T) => { + if !T.is_empty() { + query_result_vec = T; + break; + } else { + sleep(Duration::from_millis(200)).await; + query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await; + } + } + Err(e) => { + sleep(Duration::from_millis(200)).await; + query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await; + } + } + } + Ok(query_result_vec) +} + +// select record from table. No loop +pub async fn try_select_record( + table_name: &String, + column: &String, + condition: &Option, + data_struct: &T, +) -> Result, Box> +where + T: for<'r> FromRow<'r, MySqlRow> + Send + Unpin, +{ + let mut query = String::from("SELECT "); + query.push_str(column.to_lowercase().as_str()); + query.push_str(" FROM "); + query.push_str(table_name.to_lowercase().as_str()); + + match condition { + None => {} + Some(condition) => { + query.push(' '); + query.push_str(condition.as_str()); + query.push(';'); + } + } + + let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + //retry connection until it will be done. + while conn_result.is_err() { + sleep(Duration::from_millis(200)).await; + conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await; + } + let mut conn = conn_result.unwrap(); + + let mut query_result: Vec = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await?; + + Ok(query_result) +} diff --git a/src/decimal_funcs.rs b/src/decimal_funcs.rs new file mode 100644 index 0000000..8989d18 --- /dev/null +++ b/src/decimal_funcs.rs @@ -0,0 +1,38 @@ +use rust_decimal::{Decimal, RoundingStrategy}; + +pub fn decimal(string_num: &String) -> Decimal { + let mut temp_num = string_num.clone(); + if temp_num.contains('.') { + temp_num.remove(temp_num.find('.').unwrap()); + } + let decimal_num = temp_num.parse::().unwrap(); + Decimal::from_i128_with_scale(decimal_num, floating_scale(string_num)) + .round_dp_with_strategy(8, RoundingStrategy::ToZero) +} + +fn floating_scale(string_num: &String) -> u32 { + let temp_num = string_num.clone(); + if temp_num.contains('.') { + let len = temp_num.to_string().len(); + let point_index = temp_num.find('.').unwrap(); + (len - (point_index + 1)) as u32 + } else { + 0 + } +} + +pub fn decimal_mul(num1: Decimal, num2: Decimal) -> Decimal { + (num1 * num2).round_dp_with_strategy(8, RoundingStrategy::ToZero) +} + +pub fn decimal_add(num1: Decimal, num2: Decimal) -> Decimal { + (num1 + num2).round_dp_with_strategy(8, RoundingStrategy::ToZero) +} + +pub fn decimal_sub(num1: Decimal, num2: Decimal) -> Decimal { + (num1 - num2).round_dp_with_strategy(8, RoundingStrategy::ToZero) +} + +pub fn decimal_div(num1: Decimal, num2: Decimal) -> Decimal { + (num1 / num2).round_dp_with_strategy(8, RoundingStrategy::ToZero) +} diff --git a/src/initialization.rs b/src/initialization.rs new file mode 100644 index 0000000..8bcea77 --- /dev/null +++ b/src/initialization.rs @@ -0,0 +1,1442 @@ +use crate::coex::assets_managing_team; +use crate::coex::order_team; +use crate::coex::order_team::{BuyOrderedCoinList, SellOrderedCoinList}; +use crate::coin_health_check_team::*; +use crate::database_control::*; +use crate::time_checking_team::{UserTime, *}; +use crate::RunningMode::*; +use crate::RUNNING_MODE; +use futures::future::try_join_all; +use reqwest::{Client, ClientBuilder}; +use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; +use rust_decimal_macros::dec; +use sqlx::FromRow; +use std::{io, io::Write, process::Stdio}; +use tokio::{fs::*, io::ErrorKind, process::Command, task::JoinHandle, time::*}; + +const WEB_DRIVER_PATH: &str = "E:/[Cryptocurrency]/tradingbot/webdriver/chromedriver.exe"; +const WEB_DRIVER_PATH2: &str = "C:/tradingbot_complete/webdriver/chromedriver.exe"; +const STRATEGIST_NUMBER: u32 = 16; + +pub async fn initialization() { + println!("- - - initialization start - - -"); + // initialize_folders().await; + initialize_webdriver().await; + initialize_database().await; + println!("- - - initialization done - - -"); +} + +async fn initialize_folders() { + { + println!(">>> Remove folders..."); + let dir_vec = vec!["datapoints", "indicators", "images"]; + for dir_element in dir_vec { + print!("/{}...", dir_element); + io::stdout().flush(); + match remove_dir_all(dir_element).await { + Ok(()) => {} + Err(ref error) => match error.kind() { + ErrorKind::NotFound => {} + other_error => panic!("Failed to remove dir!"), + }, + } + println!("Ok"); + } + } + + { + println!(">>> Check directory..."); + let dir_vec = vec![ + "datapoints", + "datapoints/price", + "indicators", + "indicators/sma", + "indicators/ema", + "indicators/tema", + "indicators/bollingerband", + "indicators/rsi", + "images", + ]; + for element in dir_vec { + print!("/{}...", element); + io::stdout().flush(); + + let result_read_dir = read_dir(element).await; + match result_read_dir { + Err(E) => { + create_dir(element).await.expect("Failed to create dir!"); + } + Ok(T) => {} + } + let mut result_read_dir = read_dir(element).await; + while let Err(e) = result_read_dir { + result_read_dir = read_dir(element).await; + sleep(Duration::from_millis(100)); + } + println!("Ok"); + } + } +} + +async fn initialize_webdriver() { + print!("Open webdriver..."); + io::stdout().flush(); + let mut output; + + output = Command::new(WEB_DRIVER_PATH) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn(); + + if output.is_ok() { + output.unwrap(); + } else { + output = Command::new(WEB_DRIVER_PATH2) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn(); + + output.unwrap(); + } + println!("Ok"); +} + +async fn initialize_database() { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(700)) + .build() + .unwrap(); + + println!(">>> Check database..."); + { + print!("table 'time'..."); + io::stdout().flush(); + + let table_name = String::from("time"); + let exists_table = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("server_epoch", "bigint", Some("UN")), + ("local_epoch", "bigint", Some("UN")), + ("epoch_difference", "bigint", None), + ("server_ymdhs", "tinytext", None), + ("local_ymdhs", "tinytext", None), + ("last_server_epoch", "bigint", Some("UN")), + ("last_server_ymdhs", "tinytext", None), + ]; + let table_condition = None; + let initial_columns = vec![ + "id", + "server_epoch", + "local_epoch", + "epoch_difference", + "server_ymdhs", + "local_ymdhs", + "last_server_epoch", + "last_server_ymdhs", + ]; + let initial_value = vec![ + String::from("1"), + String::from("0"), + String::from("0"), + String::from("0"), + String::from("----"), + String::from("----"), + String::from("0"), + String::from("----"), + ]; + + if exists_table == false { + new_table(&table_name, &initial_table, &table_condition) + .await + .expect("Failed to create table!"); + } + + delete_all_rows((&table_name)).await; + insert_one_record(&table_name, &initial_columns, &initial_value).await; + + let mut usertime = UserTime::new(); + execute_time_check(&mut usertime, &client).await; + println!("Ok"); + } + + { + print!("table 'serverhealth'..."); + io::stdout().flush(); + + let table_name = String::from("serverhealth"); + let exists_table = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("server_on", "tinyint", None), + ("ping_on", "tinyint", None), + ("wallet_system_on", "tinyint", None), + ("waiting_maximum", "integer", Some("UN")), + ]; + let table_condition = None; + let initial_columns = vec![ + "id", + "server_on", + "ping_on", + "wallet_system_on", + "waiting_maximum", + ]; + let initial_value = vec![ + String::from("0"), + String::from("0"), + String::from("0"), + String::from("0"), + String::from("0"), + ]; + + if exists_table == false { + new_table(&table_name, &initial_table, &table_condition) + .await + .expect("Failed to create table!"); + } + + delete_all_rows(&table_name).await; + insert_one_record(&table_name, &initial_columns, &initial_value).await; + + println!("Ok"); + } + + { + print!("table 'all_24h_change'..."); + io::stdout().flush(); + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(1200)) + .build() + .unwrap(); + let table_name = String::from("all_24h_change"); + let exists_table = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("symbol", "char(20)", None), + ("priceChange", "double", None), + ("priceChangePercent", "double", None), + ("weightedAvgPrice", "double", None), + ("prevClosePrice", "double", None), + ("lastPrice", "double", None), + ("lastQty", "double", None), + ("bidPrice", "double", None), + ("bidQty", "double", None), + ("askPrice", "double", None), + ("askQty", "double", None), + ("openPrice", "double", None), + ("highPrice", "double", None), + ("lowPrice", "double", None), + ("volume", "double", None), + ("quoteVolume", "double", None), + ("openTime", "bigint", None), + ("closeTime", "bigint", None), + ("firstId", "bigint", None), + ("lastId", "bigint", None), + ("count", "int", None), + ]; + let table_condition = None; + + if exists_table == false { + let mut result = new_table(&table_name, &initial_table, &table_condition) + .await + .expect("Failed to create table!"); + } + + let mut result = request_others::request_24hr_ticker_price_change_statistics(&client).await; + while let Err(E) = result { + result = request_others::request_24hr_ticker_price_change_statistics(&client).await; + sleep(Duration::from_millis(100)); + } + println!("Ok"); + } + + // { + // print!("table 'prev_valid_usdt_trades'..."); + // io::stdout().flush(); + + // let table_name = String::from("prev_valid_usdt_trades"); + // let exists_result = exists_table(&table_name).await; + // let initial_table = vec![("id", "integer", Some("PK, AI")), ("symbol", "char(20)", None)]; + // let table_condition = None; + + // if exists_result == false { + // let mut result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_err() { + // loop { + // result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_ok(){ break; } + // sleep(Duration::from_millis(10)).await; + // } + // } + // } + // println!("Ok"); + // } + + { + print!("table 'stop_usdt_trades'..."); + io::stdout().flush(); + + let table_name = String::from("stop_usdt_trades"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("symbol", "char(20)", None), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + println!("Ok"); + } + + { + print!("table 'valid_usdt_trades'..."); + io::stdout().flush(); + + let table_name = String::from("valid_usdt_trades"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("symbol", "char(20)", None), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + let mut valid_usdt_trade_vec_temp: Vec = Vec::new(); + monitors::collect_valid_usde_trade(&mut valid_usdt_trade_vec_temp).await; + println!("Ok"); + } + + // { + // print!("table 'deceased_usdt_trades'..."); + // io::stdout().flush(); + + // let table_name = String::from("deceased_usdt_trades"); + // let exists_result = exists_table(&table_name).await; + // let initial_table = vec![("id", "integer", Some("PK, AI")), ("symbol", "char(20)", None)]; + // let table_condition = None; + + // if exists_result == false { + // let mut result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_err() { + // loop { + // result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_ok(){ break; } + // sleep(Duration::from_millis(10)).await; + // } + // } + // } + // println!("Ok"); + // } + + { + print!("table 'indices'..."); + io::stdout().flush(); + + let table_name = String::from("indices"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("server_epoch", "bigint", Some("UN")), + ("total_24h_change_profit_index", "double", None), + ("usdt_24h_change_profit_index", "double", None), + ("total_price_down_dist_index", "double", None), + ]; + let initial_columns = vec![ + "server_epoch", + "total_24h_change_profit_index", + "usdt_24h_change_profit_index", + "total_price_down_dist_index", + ]; + let initial_values = vec![ + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + + println!("Ok"); + } + + { + print!("table 'market_cap_index'..."); + io::stdout().flush(); + + let table_name = String::from("market_cap_index"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("market_cap_index", "double", None), + ("minimum", "double", None), + ("maximum", "double", None), + ("transition_point", "double", None), + ("liquidation_signal", "tinyint", None), + ("negative_buy_signal", "tinyint", None), + ("transition_buy_signal", "tinyint", None), + ("comment", "char(11)", None), + ]; + let initial_columns = vec![ + "market_cap_index", + "minimum", + "maximum", + "transition_point", + "liquidation_signal", + "negative_buy_signal", + "transition_buy_signal", + "comment", + ]; + let initial_values = vec![ + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0"), + String::from("0"), + String::from("5hrs angle"), + ]; + let initial_values2 = vec![ + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0"), + String::from("0"), + String::from("5days angle"), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + insert_one_record(&table_name, &initial_columns, &initial_values2) + .await + .expect("Failed to insert initial record!"); + + println!("Ok"); + } + + { + use crate::signal_association::exchange_rate; + print!("table 'foreign_exchange_rates'..."); + io::stdout().flush(); + + let table_name = String::from("foreign_exchange_rates"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("date", "date", None), + ("RUB", "double", None), + ("CAD", "double", None), + ("IQD", "double", None), + ("CNY", "double", None), + ("BRL", "double", None), + ("IRR", "double", None), + ("MXN", "double", None), + ("NOK", "double", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + // delete_all_rows(&table_name).await.expect("Failed to delete rows!"); + + // // initialize table + // exchange_rate::initialize_record().await; + println!("Ok"); + } + + { + use crate::signal_association::dollar_index; + print!("table 'dollar_index'..."); + io::stdout().flush(); + + let table_name = String::from("dollar_index"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("date", "date", None), + ("value", "char(10)", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + + println!("Ok"); + } + + { + print!("table 'signal_decision'..."); + io::stdout().flush(); + + let table_name = String::from("signal_decision"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("decision", "char(11)", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + let initial_columns = vec!["decision"]; + let initial_values = vec![String::from("-")]; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + println!("Ok"); + } + + // { + // print!("table 'all_coin_start_price'..."); + // io::stdout().flush(); + + // let table_name = String::from("all_coin_start_price"); + // let exists_result = exists_table(&table_name).await; + // let initial_table = vec![("id", "integer", Some("PK, AI")), + // ("symbol", "char(20)", None), + // ("start_price", "double", None)]; + // let table_condition = Some("ENGINE = MEMORY");; + + // if exists_result == false { + // let mut result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_err() { + // loop { + // result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_ok(){ break; } + // sleep(Duration::from_millis(10)).await; + // } + // } + // } + + // delete_all_rows(&table_name).await.expect("Failed to delete rows!"); + + // // insert initial records + // crate::monitors::initial_all_coin_start_price().await; + + // println!("Ok"); + // } + + // { + // print!("table 'all_coin_profit_change_avg'..."); + // io::stdout().flush(); + + // let table_name = String::from("all_coin_profit_change_avg"); + // let exists_result = exists_table(&table_name).await; + // let initial_table = vec![("id", "integer", Some("PK, AI")), + // ("server_epoch", "bigint", Some("UN")), + // ("cnt", "integer", Some("UN")), + // ("avg_profit", "double", None)]; + // let initial_columns = vec![ + // "server_epoch", + // "cnt", + // "avg_profit"]; + // let initial_values = vec![ + // String::from("0"), + // String::from("1"), + // String::from("0.0")]; + // let table_condition = Some("ENGINE = MEMORY");; + + // if exists_result == false { + // let mut result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_err() { + // loop { + // result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_ok(){ break; } + // sleep(Duration::from_millis(10)).await; + // } + // } + // } + + // delete_all_rows(&table_name).await.expect("Failed to delete rows!"); + // insert_one_record(&table_name, &initial_columns, &initial_values).await.expect("Failed to insert initial record!"); + + // println!("Ok"); + // } + + // { + // print!("table 'all_coin_profit_change_history'..."); + // io::stdout().flush(); + + // let table_name = String::from("all_coin_profit_change_history"); + // let exists_result = exists_table(&table_name).await; + // let initial_table = vec![("id", "integer", Some("PK, AI")), + // ("server_epoch", "bigint", Some("UN")), + // ("avg_profit", "double", None)]; + // let table_condition = Some("ENGINE = MEMORY");; + + // if exists_result == false { + // let mut result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_err() { + // loop { + // result = new_table(&table_name, &initial_table, &table_condition).await; + // if result.is_ok(){ break; } + // sleep(Duration::from_millis(10)).await; + // } + // } + // } + + // delete_all_rows(&table_name).await.expect("Failed to delete rows!"); + + // println!("Ok"); + // } + + { + print!("table 'signal_association_opinion'..."); + io::stdout().flush(); + + let table_name = String::from("signal_association_opinion"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("name", "char(30)", None), + ("is_working", "tinyint", None), + ("opinion", "char(5)", None), + ("weight", "double", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + + // insert initial records + crate::signal_association::coinmarketcap::initialize_association_record().await; + crate::signal_association::exchange_rate::initialize_association_record().await; + crate::signal_association::dollar_index::initialize_association_record().await; + crate::signal_association::future_ratio::initialize_association_record().await; + + println!("Ok"); + } + + // COEX DB part + { + // achievement_evaluation + print!("table 'achievement_evaluation'..."); + io::stdout().flush(); + + let table_name = String::from("achievement_evaluation"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("strategist", "smallint", Some("UN")), + ("invested_usdt", "decimal(16,8)", Some("UN")), + ("usdt_profit", "decimal(16,8)", None), // usdt_profit reflects tradefees(two takers) + ("profit_percent", "double", None), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + } + + let insert_initial_columns = vec![ + "strategist", + "invested_usdt", + "usdt_profit", + "profit_percent", + ]; + let mut insert_initial_value = vec![ + String::new(), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + ]; + + for number in (0..STRATEGIST_NUMBER) { + insert_initial_value[0] = (number + 1).to_string(); + insert_one_record(&table_name, &insert_initial_columns, &insert_initial_value) + .await + .expect("Failed to insert initial row!"); + } + + println!("Ok"); + } + + { + // suggested_coin_list + print!("table 'suggested_coin_list'..."); + io::stdout().flush(); + + let table_name = String::from("suggested_coin_list"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("symbol", "char(20)", None), + ("suggested_price", "decimal(16,8)", None), + ("close_time", "bigint", None), + ("registered_server_epoch", "bigint", None), + ("registerer", "smallint", Some("UN")), + ("is_long", "tinyint", Some("UN")), + ("already_buy", "tinyint", Some("UN")), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + } + println!("Ok"); + } + + { + print!("table 'wallet'..."); + io::stdout().flush(); + + let table_name = String::from("wallet"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("asset", "char(20)", None), + ("free", "decimal(16,8)", None), + ("locked", "decimal(16,8)", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + + // table initialization for SIMUL MODE + if RUNNING_MODE == SIMUL { + #[derive(Debug, FromRow)] + struct Symbols { + symbol: String, + } + let fetch_table_name = String::from("valid_usdt_trades"); + let column_name = String::from("symbol"); + let condition = None; + let mut symbols = Symbols { + symbol: String::new(), + }; + let symbols_vec = select_record(&fetch_table_name, &column_name, &condition, &symbols) + .await + .expect("Failed to fetch records!"); + let insert_table_name = String::from("wallet"); + let insert_columns = vec!["asset", "free", "locked"]; + let mut insert_values: Vec> = Vec::new(); + let mut insert_value_container: Vec = Vec::new(); + let mut symbol_truncated = String::new(); + + insert_value_container.clear(); + insert_value_container.push(String::from("USDT")); + insert_value_container.push(0.to_string()); + insert_value_container.push(0.to_string()); + insert_values.push(insert_value_container.clone()); + + for mut element in symbols_vec { + insert_value_container.clear(); + symbol_truncated.clear(); + element.symbol.pop(); + element.symbol.pop(); + element.symbol.pop(); + element.symbol.pop(); + insert_value_container.push(element.symbol); + insert_value_container.push(0.to_string()); + insert_value_container.push(0.to_string()); + insert_values.push(insert_value_container.clone()); + } + insert_records(&insert_table_name, &insert_columns, &insert_values).await; + } + + println!("Ok"); + } + + { + print!("table 'wallet_testnet'..."); + io::stdout().flush(); + + let table_name = String::from("wallet_testnet"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("asset", "char(20)", None), + ("free", "decimal(16,8)", None), + ("locked", "decimal(16,8)", None), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + + println!("Ok"); + } + + { + // asset_manage_announcement + print!("table 'asset_manage_announcement'..."); + io::stdout().flush(); + + let table_name = String::from("asset_manage_announcement"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, UN, AI")), + ("initial_usdt", "decimal(16,8)", None), + ("current_total_usdt", "decimal(16,8)", None), + ("profit", "decimal(16,8)", None), + ("realtime_expected_total_usdt", "double", None), + ("realtime_profit", "double", None), + ("available_usdt", "decimal(16,8)", Some("UN")), + ("is_tradable", "tinyint", Some("UN")), + ("unit_trade_usdt", "decimal(16,8)", None), + ]; + let table_condition = None; + + let insert_initial_columns = vec![ + "initial_usdt", + "current_total_usdt", + "profit", + "realtime_expected_total_usdt", + "realtime_profit", + "available_usdt", + "is_tradable", + "unit_trade_usdt", + ]; + let insert_initial_value = vec![ + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0.0"), + ]; + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + insert_one_record(&table_name, &insert_initial_columns, &insert_initial_value) + .await + .expect("Failed to insert initial row!"); + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &insert_initial_columns, &insert_initial_value) + .await + .expect("Failed to insert initial row!"); + } + + if RUNNING_MODE == SIMUL { + assets_managing_team::add_extra_usdt(dec!(1000.0)).await; + assets_managing_team::update_current_total_usdt().await; + } else { + let mut table_name = String::new(); + let column = String::from("free"); + let condition = Some(String::from("WHERE asset = 'USDT'")); + + #[derive(FromRow)] + struct OneDecimal { + free: Decimal, + }; + + let data_struct = OneDecimal { + free: Decimal::new(0, 8), + }; + + order_team::account_information(&client).await; + + if RUNNING_MODE == TEST { + table_name = String::from("wallet_testnet"); + } else if RUNNING_MODE == REAL { + table_name = String::from("wallet"); + } + + let result = select_record(&table_name, &column, &condition, &data_struct) + .await + .unwrap(); + let free_usdt = result.first().unwrap().free; + assets_managing_team::add_extra_usdt(free_usdt).await; + assets_managing_team::update_current_total_usdt().await; + } + assets_managing_team::set_available_usdt().await; + + println!("Ok"); + } + + { + // filtered_indices + print!("table 'filtered_indices'..."); + io::stdout().flush(); + + let table_name = String::from("filtered_indices"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI")), + ("server_epoch", "bigint", Some("UN")), + ("total_24h_change_profit_index", "double", None), + ("usdt_24h_change_profit_index", "double", None), + ("total_price_down_dist_index", "double", None), + ("total_price_down_dist_index_maximum", "double", None), + ("total_price_down_dist_index_flag", "integer", None), + ]; + let initial_columns = vec![ + "server_epoch", + "total_24h_change_profit_index", + "usdt_24h_change_profit_index", + "total_price_down_dist_index", + "total_price_down_dist_index_maximum", + "total_price_down_dist_index_flag", + ]; + let initial_values = vec![ + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + + println!("Ok"); + } + + { + // pre_suggested_coin_list + print!("table 'pre_suggested_coin_list'..."); + io::stdout().flush(); + + let table_name = String::from("pre_suggested_coin_list"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("symbol", "char(20)", None), + ("close_time", "bigint", None), + ("suggested_price", "decimal(16,8)", None), + ("current_price", "decimal(16,8)", None), + ("registered_server_epoch", "bigint", None), + ("profit_percent", "double", None), + ("minimum_profit_percent", "double", None), + ("maximum_profit_percent", "double", None), + ("registerer", "smallint", Some("UN")), + ]; + let table_condition = Some("ENGINE = MEMORY"); + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + } + println!("Ok"); + } + + { + // buy_ordered_coin_list + print!("table 'buy_ordered_coin_list'..."); + io::stdout().flush(); + + let table_name = String::from("buy_ordered_coin_list"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("symbol", "char(20)", None), + ("order_id", "bigint", Some("UN")), + ("transact_time", "bigint", None), + ("close_time", "bigint", None), + ("status", "char(20)", None), + ("used_usdt", "decimal(16,8)", None), + ("expected_get_usdt", "double", None), + ("expected_usdt_profit", "double", None), + ("buy_price", "decimal(16,8)", None), + ("current_price", "decimal(16,8)", None), + ("base_qty_ordered", "decimal(16,8)", None), + ("base_qty_fee_adjusted", "decimal(16,8)", None), + ("pure_profit_percent", "double", None), + ("minimum_profit_percent", "double", None), + ("maximum_profit_percent", "double", None), + ("registerer", "smallint", Some("UN")), + ("is_long", "tinyint", Some("UN")), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + // // delete records tagged SIMUL + // if RUNNING_MODE == SIMUL { + // let select_table_name = String::from("buy_ordered_coin_list"); + // let select_columns = String::from("*"); + // let select_condition = Some(String::from("WHERE status = 'SIMUL'")); + // let data_struct = BuyOrderedCoinList { + // id: 0, + // symbol: String::new(), + // order_id: 0, + // transact_time: 0, + // status: String::new(), + // used_usdt: Decimal::new(0, 8), + // expected_get_usdt: 0.0, + // expected_usdt_profit: 0.0, + // buy_price: Decimal::new(0, 8), + // current_price: Decimal::new(0, 8), + // base_qty_ordered: Decimal::new(0, 8), + // base_qty_fee_adjusted: Decimal::new(0, 8), + // pure_profit_percent: 0.0, + // minimum_profit_percent: 0.0, + // maximum_profit_percent: 0.0, + // registerer: 0, + // is_long: 0 + // }; + + // let select_result = try_select_record(&select_table_name, &select_columns, &select_condition, &data_struct).await.unwrap(); + + // if !select_result.is_empty() { + // for element in select_result { + // let mut condition = String::from("WHERE id="); + // condition.push_str(element.id.to_string().as_str()); + // delete_record(&select_table_name, &condition).await; + // } + // } + // } + println!("Ok"); + } + + { + // sell_ordered_coin_list + print!("table 'sell_ordered_coin_list'..."); + io::stdout().flush(); + + let table_name = String::from("sell_ordered_coin_list"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("symbol", "char(20)", None), + ("buy_order_id", "bigint", Some("UN")), + ("sell_order_id", "bigint", Some("UN")), + ("transact_time", "bigint", None), + ("close_time", "bigint", None), + ("status", "char(20)", None), + ("used_usdt", "decimal(16,8)", None), + ("get_usdt", "decimal(16,8)", None), + ("get_usdt_fee_adjusted", "decimal(16,8)", None), + ("buy_price", "decimal(16,8)", None), + ("sell_price", "decimal(16,8)", None), + ("base_qty_ordered", "decimal(16,8)", None), + ("pure_profit_percent", "decimal(16,8)", None), + ("maximum_profit_percent", "double", None), + ("registerer", "smallint", Some("UN")), + ("is_long", "tinyint", Some("UN")), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } + + // delete records tagged SIMUL + if RUNNING_MODE == SIMUL { + let select_table_name = String::from("sell_ordered_coin_list"); + let select_columns = String::from("*"); + let select_condition = Some(String::from("WHERE status = 'SIMUL'")); + let data_struct = SellOrderedCoinList { + id: 0, + symbol: String::new(), + buy_order_id: 0, + sell_order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + get_usdt: Decimal::new(0, 8), + get_usdt_fee_adjusted: Decimal::new(0, 8), + buy_price: Decimal::new(0, 8), + sell_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + pure_profit_percent: Decimal::new(0, 8), + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + + let select_result = try_select_record( + &select_table_name, + &select_columns, + &select_condition, + &data_struct, + ) + .await + .unwrap(); + + if !select_result.is_empty() { + for element in select_result { + let mut condition = String::from("WHERE id="); + condition.push_str(element.id.to_string().as_str()); + delete_record(&select_table_name, &condition).await; + } + } + } + println!("Ok"); + } + + { + // scoreboard + print!("table 'scoreboard'..."); + io::stdout().flush(); + + let table_name = String::from("scoreboard"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("total_number_of_coin", "integer", Some("UN")), + ("pos_profit_number", "integer", Some("UN")), + ("neg_profit_number", "integer", Some("UN")), + ("total_used_usdt", "double", None), + ("pos_used_usdt", "double", None), + ("neg_used_usdt", "double", None), + ("total_pos_profit_usdt", "double", None), + ("total_neg_profit_usdt", "double", None), + ("maximum_total_pos_profit_usdt", "double", None), + ("avg_neg_profit_percent", "double", None), + ("pos_liquidation_signal", "integer", Some("UN")), + ("neg_liquidation_signal", "integer", Some("UN")), + ("comment", "char(10)", None), + ]; + let initial_columns = vec![ + "total_number_of_coin", + "pos_profit_number", + "neg_profit_number", + "total_used_usdt", + "pos_used_usdt", + "neg_used_usdt", + "total_pos_profit_usdt", + "total_neg_profit_usdt", + "maximum_total_pos_profit_usdt", + "avg_neg_profit_percent", + "pos_liquidation_signal", + "neg_liquidation_signal", + "comment", + ]; + let initial_values = vec![ + String::from("0"), + String::from("0"), + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0"), + String::from("for_short"), + ]; + let initial_values2 = vec![ + String::from("0"), + String::from("0"), + String::from("0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0.0"), + String::from("0"), + String::from("0"), + String::from("for_long"), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + insert_one_record(&table_name, &initial_columns, &initial_values2) + .await + .expect("Failed to insert initial record!"); + break; + } + sleep(Duration::from_millis(10)).await; + } + } + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + insert_one_record(&table_name, &initial_columns, &initial_values2) + .await + .expect("Failed to insert initial record!"); + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + insert_one_record(&table_name, &initial_columns, &initial_values) + .await + .expect("Failed to insert initial record!"); + insert_one_record(&table_name, &initial_columns, &initial_values2) + .await + .expect("Failed to insert initial record!"); + } + println!("Ok"); + } + + { + // sell_history + print!("table 'sell_history'..."); + io::stdout().flush(); + + let table_name = String::from("sell_history"); + let exists_result = exists_table(&table_name).await; + let initial_table = vec![ + ("id", "integer", Some("PK, AI, UN")), + ("symbol", "char(20)", None), + ("soldtime", "bigint", None), + ("used_usdt", "decimal(16,8)", None), + ("get_usdt", "decimal(16,8)", None), + ("buy_price", "decimal(16,8)", None), + ("sell_price", "decimal(16,8)", None), + ("base_qty", "decimal(16,8)", None), + ("pure_profit_percent", "double", None), + ("pure_profit_usdt", "decimal(16,8)", None), + ("registerer", "smallint", Some("UN")), + ]; + let table_condition = None; + + if exists_result == false { + let mut result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_err() { + loop { + result = new_table(&table_name, &initial_table, &table_condition).await; + if result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } + } else { + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + } + println!("Ok"); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..058276e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,34 @@ +#![allow(unused)] +#![allow(warnings)] + +pub const DB_URL: &str = "mysql://root:Durtkarovh23!@localhost:3306/tradingbot"; + +// put your API Key and Signature in below variables respectively. +pub const API_KEY: &str = "h7HPxyfUpROa7GINE3Ktx5z5p59jESWyFRKhk2D7rhd6dgcLroyNW4urnujjGCmO"; // real account +pub const SECRET_KEY: &str = "TkhHmNUG6fCMZcCSjfYn8ZY5UfDbD9O11g9scp2P6fgdyH0eDzcCS9lNpSCLSR6Y"; // real account +pub const API_KEY_TESTNET: &str = + "NO6QbO5FvBQtYkdl2MCz819hBxbY83gpoSijCgOQbEgNyLWMny2KJOV5LwPsRGsS"; // for spot trading testnet +pub const SECRET_KEY_TESTNET: &str = + "9cQCq6ufVvxrDaOnnVDszy8ZzSYnuTTNk0WEbo3lNkFCIFM9OQ1l3IDPVz5NiTxA"; // for spot trading testnet + +// URL +pub const URL_TEST: &str = "https://testnet.binance.vision"; +pub const URL: &str = "https://api1.binance.com"; + +// Select program mode +#[derive(PartialEq)] +pub enum RunningMode { + SIMUL, + TEST, + REAL, +} +pub const RUNNING_MODE: RunningMode = RunningMode::REAL; + +mod coex; +mod coin_health_check_team; +mod database_control; +mod decimal_funcs; +mod server_health_check_team; +mod signal_association; +mod time_checking_team; +mod value_estimation_team; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0b5d6d0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,2363 @@ +#![allow(unused)] +#![allow(warnings)] + +mod coex; +mod coin_health_check_team; +mod database_control; +mod decimal_funcs; +mod initialization; +mod server_health_check_team; +mod signal_association; +mod time_checking_team; +mod value_estimation_team; + +use crate::coex::order_team::*; +use crate::coex::strategy_team::AllData; +use crate::coin_health_check_team::*; +use crate::request_candles::CandleData; +use crate::request_others::{CoinPriceData, ExchangeInfo, TradeFee}; +use crate::server_health_check_team::ServerHealth; +use crate::time_checking_team::UserTime; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::bollingerband::BollingerBandData; +use crate::value_estimation_team::indicators::ema::EmaData; +use crate::value_estimation_team::indicators::rsi::RsiData; +use crate::value_estimation_team::indicators::sma::SmaData; +use crate::value_estimation_team::indicators::stoch_rsi::StochRsiDData; +use crate::value_estimation_team::indicators::stoch_rsi::StochRsiKData; +use reqwest::{Client, ClientBuilder}; +use sqlx::{mysql::*, Connection, Executor, FromRow, Row}; +use std::{ + io::{self, Write}, + sync::Arc, +}; +use tokio::{fs::*, join, sync::mpsc, sync::watch, sync::Mutex, task::*, time::*}; +use tradingbot::{RunningMode::*, *}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + initialization::initialization().await; + + // // let c1 = vec!(("name", "Shima Nabil")); + // // println!("{:?}", database_control::update_record(&table_name, &c1, &("grade", "3")).await); + + // //database_control::exists_table(&table_name).await; + // // let table_name = String::from("all24hstatistics"); + // // let result = database_control::count_rows(&table_name).await; + // // println!("{:?}", result); + + // // let rows = sqlx::query("select * from serverhealth") + // // .map(|r| { + // // a.id = r.get::("id"); + // // a.waiting_maximum = r.get::("waiting_maximum"); + // // a.server_on = r.get::("server_on"); + // // a.ping_on = r.get::("ping_on"); + // // a.wallet_system_on = r.get::("wallet_system_on"); + // // + // // }) + // // .collect::>() + // // .fetch(&pool).await; + + // // let str_result = rows + // // .iter() + // // .map(|r| format!("{} {}", r.get::("id"), r.get::("server_on"))) + // // .collect::>() + // // .join(", "); + // // println!("{:?}", rows); + // // + + let (tx_task1, mut rx_task0) = mpsc::unbounded_channel::(); + let tx_task2 = tx_task1.clone(); // for Task#2 + let tx_task3 = tx_task1.clone(); // for Task#3 + let tx_task4 = tx_task1.clone(); // for Task#4 + let tx_task5 = tx_task1.clone(); // for Task#5 + let tx_task6 = tx_task1.clone(); // for Task#6 + let tx_task7 = tx_task1.clone(); // for Task#7 + let tx_task8 = tx_task1.clone(); // for Task#8 + let tx_task9 = tx_task1.clone(); // for Task#9 + let tx_task10 = tx_task1.clone(); // for Task#10 + let tx_task11 = tx_task1.clone(); // for Task#11 + let tx_task12 = tx_task1.clone(); // for Task#12 + let tx_task13 = tx_task1.clone(); // for Task#13 + let tx_task14 = tx_task1.clone(); // for Task#14 + let tx_task15 = tx_task1.clone(); // for Task#15 + let tx_task16 = tx_task1.clone(); // for Task#16 + let tx_task17 = tx_task1.clone(); // for Task#17 + let tx_task18 = tx_task1.clone(); // for Task#18 + let tx_task19 = tx_task1.clone(); // for Task#19 + let tx_task20 = tx_task1.clone(); // for Task#20 + let tx_task21 = tx_task1.clone(); // for Task#21 + let tx_task22 = tx_task1.clone(); // for Task#22 + let tx_task23 = tx_task1.clone(); // for Task#23 + let tx_task24 = tx_task1.clone(); // for Task#24 + let tx_task25 = tx_task1.clone(); // for Task#25 + let tx_task26 = tx_task1.clone(); // for Task#26 + + let (tx1, mut rx1_1) = watch::channel(0); // local_epoch + let mut rx1_2 = rx1_1.clone(); + let mut rx1_3 = rx1_1.clone(); + let mut rx1_4 = rx1_1.clone(); + let mut rx1_5 = rx1_1.clone(); + let mut rx1_6 = rx1_1.clone(); + let mut rx1_7 = rx1_1.clone(); + let (tx2, mut rx2) = watch::channel(0); // epoch_difference + + // trade fee data + let mut tradefee_vec: Vec = Vec::new(); // (symbol, makerCommission, takerCommission) + let (tx_tradefee_vec, mut rx_tradefee_vec) = watch::channel(tradefee_vec); + let mut rx2_tradefee_vec = rx_tradefee_vec.clone(); + let mut rx3_tradefee_vec = rx_tradefee_vec.clone(); + let mut rx4_tradefee_vec = rx_tradefee_vec.clone(); + + // valid usdt trade data + let mut valid_usdt_trade_vec: Vec = Vec::new(); // symbol + let (tx_valid_usdt_trade_vec, mut rx_valid_usdt_trade_vec) = + watch::channel(valid_usdt_trade_vec); + let mut rx2_valid_usdt_trade_vec = rx_valid_usdt_trade_vec.clone(); + let mut rx3_valid_usdt_trade_vec = rx_valid_usdt_trade_vec.clone(); + + // price per second data and channels + let mut price_vec: Vec = Vec::new(); // (symbol, price) + let (tx_price_vec, mut rx_price_vec) = watch::channel(price_vec); + let mut rx2_price_vec = rx_price_vec.clone(); + let mut rx3_price_vec = rx_price_vec.clone(); + let mut rx4_price_vec = rx_price_vec.clone(); + let mut rx5_price_vec = rx_price_vec.clone(); + + // candle data from endpoint and channels + let mut candle_1m_vec: Vec<(String, Vec)> = Vec::new(); // (symbol, Vec + let (tx_candle_1m_vec, mut rx_candle_1m_vec) = watch::channel(candle_1m_vec); + + let mut candle_30m_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_candle_30m_vec, mut rx_candle_30m_vec) = watch::channel(candle_30m_vec); + + let mut candle_1d_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_candle_1d_vec, mut rx_candle_1d_vec) = watch::channel(candle_1d_vec); + + let mut candle_1w_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_candle_1w_vec, mut rx_candle_1w_vec) = watch::channel(candle_1w_vec); + + let mut candle_1mon_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_candle_1mon_vec, mut rx_candle_1mon_vec) = watch::channel(candle_1mon_vec); + + // real-time reflected price data and channels + let mut rt_price_1m_vec: Vec<(String, Vec)> = Vec::new(); // (symbol, Vec + let (tx_rt_price_1m_vec, mut rx_rt_price_1m_vec) = watch::channel(rt_price_1m_vec); + let mut rx2_rt_price_1m_vec = rx_rt_price_1m_vec.clone(); + let mut rx3_rt_price_1m_vec = rx_rt_price_1m_vec.clone(); + let mut rx4_rt_price_1m_vec = rx_rt_price_1m_vec.clone(); + + let mut rt_price_30m_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_rt_price_30m_vec, mut rx_rt_price_30m_vec) = watch::channel(rt_price_30m_vec); + let mut rx2_rt_price_30m_vec = rx_rt_price_30m_vec.clone(); + let mut rx3_rt_price_30m_vec = rx_rt_price_30m_vec.clone(); + let mut rx4_rt_price_30m_vec = rx_rt_price_30m_vec.clone(); + + let mut rt_price_1d_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_rt_price_1d_vec, mut rx_rt_price_1d_vec) = watch::channel(rt_price_1d_vec); + let mut rx2_rt_price_1d_vec = rx_rt_price_1d_vec.clone(); + let mut rx3_rt_price_1d_vec = rx_rt_price_1d_vec.clone(); + + let mut rt_price_1w_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_rt_price_1w_vec, mut rx_rt_price_1w_vec) = watch::channel(rt_price_1w_vec); + let mut rx2_rt_price_1w_vec = rx_rt_price_1w_vec.clone(); + let mut rx3_rt_price_1w_vec = rx_rt_price_1w_vec.clone(); + + let mut rt_price_1mon_vec: Vec<(String, Vec)> = Vec::new(); + let (tx_rt_price_1mon_vec, mut rx_rt_price_1mon_vec) = watch::channel(rt_price_1mon_vec); + let mut rx2_rt_price_1mon_vec = rx_rt_price_1mon_vec.clone(); + let mut rx3_rt_price_1mon_vec = rx_rt_price_1mon_vec.clone(); + + // SMA data + let mut sma3_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_sma3_1m_data, mut rx_sma3_1m_data) = watch::channel(sma3_1m_data); + let mut rx2_sma3_1m_data = rx_sma3_1m_data.clone(); + let mut rx3_sma3_1m_data = rx_sma3_1m_data.clone(); + + let mut sma3_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma3_30m_data, mut rx_sma3_30m_data) = watch::channel(sma3_30m_data); + let mut rx2_sma3_30m_data = rx_sma3_30m_data.clone(); + + let mut sma3_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma3_1d_data, mut rx_sma3_1d_data) = watch::channel(sma3_1d_data); + let mut rx2_sma3_1d_data = rx_sma3_1d_data.clone(); + + let mut sma3_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma3_1w_data, mut rx_sma3_1w_data) = watch::channel(sma3_1w_data); + let mut rx2_sma3_1w_data = rx_sma3_1w_data.clone(); + + let mut sma3_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma3_1mon_data, mut rx_sma3_1mon_data) = watch::channel(sma3_1mon_data); + let mut rx2_sma3_1mon_data = rx_sma3_1mon_data.clone(); + + let mut sma10_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_sma10_1m_data, mut rx_sma10_1m_data) = watch::channel(sma10_1m_data); + let mut rx2_sma10_1m_data = rx_sma10_1m_data.clone(); + let mut rx3_sma10_1m_data = rx_sma10_1m_data.clone(); + + let mut sma10_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma10_30m_data, mut rx_sma10_30m_data) = watch::channel(sma10_30m_data); + let mut rx2_sma10_30m_data = rx_sma10_30m_data.clone(); + + let mut sma10_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma10_1d_data, mut rx_sma10_1d_data) = watch::channel(sma10_1d_data); + let mut rx2_sma10_1d_data = rx_sma10_1d_data.clone(); + + let mut sma10_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma10_1w_data, mut rx_sma10_1w_data) = watch::channel(sma10_1w_data); + let mut rx2_sma10_1w_data = rx_sma10_1w_data.clone(); + + let mut sma10_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma10_1mon_data, mut rx_sma10_1mon_data) = watch::channel(sma10_1mon_data); + let mut rx2_sma10_1mon_data = rx_sma10_1mon_data.clone(); + + let mut sma30_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_sma30_1m_data, mut rx_sma30_1m_data) = watch::channel(sma30_1m_data); + let mut rx2_sma30_1m_data = rx_sma30_1m_data.clone(); + + let mut sma30_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma30_30m_data, mut rx_sma30_30m_data) = watch::channel(sma30_30m_data); + let mut rx2_sma30_30m_data = rx_sma30_30m_data.clone(); + + let mut sma30_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma30_1d_data, mut rx_sma30_1d_data) = watch::channel(sma30_1d_data); + let mut rx2_sma30_1d_data = rx_sma30_1d_data.clone(); + + let mut sma30_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma30_1w_data, mut rx_sma30_1w_data) = watch::channel(sma30_1w_data); + let mut rx2_sma30_1w_data = rx_sma30_1w_data.clone(); + + let mut sma30_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_sma30_1mon_data, mut rx_sma30_1mon_data) = watch::channel(sma30_1mon_data); + let mut rx2_sma30_1mon_data = rx_sma30_1mon_data.clone(); + + // EMA data + let mut ema3_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_ema3_1m_data, mut rx_ema3_1m_data) = watch::channel(ema3_1m_data); + let mut rx2_ema3_1m_data = rx_ema3_1m_data.clone(); + let mut rx3_ema3_1m_data = rx_ema3_1m_data.clone(); + + let mut ema3_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema3_30m_data, mut rx_ema3_30m_data) = watch::channel(ema3_30m_data); + let mut rx2_ema3_30m_data = rx_ema3_30m_data.clone(); + + let mut ema3_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema3_1d_data, mut rx_ema3_1d_data) = watch::channel(ema3_1d_data); + let mut rx2_ema3_1d_data = rx_ema3_1d_data.clone(); + + let mut ema3_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema3_1w_data, mut rx_ema3_1w_data) = watch::channel(ema3_1w_data); + let mut rx2_ema3_1w_data = rx_ema3_1w_data.clone(); + + let mut ema3_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema3_1mon_data, mut rx_ema3_1mon_data) = watch::channel(ema3_1mon_data); + let mut rx2_ema3_1mon_data = rx_ema3_1mon_data.clone(); + + let mut ema10_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_ema10_1m_data, mut rx_ema10_1m_data) = watch::channel(ema10_1m_data); + let mut rx2_ema10_1m_data = rx_ema10_1m_data.clone(); + let mut rx3_ema10_1m_data = rx_ema10_1m_data.clone(); + + let mut ema10_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema10_30m_data, mut rx_ema10_30m_data) = watch::channel(ema10_30m_data); + let mut rx2_ema10_30m_data = rx_ema10_30m_data.clone(); + + let mut ema10_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema10_1d_data, mut rx_ema10_1d_data) = watch::channel(ema10_1d_data); + let mut rx2_ema10_1d_data = rx_ema10_1d_data.clone(); + + let mut ema10_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema10_1w_data, mut rx_ema10_1w_data) = watch::channel(ema10_1w_data); + let mut rx2_ema10_1w_data = rx_ema10_1w_data.clone(); + + let mut ema10_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema10_1mon_data, mut rx_ema10_1mon_data) = watch::channel(ema10_1mon_data); + let mut rx2_ema10_1mon_data = rx_ema10_1mon_data.clone(); + + let mut ema30_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + let (tx_ema30_1m_data, mut rx_ema30_1m_data) = watch::channel(ema30_1m_data); + let mut rx2_ema30_1m_data = rx_ema30_1m_data.clone(); + + let mut ema30_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema30_30m_data, mut rx_ema30_30m_data) = watch::channel(ema30_30m_data); + let mut rx2_ema30_30m_data = rx_ema30_30m_data.clone(); + + let mut ema30_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema30_1d_data, mut rx_ema30_1d_data) = watch::channel(ema30_1d_data); + let mut rx2_ema30_1d_data = rx_ema30_1d_data.clone(); + + let mut ema30_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema30_1w_data, mut rx_ema30_1w_data) = watch::channel(ema30_1w_data); + let mut rx2_ema30_1w_data = rx_ema30_1w_data.clone(); + + let mut ema30_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_ema30_1mon_data, mut rx_ema30_1mon_data) = watch::channel(ema30_1mon_data); + let mut rx2_ema30_1mon_data = rx_ema30_1mon_data.clone(); + + // TEMA data + // let mut tema3_1m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + // let (tx_tema3_1m_data, mut rx_tema3_1m_data) = watch::channel(tema3_1m_data); + // let mut tema3_30m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema3_30m_data, mut rx_tema3_30m_data) = watch::channel(tema3_30m_data); + // let mut tema3_1d_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema3_1d_data, mut rx_tema3_1d_data) = watch::channel(tema3_1d_data); + // let mut tema3_1w_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema3_1w_data, mut rx_tema3_1w_data) = watch::channel(tema3_1w_data); + // let mut tema3_1mon_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema3_1mon_data, mut rx_tema3_1mon_data) = watch::channel(tema3_1mon_data); + + // let mut tema10_1m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + // let (tx_tema10_1m_data, mut rx_tema10_1m_data) = watch::channel(tema10_1m_data); + // let mut tema10_30m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema10_30m_data, mut rx_tema10_30m_data) = watch::channel(tema10_30m_data); + // let mut tema10_1d_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema10_1d_data, mut rx_tema10_1d_data) = watch::channel(tema10_1d_data); + // let mut tema10_1w_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema10_1w_data, mut rx_tema10_1w_data) = watch::channel(tema10_1w_data); + // let mut tema10_1mon_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema10_1mon_data, mut rx_tema10_1mon_data) = watch::channel(tema10_1mon_data); + + // let mut tema30_1m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)> + // let (tx_tema30_1m_data, mut rx_tema30_1m_data) = watch::channel(tema30_1m_data); + // let mut tema30_30m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema30_30m_data, mut rx_tema30_30m_data) = watch::channel(tema30_30m_data); + // let mut tema30_1d_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema30_1d_data, mut rx_tema30_1d_data) = watch::channel(tema30_1d_data); + // let mut tema30_1w_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema30_1w_data, mut rx_tema30_1w_data) = watch::channel(tema30_1w_data); + // let mut tema30_1mon_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let (tx_tema30_1mon_data, mut rx_tema30_1mon_data) = watch::channel(tema30_1mon_data); + + // RSI data + let mut rsi3_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(rsi, closetime)>)> + let (tx_rsi3_1m_data, mut rx_rsi3_1m_data) = watch::channel(rsi3_1m_data); + let mut rsi3_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi3_30m_data, mut rx_rsi3_30m_data) = watch::channel(rsi3_30m_data); + let mut rsi3_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi3_1d_data, mut rx_rsi3_1d_data) = watch::channel(rsi3_1d_data); + let mut rsi3_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi3_1w_data, mut rx_rsi3_1w_data) = watch::channel(rsi3_1w_data); + let mut rsi3_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi3_1mon_data, mut rx_rsi3_1mon_data) = watch::channel(rsi3_1mon_data); + + let mut rsi10_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(rsi, closetime)>)> + let (tx_rsi10_1m_data, mut rx_rsi10_1m_data) = watch::channel(rsi10_1m_data); + let mut rsi10_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi10_30m_data, mut rx_rsi10_30m_data) = watch::channel(rsi10_30m_data); + let mut rsi10_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi10_1d_data, mut rx_rsi10_1d_data) = watch::channel(rsi10_1d_data); + let mut rsi10_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi10_1w_data, mut rx_rsi10_1w_data) = watch::channel(rsi10_1w_data); + let mut rsi10_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi10_1mon_data, mut rx_rsi10_1mon_data) = watch::channel(rsi10_1mon_data); + + let mut rsi30_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(rsi, closetime)>)> + let (tx_rsi30_1m_data, mut rx_rsi30_1m_data) = watch::channel(rsi30_1m_data); + let mut rsi30_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi30_30m_data, mut rx_rsi30_30m_data) = watch::channel(rsi30_30m_data); + let mut rsi30_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi30_1d_data, mut rx_rsi30_1d_data) = watch::channel(rsi30_1d_data); + let mut rsi30_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi30_1w_data, mut rx_rsi30_1w_data) = watch::channel(rsi30_1w_data); + let mut rsi30_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_rsi30_1mon_data, mut rx_rsi30_1mon_data) = watch::channel(rsi30_1mon_data); + + // Stochastic RSI10 K and D + let mut stoch_rsi3_1m_3_k_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(stoch rsi K, closetime)>)> + let (tx_stoch_rsi3_1m_3_k_data, mut rx_stoch_rsi3_1m_3_k_data) = + watch::channel(stoch_rsi3_1m_3_k_data); + let mut rx2_stoch_rsi3_1m_3_k_data = rx_stoch_rsi3_1m_3_k_data.clone(); + let mut stoch_rsi3_1m_3_d_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(stoch rsi D, closetime)>)> + let (tx_stoch_rsi3_1m_3_d_data, mut rx_stoch_rsi3_1m_3_d_data) = + watch::channel(stoch_rsi3_1m_3_d_data); + let mut rx2_stoch_rsi3_1m_3_d_data = rx_stoch_rsi3_1m_3_d_data.clone(); + let mut stoch_rsi30_1m_30_k_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(stoch rsi K, closetime)>)> + let (tx_stoch_rsi30_1m_30_k_data, mut rx_stoch_rsi30_1m_30_k_data) = + watch::channel(stoch_rsi30_1m_30_k_data); + let mut rx2_stoch_rsi30_1m_30_k_data = rx_stoch_rsi30_1m_30_k_data.clone(); + let mut stoch_rsi30_1m_30_d_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec<(stoch rsi D, closetime)>)> + let (tx_stoch_rsi30_1m_30_d_data, mut rx_stoch_rsi30_1m_30_d_data) = + watch::channel(stoch_rsi30_1m_30_d_data); + let mut rx2_stoch_rsi30_1m_30_d_data = rx_stoch_rsi30_1m_30_d_data.clone(); + + // BollingerBand data + let mut bb3_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec)> + let (tx_bb3_1m_data, mut rx_bb3_1m_data) = watch::channel(bb3_1m_data); + let mut bb3_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb3_30m_data, mut rx_bb3_30m_data) = watch::channel(bb3_30m_data); + let mut bb3_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb3_1d_data, mut rx_bb3_1d_data) = watch::channel(bb3_1d_data); + let mut bb3_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb3_1w_data, mut rx_bb3_1w_data) = watch::channel(bb3_1w_data); + let mut bb3_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb3_1mon_data, mut rx_bb3_1mon_data) = watch::channel(bb3_1mon_data); + + let mut bb10_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec)> + let (tx_bb10_1m_data, mut rx_bb10_1m_data) = watch::channel(bb10_1m_data); + let mut bb10_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb10_30m_data, mut rx_bb10_30m_data) = watch::channel(bb10_30m_data); + let mut bb10_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb10_1d_data, mut rx_bb10_1d_data) = watch::channel(bb10_1d_data); + let mut bb10_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb10_1w_data, mut rx_bb10_1w_data) = watch::channel(bb10_1w_data); + let mut bb10_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb10_1mon_data, mut rx_bb10_1mon_data) = watch::channel(bb10_1mon_data); + + let mut bb30_1m_data: Vec<(String, Vec)> = Vec::new(); // Vec<(symbol, Vec)> + let (tx_bb30_1m_data, mut rx_bb30_1m_data) = watch::channel(bb30_1m_data); + let mut bb30_30m_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb30_30m_data, mut rx_bb30_30m_data) = watch::channel(bb30_30m_data); + let mut bb30_1d_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb30_1d_data, mut rx_bb30_1d_data) = watch::channel(bb30_1d_data); + let mut bb30_1w_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb30_1w_data, mut rx_bb30_1w_data) = watch::channel(bb30_1w_data); + let mut bb30_1mon_data: Vec<(String, Vec)> = Vec::new(); + let (tx_bb30_1mon_data, mut rx_bb30_1mon_data) = watch::channel(bb30_1mon_data); + + // Exchange Information data + let mut exchange_info_data: Vec = Vec::new(); + let (tx_exchange_info_data, mut rx_exchange_info_data) = watch::channel(exchange_info_data); + let mut rx2_exchange_info_data = rx_exchange_info_data.clone(); + let mut rx3_exchange_info_data = rx_exchange_info_data.clone(); + let mut rx4_exchange_info_data = rx_exchange_info_data.clone(); + + { + // initialize candle 1d, 1w and 1mon + print!("initialize candle 1d, 1w and 1mon.."); + std::io::stdout().flush(); + let instant = Instant::now(); + let interval_1d = String::from("1d"); + let interval_1w = String::from("1w"); + let interval_1mon = String::from("1mon"); + + let mut candle_1d_vec_temp: Vec<(String, Vec)> = Vec::new(); + let mut candle_1w_vec_temp: Vec<(String, Vec)> = Vec::new(); + let mut candle_1mon_vec_temp: Vec<(String, Vec)> = Vec::new(); + + request_candles::fetch_candle_parallel(&interval_1d, &mut candle_1d_vec_temp).await; + request_candles::fetch_candle_parallel(&interval_1w, &mut candle_1w_vec_temp).await; + request_candles::fetch_candle_parallel(&interval_1mon, &mut candle_1mon_vec_temp).await; + + tx_candle_1d_vec.send_modify(|vec| *vec = candle_1d_vec_temp); + tx_candle_1w_vec.send_modify(|vec| *vec = candle_1w_vec_temp); + tx_candle_1mon_vec.send_modify(|vec| *vec = candle_1mon_vec_temp); + + // sleep as much as the loop recurs per 60 seconds, expecting child threads will have finished within 60 seconds. + println!("Ok.."); + + // if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL { + // let elapsed_time = instant.elapsed().as_secs(); + // println!("wait for {} seconds till starting tradingbot..", 60 - elapsed_time); + // if 60 > elapsed_time { + // sleep(Duration::from_secs((60 - elapsed_time) as u64)).await; + // } + // } + } + + // Task#0: monitoring tasks + tokio::task::spawn(async move { + loop { + let result = rx_task0.recv().await; + + match result { + Some(1) => { + print!("\r1[■] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(2) => { + print!("\r1[ ] 2[■] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(3) => { + print!("\r1[ ] 2[ ] 3[■] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(4) => { + print!("\r1[ ] 2[ ] 3[ ] 4[■] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(5) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[■] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(6) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[■] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(7) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[■] 8[] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(8) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[■ 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(9) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[■] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(10) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[■] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(11) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[■] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(12) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[■] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(13) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[■] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(14) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[■] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(15) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[■] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(16) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[■] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(17) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[■] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(18) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[■] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(19) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[■] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(20) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[■] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(21) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[■] 22[ ] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(22) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[■] 23[ ] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(23) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[■] 24[ ] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(24) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[■] 25[ ] 26[ ]"); + io::stdout().flush(); + } + Some(25) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[■] 26[ ]"); + io::stdout().flush(); + } + Some(26) => { + print!("\r1[ ] 2[ ] 3[ ] 4[ ] 5[ ] 6[ ] 7[ ] 8[ ] 9[ ] 10[ ] 11[ ] 12[ ] 13[ ] 14[ ] 15[ ] 16[ ] 17[ ] 18[ ] 19[ ] 20[ ] 21[ ] 22[ ] 23[ ] 24[ ] 25[ ] 26[■]"); + io::stdout().flush(); + } + Some(_) => {} + None => {} + } + sleep(Duration::from_millis(50)).await; + } + }); + + // Task#1: check server time and health + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(Duration::from_millis(1000)) + .build() + .unwrap(); + let mut usertime = UserTime::new(); + let mut serverhealth = ServerHealth::new(); + let mut prev_server_epoch: u128 = 0; + let mut epoch_difference: f64 = 0.0; + let mut epoch_difference_vec: Vec = Vec::new(); + let mut epoch_mean: f64 = 0.0; + let mut elapsed_time: u128 = 0; + + loop { + let instant = Instant::now(); + + // let mut usertime_mutex = usertime_arc.lock().await; + // server_health_check_team::execute_server_health_check(&mut usertime_mutex).await; + // time_checking_team::execute_time_check(&mut usertime_mutex).await; + let result = server_health_check_team::execute_server_health_check( + &mut usertime, + &mut serverhealth, + &client, + ) + .await; + while let Err(E) = time_checking_team::execute_time_check(&mut usertime, &client).await + { + sleep(Duration::from_millis((100))).await; + while let Err(e) = server_health_check_team::execute_server_health_check( + &mut usertime, + &mut serverhealth, + &client, + ) + .await + {} + } + + match result { + Ok(T) => { + tx1.send(usertime.local_epoch) + .expect("tx1-rx1 channel has been closed."); + tx2.send(usertime.epoch_difference) + .expect("tx2-rx2 channel has been closed."); + tx_task1.send(1).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + // considering RTT and to make the wait time 1 second, latency each second for every 30 seconds is averaged and subtracted with wait time. + elapsed_time = instant.elapsed().as_millis(); + if 1_000 > elapsed_time { + if prev_server_epoch != 0 { + epoch_mean = 0.0; + + epoch_difference = + (usertime.server_epoch as f64) - (prev_server_epoch as f64) - 1000.0; // milli second + + if epoch_difference > 300.0 || epoch_difference < -300.0 { + epoch_difference_vec.clear(); + epoch_difference = 0.0; + } + + if epoch_difference_vec.len() == 10 { + epoch_difference_vec.rotate_left(1); + epoch_difference_vec.pop(); + epoch_difference_vec.push(epoch_difference); + } else if epoch_difference_vec.len() < 10 { + epoch_difference_vec.push(epoch_difference); + } + for element in &epoch_difference_vec { + epoch_mean += *element; + } + epoch_mean = epoch_mean / (epoch_difference_vec.len() as f64); + } + if epoch_mean > 0.0 { + if elapsed_time > ((epoch_mean) as u128) + && 1_000 > elapsed_time + ((epoch_mean) as u128) + { + sleep(Duration::from_millis( + (1_000 - elapsed_time - ((epoch_mean) as u128)) as u64, + )) + .await; + } else { + sleep(Duration::from_millis((1_000 - elapsed_time) as u64)).await; + } + } else { + sleep(Duration::from_millis( + (1_000 - elapsed_time + ((epoch_mean.abs()) as u128)) as u64, + )) + .await; + } + } + prev_server_epoch = usertime.server_epoch; + } + }); + + // Task#2: request trade fee + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let tx1_changed = rx1_1.changed().await; + let tx2_changed = rx2.changed().await; + let local_epoch = *rx1_1.borrow(); + let difference_epoch = *rx2.borrow(); + match tx1_changed { + Ok(T) => match tx2_changed { + Ok(T) => { + let mut tradefee_vec_temp: Vec = Vec::new(); + let result = request_others::request_trade_fee( + API_KEY, + SECRET_KEY, + local_epoch, + difference_epoch, + &client, + &mut tradefee_vec_temp, + ) + .await; + + match result { + Ok(T) => { + tx_tradefee_vec.send_modify(|vec| *vec = tradefee_vec_temp); + tx_task2.send(2).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + } + Err(E) => { + panic!("tx2-rx2 channel has been closed.") + } + }, + Err(E) => { + panic!("tx1-rx1 channel has been closed.") + } + } + + // sleep as much as the loop recurs per 60 seconds if all operation finished within 60 seconds. + elapsed_time = instant.elapsed().as_millis(); + if 60_000 > elapsed_time { + sleep(Duration::from_millis((60_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#3: request 24h price changes, pick valid USDT Trades, monitor total_24h_change_profit_index, usdt_24h_change_profit_index, total_price_down_dist_index + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let result = request_others::request_24hr_ticker_price_change_statistics(&client).await; + match result { + Ok(T) => { + let mut valid_usdt_trade_vec_temp: Vec = Vec::new(); + let result = + monitors::collect_valid_usde_trade(&mut valid_usdt_trade_vec_temp).await; + + match result { + Ok(T) => { + tx_valid_usdt_trade_vec + .send_modify(|vec| *vec = valid_usdt_trade_vec_temp); + tx_task3.send(3).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + } + Err(E) => { + println!(">>> Failed to monitor usdt_24h_change_profit_index."); + } + } + // match result { + // Ok(T) => { + // let result = monitors::total_24h_change_profit_index().await; + // match result { + // Ok(T) => {}, + // Err(E) => { + // println!(">>> Failed to monitor total_24h_change_profit_index."); + // } + // }; + + // let result = monitors::usdt_24h_change_profit_index().await; + // match result { + // Ok(T) => {}, + // Err(E) => { + // println!(">>> Failed to monitor usdt_24h_change_profit_index."); + // } + // }; + + // let result = monitors::total_price_down_dist_index().await; + // match result { + // Ok(T) => {}, + // Err(E) => { + // println!(">>> Failed to monitor total_price_down_dist_index."); + // } + // }; + // }, + // Err(E) => { + // println!(">>> Failed to fetch 24h price change data from endpoint or parse message."); + // } + // }; + + // sleep as much as the loop recurs per 30 seconds if all operation finished within 30 seconds. + elapsed_time = instant.elapsed().as_millis(); + if 30_000 > elapsed_time { + sleep(Duration::from_millis((30_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#4: price per second + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(1000)) + .build() + .unwrap(); + let mut server_epoch = 0; + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + // let tx1_changed = rx1_7.changed().await; + // server_epoch = *rx1_7.borrow(); + // match tx1_changed { + // Ok(T) => { + // let mut price_vec_temp: Vec = Vec::new(); + // let result = request_others::request_all_coin_price(&client, &mut price_vec_temp).await; + + // match result { + // Ok(T) => { + // tx_price_vec.send_modify(|vec| *vec = price_vec_temp); + // tx_task4.send(4).expect("The mpsc channel has been closed."); + // } + // Err(E) => {} + // } + + // // monitors::monitoring_all_coin_profit_change().await; + // } + // Err(e) => {} + // } + + let mut price_vec_temp: Vec = Vec::new(); + let result = request_others::request_all_coin_price(&client, &mut price_vec_temp).await; + + match result { + Ok(T) => { + tx_price_vec.send_modify(|vec| *vec = price_vec_temp); + tx_task4.send(4).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + // sleep as much as the loop recurs per 1 seconds if all operation finished within 1 seconds. + elapsed_time = instant.elapsed().as_millis(); + if 1_000 > elapsed_time { + sleep(Duration::from_millis((1_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#5: fetch candle 1m + tokio::task::spawn(async move { + let mut elapsed_time = 0; + let interval = String::from("1m"); + loop { + let instant = Instant::now(); + let mut candle_1m_vec_temp: Vec<(String, Vec)> = Vec::new(); + let result = + request_candles::fetch_candle_parallel(&interval, &mut candle_1m_vec_temp).await; + + match result { + Ok(T) => { + tx_candle_1m_vec.send_modify(|vec| *vec = candle_1m_vec_temp); + tx_task5.send(5).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 10 seconds, expecting child threads will have finished within 10 seconds. + elapsed_time = instant.elapsed().as_millis(); + if 10_000 > elapsed_time { + sleep(Duration::from_millis((10_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#6: fetch candle 30m + tokio::task::spawn(async move { + let mut elapsed_time = 0; + let interval = String::from("30m"); + loop { + let instant = Instant::now(); + let mut candle_30m_vec_temp: Vec<(String, Vec)> = Vec::new(); + let result = + request_candles::fetch_candle_parallel(&interval, &mut candle_30m_vec_temp).await; + + match result { + Ok(T) => { + tx_candle_30m_vec.send_modify(|vec| *vec = candle_30m_vec_temp); + tx_task6.send(6).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + // sleep as much as the loop recurs per 60 seconds, expecting child threads will have finished within 60 seconds. + elapsed_time = instant.elapsed().as_millis(); + if 60_000 > elapsed_time { + sleep(Duration::from_millis((60_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#7: fetch candle 1d + tokio::task::spawn(async move { + let mut elapsed_time = 0; + let interval = String::from("1d"); + sleep(Duration::from_secs(600)).await; + loop { + let instant = Instant::now(); + let mut candle_1d_vec_temp: Vec<(String, Vec)> = Vec::new(); + let result = + request_candles::fetch_candle_delay(&interval, &mut candle_1d_vec_temp).await; + + match result { + Ok(T) => { + tx_candle_1d_vec.send_modify(|vec| *vec = candle_1d_vec_temp); + tx_task7.send(7).expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + // sleep as much as the loop recurs per 1800 seconds, expecting child threads will have finished within 1800 seconds. + elapsed_time = instant.elapsed().as_secs(); + if 1_800 > elapsed_time { + sleep(Duration::from_secs((1_800 - elapsed_time) as u64)).await; + } + } + }); + + // // Task#8: fetch candle 1w + // tokio::task::spawn(async move{ + // let interval = String::from("1w"); + // sleep(Duration::from_secs(600)).await; + // loop{ + // let mut candle_1w_vec_temp: Vec<(String, Vec)> = Vec::new(); + // let result = request_candles::fetch_candle_delay(&interval, &mut candle_1w_vec_temp).await; + + // match result { + // Ok(T) => { + // tx_candle_1w_vec.send_modify(|vec| *vec = candle_1w_vec_temp); + // tx_task8.send(8).expect("The mpsc channel has been closed."); + // } + // Err(E) => {} + // } + // } + // }); + + // // Task#9: fetch candle 1mon + // tokio::task::spawn(async move{ + // let interval = String::from("1mon"); + // sleep(Duration::from_secs(600)).await; + // loop{ + // let mut candle_1mon_vec_temp: Vec<(String, Vec)> = Vec::new(); + // let result = request_candles::fetch_candle_delay(&interval, &mut candle_1mon_vec_temp).await; + + // match result { + // Ok(T) => { + // tx_candle_1mon_vec.send_modify(|vec| *vec = candle_1mon_vec_temp); + // tx_task9.send(9).expect("The mpsc channel has been closed."); + // } + // Err(E) => {} + // } + // } + // }); + + // Task#10: make realtime price data of 1m, 30m, 1d, 1w and 1mon + tokio::task::spawn(async move { + let mut elapsed_time = 0; + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL { + sleep(Duration::from_secs(30)).await; + } else { + sleep(Duration::from_secs(5)).await; + } + + loop { + let instant = Instant::now(); + let price_changed = rx_price_vec.changed().await; + if price_changed.is_ok() { + let price_vec = rx_price_vec.borrow().clone(); + let valid_usdt_trade_vec = rx_valid_usdt_trade_vec.borrow().clone(); + + if !price_vec.is_empty() { + // 1m + let interval = String::from("1m"); + let candle_1m_vec = rx_candle_1m_vec.borrow().clone(); + let rt_price_1m_vec_read_temp: Vec<(String, Vec)> = + Vec::new(); + let mut rt_price_1m_vec_write_temp: Vec<(String, Vec)> = + Vec::new(); + let result = + value_estimation_team::datapoints::price_data::update_realtime_price_data( + &interval, + &candle_1m_vec, + &rt_price_1m_vec_read_temp, + &mut rt_price_1m_vec_write_temp, + &price_vec, + &valid_usdt_trade_vec, + ) + .await; + + match result { + Ok(T) => { + tx_rt_price_1m_vec.send_modify(|vec| *vec = rt_price_1m_vec_write_temp); + tx_task10 + .send(10) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // 30m + let interval = String::from("30m"); + let candle_30m_vec = rx_candle_30m_vec.borrow().clone(); + let rt_price_1m_vec = rx_rt_price_1m_vec.borrow().clone(); + let mut rt_price_30m_vec_write_temp: Vec<(String, Vec)> = + Vec::new(); + + if !rt_price_1m_vec.is_empty() { + let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(&interval, &candle_30m_vec, &rt_price_1m_vec, &mut rt_price_30m_vec_write_temp, &price_vec, &valid_usdt_trade_vec).await; + + match result { + Ok(T) => { + tx_rt_price_30m_vec + .send_modify(|vec| *vec = rt_price_30m_vec_write_temp); + tx_task10 + .send(10) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + } + + // 1d + let interval = String::from("1d"); + let candle_1d_vec = rx_candle_1d_vec.borrow().clone(); + let rt_price_30m_vec = rx_rt_price_30m_vec.borrow().clone(); + let mut rt_price_1d_vec_write_temp: Vec<(String, Vec)> = + Vec::new(); + + if !rt_price_30m_vec.is_empty() { + let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(&interval, &candle_1d_vec, &rt_price_30m_vec, &mut rt_price_1d_vec_write_temp, &price_vec, &valid_usdt_trade_vec).await; + + match result { + Ok(T) => { + tx_rt_price_1d_vec + .send_modify(|vec| *vec = rt_price_1d_vec_write_temp); + tx_task10 + .send(10) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + } + + // { // 1w + // let interval = String::from("1w"); + // let candle_1w_vec = rx_candle_1w_vec.borrow().clone(); + // let rt_price_1d_vec = rx_rt_price_1d_vec.borrow().clone(); + // let mut rt_price_1w_vec_write_temp: Vec<(String, Vec)> = Vec::new(); + // let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(&interval, &candle_1w_vec, &rt_price_1d_vec, &mut rt_price_1w_vec_write_temp, &price_vec, &valid_usdt_trade_vec).await; + + // match result { + // Ok(T) => { + // tx_rt_price_1w_vec.send_modify(|vec| *vec = rt_price_1w_vec_write_temp); + // tx_task10.send(10).expect("The mpsc channel has been closed."); + // } + // Err(E) => {} + // } + // } + + // { // 1mon + // let interval = String::from("1mon"); + // let candle_1mon_vec = rx_candle_1mon_vec.borrow().clone(); + // let rt_price_1w_vec = rx_rt_price_1w_vec.borrow().clone(); + // let mut rt_price_1mon_vec_write_temp: Vec<(String, Vec)> = Vec::new(); + // let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(&interval, &candle_1mon_vec, &rt_price_1w_vec, &mut rt_price_1mon_vec_write_temp, &price_vec, &valid_usdt_trade_vec).await; + + // match result { + // Ok(T) => { + // tx_rt_price_1mon_vec.send_modify(|vec| *vec = rt_price_1mon_vec_write_temp); + // tx_task10.send(10).expect("The mpsc channel has been closed."); + // } + // Err(E) => {} + // } + // } + } + } + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 1_000 > elapsed_time { + sleep(Duration::from_millis((1_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#11 + // SMA, EMA, RSI, Stoch RSI, BollingerBand (3, 10, 30) for candle 1m, 30m, 1d, 1w, and 1mon + tokio::task::spawn(async move { + let moving_number_vec = vec![3, 10, 30]; + sleep(Duration::from_secs(10)).await; + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let valid_usdt_trade_vec = rx2_valid_usdt_trade_vec.borrow().clone(); + let rt_price_1m_vec = rx2_rt_price_1m_vec.borrow().clone(); + let rt_price_30m_vec = rx2_rt_price_30m_vec.borrow().clone(); + let rt_price_1d_vec = rx2_rt_price_1d_vec.borrow().clone(); + let rt_price_1w_vec = rx2_rt_price_1w_vec.borrow().clone(); + let rt_price_1mon_vec = rx2_rt_price_1mon_vec.borrow().clone(); + + for moving_number_element in &moving_number_vec { + // calculate SMA + let mut sma_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut sma_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut sma_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut sma_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut sma_1mon_data: Vec<(String, Vec)> = Vec::new(); + let sma_1m_result = value_estimation_team::indicators::sma::sma( + *moving_number_element, + &rt_price_1m_vec, + &mut sma_1m_data, + &valid_usdt_trade_vec, + ) + .await; + let sma_30m_result = value_estimation_team::indicators::sma::sma( + *moving_number_element, + &rt_price_30m_vec, + &mut sma_30m_data, + &valid_usdt_trade_vec, + ) + .await; + let sma_1d_result = value_estimation_team::indicators::sma::sma( + *moving_number_element, + &rt_price_1d_vec, + &mut sma_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // let sma_1w_result = value_estimation_team::indicators::sma::sma(*moving_number_element, &rt_price_1w_vec, &mut sma_1w_data, &valid_usdt_trade_vec).await; + // let sma_1mon_result = value_estimation_team::indicators::sma::sma(*moving_number_element, &rt_price_1mon_vec, &mut sma_1mon_data, &valid_usdt_trade_vec).await; + + // calculate EMA + let mut ema_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut ema_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut ema_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut ema_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut ema_1mon_data: Vec<(String, Vec)> = Vec::new(); + let ema_1m_result = value_estimation_team::indicators::ema::ema( + *moving_number_element, + &rt_price_1m_vec, + &mut ema_1m_data, + &valid_usdt_trade_vec, + ) + .await; + let ema_30m_result = value_estimation_team::indicators::ema::ema( + *moving_number_element, + &rt_price_30m_vec, + &mut ema_30m_data, + &valid_usdt_trade_vec, + ) + .await; + let ema_1d_result = value_estimation_team::indicators::ema::ema( + *moving_number_element, + &rt_price_1d_vec, + &mut ema_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // let ema_1w_result = value_estimation_team::indicators::ema::ema(*moving_number_element, &rt_price_1w_vec, &mut ema_1w_data, &valid_usdt_trade_vec).await; + // let ema_1mon_result = value_estimation_team::indicators::ema::ema(*moving_number_element, &rt_price_1mon_vec, &mut ema_1mon_data, &valid_usdt_trade_vec).await; + + // // calculate TEMA + // let mut tema_1m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let mut tema_30m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let mut tema_1d_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let mut tema_1w_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // let mut tema_1mon_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + // value_estimation_team::indicators::tema::tema(*moving_number_element, &ema_1m_data, &mut tema_1m_data, &valid_usdt_trade_vec).await; + // value_estimation_team::indicators::tema::tema(*moving_number_element, &ema_30m_data, &mut tema_30m_data, &valid_usdt_trade_vec).await; + // value_estimation_team::indicators::tema::tema(*moving_number_element, &ema_1d_data, &mut tema_1d_data, &valid_usdt_trade_vec).await; + // value_estimation_team::indicators::tema::tema(*moving_number_element, &ema_1w_data, &mut tema_1w_data, &valid_usdt_trade_vec).await; + // value_estimation_team::indicators::tema::tema(*moving_number_element, &ema_1mon_data, &mut tema_1mon_data, &valid_usdt_trade_vec).await; + + // calculate RSI + let mut rsi_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut rsi_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut rsi_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut rsi_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut rsi_1mon_data: Vec<(String, Vec)> = Vec::new(); + let rsi_1m_result = value_estimation_team::indicators::rsi::rsi( + *moving_number_element, + &rt_price_1m_vec, + &mut rsi_1m_data, + &valid_usdt_trade_vec, + ) + .await; + let rsi_30m_result = value_estimation_team::indicators::rsi::rsi( + *moving_number_element, + &rt_price_30m_vec, + &mut rsi_30m_data, + &valid_usdt_trade_vec, + ) + .await; + let rsi_1d_result = value_estimation_team::indicators::rsi::rsi( + *moving_number_element, + &rt_price_1d_vec, + &mut rsi_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // let rsi_1w_result = value_estimation_team::indicators::rsi::rsi(*moving_number_element, &rt_price_1w_vec, &mut rsi_1w_data, &valid_usdt_trade_vec).await; + // let rsi_1mon_result = value_estimation_team::indicators::rsi::rsi(*moving_number_element, &rt_price_1mon_vec, &mut rsi_1mon_data, &valid_usdt_trade_vec).await; + + // calculate Stoch RSI + let mut stoch_rsi_1m_k_data: Vec<(String, Vec)> = Vec::new(); + let mut stoch_rsi_1m_d_data: Vec<(String, Vec)> = Vec::new(); + let mut stoch_rsi_1m_result; + if rsi_1m_result.is_ok() { + if *moving_number_element == 10 { + stoch_rsi_1m_result = + value_estimation_team::indicators::stoch_rsi::stoch_rsi( + &rsi_1m_data, + 3, + &mut stoch_rsi_1m_k_data, + &mut stoch_rsi_1m_d_data, + &valid_usdt_trade_vec, + ) + .await; + if stoch_rsi_1m_result.is_ok() { + tx_stoch_rsi3_1m_3_k_data.send_modify(|vec| *vec = stoch_rsi_1m_k_data); + tx_stoch_rsi3_1m_3_d_data.send_modify(|vec| *vec = stoch_rsi_1m_d_data); + } + } else if *moving_number_element == 30 { + stoch_rsi_1m_result = + value_estimation_team::indicators::stoch_rsi::stoch_rsi( + &rsi_1m_data, + 30, + &mut stoch_rsi_1m_k_data, + &mut stoch_rsi_1m_d_data, + &valid_usdt_trade_vec, + ) + .await; + if stoch_rsi_1m_result.is_ok() { + tx_stoch_rsi30_1m_30_k_data + .send_modify(|vec| *vec = stoch_rsi_1m_k_data); + tx_stoch_rsi30_1m_30_d_data + .send_modify(|vec| *vec = stoch_rsi_1m_d_data); + } + } + } + + // calculate BollingerBand + let mut bb3_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb3_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb3_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb3_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb3_1mon_data: Vec<(String, Vec)> = Vec::new(); + let mut bb10_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb10_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb10_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb10_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb10_1mon_data: Vec<(String, Vec)> = Vec::new(); + let mut bb30_1m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb30_30m_data: Vec<(String, Vec)> = Vec::new(); + let mut bb30_1d_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb30_1w_data: Vec<(String, Vec)> = Vec::new(); + // let mut bb30_1mon_data: Vec<(String, Vec)> = Vec::new(); + let mut bb_1m_result; + let mut bb_30m_result; + let mut bb_1d_result; + // let mut bb_1w_result; + // let mut bb_1mon_result; + + match moving_number_element { + 3 => { + bb_1m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1m_data, + &rt_price_1m_vec, + &mut bb3_1m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_30m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_30m_data, + &rt_price_30m_vec, + &mut bb3_30m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_1d_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1d_data, + &rt_price_1d_vec, + &mut bb3_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // bb_1w_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1w_data, &rt_price_1w_vec, &mut bb3_1w_data, &valid_usdt_trade_vec).await; + // bb_1mon_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1mon_data, &rt_price_1mon_vec, &mut bb3_1mon_data, &valid_usdt_trade_vec).await; + } + 10 => { + bb_1m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1m_data, + &rt_price_1m_vec, + &mut bb10_1m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_30m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_30m_data, + &rt_price_30m_vec, + &mut bb10_30m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_1d_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1d_data, + &rt_price_1d_vec, + &mut bb10_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // bb_1w_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1w_data, &rt_price_1w_vec, &mut bb10_1w_data, &valid_usdt_trade_vec).await; + // bb_1mon_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1mon_data, &rt_price_1mon_vec, &mut bb10_1mon_data, &valid_usdt_trade_vec).await; + } + 30 => { + bb_1m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1m_data, + &rt_price_1m_vec, + &mut bb30_1m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_30m_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_30m_data, + &rt_price_30m_vec, + &mut bb30_30m_data, + &valid_usdt_trade_vec, + ) + .await; + bb_1d_result = + value_estimation_team::indicators::bollingerband::bollingerband( + *moving_number_element, + 3.0, + &sma_1d_data, + &rt_price_1d_vec, + &mut bb30_1d_data, + &valid_usdt_trade_vec, + ) + .await; + // bb_1w_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1w_data, &rt_price_1w_vec, &mut bb30_1w_data, &valid_usdt_trade_vec).await; + // bb_1mon_result = value_estimation_team::indicators::bollingerband::bollingerband(*moving_number_element, 2.0, &sma_1mon_data, &rt_price_1mon_vec, &mut bb30_1mon_data, &valid_usdt_trade_vec).await; + } + _ => { + bb_1m_result = Ok(()); + bb_30m_result = Ok(()); + bb_1d_result = Ok(()); + // bb_1w_result = Ok(()); + // bb_1mon_result = Ok(()); + } + } + + match moving_number_element { + 3 => { + if sma_1m_result.is_ok() { + tx_sma3_1m_data.send_modify(|vec| *vec = sma_1m_data); + } + if sma_30m_result.is_ok() { + tx_sma3_30m_data.send_modify(|vec| *vec = sma_30m_data); + } + if sma_1d_result.is_ok() { + tx_sma3_1d_data.send_modify(|vec| *vec = sma_1d_data); + } + // if sma_1w_result.is_ok() { tx_sma3_1w_data.send_modify(|vec| *vec = sma_1w_data); } + // if sma_1mon_result.is_ok() { tx_sma3_1mon_data.send_modify(|vec| *vec = sma_1mon_data); } + + if ema_1m_result.is_ok() { + tx_ema3_1m_data.send_modify(|vec| *vec = ema_1m_data); + } + if ema_30m_result.is_ok() { + tx_ema3_30m_data.send_modify(|vec| *vec = ema_30m_data); + } + if ema_1d_result.is_ok() { + tx_ema3_1d_data.send_modify(|vec| *vec = ema_1d_data); + } + // if ema_1w_result.is_ok() { tx_ema3_1w_data.send_modify(|vec| *vec = ema_1w_data); } + // if ema_1mon_result.is_ok() { tx_ema3_1mon_data.send_modify(|vec| *vec = ema_1mon_data); } + + // tx_tema3_1m_data.send_modify(|vec| *vec = tema_1m_data); + // tx_tema3_30m_data.send_modify(|vec| *vec = tema_30m_data); + // tx_tema3_1d_data.send_modify(|vec| *vec = tema_1d_data); + // tx_tema3_1w_data.send_modify(|vec| *vec = tema_1w_data); + // tx_tema3_1mon_data.send_modify(|vec| *vec = tema_1mon_data); + + if rsi_1m_result.is_ok() { + tx_rsi3_1m_data.send_modify(|vec| *vec = rsi_1m_data); + } + if rsi_30m_result.is_ok() { + tx_rsi3_30m_data.send_modify(|vec| *vec = rsi_30m_data); + } + if rsi_1d_result.is_ok() { + tx_rsi3_1d_data.send_modify(|vec| *vec = rsi_1d_data); + } + // if rsi_1w_result.is_ok() { tx_rsi3_1w_data.send_modify(|vec| *vec = rsi_1w_data); } + // if rsi_1mon_result.is_ok() { tx_rsi3_1mon_data.send_modify(|vec| *vec = rsi_1mon_data); } + + if bb_1m_result.is_ok() { + tx_bb3_1m_data.send_modify(|vec| *vec = bb3_1m_data); + } + if bb_30m_result.is_ok() { + tx_bb3_30m_data.send_modify(|vec| *vec = bb3_30m_data); + } + if bb_1d_result.is_ok() { + tx_bb3_1d_data.send_modify(|vec| *vec = bb3_1d_data); + } + // if bb_1w_result.is_ok() { tx_bb3_1w_data.send_modify(|vec| *vec = bb3_1w_data); } + // if bb_1mon_result.is_ok() { tx_bb3_1mon_data.send_modify(|vec| *vec = bb3_1mon_data); } + + tx_task11 + .send(11) + .expect("The mpsc channel has been closed."); + } + 10 => { + if sma_1m_result.is_ok() { + tx_sma10_1m_data.send_modify(|vec| *vec = sma_1m_data); + } + if sma_30m_result.is_ok() { + tx_sma10_30m_data.send_modify(|vec| *vec = sma_30m_data); + } + if sma_1d_result.is_ok() { + tx_sma10_1d_data.send_modify(|vec| *vec = sma_1d_data); + } + // if sma_1w_result.is_ok() { tx_sma10_1w_data.send_modify(|vec| *vec = sma_1w_data); } + // if sma_1mon_result.is_ok() { tx_sma10_1mon_data.send_modify(|vec| *vec = sma_1mon_data); } + + if ema_1m_result.is_ok() { + tx_ema10_1m_data.send_modify(|vec| *vec = ema_1m_data); + } + if ema_30m_result.is_ok() { + tx_ema10_30m_data.send_modify(|vec| *vec = ema_30m_data); + } + if ema_1d_result.is_ok() { + tx_ema10_1d_data.send_modify(|vec| *vec = ema_1d_data); + } + // if ema_1w_result.is_ok() { tx_ema10_1w_data.send_modify(|vec| *vec = ema_1w_data); } + // if ema_1mon_result.is_ok() { tx_ema10_1mon_data.send_modify(|vec| *vec = ema_1mon_data); } + + // tx_tema10_1m_data.send_modify(|vec| *vec = tema_1m_data); + // tx_tema10_30m_data.send_modify(|vec| *vec = tema_30m_data); + // tx_tema10_1d_data.send_modify(|vec| *vec = tema_1d_data); + // tx_tema10_1w_data.send_modify(|vec| *vec = tema_1w_data); + // tx_tema10_1mon_data.send_modify(|vec| *vec = tema_1mon_data); + + if rsi_1m_result.is_ok() { + tx_rsi10_1m_data.send_modify(|vec| *vec = rsi_1m_data); + } + if rsi_30m_result.is_ok() { + tx_rsi10_30m_data.send_modify(|vec| *vec = rsi_30m_data); + } + if rsi_1d_result.is_ok() { + tx_rsi10_1d_data.send_modify(|vec| *vec = rsi_1d_data); + } + // if rsi_1w_result.is_ok() { tx_rsi10_1w_data.send_modify(|vec| *vec = rsi_1w_data); } + // if rsi_1mon_result.is_ok() { tx_rsi10_1mon_data.send_modify(|vec| *vec = rsi_1mon_data); } + + if bb_1m_result.is_ok() { + tx_bb10_1m_data.send_modify(|vec| *vec = bb10_1m_data); + } + if bb_30m_result.is_ok() { + tx_bb10_30m_data.send_modify(|vec| *vec = bb10_30m_data); + } + if bb_1d_result.is_ok() { + tx_bb10_1d_data.send_modify(|vec| *vec = bb10_1d_data); + } + // if bb_1w_result.is_ok() { tx_bb10_1w_data.send_modify(|vec| *vec = bb10_1w_data); } + // if bb_1mon_result.is_ok() { tx_bb10_1mon_data.send_modify(|vec| *vec = bb10_1mon_data); } + + tx_task11 + .send(11) + .expect("The mpsc channel has been closed."); + } + 30 => { + if sma_1m_result.is_ok() { + tx_sma30_1m_data.send_modify(|vec| *vec = sma_1m_data); + } + if sma_30m_result.is_ok() { + tx_sma30_30m_data.send_modify(|vec| *vec = sma_30m_data); + } + if sma_1d_result.is_ok() { + tx_sma30_1d_data.send_modify(|vec| *vec = sma_1d_data); + } + // if sma_1w_result.is_ok() { tx_sma30_1w_data.send_modify(|vec| *vec = sma_1w_data); } + // if sma_1mon_result.is_ok() { tx_sma30_1mon_data.send_modify(|vec| *vec = sma_1mon_data); } + + if ema_1m_result.is_ok() { + tx_ema30_1m_data.send_modify(|vec| *vec = ema_1m_data); + } + if ema_30m_result.is_ok() { + tx_ema30_30m_data.send_modify(|vec| *vec = ema_30m_data); + } + if ema_1d_result.is_ok() { + tx_ema30_1d_data.send_modify(|vec| *vec = ema_1d_data); + } + // if ema_1w_result.is_ok() { tx_ema30_1w_data.send_modify(|vec| *vec = ema_1w_data); } + // if ema_1mon_result.is_ok() { tx_ema30_1mon_data.send_modify(|vec| *vec = ema_1mon_data); } + + // tx_tema30_1m_data.send_modify(|vec| *vec = tema_1m_data); + // tx_tema30_30m_data.send_modify(|vec| *vec = tema_30m_data); + // tx_tema30_1d_data.send_modify(|vec| *vec = tema_1d_data); + // tx_tema30_1w_data.send_modify(|vec| *vec = tema_1w_data); + // tx_tema30_1mon_data.send_modify(|vec| *vec = tema_1mon_data); + + if rsi_1m_result.is_ok() { + tx_rsi30_1m_data.send_modify(|vec| *vec = rsi_1m_data); + } + if rsi_30m_result.is_ok() { + tx_rsi30_30m_data.send_modify(|vec| *vec = rsi_30m_data); + } + if rsi_1d_result.is_ok() { + tx_rsi30_1d_data.send_modify(|vec| *vec = rsi_1d_data); + } + // if rsi_1w_result.is_ok() { tx_rsi30_1w_data.send_modify(|vec| *vec = rsi_1w_data); } + // if rsi_1mon_result.is_ok() { tx_rsi30_1mon_data.send_modify(|vec| *vec = rsi_1mon_data); } + + if bb_1m_result.is_ok() { + tx_bb30_1m_data.send_modify(|vec| *vec = bb30_1m_data); + } + if bb_30m_result.is_ok() { + tx_bb30_30m_data.send_modify(|vec| *vec = bb30_30m_data); + } + if bb_1d_result.is_ok() { + tx_bb30_1d_data.send_modify(|vec| *vec = bb30_1d_data); + } + // if bb_1w_result.is_ok() { tx_bb30_1w_data.send_modify(|vec| *vec = bb30_1w_data); } + // if bb_1mon_result.is_ok() { tx_bb30_1mon_data.send_modify(|vec| *vec = bb30_1mon_data); } + + tx_task11 + .send(11) + .expect("The mpsc channel has been closed."); + } + _ => {} + } + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 1_000 > elapsed_time { + sleep(Duration::from_millis((1_000 - elapsed_time) as u64)).await; + } + } + }); + + // Task#12: monitoring total market cap + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + loop { + let result = signal_association::coinmarketcap::market_cap_index().await; + match result { + Ok(T) => { + tx_task12 + .send(12) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + } + }); + } + + // Task#13: request lot stepsize and ticksize + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + loop { + let mut exchange_info_data_temp: Vec = Vec::new(); + let result = coin_health_check_team::request_others::request_exchange_infomation( + &client, + &mut exchange_info_data_temp, + ) + .await; + match result { + Ok(T) => { + tx_exchange_info_data.send_modify(|vec| *vec = exchange_info_data_temp); + tx_task13 + .send(13) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + sleep(Duration::from_secs(1200)).await; // sleep for 20mins + } + }); + + // Task#14: monitoring foreign exchange rate + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL { + tokio::task::spawn(async move { + loop { + let result = signal_association::exchange_rate::monitoring_fx_rate_index().await; + match result { + Ok(T) => { + tx_task14 + .send(14) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + sleep(Duration::from_secs(2700)).await; // sleep for 45mins + } + }); + } + + // // // Task#15: monitoring dollar index + // // tokio::task::spawn(async move { + // // loop + // // { + // // let result = signal_association::dollar_index::monitoring_dollar_index().await; + // // match result { + // // Ok(T) => { + // // tx_task15.send(15).expect("The mpsc channel has been closed."); + // // } + // // Err(E) => {} + // // } + // // sleep(Duration::from_secs(1800)).await; // sleep for 30mins + // // } + // // }); + + // Task#16: monitoring signal decision + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + loop { + let result = + signal_association::signal_decision::monitoring_signal_decision().await; + match result { + Ok(T) => { + tx_task16 + .send(16) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + sleep(Duration::from_millis(500)).await; // sleep for 0.5sec + } + }); + } + + // Task#17: monitoring future ratio + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + loop { + let result = signal_association::future_ratio::monitoring_future_ratio().await; + match result { + Ok(T) => { + tx_task17 + .send(17) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + sleep(Duration::from_secs(60)).await; // sleep for 1min + } + }); + } + + // COEX part + // Task#18: strategists + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL { + tokio::task::spawn(async move { + sleep(Duration::from_secs(40)).await; + let mut all_data = AllData::new(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + all_data.price_vec = rx2_price_vec.borrow().clone(); + all_data.valid_symbol_vec = rx3_valid_usdt_trade_vec.borrow().clone(); + + // realtime price data + all_data.rt_price_1m_vec = rx3_rt_price_1m_vec.borrow().clone(); + all_data.rt_price_30m_vec = rx3_rt_price_30m_vec.borrow().clone(); + all_data.rt_price_1d_vec = rx3_rt_price_1d_vec.borrow().clone(); + // all_data.rt_price_1w_vec = rx3_rt_price_1w_vec.borrow().clone(); + // all_data.rt_price_1mon_vec = rx3_rt_price_1mon_vec.borrow().clone(); + + // SMA data + all_data.sma3_1m_data = rx2_sma3_1m_data.borrow().clone(); + all_data.sma3_30m_data = rx2_sma3_30m_data.borrow().clone(); + all_data.sma3_1d_data = rx2_sma3_1d_data.borrow().clone(); + // all_data.sma3_1w_data = rx2_sma3_1w_data.borrow().clone(); + // all_data.sma3_1mon_data = rx2_sma3_1mon_data.borrow().clone(); + all_data.sma10_1m_data = rx2_sma10_1m_data.borrow().clone(); + all_data.sma10_30m_data = rx2_sma10_30m_data.borrow().clone(); + all_data.sma10_1d_data = rx2_sma10_1d_data.borrow().clone(); + // all_data.sma10_1w_data = rx2_sma10_1w_data.borrow().clone(); + // all_data.sma10_1mon_data = rx2_sma10_1mon_data.borrow().clone(); + all_data.sma30_1m_data = rx2_sma30_1m_data.borrow().clone(); + all_data.sma30_30m_data = rx2_sma30_30m_data.borrow().clone(); + all_data.sma30_1d_data = rx2_sma30_1d_data.borrow().clone(); + // all_data.sma30_1w_data = rx2_sma30_1w_data.borrow().clone(); + // all_data.sma30_1mon_data = rx2_sma30_1mon_data.borrow().clone(); + + // EMA data + all_data.ema3_1m_data = rx2_ema3_1m_data.borrow().clone(); + all_data.ema3_30m_data = rx2_ema3_30m_data.borrow().clone(); + all_data.ema3_1d_data = rx2_ema3_1d_data.borrow().clone(); + // all_data.ema3_1w_data = rx2_ema3_1w_data.borrow().clone(); + // all_data.ema3_1mon_data = rx2_ema3_1mon_data.borrow().clone(); + all_data.ema10_1m_data = rx2_ema10_1m_data.borrow().clone(); + all_data.ema10_30m_data = rx2_ema10_30m_data.borrow().clone(); + all_data.ema10_1d_data = rx2_ema10_1d_data.borrow().clone(); + // all_data.ema10_1w_data = rx2_ema10_1w_data.borrow().clone(); + // all_data.ema10_1mon_data = rx2_ema10_1mon_data.borrow().clone(); + all_data.ema30_1m_data = rx2_ema30_1m_data.borrow().clone(); + all_data.ema30_30m_data = rx2_ema30_30m_data.borrow().clone(); + all_data.ema30_1d_data = rx2_ema30_1d_data.borrow().clone(); + // all_data.ema30_1w_data = rx2_ema30_1w_data.borrow().clone(); + // all_data.ema30_1mon_data = rx2_ema30_1mon_data.borrow().clone(); + + // RSI data + all_data.rsi3_1m_data = rx_rsi3_1m_data.borrow().clone(); + all_data.rsi3_30m_data = rx_rsi3_30m_data.borrow().clone(); + all_data.rsi3_1d_data = rx_rsi3_1d_data.borrow().clone(); + // all_data.rsi3_1w_data = rx_rsi3_1w_data.borrow().clone(); + // all_data.rsi3_1mon_data = rx_rsi3_1mon_data.borrow().clone(); + all_data.rsi10_1m_data = rx_rsi10_1m_data.borrow().clone(); + all_data.rsi10_30m_data = rx_rsi10_30m_data.borrow().clone(); + all_data.rsi10_1d_data = rx_rsi10_1d_data.borrow().clone(); + // all_data.rsi10_1w_data = rx_rsi10_1w_data.borrow().clone(); + // all_data.rsi10_1mon_data = rx_rsi10_1mon_data.borrow().clone(); + all_data.rsi30_1m_data = rx_rsi30_1m_data.borrow().clone(); + all_data.rsi30_30m_data = rx_rsi30_30m_data.borrow().clone(); + all_data.rsi30_1d_data = rx_rsi30_1d_data.borrow().clone(); + // all_data.rsi30_1w_data = rx_rsi30_1w_data.borrow().clone(); + // all_data.rsi30_1mon_data = rx_rsi30_1mon_data.borrow().clone(); + + // Stoch RSI data + all_data.stoch_rsi3_1m_3_k_data = rx_stoch_rsi3_1m_3_k_data.borrow().clone(); + all_data.stoch_rsi3_1m_3_d_data = rx_stoch_rsi3_1m_3_d_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_k_data = rx_stoch_rsi30_1m_30_k_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_d_data = rx_stoch_rsi30_1m_30_d_data.borrow().clone(); + + // BollingerBand data + all_data.bb3_1m_data = rx_bb3_1m_data.borrow().clone(); + all_data.bb3_30m_data = rx_bb3_30m_data.borrow().clone(); + all_data.bb3_1d_data = rx_bb3_1d_data.borrow().clone(); + // all_data.bb3_1w_data = rx_bb3_1w_data.borrow().clone(); + // all_data.bb3_1mon_data = rx_bb3_1mon_data.borrow().clone(); + all_data.bb10_1m_data = rx_bb10_1m_data.borrow().clone(); + all_data.bb10_30m_data = rx_bb10_30m_data.borrow().clone(); + all_data.bb10_1d_data = rx_bb10_1d_data.borrow().clone(); + // all_data.bb10_1w_data = rx_bb10_1w_data.borrow().clone(); + // all_data.bb10_1mon_data = rx_bb10_1mon_data.borrow().clone(); + all_data.bb30_1m_data = rx_bb30_1m_data.borrow().clone(); + all_data.bb30_30m_data = rx_bb30_30m_data.borrow().clone(); + all_data.bb30_1d_data = rx_bb30_1d_data.borrow().clone(); + // all_data.bb30_1w_data = rx_bb30_1w_data.borrow().clone(); + // all_data.bb30_1mon_data = rx_bb30_1mon_data.borrow().clone(); + + let result = coex::strategy_team::execute_strategists(&all_data).await; + + match result { + Ok(T) => { + tx_task18 + .send(18) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 250 > elapsed_time { + sleep(Duration::from_millis((250 - elapsed_time) as u64)).await; + } + } + }); + } else { + tokio::task::spawn(async move { + sleep(Duration::from_secs(10)).await; + let mut elapsed_time = 0; + let mut all_data = AllData::new(); + loop { + let instant = Instant::now(); + + all_data.price_vec = rx2_price_vec.borrow().clone(); + all_data.valid_symbol_vec = rx3_valid_usdt_trade_vec.borrow().clone(); + // realtime price data + all_data.rt_price_1m_vec = rx3_rt_price_1m_vec.borrow().clone(); + all_data.rt_price_30m_vec = rx3_rt_price_30m_vec.borrow().clone(); + all_data.rt_price_1d_vec = rx3_rt_price_1d_vec.borrow().clone(); + all_data.rt_price_1w_vec = rx3_rt_price_1w_vec.borrow().clone(); + all_data.rt_price_1mon_vec = rx3_rt_price_1mon_vec.borrow().clone(); + // SMA data + all_data.sma3_1m_data = rx2_sma3_1m_data.borrow().clone(); + all_data.sma3_30m_data = rx2_sma3_30m_data.borrow().clone(); + all_data.sma3_1d_data = rx2_sma3_1d_data.borrow().clone(); + all_data.sma3_1w_data = rx2_sma3_1w_data.borrow().clone(); + all_data.sma3_1mon_data = rx2_sma3_1mon_data.borrow().clone(); + all_data.sma10_1m_data = rx2_sma10_1m_data.borrow().clone(); + all_data.sma10_30m_data = rx2_sma10_30m_data.borrow().clone(); + all_data.sma10_1d_data = rx2_sma10_1d_data.borrow().clone(); + all_data.sma10_1w_data = rx2_sma10_1w_data.borrow().clone(); + all_data.sma10_1mon_data = rx2_sma10_1mon_data.borrow().clone(); + all_data.sma30_1m_data = rx2_sma30_1m_data.borrow().clone(); + all_data.sma30_30m_data = rx2_sma30_30m_data.borrow().clone(); + all_data.sma30_1d_data = rx2_sma30_1d_data.borrow().clone(); + all_data.sma30_1w_data = rx2_sma30_1w_data.borrow().clone(); + all_data.sma30_1mon_data = rx2_sma30_1mon_data.borrow().clone(); + // EMA data + all_data.ema3_1m_data = rx2_ema3_1m_data.borrow().clone(); + all_data.ema3_30m_data = rx2_ema3_30m_data.borrow().clone(); + all_data.ema3_1d_data = rx2_ema3_1d_data.borrow().clone(); + all_data.ema3_1w_data = rx2_ema3_1w_data.borrow().clone(); + all_data.ema3_1mon_data = rx2_ema3_1mon_data.borrow().clone(); + all_data.ema10_1m_data = rx2_ema10_1m_data.borrow().clone(); + all_data.ema10_30m_data = rx2_ema10_30m_data.borrow().clone(); + all_data.ema10_1d_data = rx2_ema10_1d_data.borrow().clone(); + all_data.ema10_1w_data = rx2_ema10_1w_data.borrow().clone(); + all_data.ema10_1mon_data = rx2_ema10_1mon_data.borrow().clone(); + all_data.ema30_1m_data = rx2_ema30_1m_data.borrow().clone(); + all_data.ema30_30m_data = rx2_ema30_30m_data.borrow().clone(); + all_data.ema30_1d_data = rx2_ema30_1d_data.borrow().clone(); + all_data.ema30_1w_data = rx2_ema30_1w_data.borrow().clone(); + all_data.ema30_1mon_data = rx2_ema30_1mon_data.borrow().clone(); + // RSI data + all_data.rsi3_1m_data = rx_rsi3_1m_data.borrow().clone(); + all_data.rsi3_30m_data = rx_rsi3_30m_data.borrow().clone(); + all_data.rsi3_1d_data = rx_rsi3_1d_data.borrow().clone(); + all_data.rsi3_1w_data = rx_rsi3_1w_data.borrow().clone(); + all_data.rsi3_1mon_data = rx_rsi3_1mon_data.borrow().clone(); + all_data.rsi10_1m_data = rx_rsi10_1m_data.borrow().clone(); + all_data.rsi10_30m_data = rx_rsi10_30m_data.borrow().clone(); + all_data.rsi10_1d_data = rx_rsi10_1d_data.borrow().clone(); + all_data.rsi10_1w_data = rx_rsi10_1w_data.borrow().clone(); + all_data.rsi10_1mon_data = rx_rsi10_1mon_data.borrow().clone(); + all_data.rsi30_1m_data = rx_rsi30_1m_data.borrow().clone(); + all_data.rsi30_30m_data = rx_rsi30_30m_data.borrow().clone(); + all_data.rsi30_1d_data = rx_rsi30_1d_data.borrow().clone(); + all_data.rsi30_1w_data = rx_rsi30_1w_data.borrow().clone(); + all_data.rsi30_1mon_data = rx_rsi30_1mon_data.borrow().clone(); + // Stoch RSI data + all_data.stoch_rsi3_1m_3_k_data = rx_stoch_rsi3_1m_3_k_data.borrow().clone(); + all_data.stoch_rsi3_1m_3_d_data = rx_stoch_rsi3_1m_3_d_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_k_data = rx_stoch_rsi30_1m_30_k_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_d_data = rx_stoch_rsi30_1m_30_d_data.borrow().clone(); + // BollingerBand data + all_data.bb3_1m_data = rx_bb3_1m_data.borrow().clone(); + all_data.bb3_30m_data = rx_bb3_30m_data.borrow().clone(); + all_data.bb3_1d_data = rx_bb3_1d_data.borrow().clone(); + all_data.bb3_1w_data = rx_bb3_1w_data.borrow().clone(); + all_data.bb3_1mon_data = rx_bb3_1mon_data.borrow().clone(); + all_data.bb10_1m_data = rx_bb10_1m_data.borrow().clone(); + all_data.bb10_30m_data = rx_bb10_30m_data.borrow().clone(); + all_data.bb10_1d_data = rx_bb10_1d_data.borrow().clone(); + all_data.bb10_1w_data = rx_bb10_1w_data.borrow().clone(); + all_data.bb10_1mon_data = rx_bb10_1mon_data.borrow().clone(); + all_data.bb30_1m_data = rx_bb30_1m_data.borrow().clone(); + all_data.bb30_30m_data = rx_bb30_30m_data.borrow().clone(); + all_data.bb30_1d_data = rx_bb30_1d_data.borrow().clone(); + all_data.bb30_1w_data = rx_bb30_1w_data.borrow().clone(); + all_data.bb30_1mon_data = rx_bb30_1mon_data.borrow().clone(); + + let result = coex::strategy_team::execute_strategist_for_test(&all_data).await; + + match result { + Ok(T) => { + tx_task18 + .send(18) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 250 > elapsed_time { + sleep(Duration::from_millis((250 - elapsed_time) as u64)).await; + } + } + }); + } + + // Task#19: monitoring pre-suggested coins + tokio::task::spawn(async move { + sleep(Duration::from_secs(30)).await; + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let coin_price_vec = rx3_price_vec.borrow().clone(); + let result = coex::exchange_team::monitoring_pre_suggested_coins(&coin_price_vec).await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task19 + .send(19) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 100 > elapsed_time { + sleep(Duration::from_millis((100 - elapsed_time) as u64)).await; + } + } + }); + + // Task#20: buy_coin + if RUNNING_MODE == REAL || RUNNING_MODE == SIMUL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + sleep(Duration::from_secs(30)).await; + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let coin_price_vec = rx4_price_vec.borrow().clone(); + let exchange_info_vec = rx_exchange_info_data.borrow().clone(); + let trade_fee_vec = rx_tradefee_vec.borrow().clone(); + let result = coex::exchange_team::buy_coin( + &client, + &coin_price_vec, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task20 + .send(20) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 200ms second if all operation finished within 200ms + elapsed_time = instant.elapsed().as_millis(); + if 200 > elapsed_time { + sleep(Duration::from_millis((200 - elapsed_time) as u64)).await; + } + } + }); + } else { + tokio::task::spawn(async move { + sleep(Duration::from_secs(15)).await; + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(800)) + .build() + .unwrap(); + let mut elapsed_time = 0; + + let instant = Instant::now(); + let coin_price_vec = rx4_price_vec.borrow().clone(); + let exchange_info_vec = rx_exchange_info_data.borrow().clone(); + let trade_fee_vec = rx_tradefee_vec.borrow().clone(); + // let result = coex::exchange_team::buy_coin_for_test(&client, &coin_price_vec, &exchange_info_vec, &trade_fee_vec).await; + let result = coex::exchange_team::buy_coin( + &client, + &coin_price_vec, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task20 + .send(20) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 200ms second if all operation finished within 200ms + elapsed_time = instant.elapsed().as_millis(); + if 200 > elapsed_time { + sleep(Duration::from_millis((200 - elapsed_time) as u64)).await; + } + }); + } + + // Task#21: monitoring_open_buy_order + if RUNNING_MODE == REAL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + if RUNNING_MODE == REAL { + sleep(Duration::from_secs(30)).await; + } else if RUNNING_MODE == TEST { + sleep(Duration::from_secs(10)).await; + } + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let exchange_info_vec = rx3_exchange_info_data.borrow().clone(); + let trade_fee_vec = rx2_tradefee_vec.borrow().clone(); + let result = coex::order_team::monitoring_open_buy_order( + &client, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task21 + .send(21) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 200 > elapsed_time { + sleep(Duration::from_millis((200 - elapsed_time) as u64)).await; + } + } + }); + } + + // Task#22: monitoring_filled_buy_order + tokio::task::spawn(async move { + if RUNNING_MODE == TEST { + sleep(Duration::from_secs(10)).await; + } else { + sleep(Duration::from_secs(30)).await; + } + let mut elapsed_time = 0; + let mut all_data = AllData::new(); + loop { + let instant = Instant::now(); + let coin_price_vec = rx5_price_vec.borrow().clone(); + let exchange_info_vec = rx2_exchange_info_data.borrow().clone(); + let trade_fee_vec = rx3_tradefee_vec.borrow().clone(); + + // realtime price data + all_data.rt_price_30m_vec = rx4_rt_price_30m_vec.borrow().clone(); + + // SMA data + all_data.sma3_1m_data = rx3_sma3_1m_data.borrow().clone(); + all_data.sma10_1m_data = rx3_sma10_1m_data.borrow().clone(); + + // EMA data + all_data.ema3_1m_data = rx3_ema3_1m_data.borrow().clone(); + all_data.ema10_1m_data = rx3_ema10_1m_data.borrow().clone(); + + // Stoch RSI data + all_data.stoch_rsi3_1m_3_k_data = rx2_stoch_rsi3_1m_3_k_data.borrow().clone(); + all_data.stoch_rsi3_1m_3_d_data = rx2_stoch_rsi3_1m_3_d_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_k_data = rx2_stoch_rsi30_1m_30_k_data.borrow().clone(); + all_data.stoch_rsi30_1m_30_d_data = rx2_stoch_rsi30_1m_30_d_data.borrow().clone(); + + let result = coex::order_team::monitoring_filled_buy_order( + &all_data, + &coin_price_vec, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task22 + .send(22) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + + if 250 > elapsed_time { + sleep(Duration::from_millis((250 - elapsed_time) as u64)).await; + } + } + }); + + // Task#23: monitoring_open_sell_order + if RUNNING_MODE == REAL || RUNNING_MODE == TEST { + tokio::task::spawn(async move { + if RUNNING_MODE == REAL { + sleep(Duration::from_secs(30)).await; + } else if RUNNING_MODE == TEST { + sleep(Duration::from_secs(10)).await; + } + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let exchange_info_vec = rx4_exchange_info_data.borrow().clone(); + let trade_fee_vec = rx4_tradefee_vec.borrow().clone(); + let result = coex::order_team::monitoring_open_sell_order( + &client, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task23 + .send(23) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 250 > elapsed_time { + sleep(Duration::from_millis((250 - elapsed_time) as u64)).await; + } + } + }); + } + + // Task#24: monitoring_filled_sell_order + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let result = coex::order_team::monitoring_filled_sell_order(&client).await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task24 + .send(24) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 200 > elapsed_time { + sleep(Duration::from_millis((200 - elapsed_time) as u64)).await; + } + } + }); + + // Task#25: monitoring_scoreboard + tokio::task::spawn(async move { + let mut elapsed_time = 0; + let mut all_data = AllData::new(); + loop { + let instant = Instant::now(); + + // realtime price data + all_data.rt_price_1m_vec = rx4_rt_price_1m_vec.borrow().clone(); + + let result = coex::exchange_team::monitoring_scoreboard(&all_data).await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task25 + .send(25) + .expect("The mpsc channel has been closed."); + } + Err(E) => {} + } + + // sleep as much as the loop recurs per 1 second if all operation finished within 1 second. + elapsed_time = instant.elapsed().as_millis(); + if 200 > elapsed_time { + sleep(Duration::from_millis((200 - elapsed_time) as u64)).await; + } + } + }); + + // Task#26: update current_total_usdt and available_usdt + tokio::task::spawn(async move { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); + let mut previous_result = false; + let mut elapsed_time = 0; + loop { + let instant = Instant::now(); + let result = + coex::assets_managing_team::monitoring_asset_usdt(&mut previous_result, &client) + .await; + + // send Task#0 a message to notify running on + match result { + Ok(T) => { + tx_task26 + .send(26) + .expect("The mpsc channel has been closed."); + } + Err(E) => { + println!("{}", E); + } + } + + // sleep as much as the loop recurs per 300ms if all operation finished within 300ms. + elapsed_time = instant.elapsed().as_millis(); + if 250 > elapsed_time { + sleep(Duration::from_millis((250 - elapsed_time) as u64)).await; + } + } + }); + + loop { + // println!("test limit order 실행"); + // let client = ClientBuilder::new().timeout(Duration::from_millis(1000)).build().unwrap(); + // limit_order_buy_test(&client).await; + sleep(Duration::from_secs(300000000000)).await; + } + + Ok(()) +} + +// #![allow(unused)] +// #![allow(warnings)] + +// mod assets_managing_team; +// mod database_control; +// mod value_estimation_team; +// mod plotting_team; +// mod request_test; + +// use std::sync::{Arc}; +// use reqwest::{Client, ClientBuilder}; +// use tokio::{task::*, time::*, sync::watch, join, fs::*}; +// use sqlx::{ mysql::*, Executor, Connection, FromRow, Row }; + +// pub const DB_URL: &str = "mysql://root:Durtkarovh23!@localhost:3306/tradingbot"; + +// #[tokio::main] +// async fn main() -> Result<(), Box>{ +// let (tx1, mut rx1) = watch::channel(0); +// let mut rx2 = rx1.clone(); + +// let mut cnt = 0; + +// tokio::task::spawn(async move{ +// loop{ +// println!("Task #1: {}", cnt); +// tx1.send(cnt).expect("tx1-rx1 channel has been closed."); +// cnt += 1; +// sleep(Duration::from_secs(1)).await; + +// } +// }); + +// tokio::task::spawn(async move{ +// loop{ +// let tx1_changed = rx1.changed().await; + +// match tx1_changed { +// Ok(T) => { +// println!(" Task #2: {:?}", rx1.borrow()); +// } +// Err(E) => { +// println!(">>> Failed to fetch tradefee data from endpoint or parse message."); +// } +// } + +// } +// }); + +// tokio::task::spawn(async move{ +// loop{ +// let tx1_changed = rx2.changed().await; + +// match tx1_changed { +// Ok(T) => { +// println!(" Task #3: {:?}", rx2.borrow()); +// } +// Err(E) => { +// println!(">>> Failed to fetch tradefee data from endpoint or parse message."); +// } +// } +// println!("what"); + +// } +// }); +// // let client = ClientBuilder::new().connect_timeout(tokio::time::Duration::from_millis(1200)).build().unwrap(); + +// loop{ +// // request_test::request_candlestick_data(&client).await; + +// // plotting_team::plot_dp_mean_open_close_candle().await; +// // value_estimation_team::mean_price_for24h_candle_30m().await; +// // let job2 = +// // let job3 = +// // join!(job1); + +// // let job4 = tokio::task::spawn(assets_managing_team::request_wallet_info(api_key, secret_key, time.return_from_local_epoch(), time.return_difference_epoch())); + +// // tokio::join!(j1, j2, j3, j4, j5); +// println!("main ended!"); +// sleep(Duration::from_secs(6)).await; +// } + +// Ok(()) +// } + +// // use plotters::prelude::*; +// // fn main() -> Result<(), Box> { +// // let root = BitMapBackend::new("plotters-doc-data/0.png", (640, 480)).into_drawing_area(); +// // root.fill(&WHITE)?; +// // let mut chart = ChartBuilder::on(&root) +// // .caption("y=x^2", ("sans-serif", 50).into_font()) +// // .margin(5) +// // .x_label_area_size(30) +// // .y_label_area_size(30) +// // .build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)?; +// // +// // chart.configure_mesh().draw()?; +// // +// // chart +// // .draw_series(LineSeries::new( +// // (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)), +// // &RED, +// // ))? +// // .label("y = x^2") +// // .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED)); +// // +// // chart +// // .configure_series_labels() +// // .background_style(&WHITE.mix(0.8)) +// // .border_style(&BLACK) +// // .draw()?; +// // +// // Ok(()) +// // } diff --git a/src/server_health_check_team.rs b/src/server_health_check_team.rs new file mode 100644 index 0000000..2e62c8f --- /dev/null +++ b/src/server_health_check_team.rs @@ -0,0 +1,146 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use crate::time_checking_team::UserTime; +use rand::*; +use reqwest::{Client, ClientBuilder, Response}; +use tokio::join; +use tokio::time::*; + +#[derive(Debug)] +pub struct ServerHealth { + pub server_on: bool, // the total status of server. Its value is decided by AND operation of ping_on and wallet_system_on + // through is_server_on method. + pub ping_on: bool, // ping test to server. + pub wallet_system_on: bool, // check wallet system of server. + pub waiting_maximum: u32, // maximum time for timer to wait until server works on again. the value will be null when the server works on. +} +impl ServerHealth { + pub fn new() -> ServerHealth { + // make an instance and initialize fields + ServerHealth { + server_on: false, + ping_on: false, + wallet_system_on: false, + waiting_maximum: 0, + } + } +} + +// to check the current server health and store data into database. +pub async fn execute_server_health_check( + usertime: &mut UserTime, + serverhealth: &mut ServerHealth, + client: &Client, +) -> Result<(), Box> { + let mut rng = rand::rngs::OsRng; + let mut waiting_time: u32; + loop { + update(serverhealth, usertime, &client).await?; + + if serverhealth.server_on == true { + serverhealth.waiting_maximum = 0; + // update_database(&serverhealth).await; + break; + } else { + usertime.update_last_server().await; + // whenever server is off, waiting_maximum increases a second + // and thread sleeps for uniform distribution random number between 1sec and waiting_maximum. + serverhealth.waiting_maximum += 1; + waiting_time = rng.gen_range(1..=serverhealth.waiting_maximum); + + // update_database(&serverhealth).await; + println!(">>> retry connection after {} second(s).", waiting_time); + sleep(Duration::from_secs(waiting_time as u64)).await; + } + } + // println!("server health 완료"); + Ok(()) +} + +// check the server on or off +async fn update( + serverHealth: &mut ServerHealth, + usertime: &mut UserTime, + client: &Client, +) -> Result<(), Box> { + let (r1, r2) = join!(check_ping(usertime, &client), check_wallet_system(&client)); + serverHealth.ping_on = r1?; + serverHealth.wallet_system_on = r2?; + serverHealth.server_on = serverHealth.ping_on && serverHealth.wallet_system_on; + + Ok(()) +} + +// test ping to server and record the last running time of server (/api, Weight 1) +async fn check_ping( + usertime: &mut UserTime, + client: &Client, +) -> Result> { + let response = client + .get("https://api.binance.com/api/v3/ping") + .send() + .await; + + match response { + Ok(T) => { + let body = T.text_with_charset("utf-8").await?; + if body.contains("{}") { + Ok(true) + } else { + Ok(false) + } + } + Err(e) => Ok(false), + } +} + +// check wallet system status (/sapi, Weight 1) +async fn check_wallet_system( + client: &Client, +) -> Result> { + let response = client + .get("https://api.binance.com/sapi/v1/system/status") + .send() + .await; + + match response { + Ok(T) => { + let body = T.text_with_charset("utf-8").await?; + if body.contains("0") { + Ok(true) + } else { + Ok(false) + } + } + Err(E) => Ok(false), + } +} + +async fn update_database(serverhealth: &ServerHealth) { + let table_name = String::from("serverhealth"); + let mut record: Vec<(&str, &str)> = Vec::new(); + let condition = vec![("id", "1")]; + + let mut server_on = serverhealth.server_on.to_string(); + let mut ping_on = serverhealth.ping_on.to_string(); + let mut wallet_system_on = serverhealth.wallet_system_on.to_string(); + let mut waiting_maximum = serverhealth.waiting_maximum.to_string(); + + record.push(("server_on", server_on.as_str())); + record.push(("ping_on", ping_on.as_str())); + record.push(("wallet_system_on", wallet_system_on.as_str())); + record.push(("waiting_maximum", waiting_maximum.as_str())); + + let mut delete_query_result = update_record(&table_name, &record, &condition).await; + if delete_query_result.is_err() { + loop { + delete_query_result = update_record(&table_name, &record, &condition).await; + if delete_query_result.is_ok() { + break; + } + sleep(Duration::from_millis(10)).await; + } + } +} diff --git a/src/signal_association.rs b/src/signal_association.rs new file mode 100644 index 0000000..6b8f516 --- /dev/null +++ b/src/signal_association.rs @@ -0,0 +1,5 @@ +pub mod coinmarketcap; +pub mod dollar_index; +pub mod exchange_rate; +pub mod future_ratio; +pub mod signal_decision; diff --git a/src/signal_association/coinmarketcap.rs b/src/signal_association/coinmarketcap.rs new file mode 100644 index 0000000..f73f2fa --- /dev/null +++ b/src/signal_association/coinmarketcap.rs @@ -0,0 +1,609 @@ +use crate::database_control::*; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use thirtyfour::{prelude::*, OptionRect}; +use tokio::{join, time::*}; + +pub async fn initialize_association_record() { + let table_name = String::from("signal_association_opinion"); + let initial_columns = vec!["name", "is_working", "opinion", "weight"]; + let initial_values = vec![ + String::from("coinmarketcap"), + String::from("0"), + String::from("-"), + String::from("0.0"), + ]; + insert_one_record(&table_name, &initial_columns, &initial_values).await; +} + +pub async fn market_cap_index() -> Result<(), Box> { + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = vec![(String::from("name"), String::from("coinmarketcap"))]; + + let caps = DesiredCapabilities::chrome(); + let driver = WebDriver::new("http://localhost:9515/", &caps).await?; + + // get the current window handle + let handle = driver.current_window_handle().await?; + + // open browser and url + driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=60").await?; + driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=1440");"#).await?; + // driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=30m").await?; + // driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=12h");"#).await?; + + // get window handles and switch to the new tab + let handles = driver.window_handles().await?; + // switch the first tab + let elem = driver.find_element(By::Tag("pre")).await?; + let text = elem.text().await?; + + // JSON Parsing + let mut v: Value = serde_json::from_str(text.as_str()).unwrap(); + let mut into_vec = v + .as_object() + .unwrap() + .get("data") + .unwrap() + .as_object() + .unwrap(); + + // initialize variables + let mut market_caps: Vec = Vec::new(); + let mut last_updated_time = String::new(); + let mut normalized_values: Vec = Vec::new(); + let mut degree_values: Vec = Vec::new(); + let mut weighted_average_degree: f64 = 0.0; + + last_updated_time = into_vec.keys().last().unwrap().clone(); + for element in into_vec { + market_caps.push( + element + .1 + .as_array() + .unwrap() + .first() + .unwrap() + .as_f64() + .unwrap(), + ); + } + + let mut market_cap_min = market_caps + .iter() + .copied() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + let mut market_cap_max = market_caps + .iter() + .copied() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + + for element in &market_caps { + normalized_values.push((element - market_cap_min) / (market_cap_max - market_cap_min)); + } + for element in &normalized_values { + degree_values.push( + (normalized_values.last().unwrap() - element) + .atan() + .to_degrees(), + ); + } + degree_values.pop(); + + for element in degree_values.iter().copied().enumerate() { + weighted_average_degree += element.1 * (element.0 + 1) as f64; + } + // weighted_average_degree /= 45.0; + weighted_average_degree /= 1770.0; // summation from 1 to 59 + + // initialize variables for Kalman Filtering + let h = 1.0; + let a = 1.0; + let mut q = 0.001; + let r = 1.0; + let mut x = weighted_average_degree; + let mut p = 5000.0; + let mut xp = 0.0; + let mut pp = 0.0; + let mut k = 0.0; // Kalman gain + let mut z = 0.0; // measurement + + // variables for using database + #[derive(FromRow)] + struct SelectData { + market_cap_index: f64, + minimum: f64, + maximum: f64, + transition_point: f64, + liquidation_signal: i8, + negative_buy_signal: i8, + transition_buy_signal: i8, + }; + let table_name = String::from("market_cap_index"); + let update_condition = vec![(String::from("id"), String::from("1"))]; + let update_condition2 = vec![(String::from("id"), String::from("2"))]; + let select_columns = String::from("market_cap_index, minimum, maximum, transition_point, liquidation_signal, negative_buy_signal, transition_buy_signal"); + let select_data_structure = SelectData { + market_cap_index: 0.0, + minimum: 0.0, + maximum: 0.0, + transition_point: 0.0, + liquidation_signal: 0, + negative_buy_signal: 0, + transition_buy_signal: 0, + }; + let select_condition = None; + + // variables for EMA calculation of 5hr angle + let mut prev_angle: Option = None; + let mut current_angle: Option = None; + + driver + .set_window_rect(OptionRect::new().with_size(1, 1)) + .await?; + // refresh page and save JSON repeatedly + loop { + // refresh page in the first tab + driver.refresh().await?; + // switch to the second tab and refresh page + driver.switch_to().window(&handles[1]).await?; + driver.refresh().await?; + // switch to the first tab + driver.switch_to().window(&handle).await?; + driver.minimize_window().await?; + + // task in the first tab starts + // save JSON + let elem = driver.find_element(By::Tag("pre")).await; + let mut text; + match elem { + Ok(T) => { + text = T.text().await?; + v = serde_json::from_str(text.as_str()).unwrap(); + into_vec = v + .as_object() + .unwrap() + .get("data") + .unwrap() + .as_object() + .unwrap(); + + if last_updated_time.as_str() != into_vec.keys().last().unwrap().as_str() { + // when working + let mut association_update_values = + vec![(String::from("is_working"), String::from("1"))]; + let mut update_value_add: (String, String) = + (String::from("opinion"), String::from("KEEP")); + + last_updated_time = into_vec.keys().last().unwrap().clone(); + + market_caps.clear(); + normalized_values.clear(); + degree_values.clear(); + weighted_average_degree = 0.0; + + for element in into_vec { + market_caps.push( + element + .1 + .as_array() + .unwrap() + .first() + .unwrap() + .as_f64() + .unwrap(), + ); + } + + market_cap_min = market_caps + .iter() + .copied() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + market_cap_max = market_caps + .iter() + .copied() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + + for element in &market_caps { + normalized_values + .push((element - market_cap_min) / (market_cap_max - market_cap_min)); + } + + for element in &normalized_values { + degree_values.push( + (normalized_values.last().unwrap() - element) + .atan() + .to_degrees(), + ); + } + degree_values.pop(); + + for element in degree_values.iter().copied().enumerate() { + weighted_average_degree += element.1 * (element.0 + 1) as f64; + } + // weighted_average_degree /= 45.0; + weighted_average_degree /= 1770.0; // summation from 1 to 59 + + // using database + let mut update_values = vec![(String::from("market_cap_index"), x.to_string())]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + let select_result = select_record( + &table_name, + &select_columns, + &select_condition, + &select_data_structure, + ) + .await?; + let record = select_result.first().unwrap(); + let record2 = select_result.last().unwrap(); + + // if record2.market_cap_index > 0.0 { q=0.01; } // lower q than r means filtering is insensitive to turbulance(input noise) -> meaning more delayed filtering + // else { q=0.004; } + + // 1st order Kalman Filtering for weighted_average_degree + z = weighted_average_degree; + xp = x; + pp = p + q; + k = pp / (pp + r); + x = xp + k * (z - xp); + p = pp - k * pp; + + if record.market_cap_index > 0.0 { + if record.minimum != 0.0 { + update_values = vec![ + (String::from("minimum"), 0.0.to_string()), + (String::from("negative_buy_signal"), 0.to_string()), + (String::from("transition_point"), 0.0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + if record.maximum < record.market_cap_index + && record.liquidation_signal == 0 + { + if record.transition_point == 0.0 || record.minimum < 0.0 { + update_values = vec![( + String::from("maximum"), + record.market_cap_index.to_string(), + )]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + if record.market_cap_index >= 5.0 + && (record.market_cap_index - record.maximum) / record.maximum + > 0.2 + { + // 현재 5 이상 지수가 이전 대비 20% 증가하면 추가 매수하기. + update_values = vec![ + ( + String::from("maximum"), + record.market_cap_index.to_string(), + ), + (String::from("transition_buy_signal"), 1.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + update_values = vec![ + ( + String::from("maximum"), + record.market_cap_index.to_string(), + ), + (String::from("transition_buy_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } + } + + if record.maximum > record.market_cap_index + && record.liquidation_signal == 0 + { + update_values = vec![ + (String::from("liquidation_signal"), 1.to_string()), + ( + String::from("transition_point"), + record.market_cap_index.to_string(), + ), + (String::from("transition_buy_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + if record.liquidation_signal == 1 + && record.market_cap_index < record.transition_point + { + update_values = vec![( + String::from("transition_point"), + record.market_cap_index.to_string(), + )]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + if record.liquidation_signal == 1 + && record.market_cap_index > record.transition_point + { + update_values = vec![ + (String::from("maximum"), record.market_cap_index.to_string()), + (String::from("liquidation_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } else { + if record.maximum != 0.0 { + update_values = vec![ + (String::from("maximum"), 0.0.to_string()), + (String::from("transition_point"), 0.0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + + if record.market_cap_index < record.minimum + && record.negative_buy_signal == 0 + { + update_values = vec![ + (String::from("minimum"), record.market_cap_index.to_string()), + (String::from("liquidation_signal"), 1.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + if record.market_cap_index > record.minimum + && record.liquidation_signal == 1 + && record.negative_buy_signal == 0 + { + if record2.market_cap_index > 0.0 { + // considering the market is going up + update_values = vec![ + (String::from("negative_buy_signal"), 1.to_string()), + ( + String::from("transition_point"), + record.market_cap_index.to_string(), + ), + (String::from("liquidation_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + // considering the market is doing down + if record.market_cap_index < -20.0 { + update_values = vec![ + (String::from("negative_buy_signal"), 1.to_string()), + ( + String::from("transition_point"), + record.market_cap_index.to_string(), + ), + (String::from("liquidation_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + update_values = vec![ + ( + String::from("transition_point"), + record.market_cap_index.to_string(), + ), + (String::from("liquidation_signal"), 0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } + } + if record.negative_buy_signal == 1 + && record.market_cap_index > record.transition_point + { + if record.market_cap_index > -5.0 { + update_values = vec![ + (String::from("minimum"), record.market_cap_index.to_string()), + (String::from("negative_buy_signal"), 0.to_string()), + (String::from("transition_point"), 0.0.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } else { + update_values = vec![( + String::from("transition_point"), + record.market_cap_index.to_string(), + )]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } + if record.negative_buy_signal == 1 + && record.market_cap_index < record.transition_point + { + update_values = vec![ + (String::from("negative_buy_signal"), 0.to_string()), + (String::from("liquidation_signal"), 1.to_string()), + (String::from("minimum"), record.market_cap_index.to_string()), + ]; + update_record2(&table_name, &update_values, &update_condition) + .await + .unwrap(); + } + } // task in the first tab ends + + // task in the second tab starts + driver.switch_to().window(&handles[1]).await?; + // save JSON + let elem = driver.find_element(By::Tag("pre")).await?; + let text = elem.text().await?; + v = serde_json::from_str(text.as_str()).unwrap(); + into_vec = v + .as_object() + .unwrap() + .get("data") + .unwrap() + .as_object() + .unwrap(); + + market_caps.clear(); + normalized_values.clear(); + degree_values.clear(); + weighted_average_degree = 0.0; + + for element in into_vec { + market_caps.push( + element + .1 + .as_array() + .unwrap() + .first() + .unwrap() + .as_f64() + .unwrap(), + ); + } + + market_cap_min = market_caps + .iter() + .copied() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + market_cap_max = market_caps + .iter() + .copied() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + + for element in &market_caps { + normalized_values + .push((element - market_cap_min) / (market_cap_max - market_cap_min)); + } + + for element in &normalized_values { + degree_values.push( + (normalized_values.last().unwrap() - element) + .atan() + .to_degrees(), + ); + } + degree_values.pop(); + + for element in degree_values.iter().copied().enumerate() { + // weighted_average_degree += element.1 * (element.0 + 1) as f64; + weighted_average_degree += element.1; // normal average + } + // weighted_average_degree /= 45.0; + // weighted_average_degree /= 1036080.0; // summation from 1 to 1439 + weighted_average_degree /= degree_values.len() as f64; // normal average + + if prev_angle.is_some() && current_angle.is_some() { + current_angle = Some( + ((1.0 / 4.0) * weighted_average_degree) + + ((3.0 / 4.0) * prev_angle.unwrap()), + ); + + // if current_angle.unwrap() >= 3.0 { + // update_value_add = ((String::from("opinion"), String::from("UP"))); + // q=0.01; + // } else if current_angle.unwrap() <= -3.0 { + // update_value_add = ((String::from("opinion"), String::from("DOWN"))); + // q=0.004; + // } else { + // update_value_add = ((String::from("opinion"), String::from("KEEP"))); + // q=0.01; + // } + + if current_angle.unwrap() > 3.0 { + if current_angle.unwrap() - prev_angle.unwrap() <= -0.15 { + update_value_add = + ((String::from("opinion"), String::from("DOWN"))); + q = 0.004; + } else { + update_value_add = ((String::from("opinion"), String::from("UP"))); + q = 0.01; + } + } else if current_angle.unwrap() < -3.0 { + if current_angle.unwrap() - prev_angle.unwrap() >= 0.15 { + update_value_add = ((String::from("opinion"), String::from("UP"))); + q = 0.01; + } else { + update_value_add = + ((String::from("opinion"), String::from("DOWN"))); + q = 0.004; + } + } else { + update_value_add = ((String::from("opinion"), String::from("KEEP"))); + q = 0.01; + } + + prev_angle = current_angle; + } else { + current_angle = Some(weighted_average_degree); + prev_angle = Some(weighted_average_degree); + } + + // using database + let update_values = vec![( + String::from("market_cap_index"), + current_angle.unwrap().to_string(), + )]; + update_record2(&table_name, &update_values, &update_condition2) + .await + .unwrap(); + // return to the first tab + driver.switch_to().window(&handle).await?; + driver.minimize_window().await?; + + // update record in [signal_association_opinion] + association_update_values.push(update_value_add); + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + // println!("5hr_angle:{},5day_angle:{}", x, current_angle.unwrap()); + } + } + Err(E) => { + // when not working + let association_update_values = vec![ + (String::from("is_working"), String::from("0")), + (String::from("opinion"), String::from("-")), + (String::from("weight"), String::from("0.0")), + ]; + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + } + } + + // sleep for 60 seconds + sleep(Duration::from_nanos(60_000_000_000)).await; + + // println!("market_cap_index 완료"); + } + Ok(()) +} diff --git a/src/signal_association/dollar_index.rs b/src/signal_association/dollar_index.rs new file mode 100644 index 0000000..83f298f --- /dev/null +++ b/src/signal_association/dollar_index.rs @@ -0,0 +1,208 @@ +use crate::database_control::*; +use chrono::naive::NaiveDate; +use reqwest::{Client, ClientBuilder}; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use tokio::{join, time::Duration, time::*}; + +#[derive(Debug, FromRow, Clone)] +pub struct DollarIndex { + pub date: NaiveDate, + pub value: String, +} + +#[derive(Debug, FromRow)] +pub struct SignalAssociaionOpinion { + pub id: i64, + pub name: String, + pub is_working: bool, + pub opinion: String, + pub weight: f64, +} + +pub async fn initialize_association_record() { + let table_name = String::from("signal_association_opinion"); + let initial_columns = vec!["name", "is_working", "opinion", "weight"]; + let initial_values = vec![ + String::from("dollar_index"), + String::from("0"), + String::from("-"), + String::from("0.0"), + ]; + insert_one_record(&table_name, &initial_columns, &initial_values).await; +} + +pub async fn initialize_record() { + let mut dollar_index_vec: Vec = Vec::new(); + + loop { + let result = reqeust_dollar_index().await; + match result { + Ok(T) => { + dollar_index_vec = T; + break; + } + Err(E) => { + println!("{}", E); + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = + vec![(String::from("name"), String::from("dollar_index"))]; + let association_update_values: Vec<(String, String)> = vec![ + (String::from("is_working"), 0.to_string()), + (String::from("opinion"), String::from("-")), + ]; + + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + sleep(Duration::from_secs(5)).await; + } + } + } + + let insert_table_name = String::from("dollar_index"); + let insert_columns = vec!["date", "value"]; + let mut initial_values: Vec = Vec::new(); + let mut insert_value_container: Vec> = Vec::new(); + for element in dollar_index_vec { + let mut insert_value_build: Vec = Vec::new(); + + insert_value_build.push(element.date.to_string()); + insert_value_build.push(element.value); + insert_value_container.push(insert_value_build.clone()); + } + + insert_records(&insert_table_name, &insert_columns, &insert_value_container).await; +} + +pub async fn reqeust_dollar_index( +) -> Result, Box> { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_secs(30)) + .build()?; + let mut url = String::from("https://api.stlouisfed.org/fred/series/observations?series_id=DTWEXAFEGS&api_key=d4af0a1b811bd0324c4210ca59db99fb&file_type=json&limit=90&sort_order=desc"); + + let res = client.get(&url).send().await?; + + let body = res.text_with_charset("utf-8").await?; + + let v = serde_json::from_str::(body.as_str())?; + + let mut a = v.get("observations").unwrap().as_array().unwrap(); + + let mut vec: Vec = Vec::new(); + let mut dollar_index_info = DollarIndex { + date: chrono::naive::MAX_DATE, + value: String::new(), + }; + let parse_from_str = NaiveDate::parse_from_str; + + for element in a { + dollar_index_info.date = + parse_from_str(element.get("date").unwrap().as_str().unwrap(), "%Y-%m-%d").unwrap(); + dollar_index_info.value = element.get("value").unwrap().as_str().unwrap().to_string(); + vec.push(dollar_index_info.clone()); + } + + Ok(vec) +} + +// Nominal Advanced Foreign Economies U.S. Dollar Index +// dollar index from: https://fred.stlouisfed.org/series/DTWEXAFEGS +pub async fn monitoring_dollar_index() -> Result<(), Box> { + let table_name = String::from("dollar_index"); + delete_all_rows(&table_name) + .await + .expect("Failed to delete rows!"); + let association_update_table_name = String::from("signal_association_opinion"); + let mut avg = 0.0; + let mut cnt = 0; + let mut opinion = String::new(); + + loop { + initialize_record().await; + let mut dollar_index_vec = select_dollar_index().await; + + for element in &dollar_index_vec { + if element.value.as_str() != "." { + avg += element.value.parse::().unwrap(); + cnt += 1; + } + } + if cnt != 0 { + avg /= cnt as f64; + + for element in dollar_index_vec { + if element.value.as_str() != "." { + if avg * 1.005 <= element.value.parse::().unwrap() { + opinion = String::from("UP"); + } else if avg * 1.005 > element.value.parse::().unwrap() + && avg * 0.997 < element.value.parse::().unwrap() + { + opinion = String::from("KEEP"); + } else { + opinion = String::from("DOWN"); + } + break; + } + } + break; + } else { + println!("cnt is 0!"); + let association_update_condition = + vec![(String::from("name"), String::from("dollar_index"))]; + let association_update_values: Vec<(String, String)> = vec![ + (String::from("is_working"), 0.to_string()), + (String::from("opinion"), String::from("-")), + ]; + + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + sleep(Duration::from_secs(5)).await; + } + } + + // update signal_association_opinion + let association_update_condition = vec![(String::from("name"), String::from("dollar_index"))]; + let association_update_values: Vec<(String, String)> = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), opinion), + ]; + + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + + Ok(()) +} + +async fn select_dollar_index() -> Vec { + let select_table_name = String::from("dollar_index"); + let select_columns = String::from("*"); + let select_condition = None; + let select_struct = DollarIndex { + date: chrono::naive::MAX_DATE, + value: String::new(), + }; + + let dollar_index_vec = select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_struct, + ) + .await + .unwrap(); + + dollar_index_vec +} diff --git a/src/signal_association/exchange_rate.rs b/src/signal_association/exchange_rate.rs new file mode 100644 index 0000000..4beb0eb --- /dev/null +++ b/src/signal_association/exchange_rate.rs @@ -0,0 +1,607 @@ +use crate::database_control::*; +use chrono::*; +use reqwest::{Client, ClientBuilder}; +use serde_json::Value; +use sqlx::{Error, FromRow}; +use tokio::{join, time::Duration, time::*}; + +const APP_ID_ACTIVE: &str = "7674e75976bd4f878a67f19926704433"; +const APP_ID_TEMP: &str = "6bd6804c165d46dda5dd65500b17f3e8"; + +#[derive(Debug)] +struct ExchangeRateInfo { + currency: String, + rate: f64, +} + +#[derive(Debug, FromRow)] +struct ExchangeRate { + id: i64, + date: NaiveDate, + RUB: f64, + CAD: f64, + IQD: f64, + CNY: f64, + BRL: f64, + IRR: f64, + MXN: f64, + NOK: f64, +} + +pub async fn initialize_association_record() { + let table_name = String::from("signal_association_opinion"); + let initial_columns = vec!["name", "is_working", "opinion", "weight"]; + let initial_values = vec![ + String::from("exchange_rate"), + String::from("0"), + String::from("-"), + String::from("0.0"), + ]; + insert_one_record(&table_name, &initial_columns, &initial_values).await; +} + +pub async fn initialize_record() { + // make date str + let today = Utc::now().date(); + let mut prior_dates: Vec> = Vec::new(); + let mut prior_date = today.clone(); + + for _ in 0..30 { + prior_date = prior_date.pred(); + prior_dates.push(prior_date); + } + + let mut today_str = String::new(); + today_str = today.to_string(); + today_str.pop(); + today_str.pop(); + today_str.pop(); + + let mut prior_dates_str: Vec = Vec::new(); + let mut temp_str = String::new(); + for element in prior_dates { + temp_str = element.to_string(); + temp_str.pop(); + temp_str.pop(); + temp_str.pop(); + prior_dates_str.push(temp_str.clone()); + } + prior_dates_str.reverse(); + + let insert_table_name = String::from("foreign_exchange_rates"); + let insert_columns = vec![ + "date", "RUB", "CAD", "IQD", "CNY", "BRL", "IRR", "MXN", "NOK", + ]; + let mut initial_values: Vec = Vec::new(); + + for element in prior_dates_str { + let mut exchange_rates: Vec = Vec::new(); + + loop { + let result = reqeust_exchange_rate_historical(&element).await; + match result { + Ok(T) => { + exchange_rates = T; + break; + } + Err(E) => { + println!("{}", E); + sleep(Duration::from_secs(5)).await; + } + } + } + initial_values.clear(); + initial_values.push(element.clone()); + for element in exchange_rates { + initial_values.push(element.rate.to_string()); + } + insert_one_record(&insert_table_name, &insert_columns, &initial_values).await; + } +} + +pub async fn reqeust_exchange_rate_historical( + date: &String, +) -> Result, Box> { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_secs(30)) + .build()?; + let mut url = String::from("https://openexchangerates.org/api/historical/"); + url.push_str(&date); + url.push_str(".json?app_id="); + url.push_str(APP_ID_TEMP); + + let res = client.get(&url).send().await?; + + let body = res.text_with_charset("utf-8").await?; + + let v = serde_json::from_str::(body.as_str())?; + let mut exchange_rates: Vec = Vec::new(); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("RUB"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("CAD"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("IQD"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("CNY"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("BRL"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("IRR"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("MXN"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("NOK"), + rate: 0.0, + }); + + for element in &mut exchange_rates { + match element.currency.as_str() { + "RUB" => { + element.rate = v + .get("rates") + .unwrap() + .get("RUB") + .unwrap() + .as_f64() + .unwrap(); + } + "CAD" => { + element.rate = v + .get("rates") + .unwrap() + .get("CAD") + .unwrap() + .as_f64() + .unwrap(); + } + "IQD" => { + element.rate = v + .get("rates") + .unwrap() + .get("IQD") + .unwrap() + .as_f64() + .unwrap(); + } + "CNY" => { + element.rate = v + .get("rates") + .unwrap() + .get("CNY") + .unwrap() + .as_f64() + .unwrap(); + } + "BRL" => { + element.rate = v + .get("rates") + .unwrap() + .get("BRL") + .unwrap() + .as_f64() + .unwrap(); + } + "IRR" => { + element.rate = v + .get("rates") + .unwrap() + .get("IRR") + .unwrap() + .as_f64() + .unwrap(); + } + "MXN" => { + element.rate = v + .get("rates") + .unwrap() + .get("MXN") + .unwrap() + .as_f64() + .unwrap(); + } + "NOK" => { + element.rate = v + .get("rates") + .unwrap() + .get("NOK") + .unwrap() + .as_f64() + .unwrap(); + } + _ => {} + } + } + Ok(exchange_rates) +} + +pub async fn reqeust_exchange_rate_latest( +) -> Result, Box> { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_secs(30)) + .build()?; + let mut url = String::from("https://openexchangerates.org/api/latest.json?app_id="); + url.push_str(APP_ID_ACTIVE); + + let res = client.get(&url).send().await.unwrap(); + + let body = res.text_with_charset("utf-8").await?; + + let v = serde_json::from_str::(body.as_str())?; + let mut exchange_rates: Vec = Vec::new(); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("RUB"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("CAD"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("IQD"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("CNY"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("BRL"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("IRR"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("MXN"), + rate: 0.0, + }); + exchange_rates.push(ExchangeRateInfo { + currency: String::from("NOK"), + rate: 0.0, + }); + + for element in &mut exchange_rates { + match element.currency.as_str() { + "RUB" => { + element.rate = v + .get("rates") + .unwrap() + .get("RUB") + .unwrap() + .as_f64() + .unwrap(); + } + "CAD" => { + element.rate = v + .get("rates") + .unwrap() + .get("CAD") + .unwrap() + .as_f64() + .unwrap(); + } + "IQD" => { + element.rate = v + .get("rates") + .unwrap() + .get("IQD") + .unwrap() + .as_f64() + .unwrap(); + } + "CNY" => { + element.rate = v + .get("rates") + .unwrap() + .get("CNY") + .unwrap() + .as_f64() + .unwrap(); + } + "BRL" => { + element.rate = v + .get("rates") + .unwrap() + .get("BRL") + .unwrap() + .as_f64() + .unwrap(); + } + "IRR" => { + element.rate = v + .get("rates") + .unwrap() + .get("IRR") + .unwrap() + .as_f64() + .unwrap(); + } + "MXN" => { + element.rate = v + .get("rates") + .unwrap() + .get("MXN") + .unwrap() + .as_f64() + .unwrap(); + } + "NOK" => { + element.rate = v + .get("rates") + .unwrap() + .get("NOK") + .unwrap() + .as_f64() + .unwrap(); + } + _ => {} + } + } + Ok(exchange_rates) +} + +// Weight to average is from amount of crude oil for each country: https://yearbook.enerdata.co.kr/crude-oil/world-production-statistics.html +// based on 2020 data +// Weight: USD(1) , RUB(0.709141), CAD(0.353186), IQD(0.285319), CNY(0.278393), BRL(0.216066), IRR(0.184211), MXN(0.131579), NOK(0.131579) +// FX rate from: https://openexchangerates.org +pub async fn monitoring_fx_rate_index() -> Result<(), Box> { + let mut exchange_rate_vec = select_fx_rate().await; + let mut today = Utc::now().date().to_string(); + let mut yesterday = Utc::now().date().pred().to_string(); + today.pop(); + today.pop(); + today.pop(); + yesterday.pop(); + yesterday.pop(); + yesterday.pop(); + + exchange_rate_vec.sort_by_key(|a| a.id); + let mut weight_avg_vec: Vec = Vec::new(); + if exchange_rate_vec.last().unwrap().date.to_string() == yesterday { + let rub_weight = 0.709141; + let cad_weight = 0.353186; + let iqd_weight = 0.285319; + let cny_weight = 0.278393; + let brl_weight = 0.216066; + let irr_weight = 0.184211; + let mxn_weight = 0.131579; + let nok_weight = 0.131579; + + // calculate each average for 30 days + let mut rub_avg = 0.0; + let mut cad_avg = 0.0; + let mut iqd_avg = 0.0; + let mut cny_avg = 0.0; + let mut brl_avg = 0.0; + let mut irr_avg = 0.0; + let mut mxn_avg = 0.0; + let mut nok_avg = 0.0; + + for element in &exchange_rate_vec { + rub_avg += element.RUB; + cad_avg += element.CAD; + iqd_avg += element.IQD; + cny_avg += element.CNY; + brl_avg += element.BRL; + irr_avg += element.IRR; + mxn_avg += element.MXN; + nok_avg += element.NOK; + } + rub_avg /= exchange_rate_vec.len() as f64; + cad_avg /= exchange_rate_vec.len() as f64; + iqd_avg /= exchange_rate_vec.len() as f64; + cny_avg /= exchange_rate_vec.len() as f64; + brl_avg /= exchange_rate_vec.len() as f64; + irr_avg /= exchange_rate_vec.len() as f64; + mxn_avg /= exchange_rate_vec.len() as f64; + nok_avg /= exchange_rate_vec.len() as f64; + + // calculate weight average for each day + for element in &exchange_rate_vec { + let weight_avg = ((element.RUB / rub_avg * rub_weight) + + (element.CAD / cad_avg * cad_weight) + + (element.IQD / iqd_avg * iqd_weight) + + (element.CNY / cny_avg * cny_weight) + + (element.BRL / brl_avg * brl_weight) + + (element.IRR / irr_avg * irr_weight) + + (element.MXN / mxn_avg * mxn_weight) + + (element.NOK / nok_avg * nok_weight)) + / (rub_weight + + cad_weight + + iqd_weight + + cny_weight + + brl_weight + + irr_weight + + mxn_weight + + nok_weight); + weight_avg_vec.push(weight_avg); + } + + let mut current_exchange_rates: Vec = Vec::new(); + loop { + let result = reqeust_exchange_rate_latest().await; + match result { + Ok(T) => { + current_exchange_rates = T; + break; + } + Err(E) => { + println!("{}", E); + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = + vec![(String::from("name"), String::from("exchange_rate"))]; + let association_update_values: Vec<(String, String)> = vec![ + (String::from("is_working"), 0.to_string()), + (String::from("opinion"), String::from("-")), + ]; + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + sleep(Duration::from_secs(60)).await; + } + } + } + + let mut current_avg = 0.0; + + for element in ¤t_exchange_rates { + match element.currency.as_str() { + "RUB" => { + current_avg += element.rate / rub_avg * rub_weight; + } + "CAD" => { + current_avg += element.rate / cad_avg * cad_weight; + } + "IQD" => { + current_avg += element.rate / iqd_avg * iqd_weight; + } + "CNY" => { + current_avg += element.rate / cny_avg * cny_weight; + } + "BRL" => { + current_avg += element.rate / brl_avg * brl_weight; + } + "IRR" => { + current_avg += element.rate / irr_avg * irr_weight; + } + "MXN" => { + current_avg += element.rate / mxn_avg * mxn_weight; + } + "NOK" => { + current_avg += element.rate / nok_avg * nok_weight; + } + _ => {} + } + } + current_avg /= (rub_weight + + cad_weight + + iqd_weight + + cny_weight + + brl_weight + + irr_weight + + mxn_weight + + nok_weight); + + // update signal_association_opinion + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = + vec![(String::from("name"), String::from("exchange_rate"))]; + let mut association_update_values: Vec<(String, String)> = Vec::new(); + if current_avg > 1.003 { + // UP Trend + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("UP")), + ]; + } else if current_avg < 0.997 { + // DOWN Trend + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("DOWN")), + ]; + } else { + // KEEP Trend + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("KEEP")), + ]; + } + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + } else { + // delete the first record in [foreign_exchange_rates] and + let table_name = String::from("foreign_exchange_rates"); + let mut delete_condition = String::from("WHERE id = "); + delete_condition.push_str(exchange_rate_vec.first().unwrap().id.to_string().as_str()); + delete_record(&table_name, &delete_condition).await; + + // add yesterday's record + let mut exchange_rates: Vec = Vec::new(); + + loop { + let result = reqeust_exchange_rate_historical(&yesterday).await; + match result { + Ok(T) => { + exchange_rates = T; + break; + } + Err(E) => { + println!("{}", E); + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = + vec![(String::from("name"), String::from("exchange_rate"))]; + let association_update_values: Vec<(String, String)> = vec![ + (String::from("is_working"), 0.to_string()), + (String::from("opinion"), String::from("-")), + ]; + sleep(Duration::from_secs(60)).await; + } + } + } + let insert_columns = vec![ + "date", "RUB", "CAD", "IQD", "CNY", "BRL", "IRR", "MXN", "NOK", + ]; + + let mut initial_values: Vec = Vec::new(); + initial_values.push(yesterday.clone()); + for element in exchange_rates { + initial_values.push(element.rate.to_string()); + } + + insert_one_record(&table_name, &insert_columns, &initial_values).await; + } + + Ok(()) +} + +async fn select_fx_rate() -> Vec { + let select_table_name = String::from("foreign_exchange_rates"); + let select_columns = String::from("*"); + let select_condition = None; + let select_struct = ExchangeRate { + id: 0, + date: chrono::naive::MAX_DATE, + RUB: 0.0, + CAD: 0.0, + IQD: 0.0, + CNY: 0.0, + BRL: 0.0, + IRR: 0.0, + MXN: 0.0, + NOK: 0.0, + }; + + let exchange_rate_vec = select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_struct, + ) + .await + .unwrap(); + + exchange_rate_vec +} diff --git a/src/signal_association/future_ratio.rs b/src/signal_association/future_ratio.rs new file mode 100644 index 0000000..66988c0 --- /dev/null +++ b/src/signal_association/future_ratio.rs @@ -0,0 +1,208 @@ +use crate::database_control::*; +use reqwest::{Client, ClientBuilder, Response}; +use serde::Deserialize; +use serde_json::Value; + +pub async fn initialize_association_record() { + let table_name = String::from("signal_association_opinion"); + let initial_columns = vec!["name", "is_working", "opinion", "weight"]; + let initial_values = vec![ + String::from("future_ratio"), + String::from("0"), + String::from("-"), + String::from("0.0"), + ]; + insert_one_record(&table_name, &initial_columns, &initial_values).await; +} + +pub async fn monitoring_future_ratio() -> Result<(), Box> { + let client = ClientBuilder::new() + .timeout(tokio::time::Duration::from_secs(30)) + .build()?; + let url = "https://fapi.binance.com/futures/data/globalLongShortAccountRatio?&period=5m&symbol=BTCUSDT"; + let mut response = client.get(url).send().await; + + let mut ratio_vec: Vec = Vec::new(); + + match response { + Ok(T) => { + let mut body = T.text_with_charset("utf-8").await; + match body { + Ok(T) => { + ratio_vec = de_json_ratio(&T).await?; + } + Err(E) => { + println!("future_ratio body failed! : {:?}", E); + } + } + } + Err(E) => { + println!("future_ratio response failed!: {:?}", E); + } + } + + let url = "https://fapi.binance.com/futures/data/takerlongshortRatio?&symbol=BTCUSDT&period=5m"; + let mut response = client.get(url).send().await; + + let mut wrapper_vec: Vec> = Vec::new(); + + match response { + Ok(T) => { + let mut body = T.text_with_charset("utf-8").await; + match body { + Ok(T) => { + wrapper_vec = de_json_vol(&T).await?; + } + Err(E) => { + println!("request_all_coin_price body failed! : {:?}", E); + } + } + } + Err(E) => { + println!("request_all_coin_price response failed!: {:?}", E); + } + } + + let association_update_table_name = String::from("signal_association_opinion"); + let association_update_condition = vec![(String::from("name"), String::from("future_ratio"))]; + let mut association_update_values: Vec<(String, String)> = Vec::new(); + + if ratio_vec.len() != 0 && wrapper_vec.len() != 0 { + let mut avg_account_ratio = 0.0; + let mut avg_vol_ratio = 0.0; + let mut sum_sell_buy_vol = 0.0; + + ratio_vec.reverse(); + ratio_vec.truncate(6); + ratio_vec.reverse(); + + for element in &ratio_vec { + avg_account_ratio += element; + } + avg_account_ratio /= ratio_vec.len() as f64; + + let mut vol_ratio_vec = wrapper_vec[0].to_vec(); + let mut buy_vol_vec = wrapper_vec[1].to_vec(); + let mut sell_vol_vec = wrapper_vec[2].to_vec(); + + vol_ratio_vec.reverse(); + vol_ratio_vec.truncate(6); + vol_ratio_vec.reverse(); + buy_vol_vec.reverse(); + buy_vol_vec.truncate(6); + sell_vol_vec.reverse(); + sell_vol_vec.truncate(6); + + for element in &vol_ratio_vec { + avg_vol_ratio += element; + } + + avg_vol_ratio /= vol_ratio_vec.len() as f64; + + for element in buy_vol_vec { + sum_sell_buy_vol += element; + } + for element in sell_vol_vec { + sum_sell_buy_vol -= element; + } + + // println!("{} {} {}", avg_account_ratio, avg_vol_ratio, sum_sell_buy_vol); + if avg_account_ratio > 1.1 && sum_sell_buy_vol > 0.0 { + // expect UP Trend + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("UP")), + ]; + // println!("UP Trend"); + } else if avg_account_ratio < 1.0 { + // expect DOWN Trend + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("DOWN")), + ]; + // println!("Down Trend"); + } else { + association_update_values = vec![ + (String::from("is_working"), 1.to_string()), + (String::from("opinion"), String::from("KEEP")), + ]; + // println!("Keep Trend"); + } + } else { + association_update_values = vec![ + (String::from("is_working"), 0.to_string()), + (String::from("opinion"), String::from("-")), + ]; + } + update_record2( + &association_update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + + Ok(()) +} + +async fn de_json_ratio( + body: &String, +) -> Result, Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + + let mut object_map = &serde_json::map::Map::new(); + let mut ratio_vec: Vec = Vec::new(); + + for element in into_vec.unwrap() { + object_map = element.as_object().unwrap(); + let mut object_map_iter = object_map.iter(); + let result = element.get("longShortRatio"); + if result.is_some() { + ratio_vec.push(result.unwrap().as_str().unwrap().parse::().unwrap()); + } + } + Ok(ratio_vec) +} + +async fn de_json_vol( + body: &String, +) -> Result>, Box> { + let v: Value = serde_json::from_str(body.as_str())?; + let mut into_vec = v.as_array(); + if into_vec == None { + return Err("Err")?; + } + + let mut object_map = &serde_json::map::Map::new(); + let mut buy_vol_vec: Vec = Vec::new(); + let mut sell_vol_vec: Vec = Vec::new(); + let mut ratio_vec: Vec = Vec::new(); + let mut wrapper_vec: Vec> = Vec::new(); + + for element in into_vec.unwrap() { + object_map = element.as_object().unwrap(); + let mut object_map_iter = object_map.iter(); + let result = element.get("buySellRatio"); + if result.is_some() { + ratio_vec.push(result.unwrap().as_str().unwrap().parse::().unwrap()); + } + + let result = element.get("buyVol"); + if result.is_some() { + buy_vol_vec.push(result.unwrap().as_str().unwrap().parse::().unwrap()); + } + + let result = element.get("sellVol"); + if result.is_some() { + sell_vol_vec.push(result.unwrap().as_str().unwrap().parse::().unwrap()); + } + } + wrapper_vec.push(ratio_vec); + wrapper_vec.push(buy_vol_vec); + wrapper_vec.push(sell_vol_vec); + + Ok(wrapper_vec) +} diff --git a/src/signal_association/signal_decision.rs b/src/signal_association/signal_decision.rs new file mode 100644 index 0000000..652d2ba --- /dev/null +++ b/src/signal_association/signal_decision.rs @@ -0,0 +1,103 @@ +use crate::database_control::*; +use sqlx::{Error, FromRow}; + +#[derive(Debug, FromRow)] +pub struct SignalDecisionInfo { + pub decision: String, +} + +#[derive(Debug, FromRow)] +pub struct SignalAssociaionOpinion { + pub id: i64, + pub name: String, + pub is_working: bool, + pub opinion: String, + pub weight: f64, +} + +pub async fn monitoring_signal_decision() -> Result<(), Box> { + let signal_association_opinions = select_signal_association_opinion().await; + let mut up_cnt = 0; + let mut down_cnt = 0; + let mut keep_cnt = 0; + + for element in signal_association_opinions { + if element.is_working == true { + match element.opinion.as_str() { + "UP" => { + up_cnt += 1; + } + "DOWN" => { + down_cnt += 1; + } + "KEEP" => { + keep_cnt += 1; + } + _ => {} + } + } + } + + let update_table_name = String::from("signal_decision"); + let mut association_update_values: Vec<(String, String)> = Vec::new(); + let association_update_condition = vec![(String::from("id"), String::from("1"))]; + if up_cnt > down_cnt && up_cnt >= keep_cnt { + association_update_values = vec![(String::from("decision"), String::from("UP"))]; + } else if down_cnt > up_cnt && down_cnt >= keep_cnt { + association_update_values = vec![(String::from("decision"), String::from("DOWN"))]; + } else { + association_update_values = vec![(String::from("decision"), String::from("KEEP"))]; + } + update_record2( + &update_table_name, + &association_update_values, + &association_update_condition, + ) + .await; + + Ok(()) +} + +pub async fn select_signal_association_opinion() -> Vec { + let select_table_name = String::from("signal_association_opinion"); + let select_columns = String::from("*"); + let select_condition = None; + let select_struct = SignalAssociaionOpinion { + id: 0, + name: String::new(), + is_working: false, + opinion: String::new(), + weight: 0.0, + }; + + let select_result = select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_struct, + ) + .await + .unwrap(); + + select_result +} + +pub async fn select_signal_decision() -> Vec { + let select_table_name = String::from("signal_decision"); + let select_columns = String::from("*"); + let select_condition = None; + let select_struct = SignalDecisionInfo { + decision: String::new(), + }; + + let select_result = select_record( + &select_table_name, + &select_columns, + &select_condition, + &select_struct, + ) + .await + .unwrap(); + + select_result +} diff --git a/src/time_checking_team.rs b/src/time_checking_team.rs new file mode 100644 index 0000000..bf593a3 --- /dev/null +++ b/src/time_checking_team.rs @@ -0,0 +1,144 @@ +use crate::database_control::*; +use chrono::TimeZone; +use reqwest::{Client, ClientBuilder}; +use serde::Deserialize; +use std::time::{SystemTime, UNIX_EPOCH}; +use tokio::{join, time::*}; + +// structure for checking server time and local time +#[derive(Debug)] +pub struct UserTime { + pub server_epoch: u128, // server time in Unix Epoch Time + pub local_epoch: u128, // local time where the tradingbot is running in Unix Epoch Time. + pub epoch_difference: i64, // epoch difference from server time to local time in Unix Epoch Time. (can be used as RTT) + // positive value means server is leading to local, negative value means server is lagging to local. + server_ymdhs: String, // human-readable date form of server time in UTC (y: year, m: month, d: day, h: hour, s: second) + local_ymdhs: String, // human-readable date form of local time in UTC (y: year, m: month, d: day, h: hour, s: second) + last_server_epoch: u128, // the last server epoch until the server is turned off. + last_server_ymdhs: String, // the last server date until the server is turned off. +} + +impl UserTime { + // make an instance and initialize fields + pub fn new() -> UserTime { + UserTime { + server_epoch: 0, + local_epoch: 0, + epoch_difference: 0, + server_ymdhs: String::new(), + local_ymdhs: String::new(), + last_server_epoch: 0, + last_server_ymdhs: String::from("----"), + } + } + + // update all field in Time struct. + async fn update( + &mut self, + client: &Client, + ) -> Result<(), Box> { + let (servertime, localtime) = join!(Self::get_server_time(&client), Self::get_local_time()); + self.server_epoch = servertime?; + self.local_epoch = localtime?; + self.epoch_difference = + Self::get_difference_time(self.server_epoch, self.local_epoch).await; + self.server_ymdhs = Self::convert_epoch_to_ymdhs(self.server_epoch); + self.local_ymdhs = Self::convert_epoch_to_ymdhs(self.local_epoch); + Ok(()) + } + + // to record the last server-on time when request doesn't work. + pub async fn update_last_server(&mut self) { + self.last_server_epoch = self.server_epoch; + if self.server_epoch == 0 { + self.last_server_ymdhs = + String::from("server has been shutdown before the bot was executed."); + } else { + self.last_server_ymdhs = self.server_ymdhs.clone(); + } + } + + async fn get_server_time( + client: &Client, + ) -> Result> { + #[derive(Deserialize)] + struct ServerTime { + serverTime: u128, + } + let mut servertime = ServerTime { serverTime: 0 }; + + let response = client + .get("https://api.binance.com/api/v3/time") + .send() + .await?; + + servertime = response.json::().await?; + Ok(servertime.serverTime) + } + + // function to get the current local time + async fn get_local_time() -> Result { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(T) => Ok(T.as_millis()), + Err(E) => Err(E), + } + } + + // function to get the difference time from the local to the server + async fn get_difference_time(servertime: u128, localtime: u128) -> i64 { + if servertime >= localtime { + (servertime - localtime) as i64 + } else { + 0 - ((localtime - servertime) as i64) + } + } + + // function to convert Unix Epoch time of server to human-readable date in UTC + fn convert_epoch_to_ymdhs(epoch_time: u128) -> String { + let time_i64 = (epoch_time / 1000) as i64; + chrono::offset::Utc.timestamp(time_i64, 0).to_string() + } +} + +// run time checking and store data into database. +pub async fn execute_time_check( + usertime: &mut UserTime, + client: &Client, +) -> Result<(), Box> { + usertime.update(&client).await?; + + let table_name = String::from("time"); + let mut record: Vec<(&str, &str)> = Vec::new(); + let condition = vec![("id", "1")]; + + let server_epoch = usertime.server_epoch.to_string(); + let local_epoch = usertime.local_epoch.to_string(); + let epoch_difference = usertime.epoch_difference.to_string(); + let last_server_epoch = usertime.last_server_epoch.to_string(); + + record.push(("server_epoch", server_epoch.as_str())); + record.push(("local_epoch", local_epoch.as_str())); + record.push(("epoch_difference", epoch_difference.as_str())); + record.push(("server_ymdhs", usertime.server_ymdhs.as_str())); + record.push(("local_ymdhs", usertime.local_ymdhs.as_str())); + record.push(("last_server_epoch", last_server_epoch.as_str())); + record.push(("last_server_ymdhs", usertime.last_server_ymdhs.as_str())); + + update_record(&table_name, &record, &condition).await?; + // println!("time check 완료"); + Ok(()) +} + +impl Clone for UserTime { + fn clone(&self) -> Self { + UserTime { + server_epoch: self.server_epoch, + local_epoch: self.local_epoch, + epoch_difference: self.epoch_difference, + server_ymdhs: self.last_server_ymdhs.clone(), + local_ymdhs: self.local_ymdhs.clone(), + last_server_epoch: self.last_server_epoch, + last_server_ymdhs: self.last_server_ymdhs.clone(), + } + } +} diff --git a/src/value_estimation_team.rs b/src/value_estimation_team.rs new file mode 100644 index 0000000..2ef7663 --- /dev/null +++ b/src/value_estimation_team.rs @@ -0,0 +1,2 @@ +pub mod datapoints; +pub mod indicators; diff --git a/src/value_estimation_team/datapoints.rs b/src/value_estimation_team/datapoints.rs new file mode 100644 index 0000000..581004c --- /dev/null +++ b/src/value_estimation_team/datapoints.rs @@ -0,0 +1 @@ +pub mod price_data; diff --git a/src/value_estimation_team/datapoints/price_data.rs b/src/value_estimation_team/datapoints/price_data.rs new file mode 100644 index 0000000..7421a8d --- /dev/null +++ b/src/value_estimation_team/datapoints/price_data.rs @@ -0,0 +1,477 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::coin_health_check_team::request_candles::CandleData; +use crate::coin_health_check_team::request_others::CoinPriceData; +use crate::database_control::*; +use csv::{DeserializeRecordsIter, StringRecord}; +use rust_decimal::{prelude::ToPrimitive, Decimal}; +use serde::Deserialize; +use sqlx::FromRow; +use std::sync::Arc; +use tokio::{fs::*, sync::Mutex, time::*}; + +#[derive(Debug, Clone, PartialEq)] +pub struct RealtimePriceData { + pub opclo_price: f64, + pub open_price: f64, + pub close_price: f64, + pub high_price: f64, + pub low_price: f64, + pub close_time: i64, + pub quote_asset_volume: f64, + pub candle_type: String, +} + +impl RealtimePriceData { + fn new() -> RealtimePriceData { + let data = RealtimePriceData { + opclo_price: 0.0, + open_price: 0.0, + close_price: 0.0, + high_price: 0.0, + low_price: 0.0, + close_time: 0, + quote_asset_volume: 0.0, + candle_type: String::new(), + }; + data + } +} + +pub async fn update_realtime_price_data( + interval: &String, + read_candle_for_opclo: &Vec<(String, Vec)>, + read_candle_for_rt: &Vec<(String, Vec)>, + write_rt_data: &mut Vec<(String, Vec)>, + read_price: &Vec, + read_symbol: &Vec, +) -> Result<(), Box> { + let instant = Instant::now(); + + let mut rt_price_vec: Vec = Vec::new(); + let mut rt_data_vec: Vec<(String, Vec)> = Vec::new(); + + for element in read_symbol { + let candle_search_result = read_candle_for_opclo.iter().position(|x| x.0 == *element); + + match candle_search_result { + Some(T) => { + for element in &read_candle_for_opclo[T].1 { + let mut realtime_price_data_builder = RealtimePriceData::new(); + realtime_price_data_builder.opclo_price = + (element.open_price + element.close_price) / 2.0; + realtime_price_data_builder.open_price = element.open_price; + realtime_price_data_builder.close_price = element.close_price; + realtime_price_data_builder.high_price = element.high_price; + realtime_price_data_builder.low_price = element.low_price; + realtime_price_data_builder.close_time = element.close_time; + realtime_price_data_builder.quote_asset_volume = element.quote_asset_volume; + if element.open_price < element.close_price { + realtime_price_data_builder.candle_type = String::from("UP"); + } else { + realtime_price_data_builder.candle_type = String::from("DOWN"); + } + rt_price_vec.push(realtime_price_data_builder); + } + // reflect realtime data to the last element in rt_price_vec + if interval.contains("1m") { + let price_search_result = read_price.iter().position(|x| x.symbol == *element); + if price_search_result.is_some() { + // update close_price + rt_price_vec.last_mut().unwrap().close_price = + read_price[price_search_result.unwrap()].current_price; + // update opclo_price + rt_price_vec.last_mut().unwrap().opclo_price = + (rt_price_vec.last_mut().unwrap().open_price + + rt_price_vec.last_mut().unwrap().close_price) + / 2.0; + // update candle_type + if rt_price_vec.last_mut().unwrap().close_price + >= rt_price_vec.last_mut().unwrap().open_price + { + rt_price_vec.last_mut().unwrap().candle_type = String::from("UP"); + } else { + rt_price_vec.last_mut().unwrap().candle_type = String::from("DOWN"); + } + // update high_price + if rt_price_vec.last_mut().unwrap().high_price + < read_price[price_search_result.unwrap()].current_price + { + rt_price_vec.last_mut().unwrap().high_price = + read_price[price_search_result.unwrap()].current_price; + } + // update low_price + if rt_price_vec.last_mut().unwrap().low_price + > read_price[price_search_result.unwrap()].current_price + { + rt_price_vec.last_mut().unwrap().low_price = + read_price[price_search_result.unwrap()].current_price; + } + } + } else { + // for 30m, uses 1m candle + // for 1d, uses 30m candle + // for 1w, uses 1d candle + // for 1mon, uses 1w candle + + // search symbol + let candle_search_result = + read_candle_for_rt.iter().position(|x| x.0 == *element); + match candle_search_result { + Some(T) => { + if read_candle_for_rt[T].1.len() >= 2 { + let mut candle_vec_clone = read_candle_for_rt[T].1.clone(); + let mut rt_price_vec_clone = rt_price_vec.clone(); + rt_price_vec_clone.reverse(); + rt_price_vec_clone.truncate(2); + rt_price_vec_clone.reverse(); + + // update realtime information for the latest candle + let mut update_closeprice = 0.0; + let mut update_highprice = 0.0; + let mut update_lowprice = 0.0; + let mut update_quote_asset_volume = 0.0; + + // search close time + let prev_closetime_result = candle_vec_clone.binary_search_by_key( + &rt_price_vec_clone.first().unwrap().close_time, + |RealtimePriceData { + opclo_price, + open_price, + close_price, + high_price, + low_price, + close_time, + quote_asset_volume, + candle_type, + }| *close_time, + ); + if prev_closetime_result.is_ok() { + let result = + candle_vec_clone.get(prev_closetime_result.unwrap() + 1..); + if result.is_some() { + let update_highprice_result = + result.unwrap().iter().max_by(|x, y| { + x.high_price.partial_cmp(&y.high_price).unwrap() + }); + if update_highprice_result.is_some() { + update_highprice = + update_highprice_result.unwrap().high_price; + } + + let update_lowprice_result = + result.unwrap().iter().min_by(|x, y| { + x.low_price.partial_cmp(&y.low_price).unwrap() + }); + if update_lowprice_result.is_some() { + update_lowprice = + update_lowprice_result.unwrap().low_price; + } + + for element in result.unwrap() { + update_quote_asset_volume += element.quote_asset_volume; + } + } + } + + let price_search_result = + read_price.iter().position(|x| x.symbol == *element); + if price_search_result.is_some() { + update_closeprice = + read_price[price_search_result.unwrap()].current_price; + } + + // update the latest candle with values + if update_highprice != 0.0 + && !update_highprice.is_nan() + && update_highprice.is_finite() + { + rt_price_vec.last_mut().unwrap().high_price = update_highprice; + } + if update_lowprice != 0.0 + && !update_lowprice.is_nan() + && update_lowprice.is_finite() + { + rt_price_vec.last_mut().unwrap().low_price = update_lowprice; + } + if update_quote_asset_volume != 0.0 + && !update_quote_asset_volume.is_nan() + && update_quote_asset_volume.is_finite() + { + rt_price_vec.last_mut().unwrap().quote_asset_volume = + update_quote_asset_volume; + } + if update_closeprice != 0.0 + && !update_closeprice.is_nan() + && update_closeprice.is_finite() + { + rt_price_vec.last_mut().unwrap().close_price = + update_closeprice; + rt_price_vec.last_mut().unwrap().opclo_price = + (update_closeprice + + rt_price_vec.last_mut().unwrap().open_price) + / 2.0; + } + } + } + None => {} + } + } + + rt_data_vec.push((element.clone(), rt_price_vec.clone())); + rt_price_vec.clear(); + } + None => {} + } + } + + *write_rt_data = rt_data_vec; + + // println!(" datapoints/price_{} 완료 elapsed:{:.2}s", interval, instant.elapsed().as_secs_f32()); + Ok(()) +} + +// [price_data] +// pub async fn price_data(candle_period: &str) -> Result<(), Box> { +// let instant = Instant::now(); + +// // select the whole symbol +// let mut usdt_trades = UsdtTrades { symbol: String::new() }; +// let valid_usdt_table_name = String::from("valid_usdt_trades"); +// let valid_usdt_column_name = String::from("symbol"); +// let table_condition = None; +// let mut select_result = select_record(&valid_usdt_table_name, &valid_usdt_column_name, &table_condition, &usdt_trades).await?; + +// let mut candle_table_name = String::from("candle_"); +// let mut table_name_build = String::new(); +// let column_name = String::from("openprice, highprice, lowprice, closeprice, closetime, quoteassetvolume"); +// let table_condition = None; +// let mut candle_data_struct = CandleData { openprice: 0.0, highprice: 0.0, lowprice: 0.0, closeprice: 0.0, closetime: 0, quoteassetvolume: 0.0 }; + +// let mut output_path = String::from("datapoints/price/price_"); +// output_path.push_str(candle_period); +// output_path.push('_'); +// let mut output_path_build = String::new(); +// let mut content_build = String::new(); + +// match candle_period { +// "1m" => { +// for usdttrade in select_result { +// content_build.clear(); +// table_name_build.clear(); +// table_name_build.push_str(candle_table_name.as_str()); +// table_name_build.push_str(usdttrade.symbol.to_lowercase().as_str()); +// table_name_build.push('_'); +// table_name_build.push_str(candle_period); + +// output_path_build.clear(); +// output_path_build.push_str(output_path.as_str()); +// output_path_build.push_str(usdttrade.symbol.as_str()); +// output_path_build.push_str(".csv"); + +// let mut query_result = select_record(&table_name_build, &column_name, &table_condition, &candle_data_struct).await?; + +// let mut file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await; +// while let Err(e) = file { +// file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await; +// sleep(Duration::from_millis(1000)); +// } + +// if file.is_ok() { +// for element2 in query_result { +// let meanprice = (element2.openprice + element2.closeprice)/2.0; +// content_build.push_str(meanprice.to_string().as_str()); // opclo price +// content_build.push(','); +// content_build.push_str(element2.openprice.to_string().as_str()); // openprice +// content_build.push(','); +// content_build.push_str(element2.closeprice.to_string().as_str()); // closeprice +// content_build.push(','); +// content_build.push_str(element2.highprice.to_string().as_str()); // highprice +// content_build.push(','); +// content_build.push_str(element2.lowprice.to_string().as_str()); // lowprice +// content_build.push(','); +// content_build.push_str(element2.closetime.to_string().as_str()); // closetime +// content_build.push(','); +// content_build.push_str(element2.quoteassetvolume.to_string().as_str()); // quote asset volume (USDT) +// content_build.push(','); +// if element2.closeprice >= element2.openprice { +// content_build.push_str("UP"); // UP candle +// } else { +// content_build.push_str("DOWN"); // DOWN candle +// } +// content_build.push('\n'); +// } +// content_build.pop(); +// file?.write_all(content_build.as_bytes()).await; +// } else if file.is_err() { +// println!(">>> File error occurred {:?} (datapoints/price_{})", file.unwrap_err(), candle_period); +// } +// } +// }, +// _ => { +// // read price +// let mut read_fixed_path = String::from("datapoints/price/price_"); + +// match candle_period { +// "30m" => {read_fixed_path.push_str("1m");}, +// "1d" => {read_fixed_path.push_str("30m");}, +// "1w" => {read_fixed_path.push_str("1d");}, +// "1mon" => {read_fixed_path.push_str("1d");}, +// _ => { println!(">>> Wrong candle_period. Check the parameter available (datapoints/price_{})", candle_period); }, +// } + +// read_fixed_path.push('_'); +// let mut read_path_build = String::new(); +// let mut read_data_vec: Vec = Vec::new(); + +// // select current coin prices +// let coinprice_table_name = String::from("coinprices"); +// let coinprice_column_name = String::from("symbol, price"); +// let mut condition = None; +// let mut select_data_structure = CoinPriceData { symbol: String::new(), price: Decimal::new(0,8) }; +// let mut coinprice_select_result = select_record(&coinprice_table_name, &coinprice_column_name, &condition, &select_data_structure).await.unwrap(); + +// for usdttrade in select_result { +// read_path_build.clear(); +// read_path_build.push_str(read_fixed_path.as_str()); +// read_path_build.push_str(usdttrade.symbol.as_str()); +// read_path_build.push_str(".csv"); +// let mut price_record = StringRecord::new(); +// let mut rdr = csv::ReaderBuilder::new() +// .has_headers(false) +// .from_path(&read_path_build)?; + +// while let false = rdr.read_record(&mut price_record)? { +// rdr = csv::ReaderBuilder::new() +// .has_headers(false) +// .from_path(&read_path_build)?; +// sleep(Duration::from_millis(1000)); +// } +// read_data_vec.clear(); + +// if price_record[5].parse::()? != 0 { // 유효한 데이터가 파일에 들어있는 경우 +// read_data_vec.push(PriceData { +// opclo_price: price_record[0].parse::()?, +// open_price: price_record[1].parse::()?, +// close_price: price_record[2].parse::()?, +// high_price: price_record[3].parse::()?, +// low_price: price_record[4].parse::()?, +// close_time: price_record[5].parse::()?, +// quote_asset_volume: price_record[6].parse::()?, +// candle_type: price_record[7].parse::()?, +// }); +// for element in rdr.deserialize() { +// read_data_vec.push(element?); +// } + +// content_build.clear(); +// table_name_build.clear(); +// table_name_build.push_str(candle_table_name.as_str()); +// table_name_build.push_str(usdttrade.symbol.to_lowercase().as_str()); +// table_name_build.push('_'); +// table_name_build.push_str(candle_period); + +// output_path_build.clear(); +// output_path_build.push_str(output_path.as_str()); +// output_path_build.push_str(usdttrade.symbol.as_str()); +// output_path_build.push_str(".csv"); +// let mut query_result = select_record(&table_name_build, &column_name, &table_condition, &candle_data_struct).await?; + +// if query_result.len() >= 2 { +// // update realtime information for the latest candle +// let mut update_closeprice = 0.0; +// let mut update_highprice = 0.0; +// let mut update_lowprice = 0.0; +// let mut update_quote_asset_volume = 0.0; + +// let mut query_result_copy = query_result.clone(); +// query_result_copy.reverse(); +// query_result_copy.truncate(2); +// query_result_copy.reverse(); + +// let prev_closetime_result = read_data_vec.binary_search_by_key(&query_result_copy.first().unwrap().closetime, |PriceData {opclo_price, open_price, close_price, high_price, low_price, close_time, quote_asset_volume, candle_type}|*close_time as i64); +// if prev_closetime_result.is_ok() { +// let result = read_data_vec.get(prev_closetime_result.unwrap()+1..); +// if result.is_some() { +// let update_highprice_result = result.unwrap().iter().max_by(|x, y| x.high_price.partial_cmp(&y.high_price).unwrap()); +// if update_highprice_result.is_some() { +// update_highprice = update_highprice_result.unwrap().high_price; +// } + +// let update_lowprice_result = result.unwrap().iter().min_by(|x, y| x.low_price.partial_cmp(&y.low_price).unwrap()); +// if update_lowprice_result.is_some() { +// update_lowprice = update_lowprice_result.unwrap().low_price; +// } + +// for element in result.unwrap() { +// update_quote_asset_volume += element.quote_asset_volume; +// } +// } + +// } +// let coinprice_result = coinprice_select_result.iter().find(|x| x.symbol==usdttrade.symbol); +// if coinprice_result.is_some() { +// update_closeprice = coinprice_result.unwrap().price.to_f64().unwrap(); +// } + +// // update the latest candle with values +// if let Some(last) = query_result.last_mut() { +// if update_highprice != 0.0 && !update_highprice.is_nan() && update_highprice.is_finite() { +// last.highprice = update_highprice; +// } +// if update_lowprice != 0.0 && !update_lowprice.is_nan() && update_lowprice.is_finite() { +// last.lowprice = update_lowprice; +// } +// if update_quote_asset_volume != 0.0 && !update_quote_asset_volume.is_nan() && update_quote_asset_volume.is_finite() { +// last.quoteassetvolume = update_quote_asset_volume; +// } +// if update_closeprice != 0.0 && !update_closeprice.is_nan() && update_closeprice.is_finite() { +// last.closeprice = update_closeprice; +// } +// } + +// let mut file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await; +// while let Err(e) = file { +// file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await; +// sleep(Duration::from_millis(1000)); +// } + +// if file.is_ok() { +// for element2 in query_result { +// let meanprice = (element2.openprice + element2.closeprice)/2.0; +// content_build.push_str(meanprice.to_string().as_str()); // opclo price +// content_build.push(','); +// content_build.push_str(element2.openprice.to_string().as_str()); // openprice +// content_build.push(','); +// content_build.push_str(element2.closeprice.to_string().as_str()); // closeprice +// content_build.push(','); +// content_build.push_str(element2.highprice.to_string().as_str()); // highprice +// content_build.push(','); +// content_build.push_str(element2.lowprice.to_string().as_str()); // lowprice +// content_build.push(','); +// content_build.push_str(element2.closetime.to_string().as_str()); // closetime +// content_build.push(','); +// content_build.push_str(element2.quoteassetvolume.to_string().as_str()); // quote asset volume (USDT) +// content_build.push(','); +// if element2.closeprice >= element2.openprice { +// content_build.push_str("UP"); // UP candle +// } else { +// content_build.push_str("DOWN"); // DOWN candle +// } +// content_build.push('\n'); +// } +// content_build.pop(); +// file?.write_all(content_build.as_bytes()).await; +// } else if file.is_err() { +// println!(">>> File error occurred {:?} (datapoints/price_{})", file.unwrap_err(), candle_period); +// } +// } +// } +// } +// } +// } + +// println!(" datapoints/price_{} 완료 elapsed:{:.2}s", candle_period, instant.elapsed().as_secs_f32()); +// Ok(()) +// } diff --git a/src/value_estimation_team/indicators.rs b/src/value_estimation_team/indicators.rs new file mode 100644 index 0000000..df13d54 --- /dev/null +++ b/src/value_estimation_team/indicators.rs @@ -0,0 +1,9 @@ +pub mod bollingerband; +pub mod ema; +pub mod heatmap_volume; +pub mod macd; +pub mod rsi; +pub mod sma; +pub mod stoch_rsi; +pub mod supertrend; +pub mod tema; diff --git a/src/value_estimation_team/indicators/bollingerband.rs b/src/value_estimation_team/indicators/bollingerband.rs new file mode 100644 index 0000000..e91814a --- /dev/null +++ b/src/value_estimation_team/indicators/bollingerband.rs @@ -0,0 +1,139 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::sma::SmaData; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +#[derive(Clone, Debug)] +pub struct BollingerBandData { + pub sma: f64, + pub upperband: f64, + pub lowerband: f64, + pub close_time: i64, +} + +impl BollingerBandData { + fn new() -> BollingerBandData { + let a = BollingerBandData { + sma: 0.0, + upperband: 0.0, + lowerband: 0.0, + close_time: 0, + }; + a + } +} +// Binance Bollingerband (SMA) +pub async fn bollingerband( + ma_number: usize, + sd_factor: f64, + input_sma_data: &Vec<(String, Vec)>, + rt_input_data: &Vec<(String, Vec)>, + output_bb_data: &mut Vec<(String, Vec)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let instant = Instant::now(); + + let mut read_rt_data_vec: Vec<(String, Vec)> = rt_input_data.clone(); + let mut read_sma_data_vec: Vec<(String, Vec)> = input_sma_data.clone(); + + let mut standard_deviation: f64 = 0.0; + let mut sd_mean: f64 = 0.0; + let mut read_data_buffer: Option<&SmaData>; + + let mut bb_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut bb_data_vec: Vec = Vec::new(); + + for symbol in valid_usdt_trades { + bb_data_vec.clear(); + let mut bb_data = BollingerBandData::new(); + + let symbol_search_result1 = read_rt_data_vec.iter().position(|x| x.0 == *symbol); + let symbol_search_result2 = read_sma_data_vec.iter().position(|x| x.0 == *symbol); + + match symbol_search_result1 { + Some(rt_index) => { + match symbol_search_result2 { + Some(sma_index) => { + // if the data has shorter than buffer + if read_sma_data_vec[sma_index].1.len() < ma_number { + bb_data.sma = 0.0; + bb_data.upperband = 0.0; + bb_data.lowerband = 0.0; + bb_data.close_time = 0; + bb_data_vec.push(bb_data.clone()); + } else { + let result = read_rt_data_vec[rt_index].1.binary_search_by_key( + &read_sma_data_vec[sma_index].1.first().unwrap().close_time, + |RealtimePriceData { + opclo_price, + open_price, + close_price, + high_price, + low_price, + close_time, + quote_asset_volume, + candle_type, + }| *close_time, + ); + match result { + Ok(T) => { + if T >= 0 && T <= ma_number - 1 { + let mut read_data_iter = + read_sma_data_vec[sma_index].1.iter(); + for _ in T..ma_number - 1 { + read_data_iter.next(); + } + let window_iter = + read_rt_data_vec[rt_index].1.windows(ma_number); + for buffer in window_iter { + sd_mean = 0.0; + standard_deviation = 0.0; + for element in buffer { + sd_mean += element.close_price; + } + sd_mean /= (ma_number as f64); + for element in buffer { + standard_deviation += + (element.close_price - sd_mean).powi(2); + } + standard_deviation = sd_factor + * ((standard_deviation / ma_number as f64).sqrt()); + + read_data_buffer = read_data_iter.next(); + + match read_data_buffer { + Some(T) => { + bb_data.sma = T.sma_value; + bb_data.upperband = + T.sma_value + standard_deviation; + bb_data.lowerband = + T.sma_value - standard_deviation; + bb_data.close_time = T.close_time; + bb_data_vec.push(bb_data.clone()); + } + None => {} + } + } + } + } + Err(E) => {} + } + bb_data_wrapper.push((symbol.clone(), bb_data_vec.clone())); + } + } + None => {} + } + } + None => {} + } + } + *output_bb_data = bb_data_wrapper; + // println!(" indicators/bb{} 완료 elapsed:{:.2}s", ma_number, instant.elapsed().as_secs_f32()); + Ok(()) +} diff --git a/src/value_estimation_team/indicators/ema.rs b/src/value_estimation_team/indicators/ema.rs new file mode 100644 index 0000000..be2d9a7 --- /dev/null +++ b/src/value_estimation_team/indicators/ema.rs @@ -0,0 +1,84 @@ +#![allow(unused)] +#![allow(warnings)] +use crate::database_control::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +#[derive(Clone, Debug)] +pub struct EmaData { + pub ema_value: f64, + pub close_time: i64, +} +impl EmaData { + fn new() -> EmaData { + let a = EmaData { + ema_value: 0.0, + close_time: 0, + }; + a + } +} + +// Binance EMA (closeprice) +pub async fn ema( + ema_number: usize, + input_rt_data: &Vec<(String, Vec)>, + output_ema_data: &mut Vec<(String, Vec)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let instant = Instant::now(); + let alpha: f64 = 2.0 / (ema_number as f64 + 1.0); + let mut ema_t: f64 = 0.0; + let mut ema_prev: f64 = 0.0; + + let mut ema_data = EmaData::new(); + let mut ema_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut ema_data_vec: Vec = Vec::new(); + for symbol in valid_usdt_trades { + let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); + ema_data_vec.clear(); + + match symbol_search_result { + Some(T) => { + if input_rt_data[T].1.len() < ema_number { + ema_data.ema_value = 0.0; + ema_data.close_time = 0; + ema_data_vec.push(ema_data.clone()); + } else { + let partial_vec1 = input_rt_data[T].1.get(..ema_number).unwrap(); + let partial_vec2 = input_rt_data[T].1.get(ema_number..).unwrap(); + + let mut sma_for_initial_value = 0.0; + for element in partial_vec1 { + sma_for_initial_value += element.close_price; + } + sma_for_initial_value /= ema_number as f64; + + ema_data.ema_value = sma_for_initial_value; + ema_data.close_time = partial_vec1.last().unwrap().close_time; + ema_data_vec.push(ema_data.clone()); + + ema_prev = sma_for_initial_value; + + for element in partial_vec2 { + ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price; + + ema_data.ema_value = ema_t; + ema_data.close_time = element.close_time; + ema_data_vec.push(ema_data.clone()); + + ema_prev = ema_t; + } + } + ema_data_wrapper.push((symbol.clone(), ema_data_vec.clone())); + } + None => {} + } + } + *output_ema_data = ema_data_wrapper; + // println!(" indicators/ema{} 완료 elapsed:{:.2}s", ema_number, instant.elapsed().as_secs_f32()); + Ok(()) +} diff --git a/src/value_estimation_team/indicators/heatmap_volume.rs b/src/value_estimation_team/indicators/heatmap_volume.rs new file mode 100644 index 0000000..1f593e4 --- /dev/null +++ b/src/value_estimation_team/indicators/heatmap_volume.rs @@ -0,0 +1,166 @@ +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; + +#[derive(Debug, Clone, PartialEq)] +pub enum HeatMapLevel { + ExtraHigh, + High, + Medium, + Normal, + Low, +} + +#[derive(Debug, Clone)] +pub struct HeatmapVolumeData { + pub heatmap_value: f64, + pub heatmap_level: HeatMapLevel, + pub close_time: i64, +} + +// Implementation from TradingView Script (HeatMap Volume by xdecow) +pub async fn heatmap_volume( + symbol: &String, + input_rt_data: &Vec<(String, Vec)>, + ma_len: usize, + std_len: usize, + extra_high_thold: f64, + high_thold: f64, + medium_thold: f64, + normal_thold: f64, +) -> Option> { + let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); + + match symbol_search_result { + Some(T) => { + if input_rt_data[T].1.len() >= ma_len && input_rt_data[T].1.len() >= std_len { + // calc mean + #[derive(Debug, Clone)] + struct MeanData { + mean_value: f64, + close_time: i64, + } + + let mut mean_vec: Vec = Vec::new(); + let mut mean_data = MeanData { + mean_value: 0.0, + close_time: 0, + }; + let window = input_rt_data[T].1.windows(ma_len); + for buffer in window { + // calculate SMA of volume + mean_data.mean_value = 0.0; + mean_data.close_time = 0; + for element in buffer { + mean_data.mean_value += element.quote_asset_volume; + } + mean_data.mean_value /= ma_len as f64; + mean_data.close_time = buffer.last().unwrap().close_time; + + mean_vec.push(mean_data.clone()); + } + + // calc pstdev + #[derive(Debug, Clone)] + struct PstdevData { + pstdev_value: f64, // population standard deviation value + close_time: i64, + } + let mut pstdev_vec: Vec = Vec::new(); + let mut pstdev_data = PstdevData { + pstdev_value: 0.0, + close_time: 0, + }; + let mut mean = 0.0; + let mut summation = 0.0; + let mut sample_minus_mean = 0.0; + let window = input_rt_data[T].1.windows(std_len); + for buffer in window { + pstdev_data.pstdev_value = 0.0; + pstdev_data.close_time = 0; + mean = 0.0; + summation = 0.0; + sample_minus_mean = 0.0; + for element in buffer { + mean += element.quote_asset_volume; + } + mean /= std_len as f64; + + for element in buffer { + sample_minus_mean = element.quote_asset_volume - mean; + summation += sample_minus_mean.powi(2); + } + + pstdev_data.pstdev_value = (summation / std_len as f64).sqrt(); + pstdev_data.close_time = buffer.last().unwrap().close_time; + + pstdev_vec.push(pstdev_data.clone()); + } + + // calc stdbar and heatmap volume + let mut heatmap_vol_vec: Vec = Vec::new(); + let mut heatmap_vol_data = HeatmapVolumeData { + heatmap_value: 0.0, + heatmap_level: HeatMapLevel::Normal, + close_time: 0, + }; + + if ma_len == std_len { + let mut rt_data_trunc_vec = + input_rt_data[T].1.get(ma_len - 1..).unwrap().iter(); + + let zipped = mean_vec.iter().zip(pstdev_vec.iter()); + for element in zipped { + heatmap_vol_data.heatmap_value = + (rt_data_trunc_vec.next().unwrap().quote_asset_volume + - element.0.mean_value) + / element.1.pstdev_value; + heatmap_vol_data.close_time = element.0.close_time; + if heatmap_vol_data.heatmap_value >= extra_high_thold { + heatmap_vol_data.heatmap_level = HeatMapLevel::ExtraHigh; + } else if heatmap_vol_data.heatmap_value > high_thold { + heatmap_vol_data.heatmap_level = HeatMapLevel::High; + } else if heatmap_vol_data.heatmap_value > medium_thold { + heatmap_vol_data.heatmap_level = HeatMapLevel::Medium; + } else if heatmap_vol_data.heatmap_value > normal_thold { + heatmap_vol_data.heatmap_level = HeatMapLevel::Normal; + } else { + heatmap_vol_data.heatmap_level = HeatMapLevel::Low; + } + heatmap_vol_vec.push(heatmap_vol_data.clone()); + } + } else if ma_len > std_len { + let mut rt_data_trunc_vec = + input_rt_data[T].1.get(std_len - 1..).unwrap().iter(); + let mut mean_trunc_vec = + mean_vec.get(mean_vec.len() - std_len..).unwrap().iter(); + let zipped = mean_trunc_vec.zip(pstdev_vec.iter()); + for element in zipped { + heatmap_vol_data.heatmap_value = + (rt_data_trunc_vec.next().unwrap().quote_asset_volume + - element.0.mean_value) + / element.1.pstdev_value; + heatmap_vol_data.close_time = element.0.close_time; + heatmap_vol_vec.push(heatmap_vol_data.clone()); + } + } else { + let mut rt_data_trunc_vec = + input_rt_data[T].1.get(ma_len - 1..).unwrap().iter(); + let mut pstdev_trunc_vec = + pstdev_vec.get(pstdev_vec.len() - ma_len..).unwrap().iter(); + let zipped = mean_vec.iter().zip(pstdev_trunc_vec); + for element in zipped { + heatmap_vol_data.heatmap_value = + (rt_data_trunc_vec.next().unwrap().quote_asset_volume + - element.0.mean_value) + / element.1.pstdev_value; + heatmap_vol_data.close_time = element.0.close_time; + heatmap_vol_vec.push(heatmap_vol_data.clone()); + } // level 구현 + } + Some(heatmap_vol_vec) + } else { + None + } + } + None => None, + } +} diff --git a/src/value_estimation_team/indicators/macd.rs b/src/value_estimation_team/indicators/macd.rs new file mode 100644 index 0000000..143a202 --- /dev/null +++ b/src/value_estimation_team/indicators/macd.rs @@ -0,0 +1,83 @@ +use crate::value_estimation_team::indicators::ema::EmaData; + +#[derive(Debug, Clone)] +pub struct EmaMacd { + pub macd_value: f64, + pub close_time: i64, +} + +impl EmaMacd { + fn new() -> EmaMacd { + let a = EmaMacd { + macd_value: 0.0, + close_time: 0, + }; + a + } +} + +pub async fn ema_macd( + fast_ema: &Vec, + slow_ema: &Vec, + signal_length: usize, +) -> Result, Box> { + let mut macd = EmaMacd::new(); + let mut macd_vec: Vec = Vec::new(); + let mut macd_signal_vec: Vec = Vec::new(); + let mut macd_oscil_vec: Vec = Vec::new(); + + if fast_ema.len() >= signal_length && slow_ema.len() >= signal_length { + let result = fast_ema.binary_search_by_key( + &slow_ema.first().unwrap().close_time, + |&EmaData { + ema_value, + close_time, + }| close_time, + ); + if result.is_ok() { + let temp_vec = fast_ema.get(result.unwrap()..).unwrap(); + let zipped = temp_vec.iter().zip(slow_ema); + for element in zipped { + macd.macd_value = element.0.ema_value - element.1.ema_value; + macd.close_time = element.0.close_time; + macd_vec.push(macd.clone()); + } + + // making signal + let macd_vec_window = macd_vec.windows(signal_length); + for window in macd_vec_window { + let mut sum_value = 0.0; + for element in window { + sum_value += element.macd_value; + } + macd.macd_value = sum_value / signal_length as f64; + macd.close_time = window.last().unwrap().close_time; + macd_signal_vec.push(macd.clone()); + } + + let result = macd_vec.binary_search_by_key( + &macd_signal_vec.first().unwrap().close_time, + |&EmaMacd { + macd_value, + close_time, + }| close_time, + ); + if result.is_ok() { + let result = macd_vec.get(result.unwrap()..); + if result.is_some() { + if result.unwrap().len() == macd_signal_vec.len() { + let zipped = result.unwrap().iter().zip(macd_signal_vec); + + for element in zipped { + macd.macd_value = element.0.macd_value - element.1.macd_value; + macd.close_time = element.0.close_time; + macd_oscil_vec.push(macd.clone()); + } + } + } + } + } + } + + Ok(macd_oscil_vec) +} diff --git a/src/value_estimation_team/indicators/rsi.rs b/src/value_estimation_team/indicators/rsi.rs new file mode 100644 index 0000000..83f78e9 --- /dev/null +++ b/src/value_estimation_team/indicators/rsi.rs @@ -0,0 +1,151 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use std::f64::NAN; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +#[derive(Clone, Debug)] +pub struct RsiData { + pub rsi_value: f64, + pub close_time: i64, +} +impl RsiData { + pub fn new() -> RsiData { + let a = RsiData { + rsi_value: 0.0, + close_time: 0, + }; + a + } +} + +// Binance RSI (EMA, Closeprice, Wilder's weight, 150 candles) +pub async fn rsi( + rsi_number: usize, + input_rt_data: &Vec<(String, Vec)>, + output_rsi_data: &mut Vec<(String, Vec)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let instant = Instant::now(); + + let mut read_data_vec: Vec = Vec::new(); + let mut read_price_buffer: Vec = Vec::new(); + let mut prev_price: f64 = 0.0; + let mut current_price: f64 = 0.0; + let mut sum_increase: f64 = 0.0; + let mut sum_decrease: f64 = 0.0; + let mut rsi: f64 = 0.0; + let mut last_close_time = 0; + + let mut rsi_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut rsi_data_vec: Vec = Vec::new(); + let mut rsi_data = RsiData::new(); + for symbol in valid_usdt_trades { + read_data_vec.clear(); + read_price_buffer.clear(); + rsi_data_vec.clear(); + let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); + match symbol_search_result { + Some(T) => { + if input_rt_data[T].1.len() < rsi_number + 1 { + rsi_data.rsi_value = 0.0; + rsi_data.close_time = 0; + rsi_data_vec.push(rsi_data.clone()); + } else { + read_data_vec = input_rt_data[T].1.clone(); + if read_data_vec.len() >= (150 + rsi_number) as usize { + read_data_vec.reverse(); + read_data_vec.truncate((150 + rsi_number) as usize); + read_data_vec.reverse(); + } + let window_iter = read_data_vec.windows(rsi_number + 1); + + let mut prev_avg_ups: Option = None; + let mut prev_avg_downs: Option = None; + let mut current_avg_ups: Option = None; + let mut current_avg_downs: Option = None; + + for buffer in window_iter { + let mut up_vec: Vec = Vec::new(); + let mut down_vec: Vec = Vec::new(); + + let buffer_window = buffer.windows(2); + for element in buffer_window { + if element.last().unwrap().close_price + - element.first().unwrap().close_price + > 0.0 + { + up_vec.push( + element.last().unwrap().close_price + - element.first().unwrap().close_price, + ); + down_vec.push(0.0); + } else if element.last().unwrap().close_price + - element.first().unwrap().close_price + < 0.0 + { + up_vec.push(0.0); + down_vec.push( + (element.last().unwrap().close_price + - element.first().unwrap().close_price) + .abs(), + ); + } else { + up_vec.push(0.0); + down_vec.push(0.0); + } + } + + if current_avg_ups == None + && current_avg_downs == None + && prev_avg_ups == None + && prev_avg_downs == None + { + // initial averages based on SMA + let mut avg_ups: f64 = up_vec.iter().sum(); + avg_ups /= rsi_number as f64; + let mut avg_downs: f64 = down_vec.iter().sum(); + avg_downs /= rsi_number as f64; + + current_avg_ups = Some(avg_ups); + current_avg_downs = Some(avg_downs); + } else { + // [EMA] + let alpha = 1.0 / (rsi_number as f64); // Wilder's weight + current_avg_ups = Some( + alpha * up_vec.last().unwrap() + + ((1.0 - alpha) * prev_avg_ups.unwrap()), + ); + current_avg_downs = Some( + alpha * down_vec.last().unwrap() + + ((1.0 - alpha) * prev_avg_downs.unwrap()), + ); + } + prev_avg_ups = current_avg_ups; + prev_avg_downs = current_avg_downs; + + let rs = + current_avg_ups.unwrap() / (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0 + + let rsi = 100.0 - (100.0 / (1.0 + rs)); + + rsi_data.rsi_value = rsi; + rsi_data.close_time = buffer.last().unwrap().close_time; + rsi_data_vec.push(rsi_data.clone()); + } + rsi_data_wrapper.push((symbol.clone(), rsi_data_vec.clone())); + } + } + None => {} + } + } + *output_rsi_data = rsi_data_wrapper; + // println!(" indicators/rsi{} 완료 elapsed:{:.2}s", rsi_number, instant.elapsed().as_secs_f32()); + + Ok(()) +} diff --git a/src/value_estimation_team/indicators/sma.rs b/src/value_estimation_team/indicators/sma.rs new file mode 100644 index 0000000..3ae5452 --- /dev/null +++ b/src/value_estimation_team/indicators/sma.rs @@ -0,0 +1,71 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +#[derive(Clone)] +pub struct SmaData { + pub sma_value: f64, + pub close_time: i64, +} +impl SmaData { + fn new() -> SmaData { + let a = SmaData { + sma_value: 0.0, + close_time: 0, + }; + a + } +} + +// Binance MA (closeprice) +pub async fn sma( + moving_number: usize, + input_rt_data: &Vec<(String, Vec)>, + output_sma_data: &mut Vec<(String, Vec)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let instant = Instant::now(); + + let mut sma_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut sma_data_vec: Vec = Vec::new(); + let mut sma_data = SmaData::new(); + + for symbol in valid_usdt_trades { + let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); + sma_data_vec.clear(); + + match symbol_search_result { + Some(T) => { + if input_rt_data[T].1.len() < moving_number { + sma_data.sma_value = 0.0; + sma_data.close_time = 0; + sma_data_vec.push(sma_data.clone()); + } else { + let mut iter = input_rt_data[T].1.windows(moving_number); + for buffer in iter { + let mut avg = 0.0; + for element in buffer { + avg += element.close_price; + } + avg /= (moving_number as f64); + + sma_data.sma_value = avg; + sma_data.close_time = buffer.last().unwrap().close_time; + sma_data_vec.push(sma_data.clone()); + } + } + sma_data_wrapper.push((symbol.clone(), sma_data_vec.clone())); + } + None => {} + } + } + *output_sma_data = sma_data_wrapper; + // println!(" indicators/sma{} 완료 elapsed:{:.2}s", moving_number, instant.elapsed().as_secs_f32()); + Ok(()) +} diff --git a/src/value_estimation_team/indicators/stoch_rsi.rs b/src/value_estimation_team/indicators/stoch_rsi.rs new file mode 100644 index 0000000..67ee269 --- /dev/null +++ b/src/value_estimation_team/indicators/stoch_rsi.rs @@ -0,0 +1,131 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use crate::value_estimation_team::indicators::rsi::RsiData; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use std::f64::NAN; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +#[derive(Clone, Debug)] +pub struct StochRsiKData { + pub stoch_rsi_k_value: f64, + pub close_time: i64, +} +impl StochRsiKData { + fn new() -> StochRsiKData { + let a = StochRsiKData { + stoch_rsi_k_value: 0.0, + close_time: 0, + }; + a + } +} + +#[derive(Clone, Debug)] +pub struct StochRsiDData { + pub stoch_rsi_d_value: f64, + pub close_time: i64, +} +impl StochRsiDData { + fn new() -> StochRsiDData { + let a = StochRsiDData { + stoch_rsi_d_value: 0.0, + close_time: 0, + }; + a + } +} + +// Binance Stoch RSI (RSI10, length 10, K: 3, D: 3) +pub async fn stoch_rsi( + input_rsi_data: &Vec<(String, Vec)>, + stoch_rsi_length: usize, + output_stoch_rsi_k_data: &mut Vec<(String, Vec)>, + output_stoch_rsi_d_data: &mut Vec<(String, Vec)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let mut read_data_vec: Vec = Vec::new(); + let mut stoch_rsi_k_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut stoch_rsi_k_data_vec: Vec = Vec::new(); + let mut stoch_rsi_k_data = StochRsiKData::new(); + let mut stoch_rsi_d_data_wrapper: Vec<(String, Vec)> = Vec::new(); + let mut stoch_rsi_d_data_vec: Vec = Vec::new(); + let mut stoch_rsi_d_data = StochRsiDData::new(); + + let mut stoch_rsi_vec: Vec = Vec::new(); + let mut stoch_rsi = RsiData::new(); + let k_length = 2; + let d_length = 2; + + for symbol in valid_usdt_trades { + stoch_rsi_k_data_vec.clear(); + stoch_rsi_d_data_vec.clear(); + stoch_rsi_vec.clear(); + let symbol_search_result = input_rsi_data.iter().position(|x| x.0 == *symbol); + match symbol_search_result { + Some(T) => { + if input_rsi_data[T].1.len() >= stoch_rsi_length { + read_data_vec = input_rsi_data[T].1.clone(); + let window_iter = read_data_vec.windows(stoch_rsi_length); + + for buffer_window in window_iter { + let max_value = buffer_window + .iter() + .max_by(|x, y| x.rsi_value.partial_cmp(&y.rsi_value).unwrap()) + .unwrap() + .rsi_value; + let min_value = buffer_window + .iter() + .min_by(|x, y| x.rsi_value.partial_cmp(&y.rsi_value).unwrap()) + .unwrap() + .rsi_value; + + let stoch_rsi_value = if max_value == min_value { + max_value + } else { + (buffer_window.last().unwrap().rsi_value - min_value) + / (max_value - min_value) + }; + + stoch_rsi.rsi_value = stoch_rsi_value; + stoch_rsi.close_time = buffer_window.last().unwrap().close_time; + + stoch_rsi_vec.push(stoch_rsi.clone()); + } + + // making Stoch RSI K data + let window_iter = stoch_rsi_vec.windows(k_length); + for buffer_window in window_iter { + stoch_rsi_k_data.stoch_rsi_k_value = + (buffer_window.iter().fold(0.0, |acc, x| acc + x.rsi_value) + / (k_length as f64)) + * 100.0; + stoch_rsi_k_data.close_time = buffer_window.last().unwrap().close_time; + stoch_rsi_k_data_vec.push(stoch_rsi_k_data.clone()); + } + stoch_rsi_k_data_wrapper.push((symbol.clone(), stoch_rsi_k_data_vec.clone())); + + // making Stoch RSI D data + let window_iter = stoch_rsi_k_data_vec.windows(d_length); + for buffer_window in window_iter { + stoch_rsi_d_data.stoch_rsi_d_value = (buffer_window + .iter() + .fold(0.0, |acc, x| acc + x.stoch_rsi_k_value) + / (k_length as f64)); + stoch_rsi_d_data.close_time = buffer_window.last().unwrap().close_time; + stoch_rsi_d_data_vec.push(stoch_rsi_d_data.clone()); + } + stoch_rsi_d_data_wrapper.push((symbol.clone(), stoch_rsi_d_data_vec.clone())); + } + } + None => {} + } + } + *output_stoch_rsi_k_data = stoch_rsi_k_data_wrapper; + *output_stoch_rsi_d_data = stoch_rsi_d_data_wrapper; + + Ok(()) +} diff --git a/src/value_estimation_team/indicators/supertrend.rs b/src/value_estimation_team/indicators/supertrend.rs new file mode 100644 index 0000000..14f94ad --- /dev/null +++ b/src/value_estimation_team/indicators/supertrend.rs @@ -0,0 +1,238 @@ +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; + +#[derive(Clone, Debug)] +pub struct SupertrendData { + pub band_value: f64, + pub signal: Option, // BUY or SELL + pub area: String, // UP or DOWN + pub close_time: i64, +} + +impl SupertrendData { + fn new() -> SupertrendData { + let a = SupertrendData { + band_value: 0.0, + signal: None, + area: String::new(), + close_time: 0, + }; + + a + } +} + +#[derive(Clone, Debug)] +struct TrueRangeData { + tr_value: f64, + close_time: i64, +} + +impl TrueRangeData { + fn new() -> TrueRangeData { + let a = TrueRangeData { + tr_value: 0.0, + close_time: 0, + }; + + a + } +} + +#[derive(Clone, Debug)] +struct ATRData { + atr_value: f64, + close_time: i64, +} + +impl ATRData { + fn new() -> ATRData { + let a = ATRData { + atr_value: 0.0, + close_time: 0, + }; + + a + } +} + +// Implementation from TradingView Script (SuperTrend by KivancOzbilgic) +pub async fn supertrend( + symbol: &String, + input_rt_data: &Vec<(String, Vec)>, + atr_period: usize, + multiplier: f64, + is_original_method: bool, +) -> Option> { + let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); + + match symbol_search_result { + Some(T) => { + if input_rt_data[T].1.len() >= atr_period && atr_period >= 2 { + // making True Range + let mut true_range_vec: Vec = Vec::new(); + let mut tr_data = TrueRangeData::new(); + let mut tr_val_hl = 0.0; // High - Low + let mut tr_val_hcp = 0.0; // Abs(High - Close_previous) + let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous) + + let window_vec = input_rt_data[T].1.windows(2); + + tr_data.tr_value = input_rt_data[T].1.first().unwrap().high_price + - input_rt_data[T].1.first().unwrap().low_price; + tr_data.close_time = input_rt_data[T].1.first().unwrap().close_time; + true_range_vec.push(tr_data.clone()); + + for buffer in window_vec { + tr_val_hl = buffer[1].high_price - buffer[1].low_price; + tr_val_hcp = (buffer[1].high_price - buffer[0].close_price).abs(); + tr_val_lcp = (buffer[1].low_price - buffer[0].close_price).abs(); + + tr_data.tr_value = tr_val_hl.max(tr_val_hcp.max(tr_val_lcp)); + tr_data.close_time = buffer[1].close_time; + + true_range_vec.push(tr_data.clone()); + } + + // making Average True Range + let mut average_true_range_vec: Vec = Vec::new(); + let mut atr_data = ATRData::new(); + + if is_original_method == true { + // original calculation of ATR + let mut first_value = 0.0; + let mut first_vec = &true_range_vec[0..atr_period]; + + for element in first_vec { + first_value += element.tr_value; + } + first_value /= (atr_period) as f64; + atr_data.atr_value = first_value; + atr_data.close_time = first_vec.last().unwrap().close_time; + average_true_range_vec.push(atr_data.clone()); + + let mut temp_prev_atr_value = first_value; + for element in &true_range_vec[atr_period..] { + atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64)) + + element.tr_value) + / atr_period as f64; + atr_data.close_time = element.close_time; + + average_true_range_vec.push(atr_data.clone()); + temp_prev_atr_value = atr_data.atr_value; + } + } else { + // Calculation of ATR from SMA True Range with atr_period + let window = true_range_vec.windows(atr_period); + + let mut sum = 0.0; + for buffer in window { + for element in buffer { + sum += element.tr_value; + } + + atr_data.atr_value = sum / atr_period as f64; + atr_data.close_time = buffer.last().unwrap().close_time; + average_true_range_vec.push(atr_data.clone()); + + sum = 0.0; + } + } + + // making Supertrend + #[derive(Clone)] + struct BandData { + basic_upperband: f64, + basic_lowerband: f64, + close_time: i64, + } + + let mut supertrend_vec: Vec = Vec::new(); + let mut supertrend_data = SupertrendData::new(); + let mut band_vec: Vec = Vec::new(); + let mut band_data = BandData { + basic_upperband: 0.0, + basic_lowerband: 0.0, + close_time: 0, + }; + + let closetime_search_result = input_rt_data[T].1.iter().position(|x| { + x.close_time == average_true_range_vec.first().unwrap().close_time + }); + let mut rt_data = &input_rt_data[T].1[closetime_search_result.unwrap()..]; + let zipped = rt_data.iter().zip(average_true_range_vec); + + for element in zipped { + band_data.basic_upperband = ((element.0.high_price + element.0.low_price) + / 2.0) + - (multiplier * element.1.atr_value); + band_data.basic_lowerband = ((element.0.high_price + element.0.low_price) + / 2.0) + + (multiplier * element.1.atr_value); + band_data.close_time = element.1.close_time; + band_vec.push(band_data.clone()); + } + + let mut zipped = rt_data.iter().zip(band_vec); + let first_element = zipped.next().unwrap(); + let mut prev_close_price = first_element.0.close_price; + let mut final_upperband = 0.0; + let mut final_lowerband = 0.0; + let mut prev_final_upperband = first_element.1.basic_upperband; + let mut prev_final_lowerband = first_element.1.basic_lowerband; + let mut trend = 1; // 1 means up trend, -1 means down trend + let mut prev_trend = 1; + for element in zipped { + // set final upperband + if prev_close_price > prev_final_upperband { + final_upperband = prev_final_upperband.max(element.1.basic_upperband); + } else { + final_upperband = element.1.basic_upperband; + } + + // set final lowerband + if prev_close_price < prev_final_lowerband { + final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband); + } else { + final_lowerband = element.1.basic_lowerband; + } + + // set supertrend + if trend == -1 && element.0.close_price > prev_final_lowerband { + supertrend_data.area = String::from("UP"); + trend = 1; + } else if trend == 1 && element.0.close_price < prev_final_upperband { + supertrend_data.area = String::from("DOWN"); + trend = -1; + } + + if supertrend_data.area.contains("UP") { + supertrend_data.band_value = final_upperband; + } else { + supertrend_data.band_value = final_lowerband; + } + + if trend == 1 && prev_trend == -1 { + supertrend_data.signal = Some(String::from("BUY")); + } else if trend == -1 && prev_trend == 1 { + supertrend_data.signal = Some(String::from("SELL")); + } else { + supertrend_data.signal = None; + } + + supertrend_data.close_time = element.1.close_time; + supertrend_vec.push(supertrend_data.clone()); + + prev_close_price = element.0.close_price; + prev_final_upperband = final_upperband; + prev_final_lowerband = final_lowerband; + prev_trend = trend; + } + + Some(supertrend_vec) + } else { + None + } + } + None => None, + } +} diff --git a/src/value_estimation_team/indicators/tema.rs b/src/value_estimation_team/indicators/tema.rs new file mode 100644 index 0000000..421609a --- /dev/null +++ b/src/value_estimation_team/indicators/tema.rs @@ -0,0 +1,87 @@ +#![allow(unused)] +#![allow(warnings)] + +use crate::database_control::*; +use csv::{DeserializeRecordsIter, StringRecord}; +use serde::Deserialize; +use sqlx::FromRow; +use tokio::{fs::*, io::AsyncWriteExt, time::*}; + +pub async fn tema( + tema_number: usize, + input_ema_data: &Vec<(String, Vec<(f64, i64)>)>, + output_tema_data: &mut Vec<(String, Vec<(f64, i64)>)>, + valid_usdt_trades: &Vec, +) -> Result<(), Box> { + let alpha: f64 = 2.0 / (tema_number as f64 + 1.0); + let mut ema_2order: Vec = Vec::new(); + let mut ema_3order: Vec = Vec::new(); + + let mut tema_data_wrapper: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); + let mut tema_data_vec: Vec<(f64, i64)> = Vec::new(); + for symbol in valid_usdt_trades { + let symbol_search_result = input_ema_data.iter().position(|x| x.0 == *symbol); + + tema_data_vec.clear(); + ema_2order.clear(); + ema_3order.clear(); + + match symbol_search_result { + Some(T) => { + if input_ema_data[T].1.len() < tema_number { + tema_data_vec.push((0.0, 0)); + } else { + // calculate 2order of ema from 1order of ema + let mut ema_prev = 0.0; + { + let partial_vec1 = input_ema_data[T].1.get(..tema_number).unwrap(); + let partial_vec2 = input_ema_data[T].1.get(tema_number..).unwrap(); + + let mut sma_for_initial_value = 0.0; + for element in partial_vec1 { + sma_for_initial_value += element.0; + } + sma_for_initial_value /= tema_number as f64; + ema_2order.push(sma_for_initial_value); + ema_prev = sma_for_initial_value; + + let mut index_2order: usize = 0; + for element in partial_vec2 { + ema_2order.push( + ((1.0 - alpha) * ema_2order[index_2order]) + (alpha * element.0), + ); + index_2order += 1; + } + } + + // calculate 3order of ema from 2order of ema + let mut index_3order: usize = 0; + for element in &ema_2order { + if element == ema_2order.first().unwrap() { + ema_3order.push(*element); + } else { + ema_3order.push( + ((1.0 - alpha) * ema_3order[index_3order]) + (alpha * (*element)), + ); + index_3order += 1; + } + } + + // write string + let mut index: usize = 0; + let mut tema_price: f64 = 0.0; + for element in &input_ema_data[T].1 { + tema_price = ((element.0 - ema_2order[index]) * 3.0) + ema_3order[index]; + tema_data_vec.push((tema_price, element.1)); + index += 1; + } + tema_data_wrapper.push((symbol.clone(), tema_data_vec.clone())); + } + } + None => {} + } + } + *output_tema_data = tema_data_wrapper; + // println!(" indicators/tema{}_sec 완료", tema_number); + Ok(()) +}