Выкачиваем информацию с биржи в Excel

Давайте что-нибудь проанализируем.. Например, историю торгов? Это и в принципе полезно, и скиллы набьются полезные… В общем план такой – научиться вытаскивать нужные данные с биржи, экспортировать в Excel, ну и что-нибудь с ними делать. Понятно, что не руками :) Поехали!

Подготовка.

Нам потребуется

а) Установленный питон с модулем requests (как установить читайте тут)

б) Установленный модуль openpyxl – но тут всё просто, запустите командную строку и вбейте pip install openpyxl

Проверка

После того, как вы все установили, давайте проверим, создаются ли валидные Excel файлы. Простейший пример (взятый с официального сайта) показывает, как создать новую книгу, внести туда данные и сохранить:

from openpyxl import Workbook
wb = Workbook()

# Выбрать активную книгу
ws = wb.active

# Запишем число 42 в ячейку А1
ws['A1'] = 42

# Добавим строку с данными
ws.append([1, 2, 3])

# В поле А2 запишем текущую дату и время
import datetime
ws['A2'] = datetime.datetime.now()

# Сохраним файл рядом со скриптом
import os

wb.save(os.path.dirname(os.path.abspath(__file__)) + "/sample.xlsx")

Копируем этот код, вставляем в IDLE (опять же, подробности тут), сохраняем под каким-нибудь именем (у меня это excel_test.py) и запускаем (F5). В папке рядом со скриптом должен появиться файл sample.xlsx. 

Можно открыть его экселем и посмотреть, что внутри:



История торгов из Exmo в Excel

Ок, работает, писать данные можем. Теперь давайте попробуем забрать с exmo.me всю историю торгов. Точнее не так – всю историю Эксмо не дает, а дает максимум 10 000 последних торгов по паре.

Вот код – вставим в IDLE, сохраним как exmo_excel.py и запустим (о том, что такое API и как работать с ним на Exmo читайте в других статьях цикла – ссылки в конце статьи, над комментариями).

import urllib, http.client
import os
import time
import json
# эти модули нужны для генерации подписи API
import hmac, hashlib

from openpyxl import Workbook
from datetime import datetime, timezone

# ключи API, которые предоставила exmo
API_KEY = 'K-...'
# обратите внимание, что добавлена 'b' перед строкой
API_SECRET = b'S-...'

# базовые настройки
API_URL = 'api.exmo.com'
API_VERSION = 'v1'

# Свой класс исключений
class ScriptError(Exception):
    pass
class ScriptQuitCondition(Exception):
    pass

# все обращения к API проходят через эту функцию
def call_api(**kwargs):
    payload = {'nonce': int(round(time.time()*1000))}

    if kwargs:
        payload.update(kwargs)
    payload =  urllib.parse.urlencode(payload)

    H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512)
    H.update(payload.encode('utf-8'))
    sign = H.hexdigest()
    
    headers = {"Content-type": "application/x-www-form-urlencoded",
           "Key":API_KEY,
           "Sign":sign}
    conn = http.client.HTTPSConnection(API_URL, timeout=60)
    conn.request("POST", "/"+API_VERSION + "/" + kwargs['method'], payload, headers)
    response = conn.getresponse().read()
    
    conn.close()

    try:
        obj = json.loads(response.decode('utf-8'))

        if 'error' in obj and obj['error']:
            raise ScriptError(obj['error'])
        return obj
    except json.decoder.JSONDecodeError:
        raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response)

# Получим список всех пар, по которым торгует биржа
pairs_list = []
pairs = call_api(method='pair_settings')
for pair in pairs:
    pairs_list.append(pair) # сложим их в словарь

pairs_str = ','.join(pairs_list) # из словаря создадим строку, с парами, разделенными запятыми

# Получим историю торгов по всем парам
trades = call_api(method='user_trades', pair=pairs_str, limit=10000)
for pair in trades: # пройдемся по каждой паре
    if not trades[pair]: #пропускаем пары, по которым не было торгов
        continue
    # Теперь проходим по всем торгам этой пары
    print(pair, len(trades[pair]))

Должны увидеть список пар, по которым торговали, и кол-во полученных сделок по каждой их них. У меня это выглядит так:

Не лишним будет упомянуть, что есть торги по сути. Когда вы выставляете ордер на продажу, например 1 BTC, биржа подбирает для вас покупателей. Ваш ордер может быть исполнен в одну сделку – если кто-то сразу купит ваш биткоин целиком, либо в несколько – когда ваш биткойн раскупят по частям. Так вот, история торгов – это история именно таких сделок.

