Арбитраж криптовалютных бирж на Python

GetCoder.ru
Изображение статьи

В этой статье мы рассмотрим вариант создания пет-проекта для арбитража криптовалютных бирж. Для этой задачи мы возьмем за основу предыдущий наш проект Web приложение на Python Flask/PySide6 в виде десктопной программы. У нас также уже есть заготовки для получения данных с криптовалютных бирж. Осталось это все объединить в один проект.

В этой статье мы рассмотрим вариант создания пет-проекта для арбитража криптовалютных бирж. Для этой задачи мы возьмем за основу предыдущий наш проект Web приложение на Python Flask/PySide6 в виде десктопной программы. У нас также уже есть заготовки для получения данных с криптовалютных бирж. Осталось это все объединить в один проект.

Как это работает

  • start: Основной скрипт, который запускает веб-службу, браузер, сокет сервер в подпроцессы. Так же запускается моудль exchanges для сбора данных с криптовалютных бирж;
  • web_service: Веб-служба, работающая на Flask, обрабатывает запросы и предоставляет интерфейс;
  • browser: Интерфейсный модуль на PySide6, который использует встроенный браузер (QWebEngineView) для отображения сайта;
  • socket_server: Запускает websockets для быстрой передачи данных;
  • socket_client: Подключается к socket_server для передачи данных от модуля exchanges к web_service;
  • exchanges: Директория, в которой находится WebSocket для криптовалютных бирж (Binance, Bitget, Bybit, Coinbase, Gate, Kraken, Okx).
  • design: Директория, в которой находится структура сайта (HTML, CSS, JS);

start

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

  • copy
#-*- coding: UTF-8 -*- import time, subprocess from modules.exchanges import Trading_platforms class Main(): def __init__(self): host = '127.0.0.1' port = '5000' try: socket_server = subprocess.Popen(["python", "modules/socket_server.py"]) web_service = subprocess.Popen(["python", "modules/web_service.py", host, port]) browser = subprocess.Popen(["python", "modules/browser.py", host, port]) # Данные с торговых площадок Trading_platforms() while True: time.sleep(10) except KeyboardInterrupt: pass print("Основной процесс завершен. Завершаем субпроцесс.") socket_server.terminate() web_service.terminate() browser.terminate() socket_server.wait() web_service.wait() browser.wait() print("Субпроцессы завершены.") if __name__ == "__main__": Main()

web_service

Пример примитивного web-ядра. Для наших задач на данный момент более чем подходит. Это web-ядро может обробатьвать GET запросы с параметрами. Проводить очистку передаваемых параметров и структурировать их в словарь. Обработку POST запросов реализуем позднее.

  • copy
