finalytics 0.2.4__tar.gz → 0.3.0__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.2.4 → finalytics-0.3.0}/PKG-INFO +7 -42
- finalytics-0.3.0/README.md +235 -0
- finalytics-0.3.0/finalytics/Cargo.toml +50 -0
- finalytics-0.3.0/finalytics/src/analytics/fundamentals.rs +282 -0
- finalytics-0.3.0/finalytics/src/analytics/mod.rs +7 -0
- finalytics-0.3.0/finalytics/src/analytics/optimization.rs +149 -0
- finalytics-0.3.0/finalytics/src/analytics/performance.rs +264 -0
- finalytics-0.3.0/finalytics/src/analytics/sentiment.rs +114 -0
- finalytics-0.3.0/finalytics/src/analytics/statistics.rs +483 -0
- finalytics-0.3.0/finalytics/src/analytics/stochastics.rs +292 -0
- finalytics-0.3.0/finalytics/src/analytics/technicals.rs +489 -0
- finalytics-0.3.0/finalytics/src/charts/mod.rs +2 -0
- finalytics-0.3.0/finalytics/src/charts/portfolio.rs +284 -0
- finalytics-0.3.0/finalytics/src/charts/ticker.rs +568 -0
- finalytics-0.3.0/finalytics/src/data/db.rs +257 -0
- finalytics-0.3.0/finalytics/src/data/keys.rs +479 -0
- finalytics-0.3.0/finalytics/src/data/mod.rs +3 -0
- finalytics-0.3.0/finalytics/src/data/sqlite/symbols.db +0 -0
- finalytics-0.3.0/finalytics/src/data/symbols.rs +170 -0
- finalytics-0.3.0/finalytics/src/data/ticker.rs +517 -0
- finalytics-0.3.0/finalytics/src/lib.rs +57 -0
- finalytics-0.3.0/finalytics/src/models/mod.rs +2 -0
- finalytics-0.3.0/finalytics/src/models/portfolio.rs +138 -0
- finalytics-0.3.0/finalytics/src/models/ticker.rs +257 -0
- finalytics-0.3.0/finalytics/src/utils/chart_utils.rs +36 -0
- finalytics-0.3.0/finalytics/src/utils/date_utils.rs +60 -0
- finalytics-0.3.0/finalytics/src/utils/mod.rs +5 -0
- finalytics-0.3.0/finalytics/src/utils/web_utils.rs +75 -0
- {finalytics-0.2.4 → finalytics-0.3.0}/pyproject.toml +6 -1
- {finalytics-0.2.4 → finalytics-0.3.0/python}/Cargo.lock +4 -4
- {finalytics-0.2.4 → finalytics-0.3.0/python}/Cargo.toml +5 -4
- {finalytics-0.2.4 → finalytics-0.3.0/python}/README.md +5 -39
- {finalytics-0.2.4 → finalytics-0.3.0/python}/index.rst +6 -182
- {finalytics-0.2.4 → finalytics-0.3.0/python}/src/ffi.rs +0 -20
- {finalytics-0.2.4 → finalytics-0.3.0/python}/src/lib.rs +0 -5
- {finalytics-0.2.4 → finalytics-0.3.0/python}/src/portfolio.rs +12 -18
- {finalytics-0.2.4 → finalytics-0.3.0/python}/src/symbols.rs +1 -2
- {finalytics-0.2.4 → finalytics-0.3.0/python}/src/ticker.rs +57 -92
- finalytics-0.3.0/python/test.py +40 -0
- finalytics-0.2.4/.DS_Store +0 -0
- finalytics-0.2.4/.github/workflows/CI.yml +0 -127
- finalytics-0.2.4/.gitignore +0 -8
- finalytics-0.2.4/.readthedocs.yaml +0 -17
- finalytics-0.2.4/LICENSE +0 -21
- finalytics-0.2.4/examples/portfolio_optimization.ipynb +0 -1
- finalytics-0.2.4/examples/security_analysis.ipynb +0 -1
- finalytics-0.2.4/src/defi.rs +0 -479
- {finalytics-0.2.4 → finalytics-0.3.0/python}/build.sh +0 -0
- {finalytics-0.2.4 → finalytics-0.3.0/python}/conf.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: finalytics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
7
|
Requires-Dist: polars ==0.19.12
|
|
8
8
|
Requires-Dist: pyarrow ==14.0.0
|
|
9
9
|
Requires-Dist: ipython ==8.18.0
|
|
10
|
-
License-File: LICENSE
|
|
11
10
|
Requires-Python: >=3.7
|
|
12
11
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
12
|
|
|
@@ -17,6 +16,8 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
|
17
16
|