Вообще, стоило бы сравнить с историей торгов на сайте Эксмо, но там как будто специально неудобно сделано.. В общем, похоже на правду, на этом и остановимся пока. Если что, историю торгов можно смотреть тут (раздел сделки):

Теперь, собственно, нужно объединить оба скрипта, что бы получить историю торгов и выгрузить в эксель. Немного магии и вуаля:

import urllib, http.client
import os
import time
import json
# эти модули нужны для генерации подписи API
import hmac, hashlib

from openpyxl import Workbook
from datetime import datetime, timezone

# ключи API, которые предоставила exmo
API_KEY = 'K-...'
# обратите внимание, что добавлена 'b' перед строкой
API_SECRET = b'S-...'

# базовые настройки
API_URL = 'api.exmo.com'
API_VERSION = 'v1'

# Свой класс исключений
class ScriptError(Exception):
    pass
class ScriptQuitCondition(Exception):
    pass

# все обращения к API проходят через эту функцию
def call_api(**kwargs):
    payload = {'nonce': int(round(time.time()*1000))}

    if kwargs:
        payload.update(kwargs)
    payload =  urllib.parse.urlencode(payload)

    H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512)
    H.update(payload.encode('utf-8'))
    sign = H.hexdigest()
    
    headers = {"Content-type": "application/x-www-form-urlencoded",
           "Key":API_KEY,
           "Sign":sign}
    conn = http.client.HTTPSConnection(API_URL, timeout=60)
    conn.request("POST", "/"+API_VERSION + "/" + kwargs['method'], payload, headers)
    response = conn.getresponse().read()
    
    conn.close()

    try:
        obj = json.loads(response.decode('utf-8'))

        if 'error' in obj and obj['error']:
            raise ScriptError(obj['error'])
        return obj
    except json.decoder.JSONDecodeError:
        raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response)

# Получим список всех пар, по которым торгует биржа
pairs_list = []
pairs = call_api(method='pair_settings')
for pair in pairs:
    pairs_list.append(pair) # сложим их в словарь

pairs_str = ','.join(pairs_list) # из словаря создадим строку, с парами, разделенными запятыми

# Создадим Excel файл
wb = Workbook()
ws = wb.active
# Вставим заголовки
ws.append(["Дата сделки", "Пара сделки", "ID ордера", "ID сделки", "Тип сделки", "Кол-во по сделке", "Цена сделки", "Сумма сделки"])

# Получим историю торгов по всем парам
trades = call_api(method='user_trades', pair=pairs_str, limit=10000)
for pair in trades: # пройдемся по каждой паре
    if not trades[pair]: #пропускаем пары, по которым не было торгов
        continue
    # Теперь проходим по всем торгам этой пары
    for trade in trades[pair]:
        # Мы бы могли использовать метод dict.values(), но нам нужны данные в определенном порядке, причем каждый раз для каждого массива, так что немного усложним код
        # Форматируем и вставляем строку с данными в Excel
        ws.append([
            datetime.fromtimestamp(trade['date'], timezone.utc), # дата сделки
            pair, # Пара сделки
            trade['order_id'], # ID ордера
            trade['trade_id'], # ID сделки
            'Покупка' if trade['type'] == 'buy' else 'Продажа',
            float(trade['quantity']), # Кол-во по сделке
            float(trade['price']), # Цена сделки
            float(trade['amount']) * (-1 if trade['type'] == 'buy' else 0.998), # сумма сделки, если buy то отрицательная - так удобнее считать потом
        ])
    
# Сохраняем файл
wb.save(os.path.dirname(os.path.abspath(__file__)) + "/exmo_excel.xlsx")
print('Работу закончил')

Вставляем этот код в IDLE, сохраняем, запускаем.. У вас должен появиться Excel-файл exmo_excel.xlsx рядом со скриптом. Откроем, посмотрим.

Я поставил фильтр по паре LTC_RUB, что бы еще раз акцентировать внимание на сделках, так в файле все торги по всем парам (точнее, последние 10 000 торгов). В первой колонке время сделки (по времени биржи), в остальных вроде понятно, объясню про ID ордера и ID сделки.

Смотреть стоит по времени, т.е. снизу вверх. 14 августа я на почти 150р купил 0, 618LTC по курсу 242,38. Для этого я создавал отложенный ордер (52466257), который был исполнен в рамках одной сделки (5227092).

После этого я, видимо, выставил отложенный ордер на продажу купленного (52466294), который исполнился опять же одной сделкой.

После этого я создал отложенный ордер на покупку (62752648), который был исполнен частями –обратите внимание, что тут две строки с одним и тем же номером ордера и одной и той же ценой, но разными суммами, временем и id сделки. Т.е. сначала кто-то купил почти всё, а потом кто-то еще докупил, что осталось.

