Skip to content

Commit

Permalink
Added ohlcv-fetchers
Browse files Browse the repository at this point in the history
  • Loading branch information
dmotte committed Oct 3, 2024
1 parent cde17b0 commit c73ea87
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 0 deletions.
3 changes: 3 additions & 0 deletions python-scripts/ohlcv-fetchers/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/venv/

/*.csv
18 changes: 18 additions & 0 deletions python-scripts/ohlcv-fetchers/README.md
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
```
79 changes: 79 additions & 0 deletions python-scripts/ohlcv-fetchers/binance.py
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())
3 changes: 3 additions & 0 deletions python-scripts/ohlcv-fetchers/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ccxt==4.4.*
python-dateutil==2.9.*
yfinance==0.2.*
75 changes: 75 additions & 0 deletions python-scripts/ohlcv-fetchers/yahoo-finance.py
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())

0 comments on commit c73ea87

Please sign in to comment.