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.
Files changed (65) hide show
  1. {finalytics-0.8.3 → finalytics-0.8.4}/Cargo.lock +35 -35
  2. finalytics-0.8.4/Cargo.toml +17 -0
  3. {finalytics-0.8.3 → finalytics-0.8.4}/PKG-INFO +5 -4
  4. {finalytics-0.8.3 → finalytics-0.8.4}/pyproject.toml +2 -2
  5. {finalytics-0.8.3 → finalytics-0.8.4}/python/Cargo.toml +1 -1
  6. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/index.qmd +1 -1
  7. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/portfolio.qmd +3 -1
  8. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/screener.qmd +0 -12
  9. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/ticker.qmd +5 -3
  10. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/tickers.qmd +4 -2
  11. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/portfolio.rs +30 -11
  12. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/screener.rs +35 -6
  13. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/ticker.rs +20 -6
  14. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/tickers.rs +26 -10
  15. {finalytics-0.8.3 → finalytics-0.8.4}/rust/Cargo.toml +1 -1
  16. {finalytics-0.8.3 → finalytics-0.8.4}/rust/README.md +1 -1
  17. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/performance.rs +29 -5
  18. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/portfolio.rs +7 -2
  19. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/lib.rs +3 -3
  20. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/kline.rs +1 -1
  21. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/portfolio.rs +8 -1
  22. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/tickers.rs +7 -3
  23. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/report.rs +5 -3
  24. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/table.rs +2 -2
  25. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/tabs.rs +2 -2
  26. finalytics-0.8.3/Cargo.toml +0 -5
  27. {finalytics-0.8.3 → finalytics-0.8.4}/README.md +0 -0
  28. {finalytics-0.8.3 → finalytics-0.8.4}/python/README.md +0 -0
  29. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/_publish.yml +0 -0
  30. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/_quarto.yml +0 -0
  31. {finalytics-0.8.3 → finalytics-0.8.4}/python/docs/dataframe.qmd +0 -0
  32. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/ffi.rs +0 -0
  33. {finalytics-0.8.3 → finalytics-0.8.4}/python/src/lib.rs +0 -0
  34. {finalytics-0.8.3 → finalytics-0.8.4}/python/test.py +0 -0
  35. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/mod.rs +0 -0
  36. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/optimization.rs +0 -0
  37. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/statistics.rs +0 -0
  38. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/stochastics.rs +0 -0
  39. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/analytics/technicals.rs +0 -0
  40. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/mod.rs +0 -0
  41. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/ticker.rs +0 -0
  42. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/charts/tickers.rs +0 -0
  43. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/api.rs +0 -0
  44. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/mod.rs +0 -0
  45. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/google/web.rs +0 -0
  46. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/mod.rs +0 -0
  47. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/ticker.rs +0 -0
  48. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/tickers.rs +0 -0
  49. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/api.rs +0 -0
  50. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/config.rs +0 -0
  51. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/financials.rs +0 -0
  52. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/mod.rs +0 -0
  53. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/builder.rs +0 -0
  54. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/keys.rs +0 -0
  55. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/mod.rs +0 -0
  56. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/screeners.json +0 -0
  57. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/screeners/screeners.rs +0 -0
  58. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/data/yahoo/web.rs +0 -0
  59. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/mod.rs +0 -0
  60. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/screener.rs +0 -0
  61. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/models/ticker.rs +0 -0
  62. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/reports/mod.rs +0 -0
  63. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/utils/chart_utils.rs +0 -0
  64. {finalytics-0.8.3 → finalytics-0.8.4}/rust/src/utils/date_utils.rs +0 -0
  65. {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.1"
421
+ version = "1.23.2"
422
422
  source = "registry+https://github.com/rust-lang/crates.io-index"
423
- checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
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.0"
430
+ version = "1.10.1"
431
431
  source = "registry+https://github.com/rust-lang/crates.io-index"
432
- checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
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.4",
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.31"
510
+ version = "1.2.32"
511
511
  source = "registry+https://github.com/rust-lang/crates.io-index"
512
- checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
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.3"
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.3"
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.11"
2064
+ version = "0.4.12"
2065
2065
  source = "registry+https://github.com/rust-lang/crates.io-index"
2066
- checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
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.4"
2121
+ version = "0.15.5"
2122
2122
  source = "registry+https://github.com/rust-lang/crates.io-index"
2123
- checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
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.4",
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.1"
2674
+ version = "0.2.2"
2675
2675
  source = "registry+https://github.com/rust-lang/crates.io-index"
2676
- checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb"
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.4",
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.2.0",
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.2.0"
5295
+ version = "3.3.0"
5296
5296
  source = "registry+https://github.com/rust-lang/crates.io-index"
5297
- checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
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.10"
5664
+ version = "0.4.11"
5665
5665
  source = "registry+https://github.com/rust-lang/crates.io-index"
5666
- checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
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.3"
7258
+ version = "0.11.4"
7259
7259
  source = "registry+https://github.com/rust-lang/crates.io-index"
7260
- checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd"
7260
+ checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
7261
7261
  dependencies = [
7262
7262
  "yoke",
7263
7263
  "zerofrom",
@@ -0,0 +1,17 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = ["python"]
4
+
5
+ exclude = ["web"]
6
+
7
+ [profile]
8
+
9
+ [profile.wasm-dev]
10
+ inherits = "dev"
11
+ opt-level = 1
12
+
13
+ [profile.server-dev]
14
+ inherits = "dev"
15
+
16
+ [profile.android-dev]
17
+ inherits = "dev"
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: finalytics
3
- Version: 0.8.3
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==1.31.0
8
- Requires-Dist: pyarrow==21.0.0
9
- Requires-Dist: plotly==6.2.0
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.3"
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==1.31.0", "pyarrow==21.0.0", "plotly==6.2.0"]
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
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "finalytics-py"
3
- version = "0.8.3"
3
+ version = "0.8.4"
4
4
  edition = "2021"
5
5
  readme = "README.md"
6
6
 
@@ -31,7 +31,7 @@ pip install finalytics
31
31
  ### Example
32
32
 
33
33
  ```python
34
- from finalytics import Tickers
34
+ from finalytics import Screener, Tickers
35
35
 
36
36
  # Screen for the top 10 stocks by market cap on the NASDAQ exchange
37
37
  screener = Screener(
@@ -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
- ```{python}
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
- ```{python}
351
- ticker.report(report_type="performance")
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
- ```{python}
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 = (ticker_symbols, benchmark_symbol=None, start_date=None, end_date=None, interval=None,
60
- confidence_level=None, risk_free_rate=None, objective_function=None, constraints=None, tickers_data=None, benchmark_data=None))]
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
- #[pyo3(signature = (report_type=None))]
229
- pub fn report(&self, report_type: Option<String>) {
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().unwrap().block_on(
236
- self.portfolio.report(Some(report_type))).unwrap();
237
- report.show().unwrap();
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
- pub fn display(&self) {
133
- let overview = self.screener.overview();
134
- overview.show().unwrap();
135
- let metrics = tokio::runtime::Runtime::new().unwrap().block_on(self.screener.metrics()).unwrap();
136
- metrics.show().unwrap();
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
- #[pyo3(signature = (report_type=None))]
389
- pub fn report(&self, report_type: Option<String>) {
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().unwrap().block_on(
396
- self.ticker.report(Some(report_type))).unwrap();
397
- report.show().unwrap();
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` - `optional str` - The type of report to display (performance)
291
- #[pyo3(signature = (report_type=None))]
292
- pub fn report(&self, report_type: Option<String>) {
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().unwrap().block_on(
299
- self.tickers.report(Some(report_type))).unwrap();
300
- report.show().unwrap();
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
  }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "finalytics"
3
- version = "0.8.3"
3
+ version = "0.8.4"
4
4
  authors = ["Nnamdi Olisaeloka <nnamdiolisaeloka@gmail.com>"]
5
5
  edition = "2021"
6
6
  homepage = "https://finalytics.rs/"
@@ -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 opt_result = portfolio_optimization(&mean_returns, &cov_matrix, &portfolio_returns, risk_free_rate,
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
- let optimal_weights = opt_result.optimal_weights;
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: opt_result.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 = self.performance_stats.efficient_frontier.clone().iter()
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 = self.performance_stats.efficient_frontier.clone().iter()
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, objective_function: Option<ObjectiveFunction>, constraints: Option<Vec<(f64, f64)>>) -> Result<Portfolio, Box<dyn Error>> {
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 optimization_chart = self.optimization_chart(None, None)?
112
- .to_html().replace("plotly-html-element", "optimization_chart");
113
- tabs.push(("Optimization Chart".to_string(), optimization_chart));
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 html file with webbrowser: {e:?}");
334
- println!("Open the file manually at: {temp_file_path:?}");
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 html file with webbrowser: {e:?}");
123
- println!("Open the file manually at: {temp_file_path:?}");
122
+ println!("Error opening report with webbrowser: {e}");
123
+ println!("Report Saved at: {temp_file_path:?}");
124
124
  });
125
125
  Ok(())
126
126
  }
@@ -1,5 +0,0 @@
1
- [workspace]
2
- resolver = "2"
3
- members = ["python"]
4
-
5
- exclude = ["web"]
File without changes
File without changes
File without changes
File without changes
File without changes