Ну а потом сработала еще одна продажа купленного.

Если сложить все суммы сделки (вот почему покупку мы делали отрицательной), то получим сальдо или как там его – мне больше нравится слово профит, хотя и «прибыль» тоже ничего. В данном случае вроде бы 63 копейки прибыли, но….. Тут, насколько я вижу, хотя надо бы и проверить, сумма указывается ДО вычета комиссии биржи – 0.2%.

Если еще раз обратите внимание, то первую сделку я провел за 150 рублей, купив 0,618LTC, а продаю 0,617LTC – потому что часть купленного в LTC съела комиссия (рубли не псотрадали). Когда же я продаю купленное, комиссия берется с рублей – но тут я не вижу, что бы это отражалось. Поэтому, наверное, более точно было считать так (дополнил столбец руками):

Для положительных сумм сделок мы вычитаем комиссию 0.2% (формула видна на рисунке), отрицательные считаем как есть – так правильно. Итого, профит составил 2,8 копейки, юхууу))

Не помню, что я тестил на этой паре год назад, и какие настройки прибыльности стояли, но в общем имейте в виду такую выдачу инфы, что через API, что на сайте.

Если хотите, можете сразу выводить в эксель прибыль за вычетом комиссии. Для этого строку

float(trade['amount']) * (-1 if trade['type'] == 'buy' else 1),

нужно заменить на

float(trade['amount']) * (-1 if trade['type'] == 'buy' else 0.998),

итог будет правильным:

В общем, делайте с экселем теперь что хотите, стройте графики или сводные таблицы, инструмент для получения данных у вас есть.



История торгов из Yobit в Excel

Ну и как бонус давайте сделаем тоже самое для биржи yobit.net. Тут уже будет меньше комментариев, т.к. по сути ничего не меняется, только немного кода. Более детально о том, как работать с API yobit, читайте так же по ссылкам в конце статьи.

В общем, вот код, вбивайте свои ключи, запускайте.. Он будет работать мучительно долго, т.к. во первых очень много пар, а во вторых, после каждой пары добавлено time.sleep(1), что бы вас Yobit не забанил.. Хотя, он может быть и так не забанит, так что можете удалить эту строку и все пойдет шустрее, но это на свой страх и риск.

import os
import json
import requests
import math
import time
import urllib, http.client
import hmac, hashlib

from openpyxl import Workbook
from datetime import datetime, timezone

# Вписываем свои ключи
API_KEY = '...'
API_SECRET = b'...'

"""
    Каждый новый запрос к серверу должен содержать увеличенное число в диапазоне 1-2147483646
    Поэтому храним число в файле поблизости, каждый раз обновляя его
"""
nonce_file = "./nonce"
if not os.path.exists(nonce_file):
    with open(nonce_file, "w") as out:
        out.write('1')
# Будем перехватывать все сообщения об ошибках с биржи
class YobitException(Exception):
    pass
        
def call_api(**kwargs):

    # При каждом обращении к торговому API увеличиваем счетчик nonce на единицу
    with open(nonce_file, 'r+') as inp:
        nonce = int(inp.read())
        inp.seek(0)
        inp.write(str(nonce+1))
        inp.truncate()

    payload = {'nonce': nonce}

    if kwargs:
        payload.update(kwargs)
    payload =  urllib.parse.urlencode(payload)

    H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512)
    H.update(payload.encode('utf-8'))
    sign = H.hexdigest()
    
    headers = {"Content-type": "application/x-www-form-urlencoded",
           "Key":API_KEY,
           "Sign":sign}
    conn = http.client.HTTPSConnection("yobit.net", timeout=60)
    conn.request("POST", "/tapi/", payload, headers)
    response = conn.getresponse().read()
    
    conn.close()

    try:
        obj = json.loads(response.decode('utf-8'))

        if 'error' in obj and obj['error']:
            raise YobitException(obj['error'])
        return obj
    except json.decoder.JSONDecodeError:
        raise YobitException('Ошибка анализа возвращаемых данных, получена строка', response)

res = requests.get('https://yobit.net/api/3/info') # получаем данные info
res_obj = json.loads(res.text) # переводим полученный текст в объект с данными

pairs = [pair for pair in res_obj['pairs']] # создадим массив названий пар

# Создадим Excel файл
wb = Workbook()
ws = wb.active
# Вставим заголовки
ws.append(["Дата сделки", "Пара сделки", "ID ордера", "ID сделки", "Тип сделки", "Цена сделки", "Сумма сделки"])

