diff --git a/skills/hyperliquid-funding-rate-scout/.claude-plugin/plugin.json b/skills/hyperliquid-funding-rate-scout/.claude-plugin/plugin.json new file mode 100644 index 000000000..05aac463e --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/.claude-plugin/plugin.json @@ -0,0 +1,22 @@ +{ + "name": "hyperliquid-funding-rate-scout", + "description": "Identify high-probability mean reversion opportunities by scanning Hyperliquid perpetual futures for statistically significant funding rate imbalances and crowded positioning", + "version": "1.0.0", + "author": { + "name": "Ritesh", + "github": "Ritesh59697" + }, + "homepage": "https://github.com/Ritesh59697/plugin-store", + "repository": "https://github.com/Ritesh59697/plugin-store", + "license": "MIT", + "keywords": [ + "hyperliquid", + "funding-rate", + "mean-reversion", + "strategy", + "perpetuals", + "signal", + "analytics", + "crowded-positioning" + ] +} \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/.gitignore b/skills/hyperliquid-funding-rate-scout/.gitignore new file mode 100644 index 000000000..9734a4fd0 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/.gitignore @@ -0,0 +1,4 @@ +/target +.env +.DS_Store +**/*.rs.bk \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/Cargo.lock b/skills/hyperliquid-funding-rate-scout/Cargo.lock new file mode 100644 index 000000000..730276139 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/Cargo.lock @@ -0,0 +1,572 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[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 = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hyperliquid-funding-rate-scout" +version = "1.0.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "serde", + "serde_json", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/skills/hyperliquid-funding-rate-scout/Cargo.toml b/skills/hyperliquid-funding-rate-scout/Cargo.toml new file mode 100644 index 000000000..8dc2998fe --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "hyperliquid-funding-rate-scout" +version = "1.0.0" +edition = "2021" +authors = ["Ritesh"] +description = "Scan Hyperliquid perpetual futures for extreme funding rate imbalances and generate mean reversion signal cards" +license = "MIT" + +[[bin]] +name = "hyperliquid-funding-rate-scout" +path = "src/main.rs" + +[dependencies] +anyhow = "=1.0.86" +serde = { version = "=1.0.193", features = ["derive"] } +serde_json = "=1.0.108" +clap = { version = "=4.4.18", features = ["derive"] } +chrono = { version = "=0.4.31", features = ["clock"], default-features = false } + +[profile.release] +opt-level = 3 +strip = true \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/LICENSE b/skills/hyperliquid-funding-rate-scout/LICENSE new file mode 100644 index 000000000..54f2450c0 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Ritesh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/skills/hyperliquid-funding-rate-scout/SKILL.md b/skills/hyperliquid-funding-rate-scout/SKILL.md new file mode 100644 index 000000000..2e63e2584 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/SKILL.md @@ -0,0 +1,453 @@ +--- +name: hyperliquid-funding-rate-scout +description: "Identify high-probability mean reversion opportunities by scanning Hyperliquid perpetual futures for statistically significant funding rate imbalances and crowded positioning" +version: "1.0.0" +author: "Ritesh" +tags: + - hyperliquid + - funding-rate + - mean-reversion + - strategy + - perpetuals + - signal + - analytics +--- + +# Hyperliquid Funding Rate Scout + +## Overview + +This is a **strategy skill** that enables AI agents to identify high-probability +mean reversion opportunities by scanning perpetual futures markets on Hyperliquid +for extreme funding rate imbalances. It operates as both a signal intelligence +layer and a conditional execution layer — scanning and analyzing autonomously, +then optionally triggering trade execution via `hyperliquid-plugin` only after +explicit user confirmation. + +> šŸ”’ **Scanning and analysis are strictly read-only. This plugin does not execute +> trades, move funds, or interact with user wallets in any way without explicit +> user confirmation.** + +Unlike generic funding rate trackers, this skill focuses specifically on +**statistically significant outliers** — filtering out normal market noise and +surfacing only the crowded positioning scenarios where traders are heavily biased +in one direction. These conditions historically precede mean reversion: funding +rates normalize, and positions aligned with the overcrowded side get squeezed. + +Hyperliquid is used as the data source due to its transparent on-chain orderbook +and reliable real-time funding data, making it ideal for detecting short-term +market inefficiencies. Data is fetched in real-time at request, with lightweight +caching to ensure responsiveness without stale signals. + +This plugin is designed as a signal layer within a modular trading agent system, +separating market intelligence from execution for safer and more controlled +workflows. After generating signals, it presents structured output and stops — +handing off only after explicit user confirmation. + +--- + +## Trigger Conditions + +Invoke this skill when: +- The user requests trading opportunities or market setups +- The user asks about funding rates, market sentiment, or positioning +- The user is looking for arbitrage, mean reversion, or crowding-based strategies +- The user mentions Hyperliquid, perps, or funding in any context + +--- + +## Risk Disclaimer + +> **NOTICE**: This plugin operates as a strategy skill with two distinct modes: +> read-only signal scanning (default) and optional trade execution via +> `hyperliquid-plugin` (user-triggered only). In execution mode, this strategy +> may place leveraged perpetual futures trades on Hyperliquid on the user's behalf. +> Leveraged trading carries significant risk including partial or total loss of +> funds and liquidation. All signals are generated from funding rate analysis and +> are strictly informational — not financial advice. Funding rate mean reversion +> is not guaranteed. Markets can remain in extreme funding conditions for extended +> periods. Validate all signals independently before acting. Trade sizing, risk +> management, and all execution decisions are entirely the user's responsibility. +> +> **Note**: Funding rate signals indicate crowd positioning, not guaranteed price +> reversal. Signals should be used alongside proper risk management. + +--- + +## Pre-flight Checks + +Before using this skill, ensure the following are installed and accessible: + +1. Install the `onchainos` CLI: + ```bash + npx skills add okx/onchainos-skills + export PATH="$HOME/.local/bin:$PATH" + ``` + +2. Verify `onchainos` is working: + ```bash + onchainos --version + ``` + +3. Install the Hyperliquid Plugin (needed only if user confirms execution later): + ```bash + npx skills add okx/plugin-store --skill hyperliquid-plugin + ``` + +4. The signal scanner binary is included with this skill and requires no separate + installation. It uses `curl` for HTTP requests — verify curl is available: + ```bash + curl --version + ``` + +5. To run the scanner directly (outside of agent context): + ```bash + # Default scan — top 5 signals above 0.01% hourly threshold + hyperliquid-funding-rate-scout + + # Custom threshold and limit + hyperliquid-funding-rate-scout --threshold 0.03 --limit 3 + + # Filter to specific assets + hyperliquid-funding-rate-scout --asset BTC --asset ETH + + # Raw JSON output (for programmatic use) + hyperliquid-funding-rate-scout --json + ``` + +> **Note**: No wallet connection is required for this plugin. All scan and +> analysis operations are strictly read-only. + +--- + +## Inputs + +The agent should check for optional user-provided parameters before scanning. +If not provided, use the defaults listed below. + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `asset_filter` | string or list | all markets | Limit scan to specific asset(s), e.g. BTC, ETH, SOL | +| `funding_threshold` | number (APR %) | ±50% | Minimum absolute funding rate to flag as a signal | +| `num_signals` | integer | 5 | Number of top signals to return | + +**How to collect inputs from the user:** + +If the user gives a broad request (e.g. "find me opportunities"), use all defaults. +If the user specifies constraints (e.g. "only BTC and ETH" or "show top 3 above 80% APR"), +extract those values and apply them to the scan. + +--- + +## Commands + +### Phase 1 — Scan: Fetch All Funding Rates + +```bash +onchainos signal list --chain arbitrum --platform hyperliquid +``` + +**When to use**: Always — this is the entry point of every scan session. + +**What it returns**: All active Hyperliquid perpetual markets with their current +funding rates. Each entry includes the asset symbol, funding rate (hourly and +annualized), and directional bias. + +**If asset_filter is set**, cross-reference output and retain only the +specified assets before proceeding to Phase 2. + +--- + +### Phase 1b — Get Price and Market Context per Asset + +For each asset flagged in Phase 1, fetch current market data: + +```bash +onchainos market price --address --chain arbitrum +``` + +**When to use**: After the scan, for every asset that passes the threshold filter. +This provides the current price, 24h change, and open interest context needed +to build the signal card. + +**Example**: +```bash +onchainos market price --address BTC --chain arbitrum +onchainos market price --address SOL --chain arbitrum +``` + +--- + +### Phase 2 — Analyze: Get Smart Money Positioning + +Cross-reference funding extremes with on-chain smart money signals to assess +whether the crowding thesis is supported by institutional positioning data. + +```bash +onchainos signal list --chain arbitrum +``` + +**When to use**: After filtering by funding threshold, run this to check +smart money direction. If whale/smart money positioning is **opposite** to the +crowded side, the mean reversion thesis is strengthened. If aligned with the +crowd, note it as a risk factor. + +--- + +### Phase 2b — Analyze: Get Recent Price History + +Fetch recent kline data to assess whether the market is trending or ranging. + +```bash +onchainos market kline --address --chain arbitrum --interval 1h +``` + +**When to use**: For the top 1–3 signals. Mean reversion strategies have +higher probability in ranging markets. A strong directional trend is a risk +factor that must be noted in the signal card. + +--- + +## Agent Execution Flow + +The agent follows three strict phases. **Phase 3 ends with a full stop — no +execution occurs without explicit user confirmation.** + +--- + +### PHASE 1 — SCAN + +``` +1. Check for optional inputs: asset_filter, funding_threshold, num_signals +2. Run: onchainos signal list --chain arbitrum --platform hyperliquid +3. Parse all funding rates from output +4. If asset_filter is set → retain only matching assets +5. Apply threshold filter: keep only markets where |funding rate APR| > funding_threshold +6. If no markets pass the filter → go to "No Signals" response (see below) +7. Sort remaining markets by |funding rate| descending +8. Retain top N markets where N = num_signals +9. For each retained market: run onchainos market price to fetch price + OI context +``` + +--- + +### PHASE 2 — ANALYZE + +``` +For each signal candidate (top N assets from Phase 1): + +10. Classify direction: + - funding rate > 0 → longs overcrowded → SHORT signal (mean reversion: shorts collect funding) + - funding rate < 0 → shorts overcrowded → LONG signal (mean reversion: longs collect funding) + +11. Assess crowding severity using hourly funding rate (not APR): + - hourly rate > 0.01% → **Elevated** (moderate signal, worth monitoring) + - hourly rate > 0.03% → **High** (strong signal, crowding is significant) + - hourly rate > 0.05% → **Extreme** (highest conviction, historically unsustainable) + +12. Run smart money check (onchainos signal list --chain arbitrum) + - Smart money opposite to crowd → Bullish confirmation, note as supporting factor + - Smart money aligned with crowd → Note as risk factor + +13. Run kline check for top 1–3 signals + - Ranging / consolidating price action → Favorable for mean reversion + - Strong directional trend → Note as risk factor, lower conviction rating + +14. Build signal card for each (see output format below) +``` + +--- + +### PHASE 3 — PRESENT AND STOP + +``` +15. Present all signal cards to user (ranked #1 to #N) +16. Add a summary line: total markets scanned, signals found, threshold used +17. Include timing note: minutes until next 8h settlement window (if within 90 min, highlight urgently) +18. Ask: "Would you like to act on any of these signals?" +19. *** STOP: DO NOT EXECUTE WITHOUT USER CONFIRMATION *** + +If user says YES to a signal: +20. Collect: asset, position side, size (USDC), leverage, stop-loss price +21. Present trade summary for user review +22. Ask: "Confirm execution?" — wait for explicit approval +23. Only after confirmed → hand off to hyperliquid-plugin (see Strategy Execution Mode) +24. After execution completes → immediately offer: "Scan for more opportunities?" to + encourage re-engagement and help users capture multiple signals per session. +``` + +--- + +## Signal Card Output Format + +Present each signal in this structured format. Keep language clear and +non-technical enough for any trader to understand. + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +šŸ“” SIGNAL # — /USD-PERP +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Direction: +Signal Type: Mean Reversion — Funding Rate Extreme +Conviction: + +Funding Rate: <+XX.XX% / -XX.XX%> APR (hourly: <±X.XXXX%>) +Current Price: $ +24h Change: <+X.X% / -X.X%> + +šŸ“Œ Thesis +<2–3 sentences explaining why this is an opportunity. +Example: "BTC perp funding is at +187% APR, meaning longs are paying shorts +at nearly 0.51% every 8 hours. This level of crowding is historically +unsustainable and typically precedes a funding rate normalization. A short +position here collects funding while waiting for the imbalance to unwind."> + +āœ… Supporting Factors +- +- + +āš ļø Risk Factors +- +- + +Entry Context: ~$ (current market) +Watch Level: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +--- + +## No Signals Response + +When no markets pass the threshold filter, respond with: + +``` +No statistically significant funding rate imbalances detected above the +±% APR threshold at this time. + +Markets scanned: +Signals found: 0 +Threshold used: ±% APR + +This suggests Hyperliquid perp markets are currently near equilibrium. +Funding rates tend to spike most around the 8-hour settlement windows +(00:00, 08:00, 16:00 UTC). Consider checking back then, or lower the +threshold to see moderate-level signals. +``` + +--- + +## Strategy Execution Mode + +This section is only reached after the user has explicitly confirmed a trade. +Read-only scanning is always the default behavior. Execution is strictly +optional and user-triggered — the agent must never initiate execution +autonomously under any circumstances. + +**Behavior rules:** +- Scanning and signal generation: always autonomous, always read-only +- Execution: optional, requires hard confirmation from the user +- NEVER proceed to execution based on inference, assumption, or partial confirmation +- NEVER re-trigger a previously confirmed trade without a new explicit confirmation + +Once the user confirms, pass control to `hyperliquid-plugin` with the confirmed +parameters and the required `--strategy-id` attribution flag: + +```bash +# Short execution +hyperliquid-plugin place-order \ + --side short \ + --asset \ + --size \ + --leverage \ + --stop-loss \ + --strategy-id hyperliquid-funding-rate-scout \ + --confirm + +# Long execution +hyperliquid-plugin place-order \ + --side long \ + --asset \ + --size \ + --leverage \ + --stop-loss \ + --strategy-id hyperliquid-funding-rate-scout \ + --confirm +``` + +> **Note**: `--strategy-id hyperliquid-funding-rate-scout` must be included on +> every execution call for leaderboard attribution. Never omit this flag. + +Before calling the execution plugin, always present this confirmation block: + +``` +šŸ“‹ TRADE CONFIRMATION +Asset: /USD-PERP +Side: +Size: USDC +Leverage: x +Stop-Loss: $ +Est. Liquidation: $ +Strategy ID: hyperliquid-funding-rate-scout + +Proceed with execution? (yes / no) +``` + +Only call `hyperliquid-plugin` after receiving an explicit "yes". + +--- + +## Timing Intelligence + +Include this context when presenting signals, especially for high-conviction ones: + +- Hyperliquid funding settles every **8 hours**: 00:00, 08:00, 16:00 UTC +- Funding rates tend to spike or accelerate in the **60–90 minutes before settlement** +- The highest-conviction scan window is **30–60 minutes before each settlement** +- Post-settlement, rates often reset — a new scan is recommended after each window + +**Urgency rules for the agent:** +- If current time is within 90 minutes of a settlement window → prepend signals with: + `ā° Settlement in ~ minutes — funding rates are near peak. High-conviction window.` +- If current time is within 30 minutes → prepend with: + `šŸ”“ Settlement in minutes — act now or wait for next window after rates reset.` +- After a settlement passes → proactively suggest: + `Funding rates just settled. This is a good time to scan for new imbalances building up.` + +If the user's current time is near a settlement window, proactively mention it. + +--- + +## Error Handling + +| Error | Cause | Resolution | +|-------|-------|------------| +| No markets returned from scan | API unavailable or network issue | Wait 10 seconds, retry scan | +| `Platform not found` | Hyperliquid flag not supported | Remove `--platform hyperliquid` and filter manually | +| All rates in neutral zone | Markets at equilibrium | Inform user, suggest lower threshold or retry near settlement window | +| Asset not found in price fetch | Delisted or low-liquidity market | Skip price fetch for that asset, note in signal card | +| Rate limit error | Too many requests in short window | Wait 15 seconds before retrying | +| kline data unavailable | New listing or no history | Skip kline analysis, note absence in signal card | +| Hyperliquid Plugin not installed | Only needed at execution stage | Run `npx skills add okx/plugin-store --skill hyperliquid-plugin` | + +--- + +## Security Notices + +- **Risk Level**: STANDARD — read-only by default; optional execution mode places leveraged perpetual trades +- Scanning and analysis require no wallet connection and access no private keys +- Execution only occurs via `hyperliquid-plugin` after explicit user confirmation +- All execution calls include `--strategy-id hyperliquid-funding-rate-scout` for leaderboard attribution +- All on-chain writes (if triggered) are handled securely by Onchain OS +- This plugin does not store, log, or transmit any user data or wallet addresses + +--- + +## Skill Routing + +- To execute a confirmed trade → `hyperliquid-plugin` +- To DCA into an identified position → `hyperliquid-dca-bot` +- To check wallet balance before execution → `onchainos wallet balance --chain arbitrum` +- To verify token contract security → `okx-security` skill +- To view current portfolio and PnL → `okx-wallet-portfolio` skill +- For prediction market opportunities → Polymarket plugin \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/SUMMARY.md b/skills/hyperliquid-funding-rate-scout/SUMMARY.md new file mode 100644 index 000000000..15100fd2c --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/SUMMARY.md @@ -0,0 +1,52 @@ +# hyperliquid-funding-rate-scout + +## Overview + +Hyperliquid Funding Rate Scout is a strategy skill that identifies high-probability +mean reversion opportunities by scanning perpetual futures markets on Hyperliquid +for statistically significant funding rate imbalances and crowded positioning. + +Core operations: + +- Scan all active Hyperliquid perpetual markets for extreme funding rate conditions +- Rank signals by deviation severity (Elevated / High / Extreme) using hourly rate thresholds +- Generate structured signal cards with direction, thesis, supporting factors, and risk notes +- Cross-reference funding extremes with smart money positioning and recent price action +- Hand off confirmed trades to `hyperliquid-plugin` with full strategy attribution + +Tags: `hyperliquid` `funding-rate` `mean-reversion` `strategy` `perpetuals` `signal` `crowded-positioning` + +## Prerequisites + +- No IP or region restrictions +- Supported chain: Arbitrum (Hyperliquid perpetuals are settled on Arbitrum) +- Supported markets: All active Hyperliquid perpetual futures pairs +- `onchainos` CLI installed: `npx skills add okx/onchainos-skills` +- `hyperliquid-plugin` installed (required only if user confirms execution): + `npx skills add okx/plugin-store --skill hyperliquid-plugin` +- No wallet connection required for scanning and signal generation +- A funded wallet with USDC on Arbitrum is required only if proceeding to execution + +## Quick Start + +1. **Request a scan**: Ask the agent "find me funding rate opportunities" or "what are + the most extreme funding rates on Hyperliquid right now?" The agent will fetch + live data across all active perp markets and filter by the default ±50% APR threshold. + +2. **Review signal cards**: The agent returns ranked signal cards — each showing the + asset, direction (LONG or SHORT), conviction level, funding rate, mean reversion + thesis, supporting factors, and risk notes. Signals are sorted by funding rate + deviation, highest conviction first. + +3. **Optional — filter results**: Narrow the scan by providing an asset filter + (e.g. "only BTC and ETH"), a custom threshold (e.g. "only show above 100% APR"), + or a signal count (e.g. "top 3 signals only"). + +4. **Confirm a trade (optional)**: If you want to act on a signal, tell the agent + which one. It will collect your position size, leverage, and stop-loss, then + present a full trade summary for your review. No trade is placed until you + explicitly confirm. + +5. **Execution handoff**: After confirmation, the agent calls `hyperliquid-plugin` + with `--strategy-id hyperliquid-funding-rate-scout` for leaderboard attribution. + Autonomous execution is never permitted — every trade requires explicit approval. \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/plugin.yaml b/skills/hyperliquid-funding-rate-scout/plugin.yaml new file mode 100644 index 000000000..d152a37c4 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/plugin.yaml @@ -0,0 +1,35 @@ +schema_version: 1 +name: hyperliquid-funding-rate-scout +version: "1.0.0" +description: "Identify high-probability mean reversion opportunities by scanning Hyperliquid perpetual futures for statistically significant funding rate imbalances and crowded positioning" +author: + name: "Ritesh" + github: "Ritesh59697" +license: MIT +category: strategy +risk_level: standard +tags: + - hyperliquid + - funding-rate + - mean-reversion + - strategy + - perpetuals + - signal + - analytics + - crowded-positioning + +dependent_plugin: + - name: hyperliquid-plugin + version: "^0.x.0" + +components: + skill: + dir: "." + binary: + language: rust + entry: src/main.rs + build: cargo build --release + bin: target/release/hyperliquid-funding-rate-scout + +api_calls: + - api.hyperliquid.xyz \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/src/api.rs b/skills/hyperliquid-funding-rate-scout/src/api.rs new file mode 100644 index 000000000..02207fac0 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/src/api.rs @@ -0,0 +1,63 @@ +use anyhow::{Context, Result}; +use serde_json::Value; +use std::process::Command; + +const HL_API: &str = "https://api.hyperliquid.xyz/info"; + +/// POST to the Hyperliquid info endpoint using curl. +/// This avoids any HTTP library dependency issues. +pub fn info_post(body: &Value) -> Result { + let body_str = serde_json::to_string(body)?; + + let output = Command::new("curl") + .args([ + "-s", + "-X", "POST", + HL_API, + "-H", "Content-Type: application/json", + "-d", &body_str, + "--max-time", "15", + ]) + .output() + .context("Failed to run curl — is curl installed?")?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("curl failed: {}", stderr); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + if stdout.trim().is_empty() { + anyhow::bail!("Hyperliquid API returned empty response. Check network connection."); + } + + serde_json::from_str(&stdout) + .with_context(|| format!("Failed to parse API response as JSON: {}", &stdout[..stdout.len().min(200)])) +} + +/// Fetch meta + asset contexts (funding rates, open interest, mark prices). +pub fn get_meta_and_asset_ctxs() -> Result<(Value, Vec)> { + let resp = info_post(&serde_json::json!({ "type": "metaAndAssetCtxs" }))?; + + let arr = resp + .as_array() + .context("metaAndAssetCtxs: expected array")? + .clone(); + + if arr.len() < 2 { + anyhow::bail!("metaAndAssetCtxs: expected [meta, ctxs] array"); + } + + let meta = arr[0].clone(); + let ctxs = arr[1] + .as_array() + .context("metaAndAssetCtxs: ctxs is not an array")? + .clone(); + + Ok((meta, ctxs)) +} + +/// Fetch all mid prices. +pub fn get_all_mids() -> Result { + info_post(&serde_json::json!({ "type": "allMids" })) +} \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/src/main.rs b/skills/hyperliquid-funding-rate-scout/src/main.rs new file mode 100644 index 000000000..de1f2f3e1 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/src/main.rs @@ -0,0 +1,56 @@ +mod api; +mod scout; +mod signal; +mod render; + +use anyhow::Result; +use clap::Parser; + +/// Scan Hyperliquid perpetual futures markets for extreme funding rate imbalances +/// and generate mean reversion signal cards. +#[derive(Parser, Debug)] +#[command(name = "hyperliquid-funding-rate-scout")] +#[command(version = "1.0.0")] +#[command(about = "Scan Hyperliquid perp markets for extreme funding rate imbalances")] +pub struct Args { + /// Minimum hourly funding rate threshold in % (default: 0.01) + #[arg(short, long, default_value = "0.01")] + pub threshold: f64, + + /// Maximum number of signals to return (default: 5) + #[arg(short, long, default_value = "5")] + pub limit: usize, + + /// Filter to specific assets e.g. --asset BTC --asset ETH + #[arg(short, long)] + pub asset: Vec, + + /// Output raw JSON instead of formatted signal cards + #[arg(long)] + pub json: bool, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + // Convert threshold from percentage to decimal (0.01% -> 0.0001) + let threshold_decimal = args.threshold / 100.0; + + let asset_filter: Vec = args.asset.iter().map(|a| a.to_uppercase()).collect(); + + let mut scout = scout::Scout::new(threshold_decimal, args.limit, asset_filter); + let signals = scout.scan()?; + + if signals.is_empty() { + render::render_no_signals(threshold_decimal, scout.last_scanned_count); + return Ok(()); + } + + if args.json { + println!("{}", serde_json::to_string_pretty(&signals)?); + } else { + render::render_signal_cards(&signals, scout.last_scanned_count, threshold_decimal); + } + + Ok(()) +} \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/src/render.rs b/skills/hyperliquid-funding-rate-scout/src/render.rs new file mode 100644 index 000000000..13965eec1 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/src/render.rs @@ -0,0 +1,134 @@ +use crate::signal::{Direction, SignalCard}; +use chrono::Timelike; + +const DIVIDER: &str = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; + +pub fn render_signal_cards(signals: &[SignalCard], total_scanned: usize, threshold: f64) { + let threshold_pct = threshold * 100.0; + let now = chrono::Utc::now(); + + println!("\nšŸ“” Hyperliquid Funding Rate Scout"); + println!("ā° Scan time: {} UTC", now.format("%Y-%m-%d %H:%M:%S")); + println!( + "šŸ“Š Markets scanned: {} | Signals found: {} | Threshold: >{:.4}% hourly\n", + total_scanned, + signals.len(), + threshold_pct + ); + + // Settlement urgency warning + if let Some(warning) = get_settlement_warning() { + println!("{}\n", warning); + } + + for signal in signals { + render_card(signal); + } + + println!("\n{}", DIVIDER); + println!("āœ‹ STOP: DO NOT EXECUTE WITHOUT USER CONFIRMATION"); + println!("{}", DIVIDER); + println!("\nWould you like to act on any of these signals?"); + println!("If yes, provide: asset, position size (USDC), leverage, and stop-loss price.\n"); +} + +fn render_card(signal: &SignalCard) { + let dir_emoji = match signal.direction { + Direction::Short => "šŸ“‰", + Direction::Long => "šŸ“ˆ", + }; + + let conviction_emoji = signal.conviction.emoji(); + + println!("\n{}", DIVIDER); + println!("{} SIGNAL #{} — {}/USD-PERP", dir_emoji, signal.rank, signal.asset); + println!("{}", DIVIDER); + println!("Direction: {}", signal.direction); + println!("Signal Type: Mean Reversion — Funding Rate Extreme"); + println!("Conviction: {} {}", conviction_emoji, signal.conviction); + println!(); + println!( + "Funding Rate: {} hourly ({} APR)", + signal.hourly_rate_pct, signal.apr_pct + ); + println!("Current Price: {}", signal.current_price); + println!("Next Settlement: {}", signal.next_settlement); + println!(); + println!("šŸ“Œ Thesis"); + println!("{}", wrap_text(&signal.thesis, 70)); + println!(); + println!("āœ… Supporting Factors"); + for f in &signal.supporting_factors { + println!(" • {}", f); + } + println!(); + println!("āš ļø Risk Factors"); + for r in &signal.risk_factors { + println!(" • {}", r); + } + println!(); + println!("Entry Context: ~{} (current market)", signal.current_price); +} + +pub fn render_no_signals(threshold: f64, total_scanned: usize) { + let threshold_pct = threshold * 100.0; + println!("\nšŸ“” Hyperliquid Funding Rate Scout"); + println!("\nNo statistically significant funding rate imbalances detected."); + println!(); + println!("Markets scanned: {}", total_scanned); + println!("Signals found: 0"); + println!("Threshold used: >{:.4}% hourly", threshold_pct); + println!(); + println!("Markets appear to be near equilibrium."); + println!("Funding rates spike most around settlement windows: 00:00, 08:00, 16:00 UTC."); + println!("Consider checking back then, or lower --threshold to see moderate signals.\n"); +} + +fn get_settlement_warning() -> Option { + let now = chrono::Utc::now(); + let current_minutes = now.hour() * 60 + now.minute(); + let settlement_minutes = [0u32, 480, 960]; + + let next = settlement_minutes + .iter() + .find(|&&m| m > current_minutes) + .copied() + .unwrap_or(settlement_minutes[0] + 24 * 60); + + let diff = next - current_minutes; + + if diff <= 30 { + Some(format!( + "šŸ”“ Settlement in {} minutes — funding rates at peak. Act now or wait for next window.", + diff + )) + } else if diff <= 90 { + Some(format!( + "ā° Settlement in ~{} minutes — high-conviction scan window approaching.", + diff + )) + } else { + None + } +} + +fn wrap_text(text: &str, width: usize) -> String { + let mut lines = Vec::new(); + let mut line = String::new(); + + for word in text.split_whitespace() { + if !line.is_empty() && line.len() + 1 + word.len() > width { + lines.push(line.clone()); + line.clear(); + } + if !line.is_empty() { + line.push(' '); + } + line.push_str(word); + } + if !line.is_empty() { + lines.push(line); + } + + lines.join("\n") +} \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/src/scout.rs b/skills/hyperliquid-funding-rate-scout/src/scout.rs new file mode 100644 index 000000000..cea820b01 --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/src/scout.rs @@ -0,0 +1,147 @@ +use anyhow::Result; +use crate::api; +use crate::signal::{ + Direction, SignalCard, + build_thesis, build_supporting_factors, build_risk_factors, classify_conviction, +}; +use chrono::Timelike; + +pub struct Scout { + threshold: f64, + limit: usize, + asset_filter: Vec, + pub last_scanned_count: usize, +} + +impl Scout { + pub fn new(threshold: f64, limit: usize, asset_filter: Vec) -> Self { + Self { threshold, limit, asset_filter, last_scanned_count: 0 } + } + + pub fn scan(&mut self) -> Result> { + let (meta, ctxs) = api::get_meta_and_asset_ctxs()?; + let mids = api::get_all_mids()?; + + let universe = meta["universe"] + .as_array() + .ok_or_else(|| anyhow::anyhow!("Missing universe in meta"))?; + + self.last_scanned_count = universe.len(); + + let mut candidates: Vec<(String, f64, f64)> = Vec::new(); + + for (i, asset_meta) in universe.iter().enumerate() { + let coin = match asset_meta["name"].as_str() { + Some(c) => c.to_string(), + None => continue, + }; + + if !self.asset_filter.is_empty() + && !self.asset_filter.contains(&coin.to_uppercase()) + { + continue; + } + + let ctx = match ctxs.get(i) { + Some(c) => c, + None => continue, + }; + + let funding_rate: f64 = match ctx["funding"].as_str() { + Some(f) => match f.parse() { + Ok(v) => v, + Err(_) => continue, + }, + None => continue, + }; + + if funding_rate.abs() < self.threshold { + continue; + } + + let price = mids[&coin] + .as_str() + .and_then(|p| p.parse::().ok()) + .unwrap_or(0.0); + + candidates.push((coin, funding_rate, price)); + } + + candidates.sort_by(|a, b| { + b.1.abs().partial_cmp(&a.1.abs()) + .unwrap_or(std::cmp::Ordering::Equal) + }); + candidates.truncate(self.limit); + + let next_settlement = get_next_settlement(); + + Ok(candidates + .into_iter() + .enumerate() + .map(|(i, (coin, rate, price))| { + build_signal_card(i + 1, coin, rate, price, &next_settlement) + }) + .collect()) + } +} + +fn build_signal_card( + rank: usize, + coin: String, + rate: f64, + price: f64, + next_settlement: &str, +) -> SignalCard { + let abs_rate = rate.abs(); + let direction = if rate > 0.0 { Direction::Short } else { Direction::Long }; + let conviction = classify_conviction(abs_rate); + + let hourly_rate_pct = format!( + "{}{:.4}%", + if rate >= 0.0 { "+" } else { "" }, + rate * 100.0 + ); + let apr = rate * 3.0 * 365.0 * 100.0; + let apr_pct = format!("{}{:.1}%", if apr >= 0.0 { "+" } else { "" }, apr); + + let current_price = if price > 0.0 { + format!("${:.4}", price) + } else { + "N/A".to_string() + }; + + let thesis = build_thesis(&coin, rate, abs_rate, &conviction); + let supporting_factors = build_supporting_factors(&direction, &conviction); + let risk_factors = build_risk_factors(&direction, &conviction); + + SignalCard { + rank, + asset: coin, + direction, + conviction, + hourly_rate: rate, + hourly_rate_pct, + apr_pct, + current_price: current_price.clone(), + thesis, + supporting_factors, + risk_factors, + entry_context: current_price, + next_settlement: next_settlement.to_string(), + } +} + +fn get_next_settlement() -> String { + let now = chrono::Utc::now(); + let current_minutes = now.hour() * 60 + now.minute(); + let settlement_minutes = [0u32, 480, 960]; + + let next = settlement_minutes + .iter() + .find(|&&m| m > current_minutes) + .copied() + .unwrap_or(settlement_minutes[0] + 24 * 60); + + let diff = next - current_minutes; + format!("in {}h {}m", diff / 60, diff % 60) +} \ No newline at end of file diff --git a/skills/hyperliquid-funding-rate-scout/src/signal.rs b/skills/hyperliquid-funding-rate-scout/src/signal.rs new file mode 100644 index 000000000..414544ebd --- /dev/null +++ b/skills/hyperliquid-funding-rate-scout/src/signal.rs @@ -0,0 +1,183 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SignalCard { + pub rank: usize, + pub asset: String, + pub direction: Direction, + pub conviction: Conviction, + pub hourly_rate: f64, + pub hourly_rate_pct: String, + pub apr_pct: String, + pub current_price: String, + pub thesis: String, + pub supporting_factors: Vec, + pub risk_factors: Vec, + pub entry_context: String, + pub next_settlement: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum Direction { + Long, + Short, +} + +impl std::fmt::Display for Direction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Direction::Long => write!(f, "LONG"), + Direction::Short => write!(f, "SHORT"), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum Conviction { + Elevated, + High, + Extreme, +} + +impl std::fmt::Display for Conviction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Conviction::Elevated => write!(f, "Elevated"), + Conviction::High => write!(f, "High"), + Conviction::Extreme => write!(f, "Extreme"), + } + } +} + +impl Conviction { + pub fn emoji(&self) -> &str { + match self { + Conviction::Elevated => "🟔", + Conviction::High => "🟠", + Conviction::Extreme => "šŸ”“", + } + } +} + +/// Classify conviction from hourly rate (decimal, not percent). +/// Thresholds per SKILL.md: +/// > 0.0005 = Extreme (0.05%) +/// > 0.0003 = High (0.03%) +/// else = Elevated (> 0.01%) +pub fn classify_conviction(abs_rate: f64) -> Conviction { + if abs_rate > 0.0005 { + Conviction::Extreme + } else if abs_rate > 0.0003 { + Conviction::High + } else { + Conviction::Elevated + } +} + +/// Build the mean reversion thesis string. +pub fn build_thesis(coin: &str, rate: f64, abs_rate: f64, conviction: &Conviction) -> String { + let direction_str = if rate > 0.0 { + "longs are paying shorts" + } else { + "shorts are paying longs" + }; + let side = if rate > 0.0 { "short" } else { "long" }; + let squeeze = if rate > 0.0 { + "longs may be forced to close, accelerating a downside move" + } else { + "shorts may be forced to close, accelerating an upside move" + }; + let apr = abs_rate * 3.0 * 365.0 * 100.0; + + let conviction_note = match conviction { + Conviction::Extreme => " This is among the most extreme funding conditions on Hyperliquid.", + Conviction::High => " This level of crowding is historically significant.", + Conviction::Elevated => " This level of crowding historically precedes mean reversion.", + }; + + format!( + "{coin} perp funding is at {rate:.4}% per hour ({apr:.1}% APR), meaning \ + {direction_str} at an unsustainable rate.{conviction_note} \ + A {side} position here collects funding while waiting for the imbalance to unwind — \ + if momentum reverses, {squeeze}.", + coin = coin, + rate = abs_rate * 100.0, + apr = apr, + direction_str = direction_str, + conviction_note = conviction_note, + side = side, + squeeze = squeeze, + ) +} + +/// Build supporting factors list. +pub fn build_supporting_factors(direction: &Direction, conviction: &Conviction) -> Vec { + let mut factors = vec![ + format!( + "Funding rate deviation classified as {} — statistically significant outlier", + conviction + ), + "Mean reversion thesis: overcrowded side typically unwinds toward equilibrium".to_string(), + ]; + + if *conviction == Conviction::Extreme { + factors.push( + "Rate > 0.05% hourly — historically among the top 5% of extreme readings".to_string(), + ); + } + + match direction { + Direction::Short => { + factors.push( + "Longs paying premium incentivizes new shorts to enter and collect".to_string(), + ); + } + Direction::Long => { + factors.push( + "Shorts paying premium incentivizes new longs to enter and collect".to_string(), + ); + } + } + + factors +} + +/// Build risk factors list. +pub fn build_risk_factors(direction: &Direction, conviction: &Conviction) -> Vec { + let mut risks = vec![ + "Strong price momentum can delay or override funding normalization".to_string(), + "High open interest increases risk of volatile liquidation cascade".to_string(), + ]; + + match direction { + Direction::Short => { + risks.push( + "If price continues rising, short position faces mark-to-market loss \ + despite collecting funding" + .to_string(), + ); + } + Direction::Long => { + risks.push( + "If price continues falling, long position faces mark-to-market loss \ + despite collecting funding" + .to_string(), + ); + } + } + + match conviction { + Conviction::Extreme => { + risks.push( + "Extreme rates may persist longer than expected in trending markets".to_string(), + ); + } + _ => { + risks.push( + "Funding may normalize before sufficient premium is collected".to_string(), + ); + } + } + + risks +} \ No newline at end of file