|
|
18
17
|
[](https://finalytics.rs/)
|
|
19
18
|
[](https://finalytics-py.readthedocs.io/en/latest/?badge=latest)
|
|
19
|
+

|
|
20
|
+

|
|
20
21
|

|
|
21
22
|
[](https://www.codefactor.io/repository/github/nnamdi-sys/finalytics-py)
|
|
22
23
|
|
|
@@ -48,7 +49,6 @@ print(get_symbols(query="SPY", asset_class="ETF"))
|
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
### Security Analysis
|
|
51
|
-
[](https://mybinder.org/v2/gh/Nnamdi-sys/finalytics-py/HEAD?labpath=examples%2Fsecurity_analysis.ipynb)
|
|
52
52
|
|
|
53
53
|
```python
|
|
54
54
|
from finalytics import Ticker
|
|
@@ -58,7 +58,7 @@ print(ticker.get_current_price())
|
|
|
58
58
|
print(ticker.get_summary_stats())
|
|
59
59
|
print(ticker.get_price_history(start="2023-01-01", end="2023-10-31", interval="1d"))
|
|
60
60
|
print(ticker.get_options_chain())
|
|
61
|
-
print(ticker.get_news(
|
|
61
|
+
print(ticker.get_news(compute_sentiment=False))
|
|
62
62
|
print(ticker.get_income_statement())
|
|
63
63
|
print(ticker.get_balance_sheet())
|
|
64
64
|
print(ticker.get_cashflow_statement())
|
|
@@ -72,51 +72,16 @@ ticker.display_options_chart(risk_free_rate=0.02, chart_type="surface", display_
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
### Portfolio Optimization
|
|
75
|
-
[](https://mybinder.org/v2/gh/Nnamdi-sys/finalytics-py/HEAD?labpath=examples%2Fportfolio_optimization.ipynb)
|
|
76
75
|
|
|
77
76
|
```python
|
|
78
77
|
from finalytics import Portfolio
|
|
79
78
|
|
|
80
79
|
portfolio = Portfolio(ticker_symbols=["AAPL", "GOOG", "MSFT", "BTC-USD"],
|
|
81
|
-
|
|
80
|
+
benchmark_symbol="^GSPC", start_date="2020-01-01", end_date="2022-01-01", interval="1d",
|
|
82
81
|
confidence_level=0.95, risk_free_rate=0.02, max_iterations=1000,
|
|
83
82
|
objective_function="max_sharpe")
|
|
84
83
|
print(portfolio.get_optimization_results())
|
|
85
|
-
portfolio.display_portfolio_charts("html")
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### DeFi Liquidity Pools
|
|
89
|
-
|
|
90
|
-
```python
|
|
91
|
-
from finalytics import DefiPools
|
|
92
|
-
|
|
93
|
-
defi_pools = DefiPools()
|
|
94
|
-
print(f"Total Value Locked: ${defi_pools.total_value_locked:,.0f}")
|
|
95
|
-
defi_pools.display_top_protocols_by_tvl(pool_symbol="USDC-USDT", num_protocols=20, display_format="html")
|
|
96
|
-
defi_pools.display_top_protocols_by_apy(pool_symbol="USDC-USDT", num_protocols=20, display_format="html")
|
|
97
|
-
defi_pools.display_pool_tvl_history(pool_symbol="USDC-USDT", protocol="uniswap-v3", chain="ethereum", display_format="html")
|
|
98
|
-
defi_pools.display_pool_apy_history(pool_symbol="USDC-USDT", protocol="uniswap-v3", chain="ethereum", display_format="html")
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### DeFi User Balances
|
|
102
|
-
|
|
103
|
-
```python
|
|
104
|
-
from finalytics import DefiBalances
|
|
105
|
-
from finalytics import get_supported_protocols
|
|
106
|
-
|
|
107
|
-
supported_protocols = get_supported_protocols()
|
|
108
|
-
print(supported_protocols)
|
|
109
|
-
|
|
110
|
-
# This function requires node.js and pnpm to be installed on the system
|
|
111
|
-
# for macos: brew install node && npm install -g pnpm
|
|
112
|
-
# for ubuntu: sudo apt install nodejs && npm install -g pnpm
|
|
113
|
-
# for windows: https://nodejs.org/en/download/ && npm install -g pnpm
|
|
114
|
-
|
|
115
|
-
defi_balances = DefiBalances(protocols=["wallet", "eigenlayer", "blast", "ether.fi"],
|
|
116
|
-
chains=["ethereum", "arbitrum"],
|
|
117
|
-
address="0x7ac34681f6aaeb691e150c43ee494177c0e2c183",
|
|
118
|
-
display_format="html")
|
|
119
|
-
print(defi_balances.balances)
|
|
84
|
+
portfolio.display_portfolio_charts("performance", "html")
|
|
120
85
|
```
|
|
121
86
|
|
|
122
87
|
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/finalytics)
|
|
4
|
+
[](https://docs.rs/finalytics/)
|
|
5
|
+

|
|
6
|
+
[](https://finalytics.rs/)
|
|
7
|
+
[](https://www.codefactor.io/repository/github/nnamdi-sys/finalytics)
|
|
8
|
+
|
|
9
|
+
**Finalytics** is a Rust library designed for retrieving financial data and performing security analysis and portfolio optimization.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add the following to your `Cargo.toml` file:
|
|
14
|
+
|
|
15
|
+
```toml
|
|
16
|
+
[dependencies]
|
|
17
|
+
finalytics = {version = "0.4.0", features = ["kaleido"]}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or run the following command:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cargo install finalytics --features kaleido
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
View the library documentation on [docs.rs](https://docs.rs/finalytics/) or visit the [homepage](https://finalytics.rs/)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Security Analysis
|
|
32
|
+
|
|
33
|
+
```rust
|
|
34
|
+
use std::error::Error;
|
|
35
|
+
use finalytics::prelude::*;
|
|
36
|
+
|
|
37
|
+
#[tokio::main]
|
|
38
|
+
async fn main() -> Result<(), Box<dyn Error>> {
|
|
39
|
+
|
|
40
|
+
// Instantiate the Ticker Object
|
|
41
|
+
let ticker = TickerBuilder::new().ticker("AAPL")?
|
|
42
|
+
.start_date("2023-01-01")
|
|
43
|
+
.end_date("2023-02-01")
|
|
44
|
+
.interval(Interval::OneDay)
|
|
45
|
+
.benchmark_symbol("^GSPC")
|
|
46
|
+
.confidence_level(0.95)
|
|
47
|
+
.risk_free_rate(0.02)
|
|
48
|
+
.build()?;
|
|
49
|
+
|
|
50
|
+
// Fetch Ticker Data
|
|
51
|
+
let quote = ticker.get_quote().await?;
|
|
52
|
+
let stats = ticker.get_ticker_stats().await?;
|
|
53
|
+
let chart = ticker.get_chart().await?;
|
|
54
|
+
let options = ticker.get_options().await?;
|
|
55
|
+
|
|
56
|
+
println!("{:?}", quote);
|
|
57
|
+
println!("{:?}", stats);
|
|
58
|
+
println!("{:?}", chart);
|
|
59
|
+
println!("{:?}", options);
|
|
60
|
+
|
|
61
|
+
// Fundamental Analysis
|
|
62
|
+
let income_statement = ticker.income_statement().await?;
|
|
63
|
+
let balance_sheet = ticker.balance_sheet().await?;
|
|
64
|
+
let cash_flow = ticker.cashflow_statement().await?;
|
|
65
|
+
let financial_ratios = ticker.financial_ratios().await?;
|
|
66
|
+
let performance_stats = ticker.performance_stats().await?;
|
|
67
|
+
let volatility_surface = ticker.volatility_surface().await?;
|
|
68
|
+
|
|
69
|
+
println!("{:?}", income_statement);
|
|
70
|
+
println!("{:?}", balance_sheet);
|
|
71
|
+
println!("{:?}", cash_flow);
|
|
72
|
+
println!("{:?}", financial_ratios);
|
|
73
|
+
println!("{:?}", performance_stats);
|
|
74
|
+
println!("{:?}", volatility_surface);
|
|
75
|
+
|
|
76
|
+
// Technical Analysis
|
|
77
|
+
let sma = ticker.sma(50).await?;
|
|
78
|
+
let ema = ticker.ema(3).await?;
|
|
79
|
+
let macd = ticker.macd(12, 26, 9).await?;
|
|
80
|
+
let rsi = ticker.rsi(14).await?;
|
|
81
|
+
let fs = ticker.fs(14).await?;
|
|
82
|
+
let ss = ticker.ss(7, 3).await?;
|
|
83
|
+
let ppo = ticker.ppo(12, 26, 9).await?;
|
|
84
|
+
let roc = ticker.roc(1).await?;
|
|
85
|
+
let mfi = ticker.mfi(14).await?;
|
|
86
|
+
let bb = ticker.bb(20, 2.0).await?;
|
|
87
|
+
let sd = ticker.sd(20).await?;
|
|
88
|
+
let mad = ticker.mad(20).await?;
|
|
89
|
+
let atr = ticker.atr(14).await?;
|
|
90
|
+
let max = ticker.max(20).await?;
|
|
91
|
+
let min = ticker.min(20).await?;
|
|
92
|
+
let obv = ticker.obv().await?;
|
|
93
|
+
|
|
94
|
+
println!("SMA:{:?}\nEMA:{:?}\nMACD:{:?}\nRSI:{:?}\nFS:{:?}\nSS:{:?}\nPPO:{:?}\nROC:{:?}\nMFI:{:?}\
|
|
95
|
+
\nBB:{:?}\nSD:{:?}\nMAD:{:?}\nATR:{:?}\nMAX:{:?}\nMIN:{:?}\nOBV:{:?}\n",
|
|
96
|
+
sma, ema, macd, rsi, fs, ss, ppo, roc, mfi, bb, sd, mad, atr, max, min, obv);
|
|
97
|
+
|
|
98
|
+
// News Sentiment Analysis
|
|
99
|
+
let news_sentiment = ticker.get_news(true).await?;
|
|
100
|
+
println!("{:?}", news_sentiment);
|
|
101
|
+
|
|
102
|
+
// Display Ticker Charts
|
|
103
|
+
let candlestick_chart = ticker.candlestick_chart().await?;
|
|
104
|
+
let performance_chart = ticker.performance_chart().await?;
|
|
105
|
+
let volatility_charts = ticker.volatility_charts().await?;
|
|
106
|
+
let summary_stats_table = ticker.summary_stats_table().await?;
|
|
107
|
+
let performance_stats_table = ticker.performance_stats_table().await?;
|
|
108
|
+
let financials_tables = ticker.financials_tables().await?;
|
|
109
|
+
|
|
110
|
+
candlestick_chart.show();
|
|
111
|
+
performance_chart.show();
|
|
112
|
+
summary_stats_table.show();
|
|
113
|
+
performance_stats_table.show();
|
|
114
|
+
volatility_charts["Volatility Surface"].show();
|
|
115
|
+
volatility_charts["Volatility Smile"].show();
|
|
116
|
+
volatility_charts["Volatility Term Structure"].show();
|
|
117
|
+
financials_tables["Income Statement"].show();
|
|
118
|
+
financials_tables["Balance Sheet"].show();
|
|
119
|
+
financials_tables["Cashflow Statement"].show();
|
|
120
|
+
financials_tables["Financial Ratios"].show();
|
|
121
|
+
|
|
122
|
+
Ok(())
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Portfolio Optimization
|
|
128
|
+
|
|
129
|
+
```rust
|
|
130
|
+
use std::error::Error;
|
|
131
|
+
use finalytics::prelude::*;
|
|
132
|
+
|
|
133
|
+
#[tokio::main]
|
|
134
|
+
async fn main() -> Result<(), Box<dyn Error>> {
|
|
135
|
+
// Construct the Portfolio Object
|
|
136
|
+
let ticker_symbols = Vec::from(["NVDA", "BRK-A", "AAPL", "ZN=F"]);
|
|
137
|
+
let portfolio = PortfolioBuilder::new().ticker_symbols(ticker_symbols)
|
|
138
|
+
.benchmark_symbol("^GSPC")
|
|
139
|
+
.start_date("2017-01-01")
|
|
140
|
+
.end_date("2023-01-01")
|
|
141
|
+
.interval(Interval::OneDay)
|
|
142
|
+
.confidence_level(0.95)
|
|
143
|
+
.risk_free_rate(0.02)
|
|
144
|
+
.max_iterations(1000)
|
|
145
|
+
.objective_function(ObjectiveFunction::MaxSharpe)
|
|
146
|
+
.build().await?;
|
|
147
|
+
|
|
148
|
+
// Display Portfolio Optimization Results
|
|
149
|
+
println!("{:?}", portfolio.performance_stats);
|
|
150
|
+
|
|
151
|
+
// Display Portfolio Analytics Charts
|
|
152
|
+
portfolio.optimization_chart()?.show();
|
|
153
|
+
portfolio.performance_chart()?.show();
|
|
154
|
+
portfolio.asset_returns_chart()?.show();
|
|
155
|
+
portfolio.performance_stats_table()?.show();
|
|
156
|
+
|
|
157
|
+
Ok(())
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Sample Applications
|
|
162
|
+
|
|
163
|
+
<h3><a href="https://finalytics.rs/ticker">Ticker Charts Viewer</a></h3>
|
|
164
|
+
|
|
165
|
+
This sample application allows you to perform security analysis based on the Finalytics Library.
|
|
166
|
+
|
|
167
|
+
<h3><a href="https://finalytics.rs/portfolio">Portfolio Charts Viewer</a></h3>
|
|
168
|
+
|
|
169
|
+
This sample application enables you to perform portfolio optimization based on the Finalytics Library.
|
|
170
|
+
|
|
171
|
+
<h3><a href="https://t.me/finalytics_bot">Telegram Bot</a></h3>
|
|
172
|
+
The Finalytics Telegram Bot allows you to perform security analysis, portfolio optimization and news sentiment analysis directly from Telegram.
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## Python Binding
|
|
176
|
+
|
|
177
|
+
[](https://pypi.org/project/finalytics/)
|
|
178
|
+

|
|
179
|
+
[](https://finalytics.rs/)
|
|
180
|
+
[](https://finalytics-py.readthedocs.io/en/latest/?badge=latest)
|
|
181
|
+

|
|
182
|
+

|
|
183
|
+

|
|
184
|
+
[](https://www.codefactor.io/repository/github/nnamdi-sys/finalytics-py)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Installation
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pip install finalytics
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Documentation
|
|
194
|
+
|
|
195
|
+
View Library documentation on readthedocs [here](https://finalytics-py.readthedocs.io/en/latest/)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
### Security Analysis
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from finalytics import Ticker
|
|
202
|
+
|
|
203
|
+
ticker = Ticker(symbol="AAPL")
|
|
204
|
+
print(ticker.get_current_price())
|
|
205
|
+
print(ticker.get_summary_stats())
|
|
206
|
+
print(ticker.get_price_history(start="2023-01-01", end="2023-10-31", interval="1d"))
|
|
207
|
+
print(ticker.get_options_chain())
|
|
208
|
+
print(ticker.get_news(compute_sentiment=True))
|
|
209
|
+
print(ticker.get_income_statement())
|
|
210
|
+
print(ticker.get_balance_sheet())
|
|
211
|
+
print(ticker.get_cashflow_statement())
|
|
212
|
+
print(ticker.get_financial_ratios())
|
|
213
|
+
print(ticker.compute_performance_stats(start="2023-01-01", end="2023-10-31", interval="1d", benchmark="^GSPC",
|
|
214
|
+
confidence_level=0.95, risk_free_rate=0.02))
|
|
215
|
+
ticker.display_performance_chart(start="2023-01-01", end="2023-10-31", interval="1d", benchmark="^GSPC",
|
|
216
|
+
confidence_level=0.95, risk_free_rate=0.02, display_format="notebook")
|
|
217
|
+
ticker.display_candlestick_chart(start="2023-01-01", end="2023-10-31", interval="1d", display_format="html")
|
|
218
|
+
ticker.display_options_chart(risk_free_rate=0.02, chart_type="surface", display_format="png")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Portfolio Optimization
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from finalytics import Portfolio
|
|
225
|
+
|
|
226
|
+
portfolio = Portfolio(ticker_symbols=["AAPL", "GOOG", "MSFT", "BTC-USD"],
|
|
227
|
+
benchmark_symbol="^GSPC", start_date="2020-01-01", end_date="2022-01-01", interval="1d",
|
|
228
|
+
confidence_level=0.95, risk_free_rate=0.02, max_iterations=1000,
|
|
229
|
+
objective_function="max_sharpe")
|
|
230
|
+
print(portfolio.get_optimization_results())
|
|
231
|
+
portfolio.display_portfolio_charts("performance", "html")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "finalytics"
|
|
3
|
+
version = "0.4.0"
|
|
4
|
+
authors = ["Nnamdi Olisaeloka <nnamdiolisaeloka@gmail.com>"]
|
|
5
|
+
edition = "2021"
|
|
6
|
+
homepage = "https://finalytics.rs/"
|
|
7
|
+
repository = "https://github.com/Nnamdi-sys/finalytics"
|
|
8
|
+
license = "MIT"
|
|
9
|
+
description = "A rust library for financial data analysis"
|
|
10
|
+
readme = "../README.md"
|
|
11
|
+
keywords = ["finance", "analytics"]
|
|
12
|
+
|
|
13
|
+
[lib]
|
|
14
|
+
path = "src/lib.rs"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
[dependencies]
|
|
18
|
+
openssl = { version = "0.10.64", features = ["vendored"] }
|
|
19
|
+
serde = { version = "1.0.197", features = ["derive"] }
|
|
20
|
+
serde_json = "1.0.115"
|
|
21
|
+
reqwest = { version = "0.12.2", features = ["json"] }
|
|
22
|
+
tokio = { version = "1.32.0", features = ["full"] }
|
|
23
|
+
chrono = "0.4.37"
|
|
24
|
+
select = "0.6.0"
|
|
25
|
+
scraper = "0.19.0"
|
|
26
|
+
sentiment = "0.1.1"
|
|
27
|
+
polars = { version = "0.33.2", default-features = false, features = ["lazy", "dtype-datetime", "rows"] }
|
|
28
|
+
ta = "0.5.0"
|
|
29
|
+
num-format = "0.4.4"
|
|
30
|
+
smartcore = "0.3.2"
|
|
31
|
+
statrs = "0.16.0"
|
|
32
|
+
rand = "0.8.5"
|
|
33
|
+
ndarray = "0.15.6"
|
|
34
|
+
rand_distr = "0.4.3"
|
|
35
|
+
optimization = "0.2.0"
|
|
36
|
+
rusqlite = { version = "0.31.0", features = ["bundled"] }
|
|
37
|
+
lazy_static = "1.4.0"
|
|
38
|
+
r2d2 = "0.8.10"
|
|
39
|
+
r2d2_sqlite = "0.24.0"
|
|
40
|
+
html-escape = "0.2.13"
|
|
41
|
+
plotly = { version = "0.8.4", package = "plotly-fork"}
|
|
42
|
+
|
|
43
|
+
[features]
|
|
44
|
+
kaleido = ["plotly/kaleido"]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::error::Error;
|
|
3
|
+
use polars::prelude::*;
|
|
4
|
+
use crate::data::ticker::TickerData;
|
|
5
|
+
use crate::models::ticker::Ticker;
|
|
6
|
+
|
|
7
|
+
pub trait Financials {
|
|
8
|
+
fn financial_ratios(&self) -> impl std::future::Future<Output = Result<DataFrame, Box<dyn Error>>>;
|
|
9
|
+
fn income_statement(&self) -> impl std::future::Future<Output = Result<DataFrame, Box<dyn Error>>>;
|
|
10
|
+
fn balance_sheet(&self) -> impl std::future::Future<Output = Result<DataFrame, Box<dyn Error>>>;
|
|
11
|
+
fn cashflow_statement(&self) -> impl std::future::Future<Output = Result<DataFrame, Box<dyn Error>>>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl Financials for Ticker {
|
|
15
|
+
/// Computes financial ratios
|
|
16
|
+
///
|
|
17
|
+
/// # Returns
|
|
18
|
+
///
|
|
19
|
+
/// * `Financials` struct
|
|
20
|
+
async fn financial_ratios(&self) -> Result<DataFrame, Box<dyn Error>>{
|
|
21
|
+
let income_statement = self.get_fundamentals("income-statement", "quarterly").await?;
|
|
22
|
+
let balance_sheet = self.get_fundamentals("balance-sheet", "quarterly").await?;
|
|
23
|
+
let cash_flow = self.get_fundamentals("cash-flow", "quarterly").await?;
|
|
24
|
+
|
|
25
|
+
let df = df!(
|
|
26
|
+
"date" => *&income_statement.column("asOfDate")?,
|
|
27
|
+
"gross_profit_margin" => *&income_statement.column("GrossProfit")? / *&income_statement.column("TotalRevenue")?,
|
|
28
|
+
"operating_profit_margin" => *&income_statement.column("EBIT")? / *&income_statement.column("TotalRevenue")?,
|
|
29
|
+
"net_profit_margin" => *&income_statement.column("NetIncome")? / *&income_statement.column("TotalRevenue")?,
|
|
30
|
+
"return_on_assets" => *&income_statement.column("NetIncome")? / *&balance_sheet.column("TotalAssets")?,
|
|
31
|
+
"return_on_equity" => *&income_statement.column("NetIncome")? / *&balance_sheet.column("TotalEquityGrossMinorityInterest")?,
|
|
32
|
+
"quick_ratio" => *&balance_sheet.column("CurrentAssets")? / *&balance_sheet.column("CurrentLiabilities")?,
|
|
33
|
+
"current_ratio" => *&balance_sheet.column("CurrentAssets")? / *&balance_sheet.column("CurrentLiabilities")?,
|
|
34
|
+
"debt_to_equity" => *&balance_sheet.column("TotalLiabilitiesNetMinorityInterest")? / *&balance_sheet.column("TotalEquityGrossMinorityInterest")?,
|
|
35
|
+
"debt_to_assets" => *&balance_sheet.column("TotalLiabilitiesNetMinorityInterest")? / *&balance_sheet.column("TotalAssets")?,
|
|
36
|
+
"interest_coverage" => *&income_statement.column("EBIT")? / *&income_statement.column("InterestExpense")?,
|
|
37
|
+
"asset_turnover" => *&income_statement.column("TotalRevenue")? / *&balance_sheet.column("TotalAssets")?,
|
|
38
|
+
"inventory_turnover" => *&income_statement.column("CostOfRevenue")? / *&balance_sheet.column("Inventory")?,
|
|
39
|
+
"days_receivable" => *&balance_sheet.column("AccountsReceivable")? / *&income_statement.column("TotalRevenue")? * 365.0,
|
|
40
|
+
"days_inventory" => *&balance_sheet.column("Inventory")? / *&income_statement.column("CostOfRevenue")? * 365.0,
|
|
41
|
+
"days_payable" => *&balance_sheet.column("AccountsPayable")? / *&income_statement.column("CostOfRevenue")? * 365.0,
|
|
42
|
+
"earnings_per_share" => *&income_statement.column("DilutedEPS")?,
|
|
43
|
+
"price_to_earnings" => *&balance_sheet.column("TotalCapitalization")? / *&income_statement.column("NetIncome")?,
|
|
44
|
+
"price_to_book" => *&balance_sheet.column("TotalCapitalization")? / *&balance_sheet.column("TotalEquityGrossMinorityInterest")?,
|
|
45
|
+
"price_to_sales" => *&balance_sheet.column("TotalCapitalization")? / *&income_statement.column("TotalRevenue")?,
|
|
46
|
+
"price_to_cash_flow" => *&balance_sheet.column("TotalCapitalization")? / *&cash_flow.column("OperatingCashFlow")?,
|
|
47
|
+
"price_to_free_cash_flow" => *&balance_sheet.column("TotalCapitalization")? / *&cash_flow.column("FreeCashFlow")?,
|
|
48
|
+
)?;
|
|
49
|
+
|
|
50
|
+
// Transpose the DataFrame
|
|
51
|
+
let dates = df.column("date")?.utf8()?.into_iter()
|
|
52
|
+
.map(|x| x.unwrap()).collect::<Vec<&str>>();
|
|
53
|
+
let df = df.drop("date").unwrap();
|
|
54
|
+
let items = Series::new("Items", df.get_column_names());
|
|
55
|
+
let mut transposed_df = df.transpose(None, None)?;
|
|
56
|
+
let _ = transposed_df.set_column_names(&dates)?;
|
|
57
|
+
let _ = transposed_df.insert_at_idx(0, items)?;
|
|
58
|
+
Ok(transposed_df)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Formats the income statement
|
|
62
|
+
///
|
|
63
|
+
/// # Returns
|
|
64
|
+
///
|
|
65
|
+
/// * `DataFrame` - Formatted income statement
|
|
66
|
+
async fn income_statement(&self) -> Result<DataFrame, Box<dyn Error>> {
|
|
67
|
+
let income_statement = self.get_fundamentals("income-statement", "quarterly").await?;
|
|
68
|
+
|
|
69
|
+
let mut ifrs_mapping = HashMap::new();
|
|
70
|
+
ifrs_mapping.insert("TotalRevenue", "Revenue");
|
|
71
|
+
ifrs_mapping.insert("GrossProfit", "Gross Profit");
|
|
72
|
+
ifrs_mapping.insert("OperatingExpense", "Operating Expenses");
|
|
73
|
+
ifrs_mapping.insert("NetIncome", "Net Income");
|
|
74
|
+
ifrs_mapping.insert("TaxProvision", "Income Tax Expense");
|
|
75
|
+
ifrs_mapping.insert("BasicEPS", "Earnings per Share - Basic");
|
|
76
|
+
ifrs_mapping.insert("DilutedEPS", "Earnings per Share - Diluted");
|
|
77
|
+
ifrs_mapping.insert("EBIT", "Operating Profit (EBIT)");
|
|
78
|
+
ifrs_mapping.insert("EBITDA", "EBITDA");
|
|
79
|
+
ifrs_mapping.insert("ReconciledCostOfRevenue", "Cost of Goods Sold");
|
|
80
|
+
ifrs_mapping.insert("ReconciledDepreciation", "Depreciation and Amortization");
|
|
81
|
+
ifrs_mapping.insert("InterestIncome", "Interest Income");
|
|
82
|
+
ifrs_mapping.insert("InterestExpense", "Interest Expense");
|
|
83
|
+
|
|
84
|
+
let mut cols = vec![
|
|
85
|
+
"TotalRevenue", "ReconciledCostOfRevenue", "GrossProfit", "OperatingExpense",
|
|
86
|
+
"EBITDA", "ReconciledDepreciation", "EBIT", "InterestIncome", "InterestExpense",
|
|
87
|
+
"TaxProvision", "NetIncome", "BasicEPS", "DilutedEPS",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
// remove item from cols if it doesnt exist in the income statement dataframe
|
|
91
|
+
cols.retain(|x| income_statement.column(*x).is_ok());
|
|
92
|
+
|
|
93
|
+
let df = income_statement.clone().select(&cols)?;
|
|
94
|
+
|
|
95
|
+
// Create a vector of expressions for renaming and selecting columns
|
|
96
|
+
let expressions: Vec<Expr> = cols
|
|
97
|
+
.iter()
|
|
98
|
+
.map(|old_name| {
|
|
99
|
+
let new_name = ifrs_mapping.get(*old_name).unwrap_or(old_name);
|
|
100
|
+
col(*old_name).alias(new_name)
|
|
101
|
+
})
|
|
102
|
+
.collect();
|
|
103
|
+
|
|
104
|
+
// Select and alias columns using the expressions
|
|
105
|
+
let renamed_df = df.lazy().select(expressions).collect()?;
|
|
106
|
+
|
|
107
|
+
// Transpose the DataFrame
|
|
108
|
+
let dates = income_statement.column("asOfDate")?.utf8()?.into_iter()
|
|
109
|
+
.map(|x| x.unwrap()).collect::<Vec<&str>>();
|
|
110
|
+
let items = Series::new("Items", renamed_df.get_column_names());
|
|
111
|
+
let mut transposed_df = renamed_df.transpose(None, None)?;
|
|
112
|
+
let _ = transposed_df.set_column_names(&dates)?;
|
|
113
|
+
let _ = transposed_df.insert_at_idx(0, items)?;
|
|
114
|
+
Ok(transposed_df)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Formats the balance sheet
|
|
118
|
+
///
|
|
119
|
+
/// # Returns
|
|
120
|
+
///
|
|
121
|
+
/// * `DataFrame` - Formatted balance sheet
|
|
122
|
+
async fn balance_sheet(&self) -> Result<DataFrame, Box<dyn Error>> {
|
|
123
|
+
let balance_sheet = self.get_fundamentals("balance-sheet", "quarterly").await?;
|
|
124
|
+
|
|
125
|
+
let mut ifrs_mapping = HashMap::new();
|
|
126
|
+
|
|
127
|
+
// Assets
|
|
128
|
+
ifrs_mapping.insert("CashAndCashEquivalents", "Cash and Cash Equivalents");
|
|
129
|
+
ifrs_mapping.insert("AccountsReceivable", "Accounts Receivable");
|
|
130
|
+
ifrs_mapping.insert("Inventory", "Inventories");
|
|
131
|
+
ifrs_mapping.insert("OtherCurrentAssets", "Other Current Assets");
|
|
132
|
+
ifrs_mapping.insert("CurrentAssets", "Total Current Assets");
|
|
133
|
+
ifrs_mapping.insert("NetPPE", "Property, Plant, and Equipment (Net)");
|
|
134
|
+
ifrs_mapping.insert("GoodwillAndOtherIntangibleAssets", "Intangible Assets");
|
|
135
|
+
ifrs_mapping.insert("OtherNonCurrentAssets", "Other Non-Current Assets");
|
|
136
|
+
ifrs_mapping.insert("TotalNonCurrentAssets", "Total Non-Current Assets");
|
|
137
|
+
ifrs_mapping.insert("TotalAssets", "Total Assets");
|
|
138
|
+
|
|
139
|
+
// Liabilities
|
|
140
|
+
ifrs_mapping.insert("AccountsPayable", "Accounts Payable");
|
|
141
|
+
ifrs_mapping.insert("CurrentDebt", "Short-Term Debt");
|
|
142
|
+
ifrs_mapping.insert("OtherCurrentLiabilities", "Other Current Liabilities");
|
|
143
|
+
ifrs_mapping.insert("CurrentLiabilities", "Total Current Liabilities");
|
|
144
|
+
ifrs_mapping.insert("LongTermDebt", "Long-Term Debt");
|
|
145
|
+
ifrs_mapping.insert("OtherNonCurrentLiabilities", "Other Non-Current Liabilities");
|
|
146
|
+
ifrs_mapping.insert("TotalNonCurrentLiabilitiesNetMinorityInterest", "Total Non-Current Liabilities");
|
|
147
|
+
ifrs_mapping.insert("TotalLiabilitiesNetMinorityInterest", "Total Liabilities");
|
|
148
|
+
|
|
149
|
+
// Equity
|
|
150
|
+
ifrs_mapping.insert("CommonStock", "Common Stock");
|
|
151
|
+
ifrs_mapping.insert("RetainedEarnings", "Retained Earnings");
|
|
152
|
+
ifrs_mapping.insert("CommonStockEquity", "Total Equity");
|
|
153
|
+
ifrs_mapping.insert("TotalEquityGrossMinorityInterest", "Total Liabilities and Equity");
|
|
154
|
+
|
|
155
|
+
let mut cols = vec![
|
|
156
|
+
"CashAndCashEquivalents", "AccountsReceivable", "Inventory", "OtherCurrentAssets",
|
|
157
|
+
"CurrentAssets", "NetPPE", "GoodwillAndOtherIntangibleAssets",
|
|
158
|
+
"OtherNonCurrentAssets", "TotalNonCurrentAssets", "TotalAssets",
|
|
159
|
+
"AccountsPayable", "CurrentDebt", "OtherCurrentLiabilities", "CurrentLiabilities", "LongTermDebt",
|
|
160
|
+
"OtherNonCurrentLiabilities", "TotalNonCurrentLiabilitiesNetMinorityInterest", "TotalLiabilitiesNetMinorityInterest",
|
|
161
|
+
"CommonStock", "RetainedEarnings", "CommonStockEquity", "TotalEquityGrossMinorityInterest",
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
// remove item from cols if it doesnt exist in the balance sheet dataframe
|
|
165
|
+
cols.retain(|x| balance_sheet.column(*x).is_ok());
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
let df = balance_sheet.clone().select(&cols)?;
|
|
169
|
+
|
|
170
|
+
// Create a vector of expressions for renaming and selecting columns
|
|
171
|
+
let expressions: Vec<Expr> = cols
|
|
172
|
+
.iter()
|
|
173
|
+
.map(|old_name| {
|
|
174
|
+
let new_name = ifrs_mapping.get(*old_name).unwrap_or(old_name);
|
|
175
|
+
col(*old_name).alias(new_name)
|
|
176
|
+
})
|
|
177
|
+
.collect();
|
|
178
|
+
|
|
179
|
+
// Select and alias columns using the expressions
|
|
180
|
+
let renamed_df = df.lazy().select(expressions).collect()?;
|
|
181
|
+
|
|
182
|
+
// Transpose the DataFrame
|
|
183
|
+
let dates = balance_sheet.column("asOfDate")?.utf8()?.into_iter()
|
|
184
|
+
.map(|x| x.unwrap()).collect::<Vec<&str>>();
|
|
185
|
+
let items = Series::new("Items", renamed_df.get_column_names());
|
|
186
|
+
let mut transposed_df = renamed_df.transpose(None, None)?;
|
|
187
|
+
let _ = transposed_df.set_column_names(&dates)?;
|
|
188
|
+
let _ = transposed_df.insert_at_idx(0, items)?;
|
|
189
|
+
|
|
190
|
+
Ok(transposed_df)
|
|
191
|
+
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// Formats the cash flow statement
|
|
195
|
+
///
|
|
196
|
+
/// # Returns
|
|
197
|
+
///
|
|
198
|
+
/// * `DataFrame` - Formatted cash flow statement
|
|
199
|
+
async fn cashflow_statement(&self) -> Result<DataFrame, Box<dyn Error>> {
|
|
200
|
+
let cash_flow = self.get_fundamentals("cash-flow", "quarterly").await?;
|
|
201
|
+
|
|
202
|
+
let mut ifrs_mapping = HashMap::new();
|
|
203
|
+
|
|
204
|
+
// Operating Activities
|
|
205
|
+
ifrs_mapping.insert("NetIncomeFromContinuingOperations", "Net Income from Continuing Operations");
|
|
206
|
+
ifrs_mapping.insert("DepreciationAmortizationDepletion", "Depreciation, Amortization, and Depletion");
|
|
207
|
+
ifrs_mapping.insert("StockBasedCompensation", "Stock-Based Compensation");
|
|
208
|
+
ifrs_mapping.insert("DeferredIncomeTax", "Deferred Income Tax");
|
|
209
|
+
ifrs_mapping.insert("ChangeInWorkingCapital", "Changes in Working Capital");
|
|
210
|
+
ifrs_mapping.insert("CashFlowFromContinuingOperatingActivities", "Cash Flow from Continuing Operating Activities");
|
|
211
|
+
|
|
212
|
+
// Investing Activities
|
|
213
|
+
ifrs_mapping.insert("PurchaseOfPPE", "Purchase of Property, Plant, and Equipment");
|
|
214
|
+
ifrs_mapping.insert("PurchaseOfBusiness", "Purchase of Business");
|
|
215
|
+
ifrs_mapping.insert("NetInvestmentPurchaseAndSale", "Net Investment Purchase and Sale");
|
|
216
|
+
ifrs_mapping.insert("SaleOfInvestment", "Sale of Investment");
|
|
217
|
+
ifrs_mapping.insert("PurchaseOfInvestment", "Purchase of Investment");
|
|
218
|
+
ifrs_mapping.insert("NetOtherInvestingChanges", "Net Other Investing Changes");
|
|
219
|
+
ifrs_mapping.insert("CashFlowFromContinuingInvestingActivities", "Cash Flow from Continuing Investing Activities");
|
|
220
|
+
|
|
221
|
+
// Financing Activities
|
|
222
|
+
ifrs_mapping.insert("IssuanceOfCapitalStock", "Issuance of Capital Stock");
|
|
223
|
+
ifrs_mapping.insert("CommonStockIssuance", "Common Stock Issuance");
|
|
224
|
+
ifrs_mapping.insert("NetCommonStockIssuance", "Net Common Stock Issuance");
|
|
225
|
+
ifrs_mapping.insert("CommonStockDividendPaid", "Common Stock Dividend Paid");
|
|
226
|
+
ifrs_mapping.insert("CashDividendsPaid", "Cash Dividends Paid");
|
|
227
|
+
ifrs_mapping.insert("RepurchaseOfCapitalStock", "Repurchase of Capital Stock");
|
|
228
|
+
ifrs_mapping.insert("LongTermDebtPayments", "Long-Term Debt Payments");
|
|
229
|
+
ifrs_mapping.insert("NetLongTermDebtIssuance", "Net Long-Term Debt Issuance");
|
|
230
|
+
ifrs_mapping.insert("NetIssuancePaymentsOfDebt", "Net Issuance/Payments of Debt");
|
|
231
|
+
ifrs_mapping.insert("NetOtherFinancingCharges", "Net Other Financing Charges");
|
|
232
|
+
ifrs_mapping.insert("CashFlowFromContinuingFinancingActivities", "Cash Flow from Continuing Financing Activities");
|
|
233
|
+
|
|
234
|
+
// Summary
|
|
235
|
+
ifrs_mapping.insert("BeginningCashPosition", "Beginning Cash Position");
|
|
236
|
+
ifrs_mapping.insert("OperatingCashFlow", "Operating Cash Flow");
|
|
237
|
+
ifrs_mapping.insert("InvestingCashFlow", "Investing Cash Flow");
|
|
238
|
+
ifrs_mapping.insert("FinancingCashFlow", "Financing Cash Flow");
|
|
239
|
+
ifrs_mapping.insert("EndCashPosition", "Ending Cash Position");
|
|
240
|
+
ifrs_mapping.insert("FreeCashFlow", "Free Cash Flow");
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
let mut cols = vec![
|
|
244
|
+
"NetIncomeFromContinuingOperations", "DepreciationAmortizationDepletion", "StockBasedCompensation",
|
|
245
|
+
"DeferredIncomeTax", "ChangeInWorkingCapital", "CashFlowFromContinuingOperatingActivities",
|
|
246
|
+
"PurchaseOfPPE", "PurchaseOfBusiness", "NetInvestmentPurchaseAndSale", "SaleOfInvestment", "PurchaseOfInvestment",
|
|
247
|
+
"NetOtherInvestingChanges", "CashFlowFromContinuingInvestingActivities", "IssuanceOfCapitalStock",
|
|
248
|
+
"CommonStockIssuance", "NetCommonStockIssuance", "CommonStockDividendPaid", "CashDividendsPaid",
|
|
249
|
+
"RepurchaseOfCapitalStock", "LongTermDebtPayments", "NetLongTermDebtIssuance", "NetIssuancePaymentsOfDebt",
|
|
250
|
+
"NetOtherFinancingCharges", "CashFlowFromContinuingFinancingActivities", "EffectOfExchangeRateChanges",
|
|
251
|
+
"BeginningCashPosition", "OperatingCashFlow", "InvestingCashFlow", "FinancingCashFlow", "EndCashPosition", "FreeCashFlow",
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
// remove item from cols if it doesnt exist in the cash flow dataframe
|
|
255
|
+
cols.retain(|x| cash_flow.column(*x).is_ok());
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
let df = cash_flow.clone().select(&cols)?;
|
|
259
|
+
|
|
260
|
+
// Create a vector of expressions for renaming and selecting columns
|
|
261
|
+
let expressions: Vec<Expr> = cols
|
|
262
|
+
.iter()
|
|
263
|
+
.map(|old_name| {
|
|
264
|
+
let new_name = ifrs_mapping.get(*old_name).unwrap_or(old_name);
|
|
265
|
+
col(*old_name).alias(new_name)
|
|
266
|
+
})
|
|
267
|
+
.collect();
|
|
268
|
+
|
|
269
|
+
// Select and alias columns using the expressions
|
|
270
|
+
let renamed_df = df.lazy().select(expressions).collect()?;
|
|
271
|
+
|
|
272
|
+
// Transpose the DataFrame
|
|
273
|
+
let dates = cash_flow.column("asOfDate")?.utf8()?.into_iter()
|
|
274
|
+
.map(|x| x.unwrap()).collect::<Vec<&str>>();
|
|
275
|
+
let items = Series::new("Items", renamed_df.get_column_names());
|
|
276
|
+
let mut transposed_df = renamed_df.transpose(None, None)?;
|
|
277
|
+
let _ = transposed_df.set_column_names(&dates)?;
|
|
278
|
+
let _ = transposed_df.insert_at_idx(0, items)?;
|
|
279
|
+
|
|
280
|
+
Ok(transposed_df)
|
|
281
|
+
}
|
|
282
|
+
}
|