degiro-cli 0.1.2__tar.gz → 0.1.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.
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/PKG-INFO +55 -15
- degiro-cli-0.1.2/degiro_cli.egg-info/PKG-INFO → degiro_cli-0.1.4/README.md +34 -27
- degiro-cli-0.1.2/README.md → degiro_cli-0.1.4/degiro_cli.egg-info/PKG-INFO +67 -9
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degiro_cli.egg-info/SOURCES.txt +2 -6
- degiro_cli-0.1.4/degiro_cli.egg-info/entry_points.txt +5 -0
- degiro_cli-0.1.4/degiro_cli.egg-info/requires.txt +14 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degirocli/history.py +5 -5
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degirocli/login.py +31 -24
- degiro_cli-0.1.4/degirocli/portfolio.py +173 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degirocli/search.py +4 -4
- degiro_cli-0.1.4/pyproject.toml +60 -0
- degiro-cli-0.1.2/bin/degiro-history +0 -8
- degiro-cli-0.1.2/bin/degiro-login +0 -8
- degiro-cli-0.1.2/bin/degiro-logout +0 -0
- degiro-cli-0.1.2/bin/degiro-portfolio +0 -0
- degiro-cli-0.1.2/bin/degiro-search +0 -8
- degiro-cli-0.1.2/degiro_cli.egg-info/requires.txt +0 -9
- degiro-cli-0.1.2/pyproject.toml +0 -4
- degiro-cli-0.1.2/setup.py +0 -68
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/LICENSE +0 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degiro_cli.egg-info/dependency_links.txt +0 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degiro_cli.egg-info/top_level.txt +0 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degirocli/__init__.py +0 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/degirocli/helpers.py +0 -0
- {degiro-cli-0.1.2 → degiro_cli-0.1.4}/setup.cfg +0 -0
|
@@ -1,27 +1,41 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: degiro-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Unofficial command line tools for Degiro
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Author-email: ohmajesticlama <ohmajesticlama@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/OhMajesticLama/degiro-cli
|
|
7
8
|
Project-URL: Documentation, https://ohmajesticlama.github.io/degiro-cli
|
|
9
|
+
Keywords: degiro,cli,finance,investment
|
|
8
10
|
Classifier: Programming Language :: Python :: 3
|
|
9
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
12
|
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
14
|
Classifier: Intended Audience :: End Users/Desktop
|
|
13
15
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
14
16
|
Requires-Python: >=3.8
|
|
15
17
|
Description-Content-Type: text/markdown
|
|
16
|
-
Provides-Extra: dev
|
|
17
18
|
License-File: LICENSE
|
|
19
|
+
Requires-Dist: degiroasync>=1.1.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.1; extra == "dev"
|
|
22
|
+
Requires-Dist: coverage>=6.3; extra == "dev"
|
|
23
|
+
Requires-Dist: flake8>=4.0.1; extra == "dev"
|
|
24
|
+
Requires-Dist: mypy>=0.931; extra == "dev"
|
|
25
|
+
Requires-Dist: build>=0.7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: twine>=3.8.0; extra == "dev"
|
|
27
|
+
Requires-Dist: sphinx>=4.4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: sphinx_rtd_theme>=1.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: myst-parser>=0.17.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ipython; extra == "dev"
|
|
31
|
+
Requires-Dist: ipdb; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
18
33
|
|
|
19
34
|
# DegiroAsync Command Line Interface
|
|
20
35
|
|
|
21
36
|
## Introduction
|
|
22
37
|
|
|
23
|
-
Command line tools for DEGIRO platform.
|
|
24
|
-
is to:
|
|
38
|
+
Command line tools for DEGIRO platform. Primary features:
|
|
25
39
|
- Search for STOCK products by various criteria.
|
|
26
40
|
- Access STOCK products history.
|
|
27
41
|
|
|
@@ -31,7 +45,11 @@ orders.
|
|
|
31
45
|
## Installation
|
|
32
46
|
|
|
33
47
|
``` sh
|
|
34
|
-
|
|
48
|
+
# With [uv](https://docs.astral.sh/uv/) (recommended)
|
|
49
|
+
uv tool install degiro-cli
|
|
50
|
+
|
|
51
|
+
# With pip
|
|
52
|
+
pip3 install -u degiro-cli
|
|
35
53
|
```
|
|
36
54
|
|
|
37
55
|
## Use
|
|
@@ -40,7 +58,7 @@ manipulation tools such as [CSV Kit](https://csvkit.rtfd.org/). Especially
|
|
|
40
58
|
the `csvlook` command if you intend to just visualize outputes from command
|
|
41
59
|
line.
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
### Login
|
|
44
62
|
|
|
45
63
|
``` sh
|
|
46
64
|
# Only the session id is stored, that means a new login will be required
|
|
@@ -48,6 +66,25 @@ line.
|
|
|
48
66
|
degiro-login
|
|
49
67
|
```
|
|
50
68
|
|
|
69
|
+
### Get portfolio
|
|
70
|
+
```sh
|
|
71
|
+
degiro-portfolio
|
|
72
|
+
# Output:
|
|
73
|
+
# position_type,exchange,isin,name,currency,size,price,value
|
|
74
|
+
# PRODUCT,EPA,FR0000121972,Schneider Electric SE,EUR,10,276.05,2760
|
|
75
|
+
# PRODUCT,SCG,DE000CL5C2Q2,SG Airbus Turbo illimités Call BAR 37.08 STR 35.6901 P 10 LV 1.23,EUR,100,15.635,1563.50
|
|
76
|
+
# CASH,N/A,EUR,EUR,EUR,3000.00,1,3000.00
|
|
77
|
+
|
|
78
|
+
# With csvlook formatting
|
|
79
|
+
degiro-portfolio | csvlook --max-column-width 30
|
|
80
|
+
# Output:
|
|
81
|
+
# | position_type | exchange | isin | name | currency | size | price | value |
|
|
82
|
+
# | ------------- | -------- | ------------ | ------------------------------ | -------- | -------- | ------- | --------- |
|
|
83
|
+
# | PRODUCT | EPA | FR0000121972 | Schneider Electric SE | EUR | 10,00 | 276,050 | 2 760,00 |
|
|
84
|
+
# | PRODUCT | SCG | DE000CL5C2Q2 | SG Airbus Turbo illimités C... | EUR | 100,00 | 15,635 | 1 563,50 |
|
|
85
|
+
# | CASH | | EUR | EUR | EUR | 3 000,00 | 1,000 | 3 000,00 |
|
|
86
|
+
```
|
|
87
|
+
|
|
51
88
|
### Find products
|
|
52
89
|
|
|
53
90
|
``` sh
|
|
@@ -76,7 +113,7 @@ degiro-search -t airbus | csvlook
|
|
|
76
113
|
# | FRA | AIRA | Airbus SE | EUR | US0092791005 |
|
|
77
114
|
```
|
|
78
115
|
|
|
79
|
-
|
|
116
|
+
### List countries available on the platform
|
|
80
117
|
``` sh
|
|
81
118
|
# List country codes available on the platform
|
|
82
119
|
degiro-search --list-countries
|
|
@@ -110,7 +147,7 @@ degiro-search --list-countries
|
|
|
110
147
|
# US
|
|
111
148
|
```
|
|
112
149
|
|
|
113
|
-
|
|
150
|
+
### By Index
|
|
114
151
|
``` sh
|
|
115
152
|
# List country codes available on the platform
|
|
116
153
|
degiro-search --list-indices
|
|
@@ -151,7 +188,7 @@ degiro-search --index 'EURO STOXX 50'
|
|
|
151
188
|
|
|
152
189
|
```
|
|
153
190
|
|
|
154
|
-
|
|
191
|
+
### Get symbol history
|
|
155
192
|
``` sh
|
|
156
193
|
degiro-history --period 1m EPA.SAF
|
|
157
194
|
# exchange,symbol,date,currency,open,high,low,close
|
|
@@ -206,11 +243,14 @@ degiro-history --period 1m EPA.SAF | csvlook
|
|
|
206
243
|
|
|
207
244
|
```
|
|
208
245
|
|
|
209
|
-
|
|
246
|
+
### Chain with other tools
|
|
210
247
|
|
|
248
|
+
degiro-cli can be chained with other command line tools to achieve more complex processing.
|
|
211
249
|
``` sh
|
|
250
|
+
degiro-login
|
|
212
251
|
# Example command line to pull history for all stocks in a country with
|
|
213
252
|
# the help of the great CLI tool csvkit
|
|
214
|
-
|
|
253
|
+
countr="NL"; degiro-search --country "$country" --no-header-row | csvcut -c 1-2 | sed 's/,/./' | xargs -n 100 degiro-history -p 5y | tee -a prices.$country.csv
|
|
215
254
|
```
|
|
216
255
|
|
|
256
|
+
|
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: degiro-cli
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: Unofficial command line tools for Degiro
|
|
5
|
-
Home-page: https://github.com/OhMajesticLama/degiro-cli
|
|
6
|
-
Author-email: ohmajesticlama@gmail.com
|
|
7
|
-
Project-URL: Documentation, https://ohmajesticlama.github.io/degiro-cli
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
-
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
14
|
-
Requires-Python: >=3.8
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Provides-Extra: dev
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
|
|
19
1
|
# DegiroAsync Command Line Interface
|
|
20
2
|
|
|
21
3
|
## Introduction
|
|
22
4
|
|
|
23
|
-
Command line tools for DEGIRO platform.
|
|
24
|
-
is to:
|
|
5
|
+
Command line tools for DEGIRO platform. Primary features:
|
|
25
6
|
- Search for STOCK products by various criteria.
|
|
26
7
|
- Access STOCK products history.
|
|
27
8
|
|
|
@@ -31,7 +12,11 @@ orders.
|
|
|
31
12
|
## Installation
|
|
32
13
|
|
|
33
14
|
``` sh
|
|
34
|
-
|
|
15
|
+
# With [uv](https://docs.astral.sh/uv/) (recommended)
|
|
16
|
+
uv tool install degiro-cli
|
|
17
|
+
|
|
18
|
+
# With pip
|
|
19
|
+
pip3 install -u degiro-cli
|
|
35
20
|
```
|
|
36
21
|
|
|
37
22
|
## Use
|
|
@@ -40,7 +25,7 @@ manipulation tools such as [CSV Kit](https://csvkit.rtfd.org/). Especially
|
|
|
40
25
|
the `csvlook` command if you intend to just visualize outputes from command
|
|
41
26
|
line.
|
|
42
27
|
|
|
43
|
-
|
|
28
|
+
### Login
|
|
44
29
|
|
|
45
30
|
``` sh
|
|
46
31
|
# Only the session id is stored, that means a new login will be required
|
|
@@ -48,6 +33,25 @@ line.
|
|
|
48
33
|
degiro-login
|
|
49
34
|
```
|
|
50
35
|
|
|
36
|
+
### Get portfolio
|
|
37
|
+
```sh
|
|
38
|
+
degiro-portfolio
|
|
39
|
+
# Output:
|
|
40
|
+
# position_type,exchange,isin,name,currency,size,price,value
|
|
41
|
+
# PRODUCT,EPA,FR0000121972,Schneider Electric SE,EUR,10,276.05,2760
|
|
42
|
+
# PRODUCT,SCG,DE000CL5C2Q2,SG Airbus Turbo illimités Call BAR 37.08 STR 35.6901 P 10 LV 1.23,EUR,100,15.635,1563.50
|
|
43
|
+
# CASH,N/A,EUR,EUR,EUR,3000.00,1,3000.00
|
|
44
|
+
|
|
45
|
+
# With csvlook formatting
|
|
46
|
+
degiro-portfolio | csvlook --max-column-width 30
|
|
47
|
+
# Output:
|
|
48
|
+
# | position_type | exchange | isin | name | currency | size | price | value |
|
|
49
|
+
# | ------------- | -------- | ------------ | ------------------------------ | -------- | -------- | ------- | --------- |
|
|
50
|
+
# | PRODUCT | EPA | FR0000121972 | Schneider Electric SE | EUR | 10,00 | 276,050 | 2 760,00 |
|
|
51
|
+
# | PRODUCT | SCG | DE000CL5C2Q2 | SG Airbus Turbo illimités C... | EUR | 100,00 | 15,635 | 1 563,50 |
|
|
52
|
+
# | CASH | | EUR | EUR | EUR | 3 000,00 | 1,000 | 3 000,00 |
|
|
53
|
+
```
|
|
54
|
+
|
|
51
55
|
### Find products
|
|
52
56
|
|
|
53
57
|
``` sh
|
|
@@ -76,7 +80,7 @@ degiro-search -t airbus | csvlook
|
|
|
76
80
|
# | FRA | AIRA | Airbus SE | EUR | US0092791005 |
|
|
77
81
|
```
|
|
78
82
|
|
|
79
|
-
|
|
83
|
+
### List countries available on the platform
|
|
80
84
|
``` sh
|
|
81
85
|
# List country codes available on the platform
|
|
82
86
|
degiro-search --list-countries
|
|
@@ -110,7 +114,7 @@ degiro-search --list-countries
|
|
|
110
114
|
# US
|
|
111
115
|
```
|
|
112
116
|
|
|
113
|
-
|
|
117
|
+
### By Index
|
|
114
118
|
``` sh
|
|
115
119
|
# List country codes available on the platform
|
|
116
120
|
degiro-search --list-indices
|
|
@@ -151,7 +155,7 @@ degiro-search --index 'EURO STOXX 50'
|
|
|
151
155
|
|
|
152
156
|
```
|
|
153
157
|
|
|
154
|
-
|
|
158
|
+
### Get symbol history
|
|
155
159
|
``` sh
|
|
156
160
|
degiro-history --period 1m EPA.SAF
|
|
157
161
|
# exchange,symbol,date,currency,open,high,low,close
|
|
@@ -206,11 +210,14 @@ degiro-history --period 1m EPA.SAF | csvlook
|
|
|
206
210
|
|
|
207
211
|
```
|
|
208
212
|
|
|
209
|
-
|
|
213
|
+
### Chain with other tools
|
|
210
214
|
|
|
215
|
+
degiro-cli can be chained with other command line tools to achieve more complex processing.
|
|
211
216
|
``` sh
|
|
217
|
+
degiro-login
|
|
212
218
|
# Example command line to pull history for all stocks in a country with
|
|
213
219
|
# the help of the great CLI tool csvkit
|
|
214
|
-
|
|
220
|
+
countr="NL"; degiro-search --country "$country" --no-header-row | csvcut -c 1-2 | sed 's/,/./' | xargs -n 100 degiro-history -p 5y | tee -a prices.$country.csv
|
|
215
221
|
```
|
|
216
222
|
|
|
223
|
+
|
|
@@ -1,9 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: degiro-cli
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Unofficial command line tools for Degiro
|
|
5
|
+
Author-email: ohmajesticlama <ohmajesticlama@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/OhMajesticLama/degiro-cli
|
|
8
|
+
Project-URL: Documentation, https://ohmajesticlama.github.io/degiro-cli
|
|
9
|
+
Keywords: degiro,cli,finance,investment
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: degiroasync>=1.1.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.1; extra == "dev"
|
|
22
|
+
Requires-Dist: coverage>=6.3; extra == "dev"
|
|
23
|
+
Requires-Dist: flake8>=4.0.1; extra == "dev"
|
|
24
|
+
Requires-Dist: mypy>=0.931; extra == "dev"
|
|
25
|
+
Requires-Dist: build>=0.7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: twine>=3.8.0; extra == "dev"
|
|
27
|
+
Requires-Dist: sphinx>=4.4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: sphinx_rtd_theme>=1.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: myst-parser>=0.17.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ipython; extra == "dev"
|
|
31
|
+
Requires-Dist: ipdb; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
1
34
|
# DegiroAsync Command Line Interface
|
|
2
35
|
|
|
3
36
|
## Introduction
|
|
4
37
|
|
|
5
|
-
Command line tools for DEGIRO platform.
|
|
6
|
-
is to:
|
|
38
|
+
Command line tools for DEGIRO platform. Primary features:
|
|
7
39
|
- Search for STOCK products by various criteria.
|
|
8
40
|
- Access STOCK products history.
|
|
9
41
|
|
|
@@ -13,7 +45,11 @@ orders.
|
|
|
13
45
|
## Installation
|
|
14
46
|
|
|
15
47
|
``` sh
|
|
16
|
-
|
|
48
|
+
# With [uv](https://docs.astral.sh/uv/) (recommended)
|
|
49
|
+
uv tool install degiro-cli
|
|
50
|
+
|
|
51
|
+
# With pip
|
|
52
|
+
pip3 install -u degiro-cli
|
|
17
53
|
```
|
|
18
54
|
|
|
19
55
|
## Use
|
|
@@ -22,7 +58,7 @@ manipulation tools such as [CSV Kit](https://csvkit.rtfd.org/). Especially
|
|
|
22
58
|
the `csvlook` command if you intend to just visualize outputes from command
|
|
23
59
|
line.
|
|
24
60
|
|
|
25
|
-
|
|
61
|
+
### Login
|
|
26
62
|
|
|
27
63
|
``` sh
|
|
28
64
|
# Only the session id is stored, that means a new login will be required
|
|
@@ -30,6 +66,25 @@ line.
|
|
|
30
66
|
degiro-login
|
|
31
67
|
```
|
|
32
68
|
|
|
69
|
+
### Get portfolio
|
|
70
|
+
```sh
|
|
71
|
+
degiro-portfolio
|
|
72
|
+
# Output:
|
|
73
|
+
# position_type,exchange,isin,name,currency,size,price,value
|
|
74
|
+
# PRODUCT,EPA,FR0000121972,Schneider Electric SE,EUR,10,276.05,2760
|
|
75
|
+
# PRODUCT,SCG,DE000CL5C2Q2,SG Airbus Turbo illimités Call BAR 37.08 STR 35.6901 P 10 LV 1.23,EUR,100,15.635,1563.50
|
|
76
|
+
# CASH,N/A,EUR,EUR,EUR,3000.00,1,3000.00
|
|
77
|
+
|
|
78
|
+
# With csvlook formatting
|
|
79
|
+
degiro-portfolio | csvlook --max-column-width 30
|
|
80
|
+
# Output:
|
|
81
|
+
# | position_type | exchange | isin | name | currency | size | price | value |
|
|
82
|
+
# | ------------- | -------- | ------------ | ------------------------------ | -------- | -------- | ------- | --------- |
|
|
83
|
+
# | PRODUCT | EPA | FR0000121972 | Schneider Electric SE | EUR | 10,00 | 276,050 | 2 760,00 |
|
|
84
|
+
# | PRODUCT | SCG | DE000CL5C2Q2 | SG Airbus Turbo illimités C... | EUR | 100,00 | 15,635 | 1 563,50 |
|
|
85
|
+
# | CASH | | EUR | EUR | EUR | 3 000,00 | 1,000 | 3 000,00 |
|
|
86
|
+
```
|
|
87
|
+
|
|
33
88
|
### Find products
|
|
34
89
|
|
|
35
90
|
``` sh
|
|
@@ -58,7 +113,7 @@ degiro-search -t airbus | csvlook
|
|
|
58
113
|
# | FRA | AIRA | Airbus SE | EUR | US0092791005 |
|
|
59
114
|
```
|
|
60
115
|
|
|
61
|
-
|
|
116
|
+
### List countries available on the platform
|
|
62
117
|
``` sh
|
|
63
118
|
# List country codes available on the platform
|
|
64
119
|
degiro-search --list-countries
|
|
@@ -92,7 +147,7 @@ degiro-search --list-countries
|
|
|
92
147
|
# US
|
|
93
148
|
```
|
|
94
149
|
|
|
95
|
-
|
|
150
|
+
### By Index
|
|
96
151
|
``` sh
|
|
97
152
|
# List country codes available on the platform
|
|
98
153
|
degiro-search --list-indices
|
|
@@ -133,7 +188,7 @@ degiro-search --index 'EURO STOXX 50'
|
|
|
133
188
|
|
|
134
189
|
```
|
|
135
190
|
|
|
136
|
-
|
|
191
|
+
### Get symbol history
|
|
137
192
|
``` sh
|
|
138
193
|
degiro-history --period 1m EPA.SAF
|
|
139
194
|
# exchange,symbol,date,currency,open,high,low,close
|
|
@@ -188,11 +243,14 @@ degiro-history --period 1m EPA.SAF | csvlook
|
|
|
188
243
|
|
|
189
244
|
```
|
|
190
245
|
|
|
191
|
-
|
|
246
|
+
### Chain with other tools
|
|
192
247
|
|
|
248
|
+
degiro-cli can be chained with other command line tools to achieve more complex processing.
|
|
193
249
|
``` sh
|
|
250
|
+
degiro-login
|
|
194
251
|
# Example command line to pull history for all stocks in a country with
|
|
195
252
|
# the help of the great CLI tool csvkit
|
|
196
|
-
|
|
253
|
+
countr="NL"; degiro-search --country "$country" --no-header-row | csvcut -c 1-2 | sed 's/,/./' | xargs -n 100 degiro-history -p 5y | tee -a prices.$country.csv
|
|
197
254
|
```
|
|
198
255
|
|
|
256
|
+
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
-
setup.py
|
|
5
|
-
bin/degiro-history
|
|
6
|
-
bin/degiro-login
|
|
7
|
-
bin/degiro-logout
|
|
8
|
-
bin/degiro-portfolio
|
|
9
|
-
bin/degiro-search
|
|
10
4
|
degiro_cli.egg-info/PKG-INFO
|
|
11
5
|
degiro_cli.egg-info/SOURCES.txt
|
|
12
6
|
degiro_cli.egg-info/dependency_links.txt
|
|
7
|
+
degiro_cli.egg-info/entry_points.txt
|
|
13
8
|
degiro_cli.egg-info/requires.txt
|
|
14
9
|
degiro_cli.egg-info/top_level.txt
|
|
15
10
|
degirocli/__init__.py
|
|
16
11
|
degirocli/helpers.py
|
|
17
12
|
degirocli/history.py
|
|
18
13
|
degirocli/login.py
|
|
14
|
+
degirocli/portfolio.py
|
|
19
15
|
degirocli/search.py
|
|
@@ -87,10 +87,10 @@ def prepare_inputs(input_stream: Iterable[str]) -> Iterable[Tuple[str, str]]:
|
|
|
87
87
|
inputs = filter(lambda l: l[0] != '#', inputs)
|
|
88
88
|
inputs = more_itertools.unique_everseen(inputs)
|
|
89
89
|
inputs = (line.split('.')[:2] for line in inputs)
|
|
90
|
-
return inputs
|
|
90
|
+
return inputs # type: ignore , correct type.
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
async def
|
|
93
|
+
async def run_price_pipeline(
|
|
94
94
|
session: dapi.Session,
|
|
95
95
|
*,
|
|
96
96
|
symbols: Iterable[str],
|
|
@@ -111,7 +111,7 @@ async def run(
|
|
|
111
111
|
return await asyncio.gather(*queries)
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def
|
|
114
|
+
def main():
|
|
115
115
|
handler = logging.StreamHandler()
|
|
116
116
|
|
|
117
117
|
parser = argparse.ArgumentParser(
|
|
@@ -190,8 +190,8 @@ def run_cli():
|
|
|
190
190
|
"low",
|
|
191
191
|
"close",
|
|
192
192
|
))
|
|
193
|
-
asyncio.
|
|
194
|
-
|
|
193
|
+
asyncio.run(
|
|
194
|
+
run_price_pipeline(
|
|
195
195
|
session,
|
|
196
196
|
symbols=symbols,
|
|
197
197
|
period=period,
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import tempfile
|
|
2
2
|
import getpass
|
|
3
|
-
import functools
|
|
4
|
-
import multiprocessing
|
|
5
3
|
import stat
|
|
6
4
|
import asyncio
|
|
7
5
|
import json
|
|
8
6
|
import time
|
|
9
|
-
import pickle
|
|
10
7
|
import sys
|
|
11
8
|
import os
|
|
12
9
|
from pathlib import Path
|
|
@@ -17,10 +14,10 @@ import degiroasync.api as dapi
|
|
|
17
14
|
import degiroasync.webapi as wapi
|
|
18
15
|
import degiroasync.core as dcore
|
|
19
16
|
|
|
20
|
-
from .helpers import ERROR_CODES
|
|
21
|
-
from .helpers import LOGGER
|
|
17
|
+
from .helpers import ERROR_CODES, LOGGER
|
|
22
18
|
|
|
23
19
|
|
|
20
|
+
_USER_MASK = stat.S_IRUSR | stat.S_IWUSR
|
|
24
21
|
|
|
25
22
|
def get_credentials() -> dapi.Credentials:
|
|
26
23
|
"""
|
|
@@ -55,10 +52,17 @@ def get_credentials() -> dapi.Credentials:
|
|
|
55
52
|
|
|
56
53
|
|
|
57
54
|
def get_tmp_path():
|
|
55
|
+
"Return temporary path specific to current user."
|
|
58
56
|
tmpdir = Path(tempfile.gettempdir())
|
|
59
|
-
return tmpdir / 'degirocli'
|
|
60
57
|
|
|
61
|
-
|
|
58
|
+
# parent folder must be readable and writable by other users on multi-user systems.
|
|
59
|
+
module_folder = tmpdir / 'degirocli'
|
|
60
|
+
module_folder.mkdir(mode=0o777, exist_ok=True)
|
|
61
|
+
|
|
62
|
+
user_path = module_folder / str(os.getuid())
|
|
63
|
+
return user_path
|
|
64
|
+
|
|
65
|
+
def _get_hash(path: Union[Path, str]) -> int:
|
|
62
66
|
with open(path, 'rb') as fh:
|
|
63
67
|
fhash = hash(fh)
|
|
64
68
|
return fhash
|
|
@@ -116,17 +120,7 @@ def expire_path(
|
|
|
116
120
|
return 0
|
|
117
121
|
|
|
118
122
|
|
|
119
|
-
|
|
120
|
-
#def expire_path(*args):
|
|
121
|
-
# # Start event loop to
|
|
122
|
-
# proc = multiprocessing.Process(
|
|
123
|
-
# target=_expire_path,
|
|
124
|
-
# args=args,
|
|
125
|
-
# daemon=False)
|
|
126
|
-
# proc.start()
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
async def login():
|
|
123
|
+
async def login(*, expire_session_file: int = 3*60*60):
|
|
130
124
|
tmp_path = get_tmp_path()
|
|
131
125
|
|
|
132
126
|
if tmp_path.exists():
|
|
@@ -140,8 +134,21 @@ async def login():
|
|
|
140
134
|
# Get sessionID and write it
|
|
141
135
|
credentials = get_credentials()
|
|
142
136
|
session = await wapi.login(credentials)
|
|
143
|
-
|
|
144
|
-
tmp_path.
|
|
137
|
+
LOGGER.debug("temporary file: %s", tmp_path)
|
|
138
|
+
tmp_path.touch(exist_ok=True)
|
|
139
|
+
# Check current user owns the file.
|
|
140
|
+
tmp_stat = os.stat(tmp_path)
|
|
141
|
+
if tmp_stat.st_uid != os.getuid():
|
|
142
|
+
msg = (
|
|
143
|
+
f"Session cache file user identifier (UID) {tmp_stat.st_uid} is"
|
|
144
|
+
f" different from current user UID {os.getuid()}."
|
|
145
|
+
"This should not happen and could be a local attack. "
|
|
146
|
+
"Abort."
|
|
147
|
+
)
|
|
148
|
+
LOGGER.error(msg)
|
|
149
|
+
return
|
|
150
|
+
raise AssertionError()
|
|
151
|
+
tmp_path.chmod(_USER_MASK)
|
|
145
152
|
with open(tmp_path, 'w') as fh:
|
|
146
153
|
json.dump({
|
|
147
154
|
'version': 1,
|
|
@@ -151,7 +158,7 @@ async def login():
|
|
|
151
158
|
),
|
|
152
159
|
}, fh)
|
|
153
160
|
# Delete file in 3 hour
|
|
154
|
-
expire_path(tmp_path,
|
|
161
|
+
expire_path(tmp_path, expire_session_file)
|
|
155
162
|
|
|
156
163
|
async def _get_session_from_cache() -> dapi.Session:
|
|
157
164
|
cache_path = get_tmp_path()
|
|
@@ -171,7 +178,7 @@ def get_session_from_cache() -> dapi.Session:
|
|
|
171
178
|
"""
|
|
172
179
|
Helper to get Session when already logged in.
|
|
173
180
|
"""
|
|
174
|
-
return asyncio.
|
|
181
|
+
return asyncio.run(_get_session_from_cache())
|
|
175
182
|
|
|
176
|
-
def
|
|
177
|
-
asyncio.
|
|
183
|
+
def main():
|
|
184
|
+
asyncio.run(login())
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
import io
|
|
5
|
+
import argparse
|
|
6
|
+
import csv
|
|
7
|
+
from typing import Optional, Sequence, Literal
|
|
8
|
+
|
|
9
|
+
import degiroasync.api as dapi
|
|
10
|
+
|
|
11
|
+
from .helpers import LOGGER
|
|
12
|
+
from .login import get_session_from_cache
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def run_portfolio_pipeline(
|
|
16
|
+
session: dapi.Session,
|
|
17
|
+
output_file: io.TextIOBase,
|
|
18
|
+
*,
|
|
19
|
+
headers: bool = True,
|
|
20
|
+
position_type: Optional[str] = None,
|
|
21
|
+
filter: Literal['all', 'open', 'closed'] = 'open',
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Fetch and output portfolio data.
|
|
25
|
+
"""
|
|
26
|
+
positions: Sequence[dapi.Position] = await dapi.get_portfolio(session, filter=filter)
|
|
27
|
+
|
|
28
|
+
exc_dict: dapi.ExchangeDictionary = session.dictionary
|
|
29
|
+
writer = csv.writer(output_file)
|
|
30
|
+
if headers:
|
|
31
|
+
writer.writerow((
|
|
32
|
+
"position_type",
|
|
33
|
+
"exchange",
|
|
34
|
+
"isin",
|
|
35
|
+
"name",
|
|
36
|
+
"currency",
|
|
37
|
+
"size",
|
|
38
|
+
"price",
|
|
39
|
+
"value",
|
|
40
|
+
))
|
|
41
|
+
|
|
42
|
+
for position in positions:
|
|
43
|
+
if position_type is not None and position.position_type != position_type:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
exchange = "N/A"
|
|
47
|
+
|
|
48
|
+
info = position.product.info
|
|
49
|
+
if hasattr(info, 'exchange_id'):
|
|
50
|
+
# Should be the case for stock products at least.
|
|
51
|
+
try:
|
|
52
|
+
exchange_obj = exc_dict.exchange_by(id=info.exchange_id)
|
|
53
|
+
exchange = exchange_obj.hiq_abbr if exchange_obj else ""
|
|
54
|
+
except KeyError:
|
|
55
|
+
LOGGER.warning("Exchange id %s unknown.", info.exchange_id)
|
|
56
|
+
exchange = "Unknown"
|
|
57
|
+
|
|
58
|
+
LOGGER.debug("position: %s", position)
|
|
59
|
+
LOGGER.debug("info: %s", info)
|
|
60
|
+
writer.writerow((
|
|
61
|
+
position.position_type,
|
|
62
|
+
exchange,
|
|
63
|
+
info.isin,
|
|
64
|
+
info.name,
|
|
65
|
+
info.currency,
|
|
66
|
+
position.size,
|
|
67
|
+
position.price,
|
|
68
|
+
position.value,
|
|
69
|
+
))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def run_portfolio_total_pipeline(
|
|
73
|
+
session: dapi.Session,
|
|
74
|
+
output_file: io.TextIOBase
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Fetch and output total portfolio data.
|
|
78
|
+
"""
|
|
79
|
+
total_portfolio = await dapi.get_portfolio_total(session)
|
|
80
|
+
|
|
81
|
+
writer = csv.writer(output_file)
|
|
82
|
+
|
|
83
|
+
# TotalPortfolio is an object with attributes, iterate over public ones
|
|
84
|
+
for attr_name in dir(total_portfolio):
|
|
85
|
+
if not attr_name.startswith('_'):
|
|
86
|
+
writer.writerow((attr_name, getattr(total_portfolio, attr_name)))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def main():
|
|
90
|
+
handler = logging.StreamHandler()
|
|
91
|
+
|
|
92
|
+
parser = argparse.ArgumentParser(
|
|
93
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
94
|
+
description="Get user portfolio from DEGIRO."
|
|
95
|
+
)
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
'-f',
|
|
98
|
+
'--filter',
|
|
99
|
+
dest='filter',
|
|
100
|
+
default='open',
|
|
101
|
+
required=False,
|
|
102
|
+
help="Get either 'all', 'open' (default) or 'closed' positions only."
|
|
103
|
+
)
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
'--total',
|
|
106
|
+
dest='total',
|
|
107
|
+
default=False,
|
|
108
|
+
action='store_true',
|
|
109
|
+
required=False,
|
|
110
|
+
help="Get total portfolio summary instead of individual positions"
|
|
111
|
+
)
|
|
112
|
+
parser.add_argument(
|
|
113
|
+
'-t',
|
|
114
|
+
'--type',
|
|
115
|
+
dest='position_type',
|
|
116
|
+
default=None,
|
|
117
|
+
required=False,
|
|
118
|
+
help="Filter by position type: PRODUCT or CASH"
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
'-H',
|
|
122
|
+
'--no-header-row',
|
|
123
|
+
dest='no_headers',
|
|
124
|
+
default=False,
|
|
125
|
+
action='store_true',
|
|
126
|
+
required=False,
|
|
127
|
+
help="Do not print header line"
|
|
128
|
+
)
|
|
129
|
+
parser.add_argument(
|
|
130
|
+
'--debug',
|
|
131
|
+
default=False,
|
|
132
|
+
action='store_true',
|
|
133
|
+
dest='debug',
|
|
134
|
+
help="Enable debug logging."
|
|
135
|
+
)
|
|
136
|
+
args = parser.parse_args()
|
|
137
|
+
|
|
138
|
+
print_headers = not args.no_headers
|
|
139
|
+
position_type = args.position_type
|
|
140
|
+
if position_type is not None:
|
|
141
|
+
position_type = position_type.upper()
|
|
142
|
+
get_total = args.total
|
|
143
|
+
filter = args.filter
|
|
144
|
+
if get_total and filter != 'open':
|
|
145
|
+
print("--filter does not work with --total. Abort.")
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
logging_level = logging.WARNING
|
|
149
|
+
if args.debug:
|
|
150
|
+
logging_level = logging.DEBUG
|
|
151
|
+
handler.setLevel(logging_level)
|
|
152
|
+
LOGGER.setLevel(logging_level)
|
|
153
|
+
LOGGER.addHandler(handler)
|
|
154
|
+
|
|
155
|
+
output_io: io.TextIOBase = sys.stdout # type: ignore
|
|
156
|
+
|
|
157
|
+
session = get_session_from_cache()
|
|
158
|
+
session.update_throttling(max_requests=7, period_seconds=1)
|
|
159
|
+
|
|
160
|
+
if get_total:
|
|
161
|
+
if print_headers:
|
|
162
|
+
writer = csv.writer(sys.stdout)
|
|
163
|
+
writer.writerow(("key", "value"))
|
|
164
|
+
asyncio.run(run_portfolio_total_pipeline(session, output_io))
|
|
165
|
+
else:
|
|
166
|
+
asyncio.run(
|
|
167
|
+
run_portfolio_pipeline(
|
|
168
|
+
session,
|
|
169
|
+
output_file=output_io,
|
|
170
|
+
position_type=position_type,
|
|
171
|
+
headers=print_headers,
|
|
172
|
+
)
|
|
173
|
+
)
|
|
@@ -11,7 +11,7 @@ from .helpers import LOGGER
|
|
|
11
11
|
from .login import get_session_from_cache
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
async def
|
|
14
|
+
async def run_search_pipeline(
|
|
15
15
|
session: dapi.Session,
|
|
16
16
|
*,
|
|
17
17
|
search_txt: Optional[str],
|
|
@@ -45,7 +45,7 @@ async def run(
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def main():
|
|
49
49
|
handler = logging.StreamHandler()
|
|
50
50
|
|
|
51
51
|
parser = argparse.ArgumentParser(
|
|
@@ -163,8 +163,8 @@ def run_cli():
|
|
|
163
163
|
"currency",
|
|
164
164
|
"isin",
|
|
165
165
|
))
|
|
166
|
-
asyncio.
|
|
167
|
-
|
|
166
|
+
asyncio.run(
|
|
167
|
+
run_search_pipeline(
|
|
168
168
|
session,
|
|
169
169
|
search_txt=search_txt,
|
|
170
170
|
exchange_txt=exchange_txt,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "degiro-cli"
|
|
7
|
+
version = "0.1.4"
|
|
8
|
+
authors = [{ name = "ohmajesticlama", email = "ohmajesticlama@gmail.com" }]
|
|
9
|
+
description = "Unofficial command line tools for Degiro"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Topic :: Office/Business :: Financial :: Investment"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
keywords = ["degiro", "cli", "finance", "investment"]
|
|
24
|
+
|
|
25
|
+
dependencies = [
|
|
26
|
+
"degiroasync >= 1.1.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
"Homepage" = "https://github.com/OhMajesticLama/degiro-cli"
|
|
31
|
+
"Documentation" = "https://ohmajesticlama.github.io/degiro-cli"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
degiro-login = "degirocli.login:main"
|
|
35
|
+
degiro-history = "degirocli.history:main"
|
|
36
|
+
degiro-search = "degirocli.search:main"
|
|
37
|
+
degiro-portfolio = "degirocli.portfolio:main"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
# Tests
|
|
42
|
+
"pytest >= 7.0.1",
|
|
43
|
+
"coverage >= 6.3",
|
|
44
|
+
# Code quality
|
|
45
|
+
"flake8 >= 4.0.1",
|
|
46
|
+
"mypy >= 0.931",
|
|
47
|
+
# For shipping
|
|
48
|
+
"build >= 0.7.0",
|
|
49
|
+
"twine >= 3.8.0",
|
|
50
|
+
# Documentation
|
|
51
|
+
"sphinx >= 4.4.0",
|
|
52
|
+
"sphinx_rtd_theme >= 1.0.0",
|
|
53
|
+
"myst-parser >= 0.17.0",
|
|
54
|
+
# Other dev tools
|
|
55
|
+
"ipython",
|
|
56
|
+
"ipdb",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[tool.setuptools]
|
|
60
|
+
packages = ["degirocli"]
|
|
File without changes
|
|
File without changes
|
degiro-cli-0.1.2/pyproject.toml
DELETED
degiro-cli-0.1.2/setup.py
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
setup file for degiroasync
|
|
3
|
-
"""
|
|
4
|
-
import sys
|
|
5
|
-
import os
|
|
6
|
-
|
|
7
|
-
import setuptools
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if __name__ == '__main__':
|
|
11
|
-
description = "Unofficial command line tools for Degiro"
|
|
12
|
-
readme_path = os.path.join(os.path.dirname(__file__), 'README.md')
|
|
13
|
-
with open(readme_path, "r") as fh:
|
|
14
|
-
long_description = fh.read()
|
|
15
|
-
|
|
16
|
-
setuptools.setup(
|
|
17
|
-
name="degiro-cli",
|
|
18
|
-
version="0.1.2",
|
|
19
|
-
author_email="ohmajesticlama@gmail.com",
|
|
20
|
-
description=description,
|
|
21
|
-
long_description=long_description,
|
|
22
|
-
long_description_content_type='text/markdown',
|
|
23
|
-
url="https://github.com/OhMajesticLama/degiro-cli",
|
|
24
|
-
project_urls={
|
|
25
|
-
'Documentation':
|
|
26
|
-
'https://ohmajesticlama.github.io/degiro-cli'
|
|
27
|
-
},
|
|
28
|
-
packages=setuptools.find_packages(),
|
|
29
|
-
scripts=[
|
|
30
|
-
os.path.join('bin', 'degiro-login'),
|
|
31
|
-
os.path.join('bin', 'degiro-history'),
|
|
32
|
-
os.path.join('bin', 'degiro-search'),
|
|
33
|
-
],
|
|
34
|
-
python_requires=">=3.8",
|
|
35
|
-
install_requires=[
|
|
36
|
-
'degiroasync >= 0.20.0',
|
|
37
|
-
],
|
|
38
|
-
extras_require={
|
|
39
|
-
'dev': [
|
|
40
|
-
# Tests
|
|
41
|
-
'pytest >= 7.0.1',
|
|
42
|
-
'coverage >= 6.3',
|
|
43
|
-
# Code quality
|
|
44
|
-
'flake8 >= 4.0.1',
|
|
45
|
-
'mypy >= 0.931',
|
|
46
|
-
# For shipping
|
|
47
|
-
#'build >= 0.7.0',
|
|
48
|
-
#'twine >= 3.8.0',
|
|
49
|
-
# Documentation
|
|
50
|
-
#'sphinx >= 4.4.0',
|
|
51
|
-
#'sphinx_rtd_theme >= 1.0.0',
|
|
52
|
-
#'myst-parser >= 0.17.0', # markdown imports
|
|
53
|
-
# Other dev tools
|
|
54
|
-
'ipython',
|
|
55
|
-
'ipdb',
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
classifiers=[
|
|
59
|
-
"Programming Language :: Python :: 3",
|
|
60
|
-
"License :: OSI Approved :: MIT License",
|
|
61
|
-
"Operating System :: OS Independent",
|
|
62
|
-
"Development Status :: 4 - Beta",
|
|
63
|
-
"Intended Audience :: End Users/Desktop",
|
|
64
|
-
"Topic :: Office/Business :: Financial :: Investment"
|
|
65
|
-
],
|
|
66
|
-
test_suite='pytest',
|
|
67
|
-
tests_require=['pytest']
|
|
68
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|