siglab-py 0.1.21__tar.gz → 0.1.23__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.
Potentially problematic release.
This version of siglab-py might be problematic. Click here for more details.
- {siglab_py-0.1.21 → siglab_py-0.1.23}/PKG-INFO +1 -1
- {siglab_py-0.1.21 → siglab_py-0.1.23}/pyproject.toml +1 -1
- {siglab_py-0.1.21 → siglab_py-0.1.23}/setup.cfg +1 -1
- siglab_py-0.1.23/siglab_py/market_data_providers/futu_candles.py +187 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/util/analytic_util.py +1 -1
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/util/market_data_util.py +1 -1
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py.egg-info/PKG-INFO +1 -1
- siglab_py-0.1.21/siglab_py/market_data_providers/futu_candles.py +0 -95
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/constants.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/exchanges/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/exchanges/any_exchange.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/exchanges/futubull.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/candles_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/test_provider.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/ordergateway/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/ordergateway/client.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/ordergateway/gateway.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/ordergateway/test_ordergateway.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/tests/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/tests/integration/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/tests/unit/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/tests/unit/analytic_util_tests.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/util/__init__.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/util/aws_util.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/util/retry_util.py +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py.egg-info/SOURCES.txt +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py.egg-info/dependency_links.txt +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py.egg-info/requires.txt +0 -0
- {siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "siglab_py"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.23"
|
|
8
8
|
description = "Market data fetches, TA calculations and generic order gateway."
|
|
9
9
|
authors = [{name = "r0bbarh00d", email = "r0bbarh00d@gmail.com"}]
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import logging
|
|
3
|
+
import argparse
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Dict, Union
|
|
6
|
+
from enum import Enum
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from futu import *
|
|
10
|
+
|
|
11
|
+
from siglab_py.exchanges.futubull import Futubull
|
|
12
|
+
from siglab_py.util.market_data_util import fetch_candles
|
|
13
|
+
from siglab_py.util.analytic_util import compute_candles_stats
|
|
14
|
+
|
|
15
|
+
'''
|
|
16
|
+
Usage:
|
|
17
|
+
python futu_candles.py --symbol HK.00700 --end_date 2025-03-11 --start_date 2024-03-11 --market HK --trdmarket HK --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y
|
|
18
|
+
|
|
19
|
+
This script is NOT pypy compatible.
|
|
20
|
+
|
|
21
|
+
If debugging from VSCode, launch.json:
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
"version": "0.2.0",
|
|
25
|
+
"configurations": [
|
|
26
|
+
{
|
|
27
|
+
"name": "Python Debugger: Current File",
|
|
28
|
+
"type": "debugpy",
|
|
29
|
+
"request": "launch",
|
|
30
|
+
"program": "${file}",
|
|
31
|
+
"console": "integratedTerminal",
|
|
32
|
+
"args" : [
|
|
33
|
+
"--symbol", "HK.00700",
|
|
34
|
+
"--end_date", "2025-03-11 0:0:0",
|
|
35
|
+
"--start_date", "2024-03-11 0:0:0",
|
|
36
|
+
"--market", "HK",
|
|
37
|
+
"--trdmarket", "HK",
|
|
38
|
+
"--security_firm", "FUTUSECURITIES",
|
|
39
|
+
"--security_type", "STOCK",
|
|
40
|
+
"--compute_ta", "Y"
|
|
41
|
+
],
|
|
42
|
+
"env": {
|
|
43
|
+
"PYTHONPATH": "${workspaceFolder}"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
'''
|
|
49
|
+
end_date : datetime = datetime.today()
|
|
50
|
+
end_date = datetime(end_date.year, end_date.month, end_date.day)
|
|
51
|
+
start_date : datetime = end_date - timedelta(days=365)
|
|
52
|
+
|
|
53
|
+
param : Dict = {
|
|
54
|
+
'symbol' : None,
|
|
55
|
+
'start_date' : start_date,
|
|
56
|
+
'end_date' : end_date,
|
|
57
|
+
'trdmarket' : TrdMarket.HK,
|
|
58
|
+
'security_firm' : SecurityFirm.FUTUSECURITIES,
|
|
59
|
+
'market' : Market.HK,
|
|
60
|
+
'security_type' : SecurityType.STOCK,
|
|
61
|
+
'daemon' : {
|
|
62
|
+
'host' : '127.0.0.1',
|
|
63
|
+
'port' : 11111
|
|
64
|
+
},
|
|
65
|
+
'output_filename' : 'candles_$SYMBOL$.csv'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class LogLevel(Enum):
|
|
69
|
+
CRITICAL = 50
|
|
70
|
+
ERROR = 40
|
|
71
|
+
WARNING = 30
|
|
72
|
+
INFO = 20
|
|
73
|
+
DEBUG = 10
|
|
74
|
+
NOTSET = 0
|
|
75
|
+
|
|
76
|
+
logging.Formatter.converter = time.gmtime
|
|
77
|
+
logger = logging.getLogger()
|
|
78
|
+
log_level = logging.INFO # DEBUG --> INFO --> WARNING --> ERROR
|
|
79
|
+
logger.setLevel(log_level)
|
|
80
|
+
format_str = '%(asctime)s %(message)s'
|
|
81
|
+
formatter = logging.Formatter(format_str)
|
|
82
|
+
sh = logging.StreamHandler()
|
|
83
|
+
sh.setLevel(log_level)
|
|
84
|
+
sh.setFormatter(formatter)
|
|
85
|
+
logger.addHandler(sh)
|
|
86
|
+
|
|
87
|
+
def log(message : str, log_level : LogLevel = LogLevel.INFO):
|
|
88
|
+
if log_level.value<LogLevel.WARNING.value:
|
|
89
|
+
logger.info(f"{datetime.now()} {message}")
|
|
90
|
+
|
|
91
|
+
elif log_level.value==LogLevel.WARNING.value:
|
|
92
|
+
logger.warning(f"{datetime.now()} {message}")
|
|
93
|
+
|
|
94
|
+
elif log_level.value==LogLevel.ERROR.value:
|
|
95
|
+
logger.error(f"{datetime.now()} {message}")
|
|
96
|
+
|
|
97
|
+
def parse_args():
|
|
98
|
+
parser = argparse.ArgumentParser() # type: ignore
|
|
99
|
+
parser.add_argument("--symbol", help="symbol, example HK.00700", default=None)
|
|
100
|
+
parser.add_argument("--start_date", help="Format: %Y-%m-%d %H:%M:%S", default=None)
|
|
101
|
+
parser.add_argument("--end_date", help="Format: %Y-%m-%d %H:%M:%S", default=None)
|
|
102
|
+
|
|
103
|
+
'''
|
|
104
|
+
Enums here:
|
|
105
|
+
https://openapi.futunn.com/futu-api-doc/en/quote/quote.html#66
|
|
106
|
+
https://openapi.futunn.com/futu-api-doc/en/trade/trade.html#9434
|
|
107
|
+
'''
|
|
108
|
+
parser.add_argument("--market", help="market: HK SH SZ US AU CA FX", default=Market.HK)
|
|
109
|
+
parser.add_argument("--trdmarket", help="trdmarket: HK, HKCC, HKFUND, FUTURES, CN, CA, AU, JP, MY, SG, US, USFUND", default=TrdMarket.HK)
|
|
110
|
+
parser.add_argument("--security_firm", help="security_firm: FUTUSECURITIES (HK), FUTUINC (US), FUTUSG (SG), FUTUAU (AU)", default=SecurityFirm.FUTUSECURITIES)
|
|
111
|
+
parser.add_argument("--security_type", help="STOCK, BOND, ETF, FUTURE, WARRANT, IDX ... ", default=SecurityType.STOCK)
|
|
112
|
+
|
|
113
|
+
parser.add_argument("--compute_ta", help="Compute technical indicators?. Y or N (default).", default='N')
|
|
114
|
+
parser.add_argument("--candle_size", help="candle interval: 1m, 1h, 1d... etc", default='1h')
|
|
115
|
+
parser.add_argument("--ma_long_intervals", help="Window size in number of intervals for higher timeframe", default=24)
|
|
116
|
+
parser.add_argument("--ma_short_intervals", help="Window size in number of intervals for lower timeframe", default=8)
|
|
117
|
+
parser.add_argument("--boillenger_std_multiples", help="Boillenger bands: # std", default=2)
|
|
118
|
+
|
|
119
|
+
args = parser.parse_args()
|
|
120
|
+
param['symbol'] = args.symbol.strip().upper()
|
|
121
|
+
|
|
122
|
+
param['start_date'] = datetime.strptime(args.start_date, "%Y-%m-%d %H:%M:%S") if args.start_date else start_date
|
|
123
|
+
param['end_date'] = datetime.strptime(args.end_date, "%Y-%m-%d %H:%M:%S") if args.end_date else end_date
|
|
124
|
+
|
|
125
|
+
param['market'] = args.market
|
|
126
|
+
param['trdmarket'] = args.trdmarket
|
|
127
|
+
param['security_firm'] = args.security_firm
|
|
128
|
+
|
|
129
|
+
param['output_filename'] = param['output_filename'].replace('$SYMBOL$', param['symbol'])
|
|
130
|
+
|
|
131
|
+
if args.compute_ta:
|
|
132
|
+
if args.compute_ta=='Y':
|
|
133
|
+
param['compute_ta'] = True
|
|
134
|
+
else:
|
|
135
|
+
param['compute_ta'] = False
|
|
136
|
+
else:
|
|
137
|
+
param['compute_ta'] = False
|
|
138
|
+
param['candle_size'] = args.candle_size
|
|
139
|
+
param['ma_long_intervals'] = int(args.ma_long_intervals)
|
|
140
|
+
param['ma_short_intervals'] = int(args.ma_short_intervals)
|
|
141
|
+
param['boillenger_std_multiples'] = int(args.boillenger_std_multiples)
|
|
142
|
+
|
|
143
|
+
async def main():
|
|
144
|
+
parse_args()
|
|
145
|
+
|
|
146
|
+
fh = logging.FileHandler(f"futu_candles.log")
|
|
147
|
+
fh.setLevel(log_level)
|
|
148
|
+
fh.setFormatter(formatter)
|
|
149
|
+
logger.addHandler(fh) # type: ignore
|
|
150
|
+
|
|
151
|
+
exchange = Futubull(param)
|
|
152
|
+
|
|
153
|
+
pd_candles: Union[pd.DataFrame, None] = fetch_candles(
|
|
154
|
+
start_ts=int(start_date.timestamp()),
|
|
155
|
+
end_ts=int(end_date.timestamp()),
|
|
156
|
+
exchange=exchange,
|
|
157
|
+
normalized_symbols=[ param['symbol'] ],
|
|
158
|
+
candle_size='1d'
|
|
159
|
+
)[param['symbol']]
|
|
160
|
+
|
|
161
|
+
assert pd_candles is not None
|
|
162
|
+
|
|
163
|
+
if pd_candles is not None:
|
|
164
|
+
assert len(pd_candles) > 0, "No candles returned."
|
|
165
|
+
expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
|
|
166
|
+
assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
|
|
167
|
+
assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
|
|
168
|
+
assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
|
|
169
|
+
|
|
170
|
+
if param['compute_ta']:
|
|
171
|
+
start = time.time()
|
|
172
|
+
compute_candles_stats(
|
|
173
|
+
pd_candles=pd_candles,
|
|
174
|
+
boillenger_std_multiples=param['boillenger_std_multiples'],
|
|
175
|
+
sliding_window_how_many_candles=param['ma_long_intervals'],
|
|
176
|
+
slow_fast_interval_ratio=(param['ma_long_intervals']/param['ma_short_intervals']),
|
|
177
|
+
pypy_compat=False
|
|
178
|
+
)
|
|
179
|
+
compute_candles_stats_elapsed_ms = int((time.time() - start) *1000)
|
|
180
|
+
log(f"TA calculated, took {compute_candles_stats_elapsed_ms} ms")
|
|
181
|
+
|
|
182
|
+
log(f"Candles (# rows: {pd_candles.shape[0]}) written to {param['output_filename']}")
|
|
183
|
+
pd_candles.to_csv(param['output_filename'])
|
|
184
|
+
|
|
185
|
+
sys.exit()
|
|
186
|
+
|
|
187
|
+
asyncio.run(main())
|
|
@@ -10,7 +10,7 @@ from hurst import compute_Hc # compatible with pypy
|
|
|
10
10
|
from ccxt.base.exchange import Exchange as CcxtExchange
|
|
11
11
|
from ccxt import deribit
|
|
12
12
|
|
|
13
|
-
from util.market_data_util import fix_column_types
|
|
13
|
+
from siglab_py.util.market_data_util import fix_column_types
|
|
14
14
|
|
|
15
15
|
# Fibonacci
|
|
16
16
|
MAGIC_FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.00, 1.618, 2.618, 3.618, 4.236]
|
|
@@ -15,7 +15,7 @@ from yahoofinancials import YahooFinancials
|
|
|
15
15
|
# yfinance allows intervals '1m', '5m', '15m', '1h', '1d', '1wk', '1mo'. yahoofinancials not as flexible
|
|
16
16
|
import yfinance as yf
|
|
17
17
|
|
|
18
|
-
from exchanges.futubull import Futubull
|
|
18
|
+
from siglab_py.exchanges.futubull import Futubull
|
|
19
19
|
|
|
20
20
|
def timestamp_to_datetime_cols(pd_candles : pd.DataFrame):
|
|
21
21
|
pd_candles['datetime'] = pd_candles['timestamp_ms'].apply(
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
from datetime import datetime
|
|
3
|
-
from typing import Dict, Union
|
|
4
|
-
import asyncio
|
|
5
|
-
|
|
6
|
-
from futu import *
|
|
7
|
-
|
|
8
|
-
from siglab_py.exchanges.futubull import Futubull
|
|
9
|
-
from siglab_py.util.market_data_util import fetch_candles
|
|
10
|
-
from siglab_py.util.analytic_util import compute_candles_stats
|
|
11
|
-
|
|
12
|
-
end_date : datetime = datetime.today()
|
|
13
|
-
start_date : datetime = end_date - timedelta(days=365)
|
|
14
|
-
|
|
15
|
-
param : Dict = {
|
|
16
|
-
'symbol' : None,
|
|
17
|
-
'trdmarket' : TrdMarket.HK,
|
|
18
|
-
'security_firm' : SecurityFirm.FUTUSECURITIES,
|
|
19
|
-
'market' : Market.HK,
|
|
20
|
-
'security_type' : SecurityType.STOCK,
|
|
21
|
-
'daemon' : {
|
|
22
|
-
'host' : '127.0.0.1',
|
|
23
|
-
'port' : 11111
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
'''
|
|
28
|
-
If debugging from VSCode, launch.json:
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
"version": "0.2.0",
|
|
32
|
-
"configurations": [
|
|
33
|
-
{
|
|
34
|
-
"name": "Python Debugger: Current File",
|
|
35
|
-
"type": "debugpy",
|
|
36
|
-
"request": "launch",
|
|
37
|
-
"program": "${file}",
|
|
38
|
-
"console": "integratedTerminal",
|
|
39
|
-
"args" : [
|
|
40
|
-
"--symbol", "HK.00700",
|
|
41
|
-
"--market", "HK",
|
|
42
|
-
"--trdmarket", "HK",
|
|
43
|
-
"--security_firm", "FUTUSECURITIES",
|
|
44
|
-
"--security_type", "STOCK"
|
|
45
|
-
],
|
|
46
|
-
"env": {
|
|
47
|
-
"PYTHONPATH": "${workspaceFolder}"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
'''
|
|
53
|
-
def parse_args():
|
|
54
|
-
parser = argparse.ArgumentParser() # type: ignore
|
|
55
|
-
parser.add_argument("--symbol", help="symbol, example HK.00700", default=None)
|
|
56
|
-
|
|
57
|
-
'''
|
|
58
|
-
Enums here:
|
|
59
|
-
https://openapi.futunn.com/futu-api-doc/en/quote/quote.html#66
|
|
60
|
-
https://openapi.futunn.com/futu-api-doc/en/trade/trade.html#9434
|
|
61
|
-
'''
|
|
62
|
-
parser.add_argument("--market", help="market: HK SH SZ US AU CA FX", default=Market.HK)
|
|
63
|
-
parser.add_argument("--trdmarket", help="trdmarket: HK, HKCC, HKFUND, FUTURES, CN, CA, AU, JP, MY, SG, US, USFUND", default=TrdMarket.HK)
|
|
64
|
-
parser.add_argument("--security_firm", help="security_firm: FUTUSECURITIES (HK), FUTUINC (US), FUTUSG (SG), FUTUAU (AU)", default=SecurityFirm.FUTUSECURITIES)
|
|
65
|
-
parser.add_argument("--security_type", help="STOCK, BOND, ETF, FUTURE, WARRANT, IDX ... ", default=SecurityType.STOCK)
|
|
66
|
-
|
|
67
|
-
args = parser.parse_args()
|
|
68
|
-
param['symbol'] = args.symbol
|
|
69
|
-
param['market'] = args.market
|
|
70
|
-
param['trdmarket'] = args.trdmarket
|
|
71
|
-
param['security_firm'] = args.security_firm
|
|
72
|
-
|
|
73
|
-
async def main():
|
|
74
|
-
parse_args()
|
|
75
|
-
|
|
76
|
-
exchange = Futubull(param)
|
|
77
|
-
|
|
78
|
-
pd_candles: Union[pd.DataFrame, None] = fetch_candles(
|
|
79
|
-
start_ts=int(start_date.timestamp()),
|
|
80
|
-
end_ts=int(end_date.timestamp()),
|
|
81
|
-
exchange=exchange,
|
|
82
|
-
normalized_symbols=[ param['symbol'] ],
|
|
83
|
-
candle_size='1d'
|
|
84
|
-
)[param['symbol']]
|
|
85
|
-
|
|
86
|
-
assert pd_candles is not None
|
|
87
|
-
|
|
88
|
-
if pd_candles is not None:
|
|
89
|
-
assert len(pd_candles) > 0, "No candles returned."
|
|
90
|
-
expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
|
|
91
|
-
assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
|
|
92
|
-
assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
|
|
93
|
-
assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
|
|
94
|
-
|
|
95
|
-
asyncio.run(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/candles_ta_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
{siglab_py-0.1.21 → siglab_py-0.1.23}/siglab_py/market_data_providers/orderbooks_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|