#-*- coding: UTF-8 -*- from flask import Flask, request, Response import re, mimetypes, sys host = sys.argv[1] port = sys.argv[2] mimetypes.add_type('font/woff', '.woff') mimetypes.add_type('font/woff2', '.woff2') mimetypes.add_type('font/woff', '.woff') mimetypes.add_type('application/vnd.ms-fontobject', '.eot') mimetypes.add_type('font/ttf', '.ttf') class webService: def __init__(self): self.app = Flask(__name__) # Получение запроса @self.app.route('/', defaults={'path': ''}) @self.app.route('/', methods=['GET', 'POST']) def route(path): answer = self.process_route() return Response(response = answer['response'], status = answer['status'], mimetype = answer['mimetype']) self.app.run(host=host, port=port, debug=True) # Определение mimeType def get_mime_type(self, file_path): mime_type, _ = mimetypes.guess_type(file_path) return mime_type # Изменение данных в строке с помощью replace через цикл def replace_str(self, strData, mData): for keyItem, dataItem in mData.items(): strData = strData.replace(keyItem, dataItem) return strData # Очистка параметров def clean_string(self, input_string): cleaned_string = re.sub(r'[^a-zA-Z0-9]', '', input_string) return cleaned_string # Обработка входящих параметров def proccess_params(self): result = {} try: urlRequest = request.url mParam = urlRequest.split('?') if len(mParam) > 1: mVar = mParam[1].split('&') for item in mVar: mItem = item.split('=') if mItem[0] == '' or len(mItem) == 1: continue result[self.clean_string(mItem[0])] = self.clean_string(mItem[1]) except Exception as e: print('proccess_params: '+str(e)) return result # Работа с шаблоном def process_template(self, path_file, typeRead = 'r'): try: if typeRead == 'rb': with open(path_file, typeRead) as f: result = f.read() else: with open(path_file, typeRead, encoding='utf-8') as f: result = f.read() except Exception as e: print(e) raise Exception('404') return result # Обработка запроса def process_route(self): result = {'status': 200, 'response': '', 'mimetype': 'text/html', 'typeRead': 'r'} try: server_host = str(request.scheme)+'://'+str(request.server[0])+':'+str(request.server[1]) request_data = {'method': request.method, 'path': request.view_args['path'].split('/'), 'variable': self.proccess_params()} if request_data['method'] == 'GET': if request_data['path'][0] == '': request_data['path'][0] = 'index.html' # Подгрузка страницы if len(request_data['path']) == 1: result['response'] = self.process_template('design/template/'+request_data['path'][0]) # Подгрузка дизайна elif request_data['path'][0] == 'design': result['mimetype'] = self.get_mime_type(request.view_args['path']) mMimetype = str(result['mimetype']).split('/') result['typeRead'] = 'rb' if mMimetype[0] == 'image' or mMimetype[0] == 'font' or mMimetype[0] == 'application' else 'r' result['response'] = self.process_template(request.view_args['path'], result['typeRead']) if result['response'] == False: raise Exception('404') else: raise Exception('404') else: raise Exception('404') except Exception as e: # Подгрузка 404 страницы result['response'] = self.process_template('design/template/404.html') result['status'] = 404 if result['typeRead'] != 'rb': mData = {'SERVER_HOST': server_host} result['response'] = self.replace_str(result['response'], mData) return result if __name__ == "__main__": webService()

browser

Интерфейсный модуль на PySide6, который использует встроенный браузер (QWebEngineView) для отображения полученной информации от web_service.

  • copy
#-*- coding: UTF-8 -*- import sys from PySide6.QtCore import QUrl from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtWebEngineWidgets import QWebEngineView host = sys.argv[1] port = sys.argv[2] url = 'http://'+host+':'+port class Browser(): def __init__(self): app = QApplication(sys.argv) mw = QMainWindow() mw.setWindowTitle("Exchange arbitration") mw.setGeometry(100, 100, 1300, 700) mw.browser = QWebEngineView() mw.browser.setUrl(QUrl(url)) mw.setCentralWidget(mw.browser) mw.show() sys.exit(app.exec()) if __name__ == "__main__": Browser()

socket_server

Пример простого сервера веб-сокетов. К нему подключен модуль exchanges через модуль socket_client, который будет передавать данные с торговых бирж на внешнюю сторону веб-сервиса.

  • copy
#!/usr/bin/env python3 # -*- encoding: utf-8 -*- import asyncio import websockets client_list = [] async def handler(websocket, port): client_list.append(websocket) while True: try: message = await websocket.recv() await broadcast(message, websocket) except Exception as e: client_list.remove(websocket) break async def broadcast(message, websocket): for client in client_list: if client != websocket: await client.send(message) async def main(): async with websockets.serve(handler, "", 9501): await asyncio.Future() if __name__ == "__main__": asyncio.run(main())

socket_client

Пример web-сокет клиента для подключения к web-сокет серверу. Данные отправляются через модуль exchanges.

  • copy
#!/usr/bin/env python3 # -*- encoding: utf-8 -*- import time, threading, websocket, json class Socket_client(): def __init__(self): self.statusConnect = False ws = threading.Thread(target=self.start, daemon=True) ws.start() def sendData(self, mData): try: if self.statusConnect == True: self.wsa.send(json.dumps(mData)) except Exception as e: print(e) def on_open(self, ws): self.statusConnect = True def start(self): while True: try: self.wsa = websocket.WebSocketApp('ws://127.0.0.1:9501', on_open = self.on_open) self.wsa.run_forever() raise Exception('Socket_clietn - Off') except Exception as e: print(e) self.statusConnect = False time.sleep(5)

Запуск

Установите необходимые библиотеки:

  • copy
pip install flask PySide6

