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
+ [![PyPI - Version](https://img.shields.io/pypi/v/yfinance-exporter)](https://pypi.org/project/yfinance-exporter/) [![Docker Image Version](https://img.shields.io/docker/v/jaesivsm/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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
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()