Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorate OHLC data #1133

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions binance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import time
from operator import itemgetter
from urllib.parse import urlencode

from pandas import DataFrame

from .helpers import interval_to_milliseconds, convert_ts_str
from .exceptions import BinanceAPIException, BinanceRequestException, NotImplementedException
from .enums import HistoricalKlinesType

from .enums import HistoricalKlinesType, OHLC_COLUMNS
from .decorators import HistoricalKlinesData

class BaseClient:

Expand Down Expand Up @@ -910,6 +910,7 @@ def _get_earliest_valid_timestamp(self, symbol, interval, klines_type: Historica
return kline[0][0]

def get_historical_klines(self, symbol, interval, start_str, end_str=None, limit=500,
output_format='list',
klines_type: HistoricalKlinesType = HistoricalKlinesType.SPOT):
"""Get Historical Klines from Binance

Expand All @@ -923,15 +924,20 @@ def get_historical_klines(self, symbol, interval, start_str, end_str=None, limit
:type end_str: str|int
:param limit: Default 500; max 1000.
:type limit: int

:param output_format: 'list' or 'df' or 'decorated' (default is 'list')
:type output_format: str

:param klines_type: Historical klines type: SPOT or FUTURES
:type klines_type: HistoricalKlinesType

:return: list of OHLCV values

"""
return self._historical_klines(symbol, interval, start_str, end_str=end_str, limit=limit, klines_type=klines_type)
return self._historical_klines(symbol, interval, start_str, end_str=end_str, limit=limit, output_format=output_format, klines_type=klines_type)

def _historical_klines(self, symbol, interval, start_str, end_str=None, limit=500,
output_format='list',
klines_type: HistoricalKlinesType = HistoricalKlinesType.SPOT):
"""Get Historical Klines from Binance (spot or futures)

Expand All @@ -949,6 +955,10 @@ def _historical_klines(self, symbol, interval, start_str, end_str=None, limit=50
:type end_str: None|str|int
:param limit: Default 500; max 1000.
:type limit: int

:param output_format: 'list' or 'df' or 'decorated' (default is 'list')
:type output_format: str

:param klines_type: Historical klines type: SPOT or FUTURES
:type klines_type: HistoricalKlinesType

Expand Down Expand Up @@ -1005,7 +1015,12 @@ def _historical_klines(self, symbol, interval, start_str, end_str=None, limit=50
if idx % 3 == 0:
time.sleep(1)

return output_data
if output_format == "df":
return DataFrame(output_data, columns=OHLC_COLUMNS)
elif output_format == "decorated":
return [HistoricalKlinesData(row) for row in output_data]
else:
return output_data

def get_historical_klines_generator(self, symbol, interval, start_str, end_str=None,
klines_type: HistoricalKlinesType = HistoricalKlinesType.SPOT):
Expand Down
54 changes: 54 additions & 0 deletions binance/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@



from functools import cached_property
from datetime import datetime

class HistoricalKlinesData():
# https://github.com/binance-us/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data
#
# [
# 1499040000000, // Open time
# "0.01634790", // Open
# "0.80000000", // High
# "0.01575800", // Low
# "0.01577100", // Close
# "148976.11427815", // Volume
# 1499644799999, // Close time
# "2434.19055334", // Quote asset volume
# 308, // Number of trades
# "1756.87402397", // Taker buy base asset volume
# "28.46694368", // Taker buy quote asset volume
# "17928899.62484339" // Ignore
# ]

def __init__(self, raw_data):
self.raw_data = raw_data

@cached_property
def open_at(self):
return datetime.fromtimestamp(self.raw_data[0] / 1000)

@cached_property
def close_at(self):
return datetime.fromtimestamp(self.raw_data[6] / 1000)

@property
def open(self):
return float(self.raw_data[1])

@property
def high(self):
return float(self.raw_data[2])

@property
def low(self):
return float(self.raw_data[3])

@property
def close(self):
return float(self.raw_data[4])

@property
def volume(self):
return float(self.raw_data[5])
8 changes: 8 additions & 0 deletions binance/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@
MARGIN_BUY_TYPE = 'MARGIN_BUY'
AUTO_REPAY_TYPE = 'AUTO_REPAY'

# the names of candlestick data columns, in order
# https://github.com/binance-us/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data
OHLC_COLUMNS = [
'open_time', 'open', 'high', 'low', 'close', 'volume', 'close_time',
'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume',
'ignore'
]


class HistoricalKlinesType(Enum):
SPOT = 1
Expand Down
17 changes: 17 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@



ohlc_row = [
1519892340000,
"0.00099400",
"0.00099810",
"0.00099400",
"0.00099810",
"4806.04000000",
1519892399999,
"4.78553253",
154,
"1785.14000000",
"1.77837524",
"0",
]
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dateparser
requests
ujson
websockets==9.1
pandas
Empty file removed tests/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

from datetime import datetime

from binance.decorators import HistoricalKlinesData

from conftest import ohlc_row

def test_historical_klines_decorator():

obj = HistoricalKlinesData(ohlc_row)
assert isinstance(obj.open_at, datetime)
assert isinstance(obj.close_at, datetime)
assert isinstance(obj.open, float)
assert isinstance(obj.high, float)
assert isinstance(obj.low, float)
assert isinstance(obj.close, float)
assert isinstance(obj.volume, float)
131 changes: 95 additions & 36 deletions tests/test_historical_klines.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,58 @@
# coding=utf-8

from binance.client import Client
from binance.enums import OHLC_COLUMNS
from binance.decorators import HistoricalKlinesData

from pandas import DataFrame

import pytest
import requests_mock


client = Client("api_key", "api_secret")

first_available_res = [
[
1500004800000,
"0.00005000",
"0.00005300",
"0.00001000",
"0.00004790",
"663152.00000000",
1500004859999,
"30.55108144",
43,
"559224.00000000",
"25.65468144",
"83431971.04346950",
]
]

def test_exact_amount():
"""Test Exact amount returned"""
first_res = []
row = [
1519892340000,
"0.00099400",
"0.00099810",
"0.00099400",
"0.00099810",
"4806.04000000",
1519892399999,
"4.78553253",
154,
"1785.14000000",
"1.77837524",
"0",
]

first_available_res = [
[
1500004800000,
"0.00005000",
"0.00005300",
"0.00001000",
"0.00004790",
"663152.00000000",
1500004859999,
"30.55108144",
43,
"559224.00000000",
"25.65468144",
"83431971.04346950",
]
]
for i in range(0, 500):
first_res.append(row)

first_res = []
row = [
1519892340000,
"0.00099400",
"0.00099810",
"0.00099400",
"0.00099810",
"4806.04000000",
1519892399999,
"4.78553253",
154,
"1785.14000000",
"1.77837524",
"0",
]
second_res = []

for i in range(0, 500):
first_res.append(row)

second_res = []
def test_exact_amount():
"""Test Exact amount returned"""

with requests_mock.mock() as m:
m.get(
Expand Down Expand Up @@ -280,3 +285,57 @@ def test_historical_kline_generator_empty_response():

with pytest.raises(StopIteration):
next(klines)






def test_test_historical_kline_output_format():
"""Test output format"""

with requests_mock.mock() as m:
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=1&startTime=0&symbol=BNBBTC",
json=first_available_res,
)
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=500&startTime=1519862400000&symbol=BNBBTC",
json=first_res,
)
m.get(
"https://api.binance.com/api/v3/klines?interval=1m&limit=500&startTime=1519892400000&symbol=BNBBTC",
json=second_res,
)

#
# DEFAULT FORMAT
#
klines_list = client.get_historical_klines(
symbol="BNBBTC", interval=Client.KLINE_INTERVAL_1MINUTE, start_str="1st March 2018"
)
assert len(klines_list) == 500
assert isinstance(klines_list, list)
assert isinstance(klines_list[0], list)

#
# DATAFRAME FORMAT
#
klines_df = client.get_historical_klines(
symbol="BNBBTC", interval=Client.KLINE_INTERVAL_1MINUTE, start_str="1st March 2018",
output_format="df"
)
assert len(klines_df) == 500
assert isinstance(klines_df, DataFrame)
assert klines_df.columns.tolist() == OHLC_COLUMNS

#
# DECORATED FORMAT
#
klines_data = client.get_historical_klines(
symbol="BNBBTC", interval=Client.KLINE_INTERVAL_1MINUTE, start_str="1st March 2018",
output_format="decorated"
)
assert len(klines_data) == 500
assert isinstance(klines_data, list)
assert isinstance(klines_data[0], HistoricalKlinesData)