Запустите start.py

  • copy
python start.py

После запуска приложение откроется в виде окна, а внутри него будет отображаться веб-интерфейс, работающий на Flask.

Исходный код
  • 07.01.2025
  • 63
  • 1

Арбитраж криптовалютных бирж на Python

В этой статье мы рассмотрим вариант создания пет-проекта для арбитража криптовалютных бирж. Для этой задачи мы возьмем за основу предыдущий наш проект Web приложение на Python Flask/PySide6 в виде десктопной программы. У нас также уже есть заготовки для получения данных с криптовалютных бирж. Осталось это все объединить в один проект.

Как это работает

  • start: Основной скрипт, который запускает веб-службу, браузер, сокет сервер в подпроцессы. Так же запускается моудль exchanges для сбора данных с криптовалютных бирж;
  • web_service: Веб-служба, работающая на Flask, обрабатывает запросы и предоставляет интерфейс;
  • browser: Интерфейсный модуль на PySide6, который использует встроенный браузер (QWebEngineView) для отображения сайта;
  • socket_server: Запускает websockets для быстрой передачи данных;
  • socket_client: Подключается к socket_server для передачи данных от модуля exchanges к web_service;
  • exchanges: Директория, в которой находится WebSocket для криптовалютных бирж (Binance, Bitget, Bybit, Coinbase, Gate, Kraken, Okx).
  • design: Директория, в которой находится структура сайта (HTML, CSS, JS);

start

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

  • copy
#-*- coding: UTF-8 -*- import time, subprocess from modules.exchanges import Trading_platforms class Main(): def __init__(self): host = '127.0.0.1' port = '5000' try: socket_server = subprocess.Popen(["python", "modules/socket_server.py"]) web_service = subprocess.Popen(["python", "modules/web_service.py", host, port]) browser = subprocess.Popen(["python", "modules/browser.py", host, port]) # Данные с торговых площадок Trading_platforms() while True: time.sleep(10) except KeyboardInterrupt: pass print("Основной процесс завершен. Завершаем субпроцесс.") socket_server.terminate() web_service.terminate() browser.terminate() socket_server.wait() web_service.wait() browser.wait() print("Субпроцессы завершены.") if __name__ == "__main__": Main()

web_service

Пример примитивного web-ядра. Для наших задач на данный момент более чем подходит. Это web-ядро может обробатьвать GET запросы с параметрами. Проводить очистку передаваемых параметров и структурировать их в словарь. Обработку POST запросов реализуем позднее.

  • copy
#-*- coding: UTF-8 -*- from flask import Flask, request, Response import re, mimetypes, sys host = sys.argv[1] port = sys.argv[2] mimetypes.add_type('font/woff', '.woff') mimetypes.add_type('font/woff2', '.woff2') mimetypes.add_type('font/woff', '.woff') mimetypes.add_type('application/vnd.ms-fontobject', '.eot') mimetypes.add_type('font/ttf', '.ttf') class webService: def __init__(self): self.app = Flask(__name__) # Получение запроса @self.app.route('/', defaults={'path': ''}) @self.app.route('/', methods=['GET', 'POST']) def route(path): answer = self.process_route() return Response(response = answer['response'], status = answer['status'], mimetype = answer['mimetype']) self.app.run(host=host, port=port, debug=True) # Определение mimeType def get_mime_type(self, file_path): mime_type, _ = mimetypes.guess_type(file_path) return mime_type # Изменение данных в строке с помощью replace через цикл def replace_str(self, strData, mData): for keyItem, dataItem in mData.items(): strData = strData.replace(keyItem, dataItem) return strData # Очистка параметров def clean_string(self, input_string): cleaned_string = re.sub(r'[^a-zA-Z0-9]', '', input_string) return cleaned_string # Обработка входящих параметров def proccess_params(self): result = {} try: urlRequest = request.url mParam = urlRequest.split('?') if len(mParam) > 1: mVar = mParam[1].split('&') for item in mVar: mItem = item.split('=') if mItem[0] == '' or len(mItem) == 1: continue result[self.clean_string(mItem[0])] = self.clean_string(mItem[1]) except Exception as e: print('proccess_params: '+str(e)) return result # Работа с шаблоном def process_template(self, path_file, typeRead = 'r'): try: if typeRead == 'rb': with open(path_file, typeRead) as f: result = f.read() else: with open(path_file, typeRead, encoding='utf-8') as f: result = f.read() except Exception as e: print(e) raise Exception('404') return result # Обработка запроса def process_route(self): result = {'status': 200, 'response': '', 'mimetype': 'text/html', 'typeRead': 'r'} try: server_host = str(request.scheme)+'://'+str(request.server[0])+':'+str(request.server[1]) request_data = {'method': request.method, 'path': request.view_args['path'].split('/'), 'variable': self.proccess_params()} if request_data['method'] == 'GET': if request_data['path'][0] == '': request_data['path'][0] = 'index.html' # Подгрузка страницы if len(request_data['path']) == 1: result['response'] = self.process_template('design/template/'+request_data['path'][0]) # Подгрузка дизайна elif request_data['path'][0] == 'design': result['mimetype'] = self.get_mime_type(request.view_args['path']) mMimetype = str(result['mimetype']).split('/') result['typeRead'] = 'rb' if mMimetype[0] == 'image' or mMimetype[0] == 'font' or mMimetype[0] == 'application' else 'r' result['response'] = self.process_template(request.view_args['path'], result['typeRead']) if result['response'] == False: raise Exception('404') else: raise Exception('404') else: raise Exception('404') except Exception as e: # Подгрузка 404 страницы result['response'] = self.process_template('design/template/404.html') result['status'] = 404 if result['typeRead'] != 'rb': mData = {'SERVER_HOST': server_host} result['response'] = self.replace_str(result['response'], mData) return result if __name__ == "__main__": webService()

