diff --git a/Cargo.lock b/Cargo.lock index 461af89..2bfdba3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -60,6 +71,12 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-compression" version = "0.4.33" @@ -102,6 +119,53 @@ name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +dependencies = [ + "serde_core", +] + +[[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 = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.110", +] [[package]] name = "brotli" @@ -130,6 +194,28 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -158,6 +244,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -172,6 +264,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf 0.12.1", + "serde", +] + [[package]] name = "compression-codecs" version = "0.4.32" @@ -211,6 +314,35 @@ dependencies = [ "version_check", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie 0.18.1", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -221,12 +353,31 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -261,6 +412,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cssparser" version = "0.31.2" @@ -281,9 +442,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn", + "syn 2.0.110", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + [[package]] name = "deranged" version = "0.5.5" @@ -301,7 +468,17 @@ checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", +] + +[[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]] @@ -312,7 +489,16 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", ] [[package]] @@ -383,6 +569,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "yfinance-rs", ] [[package]] @@ -421,6 +608,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.5" @@ -461,6 +654,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futf" version = "0.1.5" @@ -527,7 +726,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -569,6 +768,16 @@ dependencies = [ "byteorder", ] +[[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 = "getopts" version = "0.2.24" @@ -585,8 +794,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -596,9 +807,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -620,12 +833,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "html5ever" version = "0.27.0" @@ -637,7 +865,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -764,7 +992,7 @@ dependencies = [ "hyper 0.14.32", "log", "rustls 0.21.12", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -783,6 +1011,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.4", "tower-service", + "webpki-roots", ] [[package]] @@ -973,7 +1202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -992,6 +1221,41 @@ dependencies = [ "serde", ] +[[package]] +name = "isin" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bcf2544150282ebe712b4615813c1986bb014564928b1c4a4a567402d6bf02" + +[[package]] +name = "iso_country" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20633e788d3948ea7336861fdb09ec247f5dae4267e8f0743fa97de26c28624d" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "iso_currency" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed4b3f0921193400b1df556228bfd917c57c7fa38bda904d552653c5c3b641b" +dependencies = [ + "iso_country", + "proc-macro2", + "quote", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -1032,6 +1296,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -1047,6 +1317,12 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mac" version = "0.1.1" @@ -1109,6 +1385,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "native-tls" version = "0.2.14" @@ -1121,7 +1403,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -1185,7 +1467,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -1206,6 +1488,119 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "paft" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c8f13ab3152b3fc1d596fdc6881ab5f0499647e8f1620b393dd04df3d8abcd" +dependencies = [ + "iso_currency", + "paft-aggregates", + "paft-core", + "paft-domain", + "paft-fundamentals", + "paft-market", + "paft-money", + "paft-utils", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-aggregates" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b122c5099ea444a5d3bdc70963311b22d14ee372ef407c60a84b1b1e446074e7" +dependencies = [ + "chrono", + "paft-domain", + "paft-fundamentals", + "paft-market", + "paft-money", + "serde", +] + +[[package]] +name = "paft-core" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40251d2efd21aa5615927400858c5fa000db5ae6de4cd58320e57b0d2d52336" +dependencies = [ + "chrono", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-domain" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7f9a2d69d11e1324d6b3a7dc1ad62f7f909442531406b8b3052d8eea69546e" +dependencies = [ + "chrono", + "isin", + "paft-core", + "paft-utils", + "regex", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-fundamentals" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d591b958232362a8121e27dc5ef570007f7e64788238b2b1b280701536cf336" +dependencies = [ + "chrono", + "iso_currency", + "paft-core", + "paft-domain", + "paft-money", + "paft-utils", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-market" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4708f3a8b55a06c277b4830fae834c03d3cd75f3407be27d0dc7f6b17863700" +dependencies = [ + "bitflags", + "chrono", + "chrono-tz", + "iso_currency", + "paft-core", + "paft-domain", + "paft-money", + "paft-utils", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-money" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078c12dfca26fe2db783d2940b1a01bcf44832ad46006fdfb54b9eee6727e13d" +dependencies = [ + "iso_currency", + "paft-utils", + "rust_decimal", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "paft-utils" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154451a10bb55eb00f81dfdc6410237ef348ead5b096ae3f186b232a240b3d6" +dependencies = [ + "thiserror 2.0.17", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -1235,6 +1630,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.10.1" @@ -1254,6 +1659,15 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared 0.12.1", +] + [[package]] name = "phf_codegen" version = "0.10.0" @@ -1281,7 +1695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand", + "rand 0.8.5", ] [[package]] @@ -1291,7 +1705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand", + "rand 0.8.5", ] [[package]] @@ -1304,7 +1718,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -1325,6 +1739,15 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1373,6 +1796,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.110", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1382,6 +1824,149 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.110", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "prost-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +dependencies = [ + "prost", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[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 = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.35", + "socket2 0.6.1", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.35", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.1", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -1397,6 +1982,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[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" @@ -1404,8 +1995,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1415,7 +2016,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1427,6 +2038,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rayon" version = "1.11.0" @@ -1456,6 +2076,18 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.13" @@ -1473,6 +2105,15 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.12.24" @@ -1482,6 +2123,8 @@ dependencies = [ "async-compression", "base64 0.22.1", "bytes", + "cookie 0.18.1", + "cookie_store", "encoding_rs", "futures-core", "futures-util", @@ -1499,6 +2142,8 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls 0.23.35", "rustls-pki-types", "serde", "serde_json", @@ -1506,6 +2151,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls 0.26.4", "tokio-util", "tower", "tower-http", @@ -1514,6 +2160,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] @@ -1530,6 +2177,57 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "1.1.2" @@ -1562,6 +2260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki 0.103.8", "subtle", @@ -1577,7 +2276,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", - "security-framework", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", ] [[package]] @@ -1595,6 +2306,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ + "web-time", "zeroize", ] @@ -1652,7 +2364,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761fb705fdf625482d2ed91d3f0559dcfeab2798fe2771c69560a774865d0802" dependencies = [ - "ahash", + "ahash 0.8.12", "cssparser", "ego-tree", "getopts", @@ -1672,6 +2384,12 @@ dependencies = [ "untrusted", ] +[[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.11.1" @@ -1679,7 +2397,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -1741,7 +2472,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -1778,6 +2509,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1808,6 +2550,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "0.3.11" @@ -1889,6 +2637,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[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.110" @@ -1917,7 +2676,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -1927,7 +2686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -1941,6 +2700,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.23.0" @@ -1971,7 +2736,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", ] [[package]] @@ -1982,7 +2756,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", ] [[package]] @@ -2035,6 +2820,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +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.48.0" @@ -2060,7 +2860,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -2093,6 +2893,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -2106,6 +2922,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "tower" version = "0.5.2" @@ -2170,7 +3016,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -2218,6 +3064,31 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.3.1", + "httparse", + "log", + "rand 0.9.2", + "rustls 0.23.35", + "rustls-pki-types", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -2266,6 +3137,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" @@ -2353,7 +3234,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.110", "wasm-bindgen-shared", ] @@ -2376,6 +3257,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webdriver" version = "0.50.0" @@ -2390,12 +3281,21 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "unicode-segmentation", "url", ] +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -2417,7 +3317,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -2428,7 +3328,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -2622,6 +3522,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.46.0" @@ -2634,6 +3543,39 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yfinance-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3617180fa13fc4c7a5702df69c202ba4566831b83ec212b85918b76872991b" +dependencies = [ + "base64 0.22.1", + "chrono", + "chrono-tz", + "futures", + "futures-util", + "paft", + "prost", + "prost-build", + "reqwest", + "rust_decimal", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-tungstenite", + "url", +] + [[package]] name = "yoke" version = "0.8.1" @@ -2653,7 +3595,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", "synstructure", ] @@ -2674,7 +3616,7 @@ checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] [[package]] @@ -2694,7 +3636,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", "synstructure", ] @@ -2734,5 +3676,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.110", ] diff --git a/Cargo.toml b/Cargo.toml index f46ff06..c59a710 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ tokio = { version = "1.38", features = ["full"] } reqwest = { version = "0.12", features = ["json", "gzip", "brotli", "deflate"] } scraper = "0.19" # HTML parsing for Yahoo earnings pages fantoccini = { version = "0.20", features = ["rustls-tls"] } # Headless Chrome for finanzen.net +yfinance-rs = "0.7.2" # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/src/continents.json b/data/continents.json similarity index 100% rename from src/continents.json rename to data/continents.json diff --git a/src/countries.json b/data/countries.json similarity index 100% rename from src/countries.json rename to data/countries.json diff --git a/data/exchanges.json b/data/exchanges.json new file mode 100644 index 0000000..af0c4d0 --- /dev/null +++ b/data/exchanges.json @@ -0,0 +1,260 @@ +{ + "exchanges": [ + { + "mic": "XNYS", + "name": "New York Stock Exchange", + "country": "United States", + "city": "New York City", + "market_cap_trillion_usd": 30.92, + "timezone": "America/New_York", + "tz_offset": "-05:00", + "dst": "Mar–Nov", + "open_local": "09:30", + "close_local": "16:00", + "lunch_break": false, + "open_utc": "14:30", + "close_utc": "21:00", + "currency": "USD" + }, + { + "mic": "XNAS", + "name": "Nasdaq", + "country": "United States", + "city": "New York City", + "market_cap_trillion_usd": 31.96, + "timezone": "America/New_York", + "tz_offset": "-05:00", + "dst": "Mar–Nov", + "open_local": "09:30", + "close_local": "16:00", + "lunch_break": false, + "open_utc": "14:30", + "close_utc": "21:00", + "currency": "USD" + }, + { + "mic": "XSHG", + "name": "Shanghai Stock Exchange", + "country": "China", + "city": "Shanghai", + "market_cap_trillion_usd": 7.96, + "timezone": "Asia/Shanghai", + "tz_offset": "+08:00", + "dst": null, + "open_local": "09:30", + "close_local": "15:00", + "lunch_break": "11:30–13:00", + "open_utc": "01:30", + "close_utc": "07:00", + "currency": "CNY" + }, + { + "mic": "XJPX", + "name": "Japan Exchange Group (Tokyo Stock Exchange)", + "country": "Japan", + "city": "Tokyo", + "market_cap_trillion_usd": 7.06, + "timezone": "Asia/Tokyo", + "tz_offset": "+09:00", + "dst": null, + "open_local": "09:00", + "close_local": "15:00", + "lunch_break": "11:30–12:30", + "open_utc": "00:00", + "close_utc": "06:00", + "currency": "JPY" + }, + { + "mic": "XHKG", + "name": "Hong Kong Stock Exchange", + "country": "Hong Kong", + "city": "Hong Kong", + "market_cap_trillion_usd": 6.41, + "timezone": "Asia/Hong_Kong", + "tz_offset": "+08:00", + "dst": null, + "open_local": "09:30", + "close_local": "16:00", + "lunch_break": "12:00–13:00", + "open_utc": "01:30", + "close_utc": "08:00", + "currency": "HKD" + }, + { + "mic": "XAMS", + "name": "Euronext Amsterdam", + "country": "Netherlands", + "city": "Amsterdam", + "market_cap_trillion_usd": 5.61, + "timezone": "Europe/Amsterdam", + "tz_offset": "+01:00", + "dst": "Mar–Oct", + "open_local": "09:00", + "close_local": "17:30", + "lunch_break": false, + "open_utc": "08:00", + "close_utc": "16:30", + "currency": "EUR" + }, + { + "mic": "XBSE", + "name": "Bombay Stock Exchange", + "country": "India", + "city": "Mumbai", + "market_cap_trillion_usd": 5.25, + "timezone": "Asia/Kolkata", + "tz_offset": "+05:30", + "dst": null, + "open_local": "09:15", + "close_local": "15:30", + "lunch_break": false, + "open_utc": "03:45", + "close_utc": "10:00", + "currency": "INR" + }, + { + "mic": "XNSE", + "name": "National Stock Exchange of India", + "country": "India", + "city": "Mumbai", + "market_cap_trillion_usd": 5.32, + "timezone": "Asia/Kolkata", + "tz_offset": "+05:30", + "dst": null, + "open_local": "09:15", + "close_local": "15:d30", + "lunch_break": false, + "open_utc": "03:45", + "close_utc": "10:00", + "currency": "INR" + }, + { + "mic": "XSHE", + "name": "Shenzhen Stock Exchange", + "country": "China", + "city": "Shenzhen", + "market_cap_trillion_usd": 5.11, + "timezone": "Asia/Shanghai", + "tz_offset": "+08:00", + "dst": null, + "open_local": "09:30", + "close_local": "15:00", + "lunch_break": "11:30–13:00", + "open_utc": "01:30", + "close_utc": "07:00", + "currency": "CNY" + }, + { + "mic": "XTSE", + "name": "Toronto Stock Exchange", + "country": "Canada", + "city": "Toronto", + "market_cap_trillion_usd": 4.00, + "timezone": "America/Toronto", + "tz_offset": "-05:00", + "dst": "Mar–Nov", + "open_local": "09:30", + "close_local": "16:00", + "lunch_break": false, + "open_utc": "14:30", + "close_utc": "21:00", + "currency": "CAD" + }, + { + "mic": "XLON", + "name": "London Stock Exchange", + "country": "United Kingdom", + "city": "London", + "market_cap_trillion_usd": 3.14, + "timezone": "Europe/London", + "tz_offset": "+00:00", + "dst": "Mar–Oct", + "open_local": "08:00", + "close_local": "16:30", + "lunch_break": false, + "open_utc": "08:00", + "close_utc": "16:30", + "currency": "GBP" + }, + { + "mic": "XTAI", + "name": "Taiwan Stock Exchange", + "country": "Taiwan", + "city": "Taipei", + "market_cap_trillion_usd": 2.87, + "timezone": "Asia/Taipei", + "tz_offset": "+08:00", + "dst": null, + "open_local": "09:00", + "close_local": "13:30", + "lunch_break": false, + "open_utc": "01:00", + "close_utc": "05:30", + "currency": "TWD" + }, + { + "mic": "XSAU", + "name": "Saudi Exchange (Tadawul)", + "country": "Saudi Arabia", + "city": "Riyadh", + "market_cap_trillion_usd": 2.73, + "timezone": "Asia/Riyadh", + "tz_offset": "+03:00", + "dst": null, + "open_local": "10:00", + "close_local": "15:00", + "lunch_break": false, + "open_utc": "07:00", + "close_utc": "12:00", + "currency": "SAR" + }, + { + "mic": "XFRA", + "name": "Deutsche Börse (Xetra)", + "country": "Germany", + "city": "Frankfurt", + "market_cap_trillion_usd": 2.04, + "timezone": "Europe/Berlin", + "tz_offset": "+01:00", + "dst": "Mar–Oct", + "open_local": "09:00", + "close_local": "17:30", + "lunch_break": false, + "open_utc": "08:00", + "close_utc": "16:30", + "currency": "EUR" + }, + { + "mic": "XSWX", + "name": "SIX Swiss Exchange", + "country": "Switzerland", + "city": "Zürich", + "market_cap_trillion_usd": 1.97, + "timezone": "Europe/Zurich", + "tz_offset": "+01:00", + "dst": "Mar–Oct", + "open_local": "09:00", + "close_local": "17:30", + "lunch_break": false, + "open_utc": "08:00", + "close_utc": "16:30", + "currency": "CHF" + }, + { + "mic": "XASX", + "name": "Australian Securities Exchange", + "country": "Australia", + "city": "Sydney", + "market_cap_trillion_usd": 1.89, + "timezone": "Australia/Sydney", + "tz_offset": "+10:00", + "dst": "Oct–Apr", + "open_local": "10:00", + "close_local": "16:00", + "lunch_break": false, + "open_utc": "00:00", + "close_utc": "06:00", + "currency": "AUD" + } + ] +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index fc02ec4..cc0a2c1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,7 @@ impl Default for Config { fn default() -> Self { Self { economic_start_date: "2007-02-13".to_string(), - corporate_start_date: "2010-01-01".to_string(), + corporate_start_date: "2007-01-01".to_string(), economic_lookahead_months: 3, } } diff --git a/src/corporate/helpers.rs b/src/corporate/helpers.rs new file mode 100644 index 0000000..9d9933c --- /dev/null +++ b/src/corporate/helpers.rs @@ -0,0 +1,52 @@ +// src/corporate/helpers.rs +use super::types::*; +use chrono::{Local, NaiveDate}; +use std::collections::{HashMap, HashSet}; + +pub fn event_key(e: &CompanyEvent) -> String { + format!("{}|{}|{}", e.ticker, e.date, e.time) +} + +pub fn detect_changes(old: &CompanyEvent, new: &CompanyEvent, today: &str) -> Vec { + let mut changes = Vec::new(); + let ts = Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); + + if new.date.as_str() <= today { return changes; } + + if old.time != new.time { + changes.push(CompanyEventChange { + ticker: new.ticker.clone(), + date: new.date.clone(), + field_changed: "time".to_string(), + old_value: old.time.clone(), + new_value: new.time.clone(), + detected_at: ts.clone(), + }); + } + + if old.eps_forecast != new.eps_forecast { + changes.push(CompanyEventChange { + ticker: new.ticker.clone(), + date: new.date.clone(), + field_changed: "eps_forecast".to_string(), + old_value: format!("{:?}", old.eps_forecast), + new_value: format!("{:?}", new.eps_forecast), + detected_at: ts.clone(), + }); + } + + if old.eps_actual != new.eps_actual { + changes.push(CompanyEventChange { + ticker: new.ticker.clone(), + date: new.date.clone(), + field_changed: "eps_actual".to_string(), + old_value: format!("{:?}", old.eps_actual), + new_value: format!("{:?}", new.eps_actual), + detected_at: ts.clone(), + }); + } + + // Add similar for revenue if applicable + + changes +} \ No newline at end of file diff --git a/src/corporate/mod.rs b/src/corporate/mod.rs index bf1e410..b762e48 100644 --- a/src/corporate/mod.rs +++ b/src/corporate/mod.rs @@ -3,6 +3,7 @@ pub mod types; pub mod scraper; pub mod storage; pub mod update; +pub mod helpers; pub use types::*; pub use update::run_full_update; \ No newline at end of file diff --git a/src/corporate/scraper.rs b/src/corporate/scraper.rs index f7c23f7..12cd53b 100644 --- a/src/corporate/scraper.rs +++ b/src/corporate/scraper.rs @@ -2,8 +2,10 @@ use super::types::{CompanyEvent, CompanyPrice}; use fantoccini::{Client, Locator}; use scraper::{Html, Selector}; -use chrono::{NaiveDate, Datelike}; +use chrono::{NaiveDate}; use tokio::time::{sleep, Duration}; +use yfinance_rs::{YfClient, Ticker, Range, Interval}; +use yfinance_rs::core::conversions::money_to_f64; const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"; @@ -54,9 +56,9 @@ pub async fn fetch_earnings_history(client: &Client, ticker: &str) -> anyhow::Re let cols: Vec = row.select(&Selector::parse("td").unwrap()) .map(|td| td.text().collect::>().join(" ").trim().to_string()) .collect(); - if cols.len() < 6 { continue; } + if cols.len() < 4 { continue; } - let full_date = &cols[2]; + let full_date = &cols[0]; let parts: Vec<&str> = full_date.split(" at ").collect(); let raw_date = parts[0].trim(); let time_str = if parts.len() > 1 { parts[1].trim() } else { "" }; @@ -66,8 +68,8 @@ pub async fn fetch_earnings_history(client: &Client, ticker: &str) -> anyhow::Re Err(_) => continue, }; - let eps_forecast = parse_float(&cols[3]); - let eps_actual = if cols[4] == "-" { None } else { parse_float(&cols[4]) }; + let eps_forecast = parse_float(&cols[1]); + let eps_actual = if cols[2] == "-" { None } else { parse_float(&cols[2]) }; let surprise_pct = if let (Some(f), Some(a)) = (eps_forecast, eps_actual) { if f.abs() > 0.001 { Some((a - f) / f.abs() * 100.0) } else { None } @@ -85,7 +87,7 @@ pub async fn fetch_earnings_history(client: &Client, ticker: &str) -> anyhow::Re ticker: ticker.to_string(), date: date.format("%Y-%m-%d").to_string(), time, - period: "".to_string(), // No period info available, set to empty + period: "".to_string(), eps_forecast, eps_actual, revenue_forecast: None, @@ -98,38 +100,46 @@ pub async fn fetch_earnings_history(client: &Client, ticker: &str) -> anyhow::Re Ok(events) } -pub async fn fetch_price_history(client: &Client, ticker: &str, start: &str, end: &str) -> anyhow::Result> { - let start_ts = NaiveDate::parse_from_str(start, "%Y-%m-%d")? - .and_hms_opt(0, 0, 0).unwrap().and_utc() - .timestamp(); +pub async fn fetch_price_history( + ticker: &str, + start: &str, + end: &str, +) -> anyhow::Result> { + let client = YfClient::default(); + let tk = Ticker::new(&client, ticker); - let end_ts = NaiveDate::parse_from_str(end, "%Y-%m-%d")? - .succ_opt().unwrap() - .and_hms_opt(0, 0, 0).unwrap().and_utc() - .timestamp(); + // We request the maximum range – the library will automatically respect Yahoo's limits + let history = tk + .history(Some(Range::Max), Some(Interval::D1), true) + .await + .map_err(|e| anyhow::anyhow!("Yahoo Finance API error for {ticker}: {e:?}"))?; - let url = format!( - "https://query1.finance.yahoo.com/v7/finance/download/{ticker}?period1={start_ts}&period2={end_ts}&interval=1d&events=history&includeAdjustedClose=true" - ); + let mut prices = Vec::with_capacity(history.len()); - client.goto(&url).await?; - let csv = client.source().await?; + for candle in history { + let date_str = candle.ts.format("%Y-%m-%d").to_string(); + + // Filter by user-defined start / end + if date_str < (*start).to_string() || date_str > (*end).to_string() { + continue; + } - let mut prices = Vec::new(); - for line in csv.lines().skip(1) { - let cols: Vec<&str> = line.split(',').collect(); - if cols.len() < 7 { continue; } prices.push(CompanyPrice { ticker: ticker.to_string(), - date: cols[0].to_string(), - open: cols[1].parse()?, - high: cols[2].parse()?, - low: cols[3].parse()?, - close: cols[4].parse()?, - adj_close: cols[5].parse()?, - volume: cols[6].parse()?, + date: date_str, + open: money_to_f64(&candle.open), + high: money_to_f64(&candle.high), + low: money_to_f64(&candle.low), + // close_unadj is the raw (non-adjusted) close; close is the adjusted one + close: money_to_f64(&candle.close_unadj.unwrap_or(candle.close.clone())), + adj_close: money_to_f64(&candle.close), + volume: candle.volume.unwrap_or(0), }); } + + // Sort just in case (normally already sorted) + prices.sort_by_key(|p| p.date.clone()); + Ok(prices) } diff --git a/src/corporate/storage.rs b/src/corporate/storage.rs index 4bcfeca..857be95 100644 --- a/src/corporate/storage.rs +++ b/src/corporate/storage.rs @@ -1,11 +1,11 @@ // src/corporate/storage.rs -use super::types::{CompanyEvent, CompanyPrice}; -use std::collections::{HashMap, HashSet}; +use super::types::{CompanyEvent, CompanyPrice, CompanyEventChange}; +use super::helpers::*; use tokio::fs; -use chrono::{Local, NaiveDate}; +use chrono::{Local, NaiveDate, Datelike}; +use std::collections::HashMap; -/// Load all events from disk into a HashMap -async fn load_all_events_map() -> anyhow::Result> { +pub async fn load_existing_events() -> anyhow::Result> { let mut map = HashMap::new(); let dir = std::path::Path::new("corporate_events"); if !dir.exists() { @@ -16,11 +16,12 @@ async fn load_all_events_map() -> anyhow::Result> while let Some(entry) = entries.next_entry().await? { let path = entry.path(); if path.extension().and_then(|s| s.to_str()) == Some("json") { - let content = fs::read_to_string(&path).await?; - if let Ok(events) = serde_json::from_str::>(&content) { + let name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if name.starts_with("events_") && name.len() == 17 { // events_yyyy-mm.json + let content = fs::read_to_string(&path).await?; + let events: Vec = serde_json::from_str(&content)?; for event in events { - let key = format!("{}|{}", event.ticker, event.date); - map.insert(key, event); + map.insert(event_key(&event), event); } } } @@ -28,34 +29,68 @@ async fn load_all_events_map() -> anyhow::Result> Ok(map) } -/// Merge new events with existing ones and save back to disk -pub async fn merge_and_save_events(ticker: &str, new_events: Vec) -> anyhow::Result<()> { - let mut existing = load_all_events_map().await?; - - // Insert or update - for event in new_events { - let key = format!("{}|{}", event.ticker, event.date); - existing.insert(key, event); - } - - // Convert back to Vec and save (simple single file for now) - let all_events: Vec = existing.into_values().collect(); +pub async fn save_optimized_events(events: HashMap) -> anyhow::Result<()> { let dir = std::path::Path::new("corporate_events"); fs::create_dir_all(dir).await?; - let path = dir.join("all_events.json"); - let json = serde_json::to_string_pretty(&all_events)?; - fs::write(&path, json).await?; + + // Delete old files + let mut entries = fs::read_dir(dir).await?; + while let Some(entry) = entries.next_entry().await? { + let path = entry.path(); + let name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if name.starts_with("events_") && path.extension().map(|e| e == "json").unwrap_or(false) { + fs::remove_file(&path).await?; + } + } + + let mut sorted: Vec<_> = events.into_values().collect(); + sorted.sort_by_key(|e| (e.ticker.clone(), e.date.clone())); + + let mut by_month: HashMap> = HashMap::new(); + for e in sorted { + if let Ok(d) = NaiveDate::parse_from_str(&e.date, "%Y-%m-%d") { + let key = format!("{}-{:02}", d.year(), d.month()); + by_month.entry(key).or_default().push(e); + } + } + + for (month, list) in by_month { + let path = dir.join(format!("events_{}.json", month)); + fs::write(&path, serde_json::to_string_pretty(&list)?).await?; + } Ok(()) } -/// Save price history for a single ticker (overwrite old file) -pub async fn save_prices_for_ticker(ticker: &str, prices: Vec) -> anyhow::Result<()> { +pub async fn save_changes(changes: &[CompanyEventChange]) -> anyhow::Result<()> { + if changes.is_empty() { return Ok(()); } + let dir = std::path::Path::new("corporate_event_changes"); + fs::create_dir_all(dir).await?; + + let mut by_month: HashMap> = HashMap::new(); + for c in changes { + if let Ok(d) = NaiveDate::parse_from_str(&c.date, "%Y-%m-%d") { + let key = format!("{}-{:02}", d.year(), d.month()); + by_month.entry(key).or_default().push(c.clone()); + } + } + + for (month, list) in by_month { + let path = dir.join(format!("changes_{}.json", month)); + let mut all = if path.exists() { + let s = fs::read_to_string(&path).await?; + serde_json::from_str(&s).unwrap_or_default() + } else { vec![] }; + all.extend(list); + fs::write(&path, serde_json::to_string_pretty(&all)?).await?; + } + Ok(()) +} + +pub async fn save_prices_for_ticker(ticker: &str, mut prices: Vec) -> anyhow::Result<()> { let dir = std::path::Path::new("corporate_prices"); fs::create_dir_all(dir).await?; let path = dir.join(format!("{}.json", ticker)); - // Optional: sort by date - let mut prices = prices; prices.sort_by_key(|p| p.date.clone()); let json = serde_json::to_string_pretty(&prices)?; diff --git a/src/corporate/types.rs b/src/corporate/types.rs index 50448cc..855dfb5 100644 --- a/src/corporate/types.rs +++ b/src/corporate/types.rs @@ -31,7 +31,7 @@ pub struct CompanyPrice { pub struct CompanyEventChange { pub ticker: String, pub date: String, - pub field: String, // "time", "eps_forecast", "eps_actual", "new_event" + pub field_changed: String, // "time", "eps_forecast", "eps_actual", "new_event" pub old_value: String, pub new_value: String, pub detected_at: String, diff --git a/src/corporate/update.rs b/src/corporate/update.rs index c10fabd..ed13625 100644 --- a/src/corporate/update.rs +++ b/src/corporate/update.rs @@ -1,31 +1,83 @@ // src/corporate/update.rs -use super::{scraper::*, storage::*, types::*}; +use super::{scraper::*, storage::*, helpers::*, types::*}; use crate::config::Config; use chrono::Local; -use std::collections::{HashMap, HashSet}; - +use std::collections::HashMap; pub async fn run_full_update(client: &fantoccini::Client, tickers: Vec, config: &Config) -> anyhow::Result<()> { println!("Updating {} tickers (prices from {})", tickers.len(), config.corporate_start_date); let today = chrono::Local::now().format("%Y-%m-%d").to_string(); - for ticker in tickers { + let mut existing = load_existing_events().await?; + + for ticker in &tickers { print!(" → {:6} ", ticker); - // Earnings - if let Ok(events) = fetch_earnings_history(client, &ticker).await { - merge_and_save_events(&ticker, events.clone()).await?; - println!("{} earnings", events.len()); + if let Ok(new_events) = fetch_earnings_history(client, ticker).await { + let result = process_batch(&new_events, &mut existing, &today); + save_changes(&result.changes).await?; + println!("{} earnings, {} changes", new_events.len(), result.changes.len()); } - // Prices – now using config.corporate_start_date - if let Ok(prices) = fetch_price_history(client, &ticker, &config.corporate_start_date, &today).await { - save_prices_for_ticker(&ticker, prices).await?; + if let Ok(prices) = fetch_price_history(ticker, &config.corporate_start_date, &today).await { + save_prices_for_ticker(ticker, prices).await?; } tokio::time::sleep(tokio::time::Duration::from_millis(250)).await; } + + save_optimized_events(existing).await?; Ok(()) +} + +pub struct ProcessResult { + pub changes: Vec, +} + +pub fn process_batch( + new_events: &[CompanyEvent], + existing: &mut HashMap, + today: &str, +) -> ProcessResult { + let mut changes = Vec::new(); + + for new in new_events { + let key = event_key(new); + + if let Some(old) = existing.get(&key) { + changes.extend(detect_changes(old, new, today)); + existing.insert(key, new.clone()); + continue; + } + + // Check for time change on same date + let date_key = format!("{}|{}", new.ticker, new.date); + let mut found_old = None; + for (k, e) in existing.iter() { + if format!("{}|{}", e.ticker, e.date) == date_key && k != &key { + found_old = Some((k.clone(), e.clone())); + break; + } + } + + if let Some((old_key, old_event)) = found_old { + if new.date.as_str() > today { + changes.push(CompanyEventChange { + ticker: new.ticker.clone(), + date: new.date.clone(), + field_changed: "time".to_string(), + old_value: old_event.time.clone(), + new_value: new.time.clone(), + detected_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), + }); + } + existing.remove(&old_key); + } + + existing.insert(key, new.clone()); + } + + ProcessResult { changes } } \ No newline at end of file