-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/venv/ | ||
|
||
/*.csv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# ohlcv-fetchers | ||
|
||
Set of scripts that can be used to **download [OHLCV data](https://en.wikipedia.org/wiki/Open-high-low-close_chart)** from various sources (e.g. [_Binance_](https://binance.com/), [_Yahoo Finance_](https://finance.yahoo.com/)). | ||
|
||
## Usage | ||
|
||
Set up a **Python venv** (virtual environment) and install some packages inside it: | ||
|
||
```bash | ||
python3 -mvenv venv | ||
venv/bin/python3 -mpip install -r requirements.txt | ||
``` | ||
|
||
Then you can use the scripts: | ||
|
||
```bash | ||
venv/bin/python3 myscript.py --help | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import argparse | ||
import sys | ||
|
||
from datetime import timedelta | ||
from datetime import datetime as dt | ||
from datetime import timezone as tz | ||
from dateutil import parser as dup | ||
|
||
import ccxt | ||
|
||
|
||
def main(argv=None): | ||
if argv is None: | ||
argv = sys.argv | ||
|
||
parser = argparse.ArgumentParser( | ||
description='Binance OHLCV data downloader') | ||
|
||
parser.add_argument('symbol', metavar='SYMBOL', type=str, | ||
help='Ticker symbol (example: BTC/USDT)') | ||
|
||
parser.add_argument('-i', '--interval', type=str, default='5m', | ||
help='Length of time each candle represents' | ||
'(default: 5m)') | ||
|
||
parser.add_argument('-d', '--dt-start', type=lambda x: dup.parse(x), | ||
default=dt.now(tz.utc) - timedelta(hours=1), | ||
help='Start date and time (default: 1 hour ago)') | ||
parser.add_argument('-D', '--dt-end', type=lambda x: dup.parse(x), | ||
default=dt.now(tz.utc), | ||
help='End date and time (default: now)') | ||
|
||
parser.add_argument('-f', '--format', type=str, default='', | ||
help='If specified, formats the float values (such as' | ||
'the asset prices) with this format string' | ||
'(e.g. "{:.6f}")') | ||
|
||
args = parser.parse_args(argv[1:]) | ||
|
||
args.dt_start = args.dt_start.astimezone(tz.utc) | ||
args.dt_end = args.dt_end.astimezone(tz.utc) | ||
|
||
############################################################################ | ||
|
||
print(f'Fetching {args.symbol} with CCXT', file=sys.stderr) | ||
|
||
exchange = ccxt.binance() | ||
|
||
# Equivalent of | ||
# https://api.binance.com/api/v3/klines?symbol=...&interval=...&startTime=...&endTime=... | ||
# See | ||
# https://developers.binance.com/docs/binance-spot-api-docs/rest-api#klinecandlestick-data | ||
data = exchange.fetch_ohlcv( | ||
args.symbol, | ||
args.interval, | ||
int(args.dt_start.timestamp() * 1000), | ||
params={ | ||
'until': int(args.dt_end.timestamp() * 1000), | ||
'paginate': True, | ||
} | ||
) | ||
|
||
if args.format != '': | ||
for candle in data: | ||
for i, v in enumerate(candle): | ||
if isinstance(v, float): | ||
candle[i] = args.format.format(v) | ||
|
||
print('Timestamp,Open,High,Low,Close,Volume') | ||
for candle in data: | ||
print(','.join(str(x) for x in candle)) | ||
|
||
return 0 | ||
|
||
|
||
if __name__ == '__main__': | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ccxt==4.4.* | ||
python-dateutil==2.9.* | ||
yfinance==0.2.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import argparse | ||
import sys | ||
|
||
from datetime import timedelta | ||
from datetime import datetime as dt | ||
from dateutil import parser as dup | ||
|
||
import yfinance as yf | ||
import pandas as pd | ||
|
||
|
||
def is_aware(d: dt): | ||
''' | ||
Returns true if the datetime object `d` is timezone-aware, false otherwise. | ||
See https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive | ||
''' | ||
return d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None | ||
|
||
|
||
def main(argv=None): | ||
if argv is None: | ||
argv = sys.argv | ||
|
||
parser = argparse.ArgumentParser( | ||
description='Yahoo Finance OHLCV data downloader') | ||
|
||
parser.add_argument('symbol', metavar='SYMBOL', type=str, | ||
help='Ticker symbol (example: ^GSPC)') | ||
|
||
parser.add_argument('-i', '--interval', type=str, default='1d', | ||
help='Length of time each candle represents' | ||
'(default: 1d)') | ||
|
||
parser.add_argument('-d', '--dt-start', type=lambda x: dup.parse(x), | ||
default=dt.now().astimezone() - timedelta(days=30), | ||
help='Start date and time (default: 30 days ago)') | ||
parser.add_argument('-D', '--dt-end', type=lambda x: dup.parse(x), | ||
default=dt.now().astimezone(), | ||
help='End date and time (default: now)') | ||
|
||
parser.add_argument('-f', '--format', type=str, default='', | ||
help='If specified, formats the float values (such as' | ||
'the asset prices) with this format string' | ||
'(e.g. "{:.6f}")') | ||
|
||
args = parser.parse_args(argv[1:]) | ||
|
||
if not is_aware(args.dt_start): | ||
args.dt_start = args.dt_start.astimezone() | ||
if not is_aware(args.dt_end): | ||
args.dt_end = args.dt_end.astimezone() | ||
|
||
############################################################################ | ||
|
||
print(f'Fetching {args.symbol} with yfinance', file=sys.stderr) | ||
|
||
# Equivalent of | ||
# https://query1.finance.yahoo.com/v8/finance/chart/...?interval=...&period1=...&period2=... | ||
# See https://finance.yahoo.com/quote/.../history | ||
data: pd.DataFrame = yf.download( | ||
args.symbol, args.dt_start, args.dt_end, interval=args.interval) | ||
|
||
if args.format != '': | ||
data = data.apply(lambda col: col.map( | ||
lambda x: args.format.format(x) if isinstance(x, float) else x)) | ||
|
||
data.to_csv(sys.stdout, lineterminator='\n') | ||
|
||
return 0 | ||
|
||
|
||
if __name__ == '__main__': | ||
sys.exit(main()) |