browser

Интерфейсный модуль на PySide6, который использует встроенный браузер (QWebEngineView) для отображения полученной информации от web_service.

  • copy
#-*- coding: UTF-8 -*- import sys from PySide6.QtCore import QUrl from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtWebEngineWidgets import QWebEngineView host = sys.argv[1] port = sys.argv[2] url = 'http://'+host+':'+port class Browser(): def __init__(self): app = QApplication(sys.argv) mw = QMainWindow() mw.setWindowTitle("Exchange arbitration") mw.setGeometry(100, 100, 1300, 700) mw.browser = QWebEngineView() mw.browser.setUrl(QUrl(url)) mw.setCentralWidget(mw.browser) mw.show() sys.exit(app.exec()) if __name__ == "__main__": Browser()

socket_server

Пример простого сервера веб-сокетов. К нему подключен модуль exchanges через модуль socket_client, который будет передавать данные с торговых бирж на внешнюю сторону веб-сервиса.

  • copy
#!/usr/bin/env python3 # -*- encoding: utf-8 -*- import asyncio import websockets client_list = [] async def handler(websocket, port): client_list.append(websocket) while True: try: message = await websocket.recv() await broadcast(message, websocket) except Exception as e: client_list.remove(websocket) break async def broadcast(message, websocket): for client in client_list: if client != websocket: await client.send(message) async def main(): async with websockets.serve(handler, "", 9501): await asyncio.Future() if __name__ == "__main__": asyncio.run(main())

socket_client

Пример web-сокет клиента для подключения к web-сокет серверу. Данные отправляются через модуль exchanges.

  • copy
#!/usr/bin/env python3 # -*- encoding: utf-8 -*- import time, threading, websocket, json class Socket_client(): def __init__(self): self.statusConnect = False ws = threading.Thread(target=self.start, daemon=True) ws.start() def sendData(self, mData): try: if self.statusConnect == True: self.wsa.send(json.dumps(mData)) except Exception as e: print(e) def on_open(self, ws): self.statusConnect = True def start(self): while True: try: self.wsa = websocket.WebSocketApp('ws://127.0.0.1:9501', on_open = self.on_open) self.wsa.run_forever() raise Exception('Socket_clietn - Off') except Exception as e: print(e) self.statusConnect = False time.sleep(5)

Запуск

Установите необходимые библиотеки:

  • copy
pip install flask PySide6

Запустите start.py

  • copy
python start.py

После запуска приложение откроется в виде окна, а внутри него будет отображаться веб-интерфейс, работающий на Flask.

Исходный код