for pair in pairs:
    try:
        print ('Получаем историю торгов по паре %s' % pair, '*'*30)
        data = call_api(method="TradeHistory", pair=pair)
        if(data.get('return')):
            for t in data['return']:
                trade = data['return'][t]
                ws.append([
                    datetime.fromtimestamp(trade['timestamp'], timezone.utc), # дата сделки
                    pair, # Пара сделки
                    trade['order_id'], # ID ордера
                    t, # ID сделки
                    'Покупка' if trade['type'] == 'buy' else 'Продажа',
                    float(trade['rate']), # Цена сделки
                    float(trade['amount']) * (-1 if trade['s'] == 'buy' else 1), # сумма сделки, если buy то отрицательная - так удобнее считать потом
                ])
        else:
            print('Торгов не было')
        #time.sleep(1)
    except YobitException as e:
        print("Облом:", e)
    
wb.save(os.path.dirname(os.path.abspath(__file__)) + "/yobit_excel.xlsx")
print('Работу закончил')

Когда оно всё доработает, рядом с файлом появится файл yobit_excel.xslx, можете открывать и делать выводы. Опять же, если нужно вычитать комиссию, то воспользуйтесь советом выше, для эксмо. Если что-то не будет работать или будет работать не так, пишите, поправим.

Удачи!

 

 


Это статья из цикла "Популярно о бирже"
Все статьи цикла:

Последнее изменение:


Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]



Комментарии
18.11.2017 16:04:14
Андрей, добрый день!
Пробовал использовать ваш код для получения данных по торгам с yobit, но после отработки программы файл есель оказался пуст (кроме шапки таблицы) , а в процессе работы проги постоянной возникала ошибка - Облом: invalid nonce (has already been used). Что можно поправить?
ПроголосоватьПроголосовать
0 0
18.11.2017 17:15:22
Добрый день!
При каждом запросе к АПИ передается nonce - некоторое число, которое должно увеличиваться с каждым запросом.
В коде я начинаю с единицы - 1,2,3 и т.п. Соответственно, если вы уже использовали API для работы с каким-нибудь ботом, в мобильном приложении и т.п., то они тоже передавали какие-то значения, и явно они выше единицы. Даже если вы запускали этот код, то увеличивали счетчик.
В общем, самое простое решение будет пересоздать API ключи на бирже - для каждых новых ключей отсчет начинается заново.
Так же в процессе работы в папке с кодом создается файлик nonce, в который записывается последнее использованное кодом значение.
Если пересоздавать ключи API неудобно, то можно открыть этот файл блокнотом, записать туда например 1000000, сохранить, что бы постараться опередить уже использованное ранее значение.
ПроголосоватьПроголосовать
0 0
18.11.2017 20:37:00
Андрей, к сожалению танцы с бубном с новыми API ключами и с увеличением цифры в nonce не принесли результата. Все тоже самое - пр работе питона на каждой скачиваемой паре пишет: Облом: invalid nonce (has already been used). Может еще есть варианты?
ПроголосоватьПроголосовать
0 0
18.11.2017 21:04:07
Загадочная история, запустил только что, всё работает, значение в nonce увеличивается с каждой парой

Торгов не было
Получаем историю торгов по паре rad_usd ******************************
Торгов не было
Получаем историю торгов по паре rpc_usd ******************************
Торгов не было
Получаем историю торгов по паре xcre_rur ******************************
Торгов не было
Получаем историю торгов по паре limx_waves ******************************
Торгов не было
Получаем историю торгов по паре hdg_btc ******************************
Торгов не было

Кстати, нашел кое-что в коде, поправил статью, скачайте новый код. Но к nonce оно не относится. Есть еще вариант что у вас два экземпляра запущено, проверьте в диспетчере задач, сколько раз python.exe встречается. Может прав нет у питона писать в этот файл (nonce) в текущей директории? Тогда нужно от админа запустить
ПроголосоватьПроголосовать
0 0
19.11.2017 07:02:24
Доброе утро! Установлен Python36-32, windows 7. При работе программы цифровое значение в файле nonce увеличивается - кроме цифр. значения в нем больше ничего нет. В екселе та же картина. Начало работы программы:
Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 17:26:49) [MSC v.1900 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>
============= RESTART: C:\Users\user\Desktop\1.py =============
Получаем историю торгов по паре ltc_btc ******************************
Облом: invalid nonce (has already been used)
Получаем историю торгов по паре nmc_btc ******************************
Облом: invalid nonce (has already been used)
Прошу помочь.
ПроголосоватьПроголосовать
0 0
19.11.2017 10:21:29
Переустановка на Python 3.6.0 так же не помогла
прописал-
pip install requests
pip install openpyxl

История торгов из Exmo в Excel - тот же результат. excel пустой
ПроголосоватьПроголосовать
0 0
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий