finalytics 0.8.3__tar.gz → 0.8.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {finalytics-0.8.3 → finalytics-0.8.4}/Cargo.lock +35 -35
- finalytics-0.8.4/Cargo.toml +17 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/PKG-INFO +5 -4
- {finalytics-0.8.3 → finalytics-0.8.4}/pyproject.toml +2 -2
- {finalytics-0.8.3 → finalytics-0.8.4}/python/Cargo.toml +1 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/index.qmd +1 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/portfolio.qmd +3 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/screener.qmd +0 -12
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/ticker.qmd +5 -3
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/tickers.qmd +4 -2
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/portfolio.rs +30 -11
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/screener.rs +35 -6
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/ticker.rs +20 -6
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/tickers.rs +26 -10
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/Cargo.toml +1 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/README.md +1 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/performance.rs +29 -5
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/portfolio.rs +7 -2
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/lib.rs +3 -3
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/kline.rs +1 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/portfolio.rs +8 -1
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/tickers.rs +7 -3
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/report.rs +5 -3
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/table.rs +2 -2
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/tabs.rs +2 -2
- finalytics-0.8.3/Cargo.toml +0 -5
- {finalytics-0.8.3 → finalytics-0.8.4}/README.md +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/README.md +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/_publish.yml +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/_quarto.yml +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/dataframe.qmd +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/ffi.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/src/lib.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/python/test.py +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/optimization.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/statistics.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/stochastics.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/technicals.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/ticker.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/tickers.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/api.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/web.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/ticker.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/tickers.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/api.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/config.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/financials.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/builder.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/keys.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/screeners.json +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/screeners.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/web.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/screener.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/ticker.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/mod.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/utils/chart_utils.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/utils/date_utils.rs +0 -0
- {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/utils/mod.rs +0 -0
|
@@ -418,18 +418,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
|
|
418
418
|
|
|
419
419
|
[[package]]
|
|
420
420
|
name = "bytemuck"
|
|
421
|
-
version = "1.23.
|
|
421
|
+
version = "1.23.2"
|
|
422
422
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
423
|
-
checksum = "
|
|
423
|
+
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
|
424
424
|
dependencies = [
|
|
425
425
|
"bytemuck_derive",
|
|
426
426
|
]
|
|
427
427
|
|
|
428
428
|
[[package]]
|
|
429
429
|
name = "bytemuck_derive"
|
|
430
|
-
version = "1.10.
|
|
430
|
+
version = "1.10.1"
|
|
431
431
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
432
|
-
checksum = "
|
|
432
|
+
checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
|
|
433
433
|
dependencies = [
|
|
434
434
|
"proc-macro2",
|
|
435
435
|
"quote",
|
|
@@ -471,7 +471,7 @@ dependencies = [
|
|
|
471
471
|
"cached_proc_macro",
|
|
472
472
|
"cached_proc_macro_types",
|
|
473
473
|
"futures",
|
|
474
|
-
"hashbrown 0.15.
|
|
474
|
+
"hashbrown 0.15.5",
|
|
475
475
|
"once_cell",
|
|
476
476
|
"thiserror 2.0.12",
|
|
477
477
|
"tokio",
|
|
@@ -507,9 +507,9 @@ dependencies = [
|
|
|
507
507
|
|
|
508
508
|
[[package]]
|
|
509
509
|
name = "cc"
|
|
510
|
-
version = "1.2.
|
|
510
|
+
version = "1.2.32"
|
|
511
511
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
512
|
-
checksum = "
|
|
512
|
+
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
|
|
513
513
|
dependencies = [
|
|
514
514
|
"jobserver",
|
|
515
515
|
"libc",
|
|
@@ -1710,7 +1710,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|
|
1710
1710
|
|
|
1711
1711
|
[[package]]
|
|
1712
1712
|
name = "finalytics"
|
|
1713
|
-
version = "0.8.
|
|
1713
|
+
version = "0.8.4"
|
|
1714
1714
|
dependencies = [
|
|
1715
1715
|
"anyhow",
|
|
1716
1716
|
"cached",
|
|
@@ -1739,7 +1739,7 @@ dependencies = [
|
|
|
1739
1739
|
|
|
1740
1740
|
[[package]]
|
|
1741
1741
|
name = "finalytics-py"
|
|
1742
|
-
version = "0.8.
|
|
1742
|
+
version = "0.8.4"
|
|
1743
1743
|
dependencies = [
|
|
1744
1744
|
"chrono",
|
|
1745
1745
|
"finalytics",
|
|
@@ -2061,9 +2061,9 @@ dependencies = [
|
|
|
2061
2061
|
|
|
2062
2062
|
[[package]]
|
|
2063
2063
|
name = "h2"
|
|
2064
|
-
version = "0.4.
|
|
2064
|
+
version = "0.4.12"
|
|
2065
2065
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2066
|
-
checksum = "
|
|
2066
|
+
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
|
2067
2067
|
dependencies = [
|
|
2068
2068
|
"atomic-waker",
|
|
2069
2069
|
"bytes",
|
|
@@ -2118,9 +2118,9 @@ dependencies = [
|
|
|
2118
2118
|
|
|
2119
2119
|
[[package]]
|
|
2120
2120
|
name = "hashbrown"
|
|
2121
|
-
version = "0.15.
|
|
2121
|
+
version = "0.15.5"
|
|
2122
2122
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2123
|
-
checksum = "
|
|
2123
|
+
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
|
2124
2124
|
dependencies = [
|
|
2125
2125
|
"allocator-api2",
|
|
2126
2126
|
"equivalent",
|
|
@@ -2498,7 +2498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
2498
2498
|
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
|
2499
2499
|
dependencies = [
|
|
2500
2500
|
"equivalent",
|
|
2501
|
-
"hashbrown 0.15.
|
|
2501
|
+
"hashbrown 0.15.5",
|
|
2502
2502
|
"serde",
|
|
2503
2503
|
]
|
|
2504
2504
|
|
|
@@ -2671,9 +2671,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
|
2671
2671
|
|
|
2672
2672
|
[[package]]
|
|
2673
2673
|
name = "libbz2-rs-sys"
|
|
2674
|
-
version = "0.2.
|
|
2674
|
+
version = "0.2.2"
|
|
2675
2675
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2676
|
-
checksum = "
|
|
2676
|
+
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
|
2677
2677
|
|
|
2678
2678
|
[[package]]
|
|
2679
2679
|
name = "libc"
|
|
@@ -2795,7 +2795,7 @@ version = "0.12.5"
|
|
|
2795
2795
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2796
2796
|
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
|
2797
2797
|
dependencies = [
|
|
2798
|
-
"hashbrown 0.15.
|
|
2798
|
+
"hashbrown 0.15.5",
|
|
2799
2799
|
]
|
|
2800
2800
|
|
|
2801
2801
|
[[package]]
|
|
@@ -3579,7 +3579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
3579
3579
|
checksum = "3daf8e3d4b712abe1d690838f6e29fb76b76ea19589c4afa39ec30e12f62af71"
|
|
3580
3580
|
dependencies = [
|
|
3581
3581
|
"array-init-cursor",
|
|
3582
|
-
"hashbrown 0.15.
|
|
3582
|
+
"hashbrown 0.15.5",
|
|
3583
3583
|
]
|
|
3584
3584
|
|
|
3585
3585
|
[[package]]
|
|
@@ -3725,7 +3725,7 @@ dependencies = [
|
|
|
3725
3725
|
"either",
|
|
3726
3726
|
"ethnum",
|
|
3727
3727
|
"getrandom 0.2.16",
|
|
3728
|
-
"hashbrown 0.15.
|
|
3728
|
+
"hashbrown 0.15.5",
|
|
3729
3729
|
"itoa",
|
|
3730
3730
|
"lz4",
|
|
3731
3731
|
"num-traits",
|
|
@@ -3788,7 +3788,7 @@ dependencies = [
|
|
|
3788
3788
|
"chrono",
|
|
3789
3789
|
"either",
|
|
3790
3790
|
"fast-float2",
|
|
3791
|
-
"hashbrown 0.15.
|
|
3791
|
+
"hashbrown 0.15.5",
|
|
3792
3792
|
"itoa",
|
|
3793
3793
|
"num-traits",
|
|
3794
3794
|
"polars-arrow 0.49.1",
|
|
@@ -3846,7 +3846,7 @@ dependencies = [
|
|
|
3846
3846
|
"comfy-table",
|
|
3847
3847
|
"either",
|
|
3848
3848
|
"hashbrown 0.14.5",
|
|
3849
|
-
"hashbrown 0.15.
|
|
3849
|
+
"hashbrown 0.15.5",
|
|
3850
3850
|
"indexmap 2.10.0",
|
|
3851
3851
|
"itoa",
|
|
3852
3852
|
"num-traits",
|
|
@@ -3919,7 +3919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
3919
3919
|
checksum = "33d3aa6722c9a3e0b721ec2bcdc4affd9e50e4cb606cd81bb94535a9a5a6ade9"
|
|
3920
3920
|
dependencies = [
|
|
3921
3921
|
"bitflags 2.9.1",
|
|
3922
|
-
"hashbrown 0.15.
|
|
3922
|
+
"hashbrown 0.15.5",
|
|
3923
3923
|
"num-traits",
|
|
3924
3924
|
"polars-arrow 0.49.1",
|
|
3925
3925
|
"polars-compute 0.49.1",
|
|
@@ -3978,7 +3978,7 @@ dependencies = [
|
|
|
3978
3978
|
"fs4",
|
|
3979
3979
|
"futures",
|
|
3980
3980
|
"glob",
|
|
3981
|
-
"hashbrown 0.15.
|
|
3981
|
+
"hashbrown 0.15.5",
|
|
3982
3982
|
"home",
|
|
3983
3983
|
"itoa",
|
|
3984
3984
|
"memchr",
|
|
@@ -4015,7 +4015,7 @@ checksum = "cd891735404ebb9d6ace066cfb4b8f6edb321bc841d354a0d917a3a1f2d1ca5b"
|
|
|
4015
4015
|
dependencies = [
|
|
4016
4016
|
"chrono",
|
|
4017
4017
|
"fallible-streaming-iterator",
|
|
4018
|
-
"hashbrown 0.15.
|
|
4018
|
+
"hashbrown 0.15.5",
|
|
4019
4019
|
"indexmap 2.10.0",
|
|
4020
4020
|
"itoa",
|
|
4021
4021
|
"num-traits",
|
|
@@ -4153,7 +4153,7 @@ dependencies = [
|
|
|
4153
4153
|
"chrono",
|
|
4154
4154
|
"chrono-tz",
|
|
4155
4155
|
"either",
|
|
4156
|
-
"hashbrown 0.15.
|
|
4156
|
+
"hashbrown 0.15.5",
|
|
4157
4157
|
"hex",
|
|
4158
4158
|
"indexmap 2.10.0",
|
|
4159
4159
|
"jsonpath_lib_polars_vendor",
|
|
@@ -4207,7 +4207,7 @@ dependencies = [
|
|
|
4207
4207
|
"bytemuck",
|
|
4208
4208
|
"ethnum",
|
|
4209
4209
|
"futures",
|
|
4210
|
-
"hashbrown 0.15.
|
|
4210
|
+
"hashbrown 0.15.5",
|
|
4211
4211
|
"num-traits",
|
|
4212
4212
|
"polars-arrow 0.49.1",
|
|
4213
4213
|
"polars-compute 0.49.1",
|
|
@@ -4265,7 +4265,7 @@ dependencies = [
|
|
|
4265
4265
|
"chrono",
|
|
4266
4266
|
"chrono-tz",
|
|
4267
4267
|
"either",
|
|
4268
|
-
"hashbrown 0.15.
|
|
4268
|
+
"hashbrown 0.15.5",
|
|
4269
4269
|
"memmap2 0.9.7",
|
|
4270
4270
|
"num-traits",
|
|
4271
4271
|
"percent-encoding",
|
|
@@ -4436,7 +4436,7 @@ dependencies = [
|
|
|
4436
4436
|
"compact_str",
|
|
4437
4437
|
"flate2",
|
|
4438
4438
|
"foldhash",
|
|
4439
|
-
"hashbrown 0.15.
|
|
4439
|
+
"hashbrown 0.15.5",
|
|
4440
4440
|
"indexmap 2.10.0",
|
|
4441
4441
|
"libc",
|
|
4442
4442
|
"memmap2 0.9.7",
|
|
@@ -5159,7 +5159,7 @@ dependencies = [
|
|
|
5159
5159
|
"openssl-probe",
|
|
5160
5160
|
"rustls-pki-types",
|
|
5161
5161
|
"schannel",
|
|
5162
|
-
"security-framework 3.
|
|
5162
|
+
"security-framework 3.3.0",
|
|
5163
5163
|
]
|
|
5164
5164
|
|
|
5165
5165
|
[[package]]
|
|
@@ -5292,9 +5292,9 @@ dependencies = [
|
|
|
5292
5292
|
|
|
5293
5293
|
[[package]]
|
|
5294
5294
|
name = "security-framework"
|
|
5295
|
-
version = "3.
|
|
5295
|
+
version = "3.3.0"
|
|
5296
5296
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5297
|
-
checksum = "
|
|
5297
|
+
checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c"
|
|
5298
5298
|
dependencies = [
|
|
5299
5299
|
"bitflags 2.9.1",
|
|
5300
5300
|
"core-foundation 0.10.1",
|
|
@@ -5661,9 +5661,9 @@ dependencies = [
|
|
|
5661
5661
|
|
|
5662
5662
|
[[package]]
|
|
5663
5663
|
name = "slab"
|
|
5664
|
-
version = "0.4.
|
|
5664
|
+
version = "0.4.11"
|
|
5665
5665
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5666
|
-
checksum = "
|
|
5666
|
+
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
|
5667
5667
|
|
|
5668
5668
|
[[package]]
|
|
5669
5669
|
name = "sledgehammer_bindgen"
|
|
@@ -7255,9 +7255,9 @@ dependencies = [
|
|
|
7255
7255
|
|
|
7256
7256
|
[[package]]
|
|
7257
7257
|
name = "zerovec"
|
|
7258
|
-
version = "0.11.
|
|
7258
|
+
version = "0.11.4"
|
|
7259
7259
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
7260
|
-
checksum = "
|
|
7260
|
+
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
|
|
7261
7261
|
dependencies = [
|
|
7262
7262
|
"yoke",
|
|
7263
7263
|
"zerofrom",
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finalytics
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
-
Requires-Dist: polars
|
|
8
|
-
Requires-Dist: pyarrow
|
|
9
|
-
Requires-Dist: plotly
|
|
7
|
+
Requires-Dist: polars>=1.1.0
|
|
8
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
9
|
+
Requires-Dist: plotly>=5.22.0
|
|
10
|
+
Requires-Dist: ipython>=7.0.0
|
|
10
11
|
Requires-Python: >=3.9
|
|
11
12
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
12
13
|
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "finalytics"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.4"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
classifiers = [
|
|
@@ -12,7 +12,7 @@ classifiers = [
|
|
|
12
12
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
13
13
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
14
14
|
]
|
|
15
|
-
dependencies = ["polars
|
|
15
|
+
dependencies = ["polars>=1.1.0", "pyarrow>=14.0.0", "plotly>=5.22.0", "ipython>=7.0.0"]
|
|
16
16
|
|
|
17
17
|
[tool]
|
|
18
18
|
|
|
@@ -26,6 +26,7 @@ Create a new Portfolio object.
|
|
|
26
26
|
- `risk_free_rate` (float): The risk-free rate for calculations.
|
|
27
27
|
- `objective_function` (Optional[str]): The objective function for optimization.
|
|
28
28
|
- `constraints` (Optional[List[Tuple[float, float]]]): Constraints for optimization.
|
|
29
|
+
- `weights` (Optional[List[float]]): Weights for the assets in the portfolio. If provided, it will override the optimization process.
|
|
29
30
|
|
|
30
31
|
**Returns:**
|
|
31
32
|
|
|
@@ -167,10 +168,11 @@ Generate a report for the portfolio.
|
|
|
167
168
|
**Parameters:**
|
|
168
169
|
|
|
169
170
|
- `report_type` (Optional[str]): The type of report to generate. Options are "performance".
|
|
171
|
+
- display (Optional[str]): The display mode for the report. set to "notebook" for Jupyter Notebook display, defaults to "browser".
|
|
170
172
|
|
|
171
173
|
**Example:**
|
|
172
174
|
|
|
173
|
-
```
|
|
175
|
+
```python
|
|
174
176
|
portfolio.report("performance")
|
|
175
177
|
```
|
|
176
178
|
</details>
|
|
@@ -123,15 +123,3 @@ metrics_df = screener.metrics()
|
|
|
123
123
|
print(metrics_df)
|
|
124
124
|
```
|
|
125
125
|
</details>
|
|
126
|
-
|
|
127
|
-
<details>
|
|
128
|
-
<summary>`display`</summary>
|
|
129
|
-
|
|
130
|
-
Display the overview and metrics DataFrames as interactive DataTables in the web browser.
|
|
131
|
-
|
|
132
|
-
**Example:**
|
|
133
|
-
|
|
134
|
-
```{python}
|
|
135
|
-
screener.display()
|
|
136
|
-
```
|
|
137
|
-
</details>
|
|
@@ -343,11 +343,13 @@ Generate a report for the ticker.
|
|
|
343
343
|
|
|
344
344
|
**Parameters:**
|
|
345
345
|
|
|
346
|
-
- `report_type` (str): Type of report to generate (performance, financials, options, news_sentiment).
|
|
346
|
+
- `report_type` (Optional[str]): Type of report to generate (performance, financials, options, news_sentiment).
|
|
347
|
+
- display (Optional[str]): The display mode for the report. set to "notebook" for Jupyter Notebook display, defaults to "browser".
|
|
347
348
|
|
|
348
349
|
**Example:**
|
|
349
350
|
|
|
350
|
-
```
|
|
351
|
-
ticker.report(
|
|
351
|
+
```python
|
|
352
|
+
ticker.report("performance")
|
|
352
353
|
```
|
|
354
|
+
|
|
353
355
|
</details>
|
|
@@ -270,11 +270,12 @@ Generate a report for all tickers.
|
|
|
270
270
|
|
|
271
271
|
**Parameters:**
|
|
272
272
|
|
|
273
|
-
- report_type (str): The type of report to generate ("performance").
|
|
273
|
+
- report_type (Optional[str]): The type of report to generate ("performance").
|
|
274
|
+
- display (Optional[str]): The display mode for the report. set to "notebook" for Jupyter Notebook display, defaults to "browser".
|
|
274
275
|
|
|
275
276
|
**Example:**
|
|
276
277
|
|
|
277
|
-
```
|
|
278
|
+
```python
|
|
278
279
|
tickers.report("performance")
|
|
279
280
|
```
|
|
280
281
|
</details>
|
|
@@ -310,6 +311,7 @@ Optimizes the tickers given the objective function and constraints.
|
|
|
310
311
|
|
|
311
312
|
- `objective_function` (Optional[str]): The objective function for optimization.
|
|
312
313
|
- `constraints` (Optional[List[Tuple[float, float]]]): List of constraints for optimization.
|
|
314
|
+
- `weights` (Optional[List[float]]): Weights for asset allocation. If provided, overrides the optimization process.
|
|
313
315
|
|
|
314
316
|
**Returns:**
|
|
315
317
|
|
|
@@ -20,6 +20,7 @@ use crate::ffi::rust_plot_to_py_plot;
|
|
|
20
20
|
/// * `risk_free_rate` - `float` - The risk-free rate to use in the calculations
|
|
21
21
|
/// * `objective_function` - `str` - The objective function to use in the optimization (max_sharpe, min_vol, max_return, nin_var, min_cvar, min_drawdown)
|
|
22
22
|
/// * `constraints` - `list` - list of tuples with the lower and upper bounds for the weights
|
|
23
|
+
/// * `weights` - `list` - weights for the assets in the portfolio, if provided, it will override the optimization process
|
|
23
24
|
///
|
|
24
25
|
/// # Optional Arguments (For Custom Data)
|
|
25
26
|
///
|
|
@@ -44,8 +45,9 @@ use crate::ffi::rust_plot_to_py_plot;
|
|
|
44
45
|
/// confidence_level = 0.95,
|
|
45
46
|
/// risk_free_rate = 0.02,
|
|
46
47
|
/// max_iterations = 1000,
|
|
47
|
-
/// objective_function = "max_sharpe"
|
|
48
|
-
/// constraints = [(0.0, 0.5), (0.0, 0.5), (0.0, 0.5), (0.0, 0.5)
|
|
48
|
+
/// objective_function = "max_sharpe",
|
|
49
|
+
/// constraints = [(0.0, 0.5), (0.0, 0.5), (0.0, 0.5), (0.0, 0.5),
|
|
50
|
+
/// /// weights = None)
|
|
49
51
|
/// ```
|
|
50
52
|
#[pyclass]
|
|
51
53
|
#[pyo3(name = "Portfolio")]
|
|
@@ -56,12 +58,14 @@ pub struct PyPortfolio {
|
|
|
56
58
|
#[pymethods]
|
|
57
59
|
impl PyPortfolio {
|
|
58
60
|
#[new]
|
|
59
|
-
#[pyo3(signature = (
|
|
60
|
-
|
|
61
|
+
#[pyo3(signature = (
|
|
62
|
+
ticker_symbols, benchmark_symbol=None, start_date=None, end_date=None, interval=None,
|
|
63
|
+
confidence_level=None, risk_free_rate=None, objective_function=None, constraints=None,
|
|
64
|
+
weights=None, tickers_data=None, benchmark_data=None))]
|
|
61
65
|
#[allow(clippy::too_many_arguments)]
|
|
62
66
|
pub fn new(ticker_symbols: Vec<String>, benchmark_symbol: Option<String>, start_date: Option<String>, end_date: Option<String>,
|
|
63
67
|
interval: Option<String>, confidence_level: Option<f64>, risk_free_rate: Option<f64>,
|
|
64
|
-
objective_function: Option<String>, constraints: Option<Vec<(f64, f64)>>,
|
|
68
|
+
objective_function: Option<String>, constraints: Option<Vec<(f64, f64)>>, weights: Option<Vec<f64>>,
|
|
65
69
|
tickers_data: Option<Vec<PyDataFrame>>, benchmark_data: Option<PyDataFrame>) -> Self {
|
|
66
70
|
let ticker_symbols: Vec<&str> = ticker_symbols.iter().map(|x| x.as_str()).collect();
|
|
67
71
|
let tickers_data = tickers_data.map(|data: Vec<PyDataFrame>| {
|
|
@@ -88,6 +92,7 @@ impl PyPortfolio {
|
|
|
88
92
|
.risk_free_rate(risk_free_rate.unwrap_or(0.02))
|
|
89
93
|
.objective_function(objective_function)
|
|
90
94
|
.constraints(constraints)
|
|
95
|
+
.weights(weights)
|
|
91
96
|
.tickers_data(tickers_data)
|
|
92
97
|
.benchmark_data(benchmark_data)
|
|
93
98
|
.build()).unwrap();
|
|
@@ -225,16 +230,30 @@ impl PyPortfolio {
|
|
|
225
230
|
/// # Arguments
|
|
226
231
|
///
|
|
227
232
|
/// * `report_type` - `optional str` - The type of report to display (performance)
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
/// * `display` - Optional str - Display mode ("notebook" to display in Jupyter, else displays to default web browser)
|
|
234
|
+
#[pyo3(signature = (report_type=None, display=None))]
|
|
235
|
+
pub fn report(&self, report_type: Option<String>, display: Option<String>) {
|
|
230
236
|
task::block_in_place(move || {
|
|
231
237
|
let report_type = match report_type {
|
|
232
238
|
Some(report_type) => ReportType::from_str(&report_type).unwrap(),
|
|
233
|
-
None => ReportType::Performance
|
|
239
|
+
None => ReportType::Performance,
|
|
234
240
|
};
|
|
235
|
-
let report = tokio::runtime::Runtime::new()
|
|
236
|
-
|
|
237
|
-
|
|
241
|
+
let report = tokio::runtime::Runtime::new()
|
|
242
|
+
.unwrap()
|
|
243
|
+
.block_on(self.portfolio.report(Some(report_type)))
|
|
244
|
+
.unwrap();
|
|
245
|
+
if display.as_deref() == Some("notebook") {
|
|
246
|
+
let html_content = report.to_html();
|
|
247
|
+
Python::with_gil(|py| {
|
|
248
|
+
let ipython_display = py.import("IPython.display").unwrap();
|
|
249
|
+
let html_class = ipython_display.getattr("HTML").unwrap();
|
|
250
|
+
let display_fn = ipython_display.getattr("display").unwrap();
|
|
251
|
+
let html_obj = html_class.call1((html_content,)).unwrap();
|
|
252
|
+
display_fn.call1((html_obj,)).unwrap();
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
report.show().unwrap();
|
|
256
|
+
}
|
|
238
257
|
});
|
|
239
258
|
}
|
|
240
259
|
}
|
|
@@ -128,12 +128,41 @@ impl PyScreener {
|
|
|
128
128
|
PyDataFrame(metrics)
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
/// Display the overview and metrics DataFrames as DataTables in the web browser
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
/// Display the overview and metrics DataFrames as DataTables in the web browser or Jupyter Notebook
|
|
132
|
+
///
|
|
133
|
+
/// # Arguments
|
|
134
|
+
///
|
|
135
|
+
/// * `display` - Optional str - Display mode ("notebook" to display in Jupyter, else uses default web browser)
|
|
136
|
+
#[pyo3(signature = (display=None))]
|
|
137
|
+
pub fn display(&self, display: Option<String>) {
|
|
138
|
+
task::block_in_place(move || {
|
|
139
|
+
let overview = self.screener.overview();
|
|
140
|
+
let metrics = tokio::runtime::Runtime::new()
|
|
141
|
+
.unwrap()
|
|
142
|
+
.block_on(self.screener.metrics())
|
|
143
|
+
.unwrap();
|
|
144
|
+
|
|
145
|
+
if display.as_deref() == Some("notebook") {
|
|
146
|
+
Python::with_gil(|py| {
|
|
147
|
+
let ipython_display = py.import("IPython.display").unwrap();
|
|
148
|
+
let html_class = ipython_display.getattr("HTML").unwrap();
|
|
149
|
+
let display_fn = ipython_display.getattr("display").unwrap();
|
|
150
|
+
|
|
151
|
+
// Display overview HTML
|
|
152
|
+
let overview_html = overview.to_html().unwrap();
|
|
153
|
+
let overview_html_obj = html_class.call1((overview_html,)).unwrap();
|
|
154
|
+
display_fn.call1((overview_html_obj,)).unwrap();
|
|
155
|
+
|
|
156
|
+
// Display metrics HTML
|
|
157
|
+
let metrics_html = metrics.to_html().unwrap();
|
|
158
|
+
let metrics_html_obj = html_class.call1((metrics_html,)).unwrap();
|
|
159
|
+
display_fn.call1((metrics_html_obj,)).unwrap();
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
overview.show().unwrap();
|
|
163
|
+
metrics.show().unwrap();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
137
166
|
}
|
|
138
167
|
}
|
|
139
168
|
|
|
@@ -385,16 +385,30 @@ impl PyTicker {
|
|
|
385
385
|
/// # Arguments
|
|
386
386
|
///
|
|
387
387
|
/// * `report_type` - `optional str` - The type of report to display (performance, financials, options, news)
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
/// * `display` - Optional str - Display mode ("notebook" to display in Jupyter, else displays to default web browser)
|
|
389
|
+
#[pyo3(signature = (report_type=None, display=None))]
|
|
390
|
+
pub fn report(&self, report_type: Option<String>, display: Option<String>) {
|
|
390
391
|
task::block_in_place(move || {
|
|
391
392
|
let report_type = match report_type {
|
|
392
393
|
Some(report_type) => ReportType::from_str(&report_type).unwrap(),
|
|
393
|
-
None => ReportType::Performance
|
|
394
|
+
None => ReportType::Performance,
|
|
394
395
|
};
|
|
395
|
-
let report = tokio::runtime::Runtime::new()
|
|
396
|
-
|
|
397
|
-
|
|
396
|
+
let report = tokio::runtime::Runtime::new()
|
|
397
|
+
.unwrap()
|
|
398
|
+
.block_on(self.ticker.report(Some(report_type)))
|
|
399
|
+
.unwrap();
|
|
400
|
+
if display.as_deref() == Some("notebook") {
|
|
401
|
+
let html_content = report.to_html();
|
|
402
|
+
Python::with_gil(|py| {
|
|
403
|
+
let ipython_display = py.import("IPython.display").unwrap();
|
|
404
|
+
let html_class = ipython_display.getattr("HTML").unwrap();
|
|
405
|
+
let display_fn = ipython_display.getattr("display").unwrap();
|
|
406
|
+
let html_obj = html_class.call1((html_content,)).unwrap();
|
|
407
|
+
display_fn.call1((html_obj,)).unwrap();
|
|
408
|
+
});
|
|
409
|
+
} else {
|
|
410
|
+
report.show().unwrap();
|
|
411
|
+
}
|
|
398
412
|
});
|
|
399
413
|
}
|
|
400
414
|
|
|
@@ -287,17 +287,31 @@ impl PyTickers {
|
|
|
287
287
|
///
|
|
288
288
|
/// # Arguments
|
|
289
289
|
///
|
|
290
|
-
/// * `report_type` -
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
/// * `report_type` - Optional str - The type of report to display (performance)
|
|
291
|
+
/// * `display` - Optional str - Display mode ("notebook" to display in Jupyter, else displays to default web browser)
|
|
292
|
+
#[pyo3(signature = (report_type=None, display=None))]
|
|
293
|
+
pub fn report(&self, report_type: Option<String>, display: Option<String>) {
|
|
293
294
|
task::block_in_place(move || {
|
|
294
295
|
let report_type = match report_type {
|
|
295
296
|
Some(report_type) => ReportType::from_str(&report_type).unwrap(),
|
|
296
|
-
None => ReportType::Performance
|
|
297
|
+
None => ReportType::Performance,
|
|
297
298
|
};
|
|
298
|
-
let report = tokio::runtime::Runtime::new()
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
let report = tokio::runtime::Runtime::new()
|
|
300
|
+
.unwrap()
|
|
301
|
+
.block_on(self.tickers.report(Some(report_type)))
|
|
302
|
+
.unwrap();
|
|
303
|
+
if display.as_deref() == Some("notebook") {
|
|
304
|
+
let html_content = report.to_html();
|
|
305
|
+
Python::with_gil(|py| {
|
|
306
|
+
let ipython_display = py.import("IPython.display").unwrap();
|
|
307
|
+
let html_class = ipython_display.getattr("HTML").unwrap();
|
|
308
|
+
let display_fn = ipython_display.getattr("display").unwrap();
|
|
309
|
+
let html_obj = html_class.call1((html_content,)).unwrap();
|
|
310
|
+
display_fn.call1((html_obj,)).unwrap();
|
|
311
|
+
});
|
|
312
|
+
} else {
|
|
313
|
+
report.show().unwrap();
|
|
314
|
+
}
|
|
301
315
|
});
|
|
302
316
|
}
|
|
303
317
|
|
|
@@ -330,12 +344,13 @@ impl PyTickers {
|
|
|
330
344
|
///
|
|
331
345
|
/// * `objective_function` - `optional str` - The objective function to optimize the portfolio ("max_sharpe", "min_vol", "max_return", "min_drawdown", "min_var", "min_cvar")
|
|
332
346
|
/// * `constraints` - `optional List[Tuple[float, float]]` - A list of tuples representing the constraints for the optimization (e.g., [(0.1, 0.5), (0.2, 0.8)])
|
|
347
|
+
/// * `weights` - `optional List[float]` - A list of weights for the tickers in the portfolio (if provided, will use these weights instead of optimizing)
|
|
333
348
|
///
|
|
334
349
|
/// # Returns
|
|
335
350
|
///
|
|
336
351
|
/// `Portfolio` - A Portfolio object containing the optimized portfolio
|
|
337
|
-
#[pyo3(signature = (objective_function=None, constraints=None))]
|
|
338
|
-
pub fn optimize(&self, objective_function: Option<String>, constraints: Option<Vec<(f64, f64)>>) -> PyPortfolio {
|
|
352
|
+
#[pyo3(signature = (objective_function=None, constraints=None, weights=None))]
|
|
353
|
+
pub fn optimize(&self, objective_function: Option<String>, constraints: Option<Vec<(f64, f64)>>, weights: Option<Vec<f64>>) -> PyPortfolio {
|
|
339
354
|
PyPortfolio::new(
|
|
340
355
|
self.tickers.tickers.clone().iter().map(|x| x.ticker.to_string()).collect(),
|
|
341
356
|
Some(self.tickers.benchmark_symbol.clone()),
|
|
@@ -346,8 +361,9 @@ impl PyTickers {
|
|
|
346
361
|
Some(self.tickers.risk_free_rate),
|
|
347
362
|
objective_function,
|
|
348
363
|
constraints,
|
|
364
|
+
weights,
|
|
365
|
+
None,
|
|
349
366
|
None,
|
|
350
|
-
None
|
|
351
367
|
)
|
|
352
368
|
}
|
|
353
369
|
}
|
|
@@ -80,7 +80,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|
|
80
80
|
tickers.report(Some(ReportType::Performance)).await?.show()?;
|
|
81
81
|
|
|
82
82
|
// Perform a Portfolio Optimization
|
|
83
|
-
let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None).await?;
|
|
83
|
+
let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None, None).await?;
|
|
84
84
|
|
|
85
85
|
// Generate a Portfolio Report
|
|
86
86
|
portfolio.report(Some(ReportType::Performance)).await?.show()?;
|
|
@@ -100,7 +100,7 @@ pub struct PortfolioPerformanceStats {
|
|
|
100
100
|
pub optimal_weights: Vec<f64>,
|
|
101
101
|
pub optimal_portfolio_returns: Series,
|
|
102
102
|
pub performance_stats: PerformanceStats,
|
|
103
|
-
pub efficient_frontier: Vec<Vec<f64
|
|
103
|
+
pub efficient_frontier: Option<Vec<Vec<f64>>>,
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
|
|
@@ -119,7 +119,8 @@ impl PortfolioPerformanceStats {
|
|
|
119
119
|
confidence_level: f64,
|
|
120
120
|
risk_free_rate: f64,
|
|
121
121
|
objective_function: ObjectiveFunction,
|
|
122
|
-
constraints: Option<Vec<(f64, f64)
|
|
122
|
+
constraints: Option<Vec<(f64, f64)>>,
|
|
123
|
+
weights: Option<Vec<f64>>,
|
|
123
124
|
) -> Result<PortfolioPerformanceStats, Box<dyn Error>> {
|
|
124
125
|
let ticker_symbols = tickers.tickers.clone().iter().map(|x| x.ticker.clone()).collect::<Vec<String>>();
|
|
125
126
|
let benchmark_symbol = benchmark_ticker.ticker.clone();
|
|
@@ -181,9 +182,32 @@ impl PortfolioPerformanceStats {
|
|
|
181
182
|
.collect::<Vec<f64>>();
|
|
182
183
|
let cov_matrix = covariance_matrix(&portfolio_returns)?;
|
|
183
184
|
|
|
184
|
-
let
|
|
185
|
+
let (efficient_frontier, optimal_weights) = if let Some(weights) = weights {
|
|
186
|
+
// If weights are provided, use them directly
|
|
187
|
+
if weights.len() != fetched_symbols.len() {
|
|
188
|
+
let fetched_symbols_set: std::collections::HashSet<_> = fetched_symbols.iter().collect();
|
|
189
|
+
let requested_symbols_set: std::collections::HashSet<_> = ticker_symbols.iter().collect();
|
|
190
|
+
let not_fetched_symbols: Vec<_> = requested_symbols_set.difference(&fetched_symbols_set)
|
|
191
|
+
.cloned().collect();
|
|
192
|
+
return if !not_fetched_symbols.is_empty() {
|
|
193
|
+
Err(Box::from(format!(
|
|
194
|
+
"The following ticker symbols were not fetched: {not_fetched_symbols:?}. Please provide new weights for the available symbols."
|
|
195
|
+
)))
|
|
196
|
+
} else {
|
|
197
|
+
Err(Box::from("Weights length must match the number of symbols"))
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if weights.iter().sum::<f64>() != 1.0 {
|
|
201
|
+
return Err(Box::from("Weights must sum to 1.0"));
|
|
202
|
+
}
|
|
203
|
+
(None, weights)
|
|
204
|
+
} else {
|
|
205
|
+
// If no weights are provided, use the optimization method to find optimal weights
|
|
206
|
+
let opt_result = portfolio_optimization(&mean_returns, &cov_matrix, &portfolio_returns, risk_free_rate,
|
|
185
207
|
confidence_level, objective_function, constraints.clone());
|
|
186
|
-
|
|
208
|
+
(Some(opt_result.efficient_frontier), opt_result.optimal_weights)
|
|
209
|
+
};
|
|
210
|
+
|
|
187
211
|
let daily_portfolio_returns = daily_portfolio_returns(&optimal_weights, &portfolio_returns);
|
|
188
212
|
|
|
189
213
|
let performance_stats = PerformanceStats::compute_stats(
|
|
@@ -208,7 +232,7 @@ impl PortfolioPerformanceStats {
|
|
|
208
232
|
optimal_weights: optimal_weights.clone(),
|
|
209
233
|
optimal_portfolio_returns: daily_portfolio_returns.clone(),
|
|
210
234
|
performance_stats,
|
|
211
|
-
efficient_frontier
|
|
235
|
+
efficient_frontier,
|
|
212
236
|
})
|
|
213
237
|
}
|
|
214
238
|
}
|
|
@@ -48,13 +48,18 @@ impl PortfolioCharts for Portfolio {
|
|
|
48
48
|
///
|
|
49
49
|
/// * `Plot` Plotly Chart struct
|
|
50
50
|
fn optimization_chart(&self, height: Option<usize>, width: Option<usize>) -> Result<Plot, Box<dyn Error>> {
|
|
51
|
+
let efficient_frontier = if let Some(ef) = &self.performance_stats.efficient_frontier {
|
|
52
|
+
ef.clone()
|
|
53
|
+
} else {
|
|
54
|
+
return Err("Efficient Frontier data is not available".into());
|
|
55
|
+
};
|
|
51
56
|
let days = self.performance_stats.interval.mode;
|
|
52
57
|
let annual_days = 365.0/self.performance_stats.interval.average;
|
|
53
58
|
|
|
54
|
-
let ef_returns =
|
|
59
|
+
let ef_returns = efficient_frontier.clone().iter()
|
|
55
60
|
.map(|x| (1.0 + (x[0]/days)/100.0).powf(annual_days) - 1.0).collect::<Vec<f64>>();
|
|
56
61
|
|
|
57
|
-
let ef_risk =
|
|
62
|
+
let ef_risk = efficient_frontier.clone().iter()
|
|
58
63
|
.map(|x| x[1]/100.0 * annual_days.sqrt()).collect::<Vec<f64>>();
|
|
59
64
|
|
|
60
65
|
let ef_trace = Scatter::new(ef_risk, ef_returns)
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
//! tickers.report(Some(ReportType::Performance)).await?.show()?;
|
|
81
81
|
//!
|
|
82
82
|
//! // Perform a Portfolio Optimization
|
|
83
|
-
//! let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None).await?;
|
|
83
|
+
//! let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None, None).await?;
|
|
84
84
|
//!
|
|
85
85
|
//! // Generate a Portfolio Report
|
|
86
86
|
//! portfolio.report(Some(ReportType::Performance)).await?.show()?;
|
|
@@ -162,7 +162,7 @@ mod tests {
|
|
|
162
162
|
use crate::prelude::*;
|
|
163
163
|
use std::error::Error;
|
|
164
164
|
|
|
165
|
-
#[ignore]
|
|
165
|
+
#[ignore]
|
|
166
166
|
#[tokio::test]
|
|
167
167
|
async fn finalytics_test() -> Result<(), Box<dyn Error>> {
|
|
168
168
|
// Screen for Large-Cap NASDAQ Stocks
|
|
@@ -209,7 +209,7 @@ mod tests {
|
|
|
209
209
|
tickers.report(Some(ReportType::Performance)).await?.show()?;
|
|
210
210
|
|
|
211
211
|
// Perform a Portfolio Optimization
|
|
212
|
-
let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None).await?;
|
|
212
|
+
let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None, None).await?;
|
|
213
213
|
|
|
214
214
|
// Generate a Portfolio Report
|
|
215
215
|
portfolio.report(Some(ReportType::Performance)).await?.show()?;
|
|
@@ -77,7 +77,7 @@ pub struct IntervalDays {
|
|
|
77
77
|
/// tickers.report(Some(ReportType::Performance)).await?.show()?;
|
|
78
78
|
///
|
|
79
79
|
/// // Portfolio optimization
|
|
80
|
-
/// let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None).await?;
|
|
80
|
+
/// let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None, None).await?;
|
|
81
81
|
/// portfolio.report(Some(ReportType::Performance)).await?.show()?;
|
|
82
82
|
///
|
|
83
83
|
/// Ok(())
|
|
@@ -13,6 +13,7 @@ pub struct PortfolioBuilder {
|
|
|
13
13
|
pub risk_free_rate: f64,
|
|
14
14
|
pub objective_function: ObjectiveFunction,
|
|
15
15
|
pub constraints: Option<Vec<(f64, f64)>>,
|
|
16
|
+
pub weights: Option<Vec<f64>>,
|
|
16
17
|
pub tickers_data: Option<Vec<KLINE>>,
|
|
17
18
|
pub benchmark_data: Option<KLINE>,
|
|
18
19
|
|
|
@@ -37,6 +38,7 @@ impl PortfolioBuilder {
|
|
|
37
38
|
risk_free_rate: 0.0,
|
|
38
39
|
objective_function: ObjectiveFunction::MaxSharpe,
|
|
39
40
|
constraints: None,
|
|
41
|
+
weights: None,
|
|
40
42
|
tickers_data: None,
|
|
41
43
|
benchmark_data: None,
|
|
42
44
|
}
|
|
@@ -87,6 +89,11 @@ impl PortfolioBuilder {
|
|
|
87
89
|
self
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
pub fn weights(mut self, weights: Option<Vec<f64>>) -> PortfolioBuilder {
|
|
93
|
+
self.weights = weights;
|
|
94
|
+
self
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
pub fn tickers_data(mut self, tickers_data: Option<Vec<KLINE>>) -> PortfolioBuilder {
|
|
91
98
|
self.tickers_data = tickers_data;
|
|
92
99
|
self
|
|
@@ -119,7 +126,7 @@ impl PortfolioBuilder {
|
|
|
119
126
|
|
|
120
127
|
let performance_stats = PortfolioPerformanceStats::performance_stats(
|
|
121
128
|
tickers.clone(), tickers.benchmark_ticker.clone(), &tickers.start_date, &tickers.end_date,
|
|
122
|
-
tickers.confidence_level, tickers.risk_free_rate, self.objective_function, self.constraints.clone()).await?;
|
|
129
|
+
tickers.confidence_level, tickers.risk_free_rate, self.objective_function, self.constraints.clone(), self.weights).await?;
|
|
123
130
|
Ok(Portfolio {
|
|
124
131
|
tickers,
|
|
125
132
|
performance_stats,
|
|
@@ -165,15 +165,19 @@ impl Tickers {
|
|
|
165
165
|
/// ### Arguments
|
|
166
166
|
/// - `objective_function` - The Objective Function to optimize the Portfolio
|
|
167
167
|
/// - `constraints` - The Portfolio Constraints
|
|
168
|
+
/// - `weights` - The Portfolio Weights (If weights are provided, they will be used instead of the optimization algorithm)
|
|
168
169
|
///
|
|
169
170
|
/// ### Returns
|
|
170
171
|
///
|
|
171
172
|
/// - A `Portfolio` Struct
|
|
172
|
-
pub async fn optimize(&self,
|
|
173
|
+
pub async fn optimize(&self,
|
|
174
|
+
objective_function: Option<ObjectiveFunction>,
|
|
175
|
+
constraints: Option<Vec<(f64, f64)>>,
|
|
176
|
+
weights: Option<Vec<f64>>) -> Result<Portfolio, Box<dyn Error>> {
|
|
173
177
|
let objective_function = objective_function.unwrap_or(ObjectiveFunction::MaxSharpe);
|
|
174
178
|
let performance_stats = PortfolioPerformanceStats::performance_stats(
|
|
175
179
|
self.clone(), self.benchmark_ticker.clone(), &self.start_date, &self.end_date,
|
|
176
|
-
self.confidence_level, self.risk_free_rate, objective_function, constraints).await?;
|
|
180
|
+
self.confidence_level, self.risk_free_rate, objective_function, constraints, weights).await?;
|
|
177
181
|
Ok(Portfolio {
|
|
178
182
|
tickers: self.clone(),
|
|
179
183
|
performance_stats,
|
|
@@ -223,7 +227,7 @@ impl Tickers {
|
|
|
223
227
|
/// tickers.report(Some(ReportType::Performance)).await?.show()?;
|
|
224
228
|
///
|
|
225
229
|
/// // Perform a Portfolio Optimization
|
|
226
|
-
/// let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None).await?;
|
|
230
|
+
/// let portfolio = tickers.optimize(Some(ObjectiveFunction::MaxSharpe), None, None).await?;
|
|
227
231
|
///
|
|
228
232
|
/// // Generate a Portfolio Report
|
|
229
233
|
/// portfolio.report(Some(ReportType::Performance)).await?.show()?;
|
|
@@ -108,9 +108,11 @@ impl Report for Portfolio {
|
|
|
108
108
|
let report = match report_type {
|
|
109
109
|
ReportType::Performance => {
|
|
110
110
|
let mut tabs: Vec<(String, String)> = Vec::new();
|
|
111
|
-
let
|
|
112
|
-
.to_html().replace(
|
|
113
|
-
|
|
111
|
+
if let Ok(oc) = self.optimization_chart(None, None) {
|
|
112
|
+
let optimization_chart = oc.to_html().replace(
|
|
113
|
+
"plotly-html-element", "optimization_chart");
|
|
114
|
+
tabs.push(("Optimization Chart".to_string(), optimization_chart));
|
|
115
|
+
}
|
|
114
116
|
let performance_chart = self.performance_chart(None, None)?
|
|
115
117
|
.to_html().replace("plotly-html-element", "performance_chart");
|
|
116
118
|
tabs.push(("Performance Chart".to_string(), performance_chart));
|
|
@@ -330,8 +330,8 @@ impl DataTable {
|
|
|
330
330
|
let temp_file_path = std::env::temp_dir().join(filename);
|
|
331
331
|
fs::write(&temp_file_path, html_content)?;
|
|
332
332
|
let _ = webbrowser::open(temp_file_path.to_str().unwrap()).map_err(|e| {
|
|
333
|
-
println!("Error opening
|
|
334
|
-
println!("
|
|
333
|
+
println!("Error opening report with webbrowser: {e}");
|
|
334
|
+
println!("Report Saved at: {temp_file_path:?}");
|
|
335
335
|
});
|
|
336
336
|
Ok(())
|
|
337
337
|
}
|
|
@@ -119,8 +119,8 @@ impl TabbedHtml {
|
|
|
119
119
|
let mut file = File::create(&temp_file_path)?;
|
|
120
120
|
file.write_all(html_content.as_bytes())?;
|
|
121
121
|
let _ = webbrowser::open(temp_file_path.to_str().unwrap()).map_err(|e| {
|
|
122
|
-
println!("Error opening
|
|
123
|
-
println!("
|
|
122
|
+
println!("Error opening report with webbrowser: {e}");
|
|
123
|
+
println!("Report Saved at: {temp_file_path:?}");
|
|
124
124
|
});
|
|
125
125
|
Ok(())
|
|
126
126
|
}
|
finalytics-0.8.3/Cargo.toml
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|