yfinance-exporter 1.0.0__py3-none-any.whl
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.
@@ -0,0 +1,61 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: yfinance-exporter
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary:
|
5
|
+
Author: François Schmidts
|
6
|
+
Author-email: francois@schmidts.fr
|
7
|
+
Requires-Python: >=3.12,<4.0
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
10
|
+
Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
|
11
|
+
Requires-Dist: the-conf (>=1.0.4,<2.0.0)
|
12
|
+
Requires-Dist: yfinance (>=0.2.51,<0.3.0)
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
|
15
|
+
[](https://pypi.org/project/yfinance-exporter/) [](https://hub.docker.com/r/jaesivsm/yfinance-exporter/tags)
|
16
|
+
|
17
|
+
# YFinance Exporter
|
18
|
+
|
19
|
+
|
20
|
+
## Metrics served
|
21
|
+
|
22
|
+
|
23
|
+
```json
|
24
|
+
{
|
25
|
+
"stocks": [
|
26
|
+
{"isin": "FR0000120073", "name": "AIR LIQUIDE", "ycode": "AI.PA"}
|
27
|
+
]
|
28
|
+
}
|
29
|
+
```
|
30
|
+
|
31
|
+
## Running it
|
32
|
+
|
33
|
+
For the next few bits of code, we'll suppose you have a working configuration above in `~/.config/yfinance-exporter.json`.
|
34
|
+
|
35
|
+
### ... with python:
|
36
|
+
|
37
|
+
```shell
|
38
|
+
pip install yfinance-exporter
|
39
|
+
python -m yfinance_exporter
|
40
|
+
```
|
41
|
+
|
42
|
+
### ... with docker:
|
43
|
+
|
44
|
+
```shell
|
45
|
+
docker run -v ~/.config/:/etc/yfinance-exporter/:ro -p 9100:9100 yfinance-exporter:main
|
46
|
+
```
|
47
|
+
|
48
|
+
You'll then be able retrieve some values:
|
49
|
+
|
50
|
+
```shell
|
51
|
+
curl localhost:9100/metrics
|
52
|
+
|
53
|
+
# HELP yfinance_exporter
|
54
|
+
# TYPE yfinance_exporter gauge
|
55
|
+
yfinance_exporter{status="loop"} 1.0
|
56
|
+
yfinance_exporter{status="loop-time"} 10.179875
|
57
|
+
yfinance_exporter{status="ok-stock"} 45.0
|
58
|
+
yfinance_exporter{status="ko-stock"} 2.0
|
59
|
+
[...]
|
60
|
+
```
|
61
|
+
|
@@ -0,0 +1,4 @@
|
|
1
|
+
yfinance_exporter.py,sha256=HSPavKv4kyGfO9oPUNP1eSDLbJOTg1nHlDTI35SxKNA,3305
|
2
|
+
yfinance_exporter-1.0.0.dist-info/METADATA,sha256=YmLQllXUmCFf4HAnOf0cvp8ZG7bCHWyPmTTvpYpiJCI,1525
|
3
|
+
yfinance_exporter-1.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
4
|
+
yfinance_exporter-1.0.0.dist-info/RECORD,,
|
yfinance_exporter.py
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import logging
|
4
|
+
|
5
|
+
from prometheus_client import Gauge, start_http_server
|
6
|
+
from datetime import datetime
|
7
|
+
import time
|
8
|
+
from the_conf import TheConf
|
9
|
+
from yfinance import Ticker
|
10
|
+
|
11
|
+
metaconf = {
|
12
|
+
"source_order": ["files"],
|
13
|
+
"config_files": [
|
14
|
+
"~/.config/yfinance-exporter.json",
|
15
|
+
"/etc/yfinance-exporter/yfinance-exporter.json",
|
16
|
+
],
|
17
|
+
"parameters": [
|
18
|
+
{
|
19
|
+
"type": "list",
|
20
|
+
"stocks": [
|
21
|
+
{"name": {"type": str}},
|
22
|
+
{"isin": {"type": str}},
|
23
|
+
{"ycode": {"type": str}},
|
24
|
+
],
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"loop": [
|
28
|
+
{"interval": {"type": int, "default": 240}},
|
29
|
+
]
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"prometheus": [
|
33
|
+
{"port": {"type": int, "default": 9100}},
|
34
|
+
{"namespace": {"type": str, "default": ""}},
|
35
|
+
]
|
36
|
+
},
|
37
|
+
{"logging": [{"level": {"default": "WARNING"}}]},
|
38
|
+
],
|
39
|
+
}
|
40
|
+
conf = TheConf(metaconf)
|
41
|
+
logger = logging.getLogger("yfinance-exporter")
|
42
|
+
try:
|
43
|
+
logger.setLevel(getattr(logging, conf.logging.level))
|
44
|
+
logger.addHandler(logging.StreamHandler())
|
45
|
+
except AttributeError as error:
|
46
|
+
raise AttributeError(
|
47
|
+
f"{conf.logging.level} isn't accepted, only DEBUG, INFO, WARNING, "
|
48
|
+
"ERROR and FATAL are accepted"
|
49
|
+
) from error
|
50
|
+
|
51
|
+
YFINANCE_EXPORTER = Gauge(
|
52
|
+
"yfinance_exporter",
|
53
|
+
"",
|
54
|
+
["status"],
|
55
|
+
namespace=conf.prometheus.namespace,
|
56
|
+
)
|
57
|
+
STOCK = Gauge(
|
58
|
+
"financial_positions",
|
59
|
+
"",
|
60
|
+
[
|
61
|
+
"bank",
|
62
|
+
"account_type",
|
63
|
+
"account_name",
|
64
|
+
"account_id",
|
65
|
+
"line_name",
|
66
|
+
"line_id",
|
67
|
+
"value_type", # par-value, shares-value, gain, gain-percent, quantity
|
68
|
+
],
|
69
|
+
namespace=conf.prometheus.namespace,
|
70
|
+
)
|
71
|
+
|
72
|
+
|
73
|
+
def collect(stock):
|
74
|
+
logger.debug("Collecting for %r", stock.name)
|
75
|
+
labels = [
|
76
|
+
stock.ycode.split(".")[1] if "." in stock.ycode else "",
|
77
|
+
"stocks",
|
78
|
+
"market",
|
79
|
+
"market",
|
80
|
+
stock.name,
|
81
|
+
stock.isin,
|
82
|
+
"par-value",
|
83
|
+
]
|
84
|
+
ticker = Ticker(stock.ycode)
|
85
|
+
try:
|
86
|
+
value = ticker.fast_info["last_price"]
|
87
|
+
except KeyError:
|
88
|
+
value = None
|
89
|
+
if not isinstance(value, (int, float)):
|
90
|
+
try:
|
91
|
+
STOCK.remove(*labels)
|
92
|
+
except KeyError:
|
93
|
+
pass
|
94
|
+
return False
|
95
|
+
STOCK.labels(*labels).set(value)
|
96
|
+
return True
|
97
|
+
|
98
|
+
|
99
|
+
def main():
|
100
|
+
YFINANCE_EXPORTER.labels("loop-count").set(0)
|
101
|
+
while True:
|
102
|
+
start = datetime.now()
|
103
|
+
|
104
|
+
results = {"ok-stock": 0, "ko-stock": 0}
|
105
|
+
for stock in conf.stocks:
|
106
|
+
if collect(stock):
|
107
|
+
results["ok-stock"] += 1
|
108
|
+
else:
|
109
|
+
results["ko-stock"] += 1
|
110
|
+
|
111
|
+
exec_interval = (datetime.now() - start).total_seconds()
|
112
|
+
YFINANCE_EXPORTER.labels("loop-duration-second").set(exec_interval)
|
113
|
+
YFINANCE_EXPORTER.labels("loop-count").inc()
|
114
|
+
for result, count in results.items():
|
115
|
+
YFINANCE_EXPORTER.labels(result).set(count)
|
116
|
+
|
117
|
+
interval = conf.loop.interval - exec_interval
|
118
|
+
if interval > 0:
|
119
|
+
time.sleep(interval)
|
120
|
+
|
121
|
+
|
122
|
+
if __name__ == "__main__":
|
123
|
+
logger.info(
|
124
|
+
"Starting yfinance exporter with %d stocks to watch", len(conf.stocks)
|
125
|
+
)
|
126
|
+
start_http_server(conf.prometheus.port)
|
127
|
+
main()
|