Add grpc client

This commit is contained in:
なるみ 2022-03-07 12:06:16 +08:00
parent 870a7a104e
commit 034a86ceb4
17 changed files with 1276 additions and 1840 deletions

43
.github/workflows/python.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Python
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
run: pip install poetry==1.1.13
- name: Install package
run: |
cd python
poetry install
- name: Test
run: |
cd python
poetry run pytest -v -s tests
- name: Lint
run: |
cd python
poetry run flake8 .

View File

@ -177,4 +177,5 @@ protobuf:
protoc -I=$(PWD)/pkg/pb --go_out=$(PWD)/pkg/pb $(PWD)/pkg/pb/bbgo.proto
protobuf-py:
python -m grpc_tools.protoc -I$(PWD)/pkg/pb --python_out=$(PWD)/python/pybbgo --grpc_python_out=$(PWD)/python/pybbgo $(PWD)/pkg/pb/bbgo.proto
python -m grpc_tools.protoc -I$(PWD)/pkg/pb --python_out=$(PWD)/python/bbgo --grpc_python_out=$(PWD)/python/bbgo $(PWD)/pkg/pb/bbgo.proto

View File

@ -18,6 +18,42 @@ service BBGO {
rpc QueryKLines(QueryKLinesRequest) returns (QueryKLinesResponse) {}
}
enum Event {
ERROR = 0;
SUBSCRIBED = 1;
UNSUBSCRIBED = 2;
SNAPSHOT = 3;
UPDATE = 4;
AUTHENTICATED = 5;
ORDER_SNAPSHOT = 6;
ORDER_UPDATE = 7;
TRADE_SNAPSHOT = 8;
TRADE_UPDATE = 9;
ACCOUNT_SNAPSHOT = 10;
ACCOUNT_UPDATE = 11;
}
enum Channel {
BOOK = 0;
TRADE = 1;
TICKER = 2;
USER = 3;
}
enum Side {
BUY = 0;
SELL = 1;
}
enum OrderType {
MARKET = 0;
LIMIT = 1;
STOP_MARKET = 2;
STOP_LIMIT = 3;
POST_ONLY = 4;
IOC_LIMIT = 5;
}
message Empty {}
message Error {
@ -31,7 +67,7 @@ message SubscribeRequest {
message Subscription {
string exchange = 1;
string channel = 2; // book, trade, ticker
Channel channel = 2; // book, trade, ticker
string symbol = 3;
int64 depth = 4;
}
@ -39,8 +75,8 @@ message Subscription {
message SubscribeResponse {
string exchange = 1;
string symbol = 2;
string channel = 3; // book, trade, ticker, user
string event = 4; // snapshot, update, order_snapshot, ...
Channel channel = 3; // book, trade, ticker, user
Event event = 4; // snapshot, update, order_snapshot, ...
Depth depth = 5;
repeated Trade trades = 6;
Ticker ticker = 7;
@ -71,7 +107,7 @@ message Trade {
double price = 4;
double volume = 5;
int64 created_at = 6;
string side = 7;
Side side = 7;
double fee = 8;
string fee_currency = 9;
bool maker = 10;
@ -94,8 +130,8 @@ message Order {
string exchange = 1;
string symbol = 2;
string id = 3;
string side = 4;
string order_type = 5;
Side side = 4;
OrderType order_type = 5;
double price = 6;
double stop_price = 7;
double avg_price = 8;
@ -111,11 +147,11 @@ message Order {
message SubmitOrder {
string exchange = 1;
string symbol = 2;
string side = 3;
Side side = 3;
double quantity = 4;
double price = 5;
double stop_price = 6;
string order_type = 7;
OrderType order_type = 7;
string client_order_id = 8;
int64 group_id = 9;
}

29
python/README.md Normal file
View File

@ -0,0 +1,29 @@
# pybbgo
## Installation
```sh
cd <path/to/bbgo/python>
pip install .
```
## Usage
### Stream
```python
from bbgo import Stream
from bbgo import bbgo_pb2
subscriptions = [
bbgo_pb2.Subscription(exchange='max', channel=bbgo_pb2.Channel.BOOK, symbol='btcusdt', depth=2),
bbgo_pb2.Subscription(exchange='max', channel=bbgo_pb2.Channel.BOOK, symbol='ethusdt', depth=2),
...
]
stream = Stream(host, port, subscriptions)
stream.on_book_event(book_event_callback)
stream.on_ticker_event(tocker_event_callback)
...
stream.start()
```

2
python/bbgo/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .bbgo import BBGO
from .stream import Stream

104
python/bbgo/bbgo.py Normal file
View File

@ -0,0 +1,104 @@
from typing import List
from . import bbgo_pb2
from .utils import create_stub
class BBGO(object):
def __init__(self, host: str, port: int):
self.stub = create_stub(host, port)
def submit_order(self,
exchange: str,
symbol: str,
side: bbgo_pb2.Side,
quantity: float,
order_type: bbgo_pb2.OrderType,
price: float = None,
stop_price: float = None,
client_order_id: float = None,
group_id: float = None) -> bbgo_pb2.Order:
submit_order = bbgo_pb2.SubmitOrder(exchange=exchange,
symbol=symbol,
side=side,
quantity=quantity,
order_type=order_type,
price=price,
stop_price=stop_price,
client_order_id=client_order_id,
group_id=group_id)
request = bbgo_pb2.SubmitOrderRequest(submit_order=submit_order)
response = self.stub.SubmitOrder(request)
return response
def cancel_order(self, exchange: str, order_id: int, client_order_id: int = None) -> bbgo_pb2.CancelOrderResponse:
request = bbgo_pb2.CancelOrderRequest(exchange=exchange, id=order_id, client_order_id=client_order_id)
response = self.stub.CancelOrder(request)
return response
def query_order(self, order_id: int = None, client_order_id: int = None) -> bbgo_pb2.QueryOrderResponse:
request = bbgo_pb2.QueryOrderRequest(id=order_id, client_order_id=client_order_id)
response = self.stub.QueryOrder(request)
return response
def query_orders(self,
exchange: str,
symbol: str,
states: List[str] = None,
order_by: str = 'asc',
group_id: int = None,
pagination: bool = True,
page: int = 0,
limit: int = 100,
offset: int = 0) -> bbgo_pb2.QueryOrdersResponse:
# set default value to ['wait', 'convert']
states = states or ['wait', 'convert']
request = bbgo_pb2.QueryOrdersRequest(exchange=exchange,
symbol=symbol,
states=states,
order_by=order_by,
group_id=group_id,
pagination=pagination,
page=page,
limit=limit,
offset=offset)
reponse = self.stub.QueryOrders(request)
return reponse
def query_trades(self,
exchange: str,
symbol: str,
timestamp: int,
order_by: str = 'asc',
pagination: bool = True,
page: int = 1,
limit: int = 100,
offset: int = 0) -> bbgo_pb2.QueryTradesResponse:
request = bbgo_pb2.QueryTradesRequest(exchange=exchange,
symbol=symbol,
timestamp=timestamp,
order_by=order_by,
pagination=pagination,
page=page,
limit=limit,
offset=offset)
response = self.stub.QueryTrades(request)
return response
def query_klines(self,
exchange: str,
symbol: str,
limit: int = 30,
interval: int = 1,
timestamp: int = None) -> bbgo_pb2.QueryKLinesResponse:
request = bbgo_pb2.QueryKLinesRequest(exchange=exchange,
symbol=symbol,
limit=limit,
interval=interval,
timestamp=timestamp)
response = self.stub.QueryKLines(request)
return response

319
python/bbgo/bbgo_pb2.py Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import bbgo_pb2 as bbgo__pb2
from . import bbgo_pb2 as bbgo__pb2
class BBGOStub(object):

141
python/bbgo/stream.py Normal file
View File

@ -0,0 +1,141 @@
import asyncio
from typing import Callable
from typing import List
import grpc
from . import bbgo_pb2
from . import bbgo_pb2_grpc
class Stream(object):
def __init__(self, host: str, port: int, subscriptions: List[bbgo_pb2.Subscription]):
self.host = host
self.port = port
self.subscriptions = subscriptions
# callbacks for public channel
self.book_event_callbacks = []
self.trade_event_callbacks = []
self.ticker_event_callbacks = []
# callbacks for private channel
self.order_snapshot_event_callbacks = []
self.order_update_event_callbacks = []
self.trade_snapshot_event_callbacks = []
self.trade_update_event_callbacks = []
self.account_snapshot_event_callbacks = []
self.account_update_event_callbacks = []
@property
def address(self):
return f'{self.host}:{self.port}'
async def subscribe(self):
async with grpc.aio.insecure_channel(self.address) as channel:
stub = bbgo_pb2_grpc.BBGOStub(channel)
request = bbgo_pb2.SubscribeRequest(subscriptions=self.subscriptions)
async for response in stub.Subcribe(request):
self.dispatch(response)
async def subscribe_user_data(self):
async with grpc.aio.insecure_channel(self.address) as channel:
stub = bbgo_pb2_grpc.BBGOStub(channel)
request = bbgo_pb2.Empty()
async for response in stub.SubcribeUserData(request):
self.dispatch_user_events(response)
def start(self):
group = asyncio.gather(
self.subscribe(),
self.subscribe_user_data(),
)
loop = asyncio.get_event_loop()
loop.run_until_complete(group)
loop.close()
def dispatch(self, response: bbgo_pb2.SubscribeResponse):
m = {
bbgo_pb2.Channel.BOOK: self.emit_book_event_callbacks,
bbgo_pb2.Channel.TRADE: self.emit_trade_event_callbacks,
bbgo_pb2.Channel.TICKER: self.emit_ticker_event_callbacks,
bbgo_pb2.Channel.USER: self.dispatch_user_events,
}
m[response.channel](response)
def dispatch_user_events(self, response: bbgo_pb2.SubscribeResponse):
m = {
bbgo_pb2.Event.ORDER_SNAPSHOT: self.emit_order_snapshot_event_callbacks,
bbgo_pb2.Event.ORDER_UPDATE: self.emit_order_update_event_callbacks,
bbgo_pb2.Event.TRADE_SNAPSHOT: self.emit_trade_snapshot_event_callbacks,
bbgo_pb2.Event.TRADE_UPDATE: self.emit_trade_update_event_callbacks,
bbgo_pb2.Event.ACCOUNT_SNAPSHOT: self.emit_account_snapshot_event_callbacks,
bbgo_pb2.Event.ACCOUNT_UPDATE: self.emit_account_update_event_callbacks,
}
m[response.event](response)
def on_book_event(self, callback: Callable) -> None:
self.book_event_callbacks.append(callback)
def emit_book_event_callbacks(self, event) -> None:
for callback in self.book_event_callbacks:
callback(event)
def on_trade_event(self, callback: Callable) -> None:
self.trade_event_callbacks.append(callback)
def emit_trade_event_callbacks(self, event) -> None:
for callback in self.trade_event_callbacks:
callback(event)
def on_ticker_event(self, callback: Callable) -> None:
self.ticker_event_callbacks.append(callback)
def emit_ticker_event_callbacks(self, event) -> None:
for callback in self.ticker_event_callbacks:
callback(event)
def on_order_snapshot_event(self, callback: Callable) -> None:
self.order_snapshot_event_callbacks.append(callback)
def emit_order_snapshot_event_callbacks(self, event) -> None:
for callback in self.order_snapshot_event_callbacks:
callback(event)
def on_order_update_event(self, callback: Callable) -> None:
self.order_update_event_callbacks.append(callback)
def emit_order_update_event_callbacks(self, event) -> None:
for callback in self.order_update_event_callbacks:
callback(event)
def on_trade_snapshot_event(self, callback: Callable) -> None:
self.trade_snapshot_event_callbacks.append(callback)
def emit_trade_snapshot_event_callbacks(self, event) -> None:
for callback in self.trade_snapshot_event_callbacks:
callback(event)
def on_trade_update_event(self, callback: Callable) -> None:
self.trade_update_event_callbacks.append(callback)
def emit_trade_update_event_callbacks(self, event) -> None:
for callback in self.trade_update_event_callbacks:
callback(event)
def on_account_snapshot_event(self, callback: Callable) -> None:
self.account_snapshot_event_callbacks.append(callback)
def emit_account_snapshot_event_callbacks(self, event) -> None:
for callback in self.account_snapshot_event_callbacks:
callback(event)
def on_account_update_event(self, callback: Callable) -> None:
self.account_update_event_callbacks.append(callback)
def emit_account_update_event_callbacks(self, event) -> None:
for callback in self.account_update_event_callbacks:
callback(event)

37
python/bbgo/utils.py Normal file
View File

@ -0,0 +1,37 @@
import os
import grpc
from . import bbgo_pb2_grpc
def read_binary(f):
with open(f, 'rb') as fp:
return fp.read()
def get_grpc_cert_file_from_env():
cert_file = os.environ.get('BBGO_GRPC_CERT_FILE')
return cert_file
def get_grpc_key_file_from_env():
key_file = os.environ.get('BBGO_GRPC_KEY_FILE')
return key_file
def get_credentials_from_env():
key_file = get_grpc_key_file_from_env()
private_key = read_binary(key_file)
cert_file = get_grpc_cert_file_from_env()
certificate_chain = read_binary(cert_file)
private_key_certificate_chain_pairs = [(private_key, certificate_chain)]
server_credentials = grpc.ssl_server_credentials(private_key_certificate_chain_pairs)
return server_credentials
def create_stub(host, port):
address = f'{host}:{port}'
channel = grpc.insecure_channel(address)
return bbgo_pb2_grpc.BBGOStub(channel)

423
python/poetry.lock generated Normal file
View File

@ -0,0 +1,423 @@
[[package]]
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "click"
version = "8.0.4"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "grpcio"
version = "1.44.0"
description = "HTTP/2-based RPC framework"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
six = ">=1.5.2"
[package.extras]
protobuf = ["grpcio-tools (>=1.44.0)"]
[[package]]
name = "grpcio-tools"
version = "1.44.0"
description = "Protobuf code generator for gRPC"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
grpcio = ">=1.44.0"
protobuf = ">=3.5.0.post1,<4.0dev"
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "loguru"
version = "0.6.0"
description = "Python logging made (stupidly) simple"
category = "main"
optional = false
python-versions = ">=3.5"
[package.dependencies]
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "protobuf"
version = "3.19.4"
description = "Protocol Buffers"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyparsing"
version = "3.0.7"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "7.0.1"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
tomli = ">=1.0.0"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "win32-setctime"
version = "1.1.0"
description = "A small Python utility to set file creation time on Windows"
category = "main"
optional = false
python-versions = ">=3.5"
[package.extras]
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "bfda359d4e023f07cd8df05859450215e9f560f50b4a77a8aa8436ac42a74fe3"
[metadata.files]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
click = [
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
grpcio = [
{file = "grpcio-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:11f811c0fffd84fca747fbc742464575e5eb130fd4fb4d6012ccc34febd001db"},
{file = "grpcio-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9a86a91201f8345502ea81dee0a55ae13add5fafadf109b17acd858fe8239651"},
{file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:5f3c54ebb5d9633a557335c01d88d3d4928e9b1b131692283b6184da1edbec0b"},
{file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d47553b8e86ab1e59b0185ba6491a187f94a0239f414c8fc867a22b0405b798"},
{file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1e22d3a510438b7f3365c0071b810672d09febac6e8ca8a47eab657ae5f347b"},
{file = "grpcio-1.44.0-cp310-cp310-win32.whl", hash = "sha256:41036a574cab3468f24d41d6ed2b52588fb85ed60f8feaa925d7e424a250740b"},
{file = "grpcio-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ee51964edfd0a1293a95bb0d72d134ecf889379d90d2612cbf663623ce832b4"},
{file = "grpcio-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:e2149077d71e060678130644670389ddf1491200bcea16c5560d4ccdc65e3f2e"},
{file = "grpcio-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:0ac72d4b953b76924f8fa21436af060d7e6d8581e279863f30ee14f20751ac27"},
{file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c30a9a7d3a05920368a60b080cbbeaf06335303be23ac244034c71c03a0fd24"},
{file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:05467acd391e3fffb05991c76cb2ed2fa1309d0e3815ac379764bc5670b4b5d4"},
{file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b81dc7894062ed2d25b74a2725aaa0a6895ce97ce854f432fe4e87cad5a07316"},
{file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d4843192e7d36278884282e100b8f305cf37d1b3d8c6b4f736d4454640a069"},
{file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898c159148f27e23c08a337fb80d31ece6b76bb24f359d83929460d813665b74"},
{file = "grpcio-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:b8d852329336c584c636caa9c2db990f3a332b19bc86a80f4646b58d27c142db"},
{file = "grpcio-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:790d7493337558ae168477d1be3178f4c9b8f91d8cd9b8b719d06fd9b2d48836"},
{file = "grpcio-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cd61b52d9cf8fcf8d9628c0b640b9e44fdc5e93d989cc268086a858540ed370c"},
{file = "grpcio-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:14eefcf623890f3f7dd7831decd2a2116652b5ce1e0f1d4b464b8f52110743b0"},
{file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:bebe90b8020b4248e5a2076b56154cc6ff45691bbbe980579fc9db26717ac968"},
{file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89b390b1c0de909965280d175c53128ce2f0f4f5c0f011382243dd7f2f894060"},
{file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:c122dac5cb299b8ad7308d61bd9fe0413de13b0347cce465398436b3fdf1f609"},
{file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6641a28cc826a92ef717201cca9a035c34a0185e38b0c93f3ce5f01a01a1570a"},
{file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb0a3e0e64843441793923d9532a3a23907b07b2a1e0a7a31f186dc185bb772"},
{file = "grpcio-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:be857b7ec2ac43455156e6ba89262f7d7ae60227049427d01a3fecd218a3f88d"},
{file = "grpcio-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f6a9cf0e77f72f2ac30c9c6e086bc7446c984c51bebc6c7f50fbcd718037edba"},
{file = "grpcio-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:19e54f0c7083c8332b5a75a9081fc5127f1dbb67b6c1a32bd7fe896ef0934918"},
{file = "grpcio-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:bfd36b959c3c4e945119387baed1414ea46f7116886aa23de0172302b49d7ff1"},
{file = "grpcio-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ccd388b8f37b19d06e4152189726ce309e36dc03b53f2216a4ea49f09a7438e6"},
{file = "grpcio-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9075c0c003c1ff14ebce8f0ba55cc692158cb55c68da09cf8b0f9fc5b749e343"},
{file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e898194f76212facbaeb6d7545debff29351afa23b53ff8f0834d66611af5139"},
{file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fa6584046a7cf281649975a363673fa5d9c6faf9dc923f261cc0e56713b5892"},
{file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a7bdd6ef9bca050c7ade8cba5f0e743343ea0756d5d3d520e915098a9dc503"},
{file = "grpcio-1.44.0-cp38-cp38-win32.whl", hash = "sha256:dc3290d0411ddd2bd49adba5793223de8de8b01588d45e9376f1a9f7d25414f4"},
{file = "grpcio-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:13343e7b840c20f43b44f0e6d3bbdc037c964f0aec9735d7cb685c407731c9ff"},
{file = "grpcio-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c5c2f8417d13386e18ccc8c61467cb6a6f9667a1ff7000a2d7d378e5d7df693f"},
{file = "grpcio-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:cf220199b7b4992729ad4d55d5d3f652f4ccfe1a35b5eacdbecf189c245e1859"},
{file = "grpcio-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4201c597e5057a9bfef9ea5777a6d83f6252cb78044db7d57d941ec2300734a5"},
{file = "grpcio-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e2de61005118ae59d48d5d749283ebfd1ba4ca68cc1000f8a395cd2bdcff7ceb"},
{file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:871078218fa9117e2a378678f327e32fda04e363ed6bc0477275444273255d4d"},
{file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8d610b7b557a7609fecee80b6dd793ecb7a9a3c3497fbdce63ce7d151cdd705"},
{file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcb53e4eb8c271032c91b8981df5fc1bb974bc73e306ec2c27da41bd95c44b5"},
{file = "grpcio-1.44.0-cp39-cp39-win32.whl", hash = "sha256:e50ddea6de76c09b656df4b5a55ae222e2a56e625c44250e501ff3c904113ec1"},
{file = "grpcio-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:d2ec124a986093e26420a5fb10fa3f02b2c232f924cdd7b844ddf7e846c020cd"},
{file = "grpcio-1.44.0.tar.gz", hash = "sha256:4bae1c99896045d3062ab95478411c8d5a52cb84b91a1517312629fa6cfeb50e"},
]
grpcio-tools = [
{file = "grpcio-tools-1.44.0.tar.gz", hash = "sha256:be37f458ea510c9a8f1caabbc2b258d12e55d189a567f5edcace90f27dc0efbf"},
{file = "grpcio_tools-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:9f58529e24f613019a85c258a274d441d89e0cad8cf7fca21ef3807ba5840c5d"},
{file = "grpcio_tools-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:1d120082236f8d2877f8a19366476b82c3562423b877b7c471a142432e31c2c4"},
{file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:65c2fe3cdc5425180f01dd303e28d4f363d38f4c2e3a7e1a87caedd5417e23bb"},
{file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5caef118deb8cdee1978fd3d8e388a9b256cd8d34e4a8895731ac0e86fa5e47c"},
{file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121c9765cee8636201cf0d4e80bc7b509813194919bccdb66e9671c4ece6dac3"},
{file = "grpcio_tools-1.44.0-cp310-cp310-win32.whl", hash = "sha256:90d1fac188bac838c4169eb3b67197887fa0572ea8a90519a20cddb080800549"},
{file = "grpcio_tools-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e16260dfe6e997330473863e01466b0992369ae2337a0249b390b4651cff424"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:608414cc1093e1e9e5980c97a6ee78e51dffff359e7a3f123d1fb9d95b8763a5"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:395609c06f69fbc79518b30a01931127088a3f9ef2cc2a35269c5f187eefd38c"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f7ce16766b24b88ec0e4355f5dd66c2eee6af210e889fcb7961c9c4634c687de"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3c9abc4a40c62f46d5e43e49c7afc567dedf12eeef95933ac9ea2986baa2420b"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b73fd87a44ba1b91866b0254193c37cdb001737759b77b637cebe0c816d38342"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b211f12e4cbc0fde8e0f982b0f581cce38874666a02ebfed93c23dcaeb8a4e0"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b421dc9b27bcaff4c73644cd3801e4893b11ba3eb39729246fd3de98d9f685b"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:33d93027840a873c7b59402fe6db8263b88c56e2f84aa0b6281c05cc8bd314a1"},
{file = "grpcio_tools-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:71fb6e7e66b918803b1bebd0231560981ab86c2546a3318a45822ce94de5e83d"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:614c427ff235d92f103e9189f0230197c8f2f817d0dd9fd078f5d2ea4d920d02"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:c13e0cb486cfa15320ddcd70452a4d736e6ce319c03d6b3c0c2513ec8d2748fb"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5ade6b13dc4e148f400c8f55a6ef0b14216a3371d7a9e559571d5981b6cec36b"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6138d2c7eec7ed57585bc58e2dbcb65635a2d574ac632abd29949d3e68936bab"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3d6c8548b199591757dbfe89ed14e23782d6079d6d201c6c314c72f4086883aa"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41c419829f01734d65958ba9b01b759061d8f7e0698f9612ba6b8837269f7a9"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9f0c5b4567631fec993826e694e83d86a972b3e2e9b05cb0c56839b0316d26c"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:3f0e1d1f3f5a6f0c9f8b5441819dbec831ce7e9ffe04768e4b0d965a95fbbe5e"},
{file = "grpcio_tools-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f87fc86d0b4181b6b4da6ec6a29511dca000e6b5694fdd6bbf87d125128bc41"},
{file = "grpcio_tools-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:cb8baa1d4cea35ca662c24098377bdd9514c56f227da0e38b43cd9b8223bfcc6"},
{file = "grpcio_tools-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:ea36a294f7c70fd2f2bfb5dcf08602006304aa65b055ebd4f7c709e2a89deba7"},
{file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1972caf8f695b91edc6444134445798692fe71276f0cde7604d55e65179adf93"},
{file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:674fb8d9c0e2d75166c4385753962485b757897223fc92a19c9e513ab80b96f7"},
{file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:37045ba850d423cdacede77b266b127025818a5a36d80f1fd7a5a1614a6a0de5"},
{file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdf72947c6b0b03aa6dac06117a095947d02d43a5c6343051f4ce161fd0abcb"},
{file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bfa6fc1515c202fe428ba9f99e2b2f947b01bafc15d868798235b2e2d36baa"},
{file = "grpcio_tools-1.44.0-cp38-cp38-win32.whl", hash = "sha256:2c516124356476d9afa126acce10ce568733120afbd9ae17ee01d44b9da20a67"},
{file = "grpcio_tools-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6441c24176705c5ab056e65a8b330e107107c5a492ba094d1b862a136d15d"},
{file = "grpcio_tools-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:398eda759194d355eb09f7beabae6e4fb45b3877cf7efe505b49095fa4889cef"},
{file = "grpcio_tools-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:a169bfd7a1fe8cc11472eeeeab3088b3c5d56caac12b2192a920b73adcbc974c"},
{file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:a58aaaec0d846d142edd8e794ebb80aa429abfd581f4493a60a603aac0c50ac8"},
{file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c3253bee8b68fe422754faf0f286aa068861c926a7b11e4daeb44b9af767c7f1"},
{file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3c0be60721ae1ba09c4f29572a145f412e561b9201e19428758893709827f472"},
{file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e44b9572c2226b85976e0d6054e22d7c59ebd6c9425ee71e5bc8910434aee3e1"},
{file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c04ec47905c4f6d6dad34d29f6ace652cc1ddc986f55aaa5559b72104c3f5cf"},
{file = "grpcio_tools-1.44.0-cp39-cp39-win32.whl", hash = "sha256:fb8c7b9d24e2c4dc77e7800e83b68081729ac6094b781b2afdabf08af18c3b28"},
{file = "grpcio_tools-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:4eb93619c8cb3773fb899504e3e30a0dc79d3904fd7a84091d15552178e1e920"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
loguru = [
{file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
{file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
protobuf = [
{file = "protobuf-3.19.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"},
{file = "protobuf-3.19.4-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb"},
{file = "protobuf-3.19.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c"},
{file = "protobuf-3.19.4-cp310-cp310-win32.whl", hash = "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0"},
{file = "protobuf-3.19.4-cp310-cp310-win_amd64.whl", hash = "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07"},
{file = "protobuf-3.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4"},
{file = "protobuf-3.19.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f"},
{file = "protobuf-3.19.4-cp36-cp36m-win32.whl", hash = "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee"},
{file = "protobuf-3.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b"},
{file = "protobuf-3.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13"},
{file = "protobuf-3.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368"},
{file = "protobuf-3.19.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909"},
{file = "protobuf-3.19.4-cp37-cp37m-win32.whl", hash = "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9"},
{file = "protobuf-3.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f"},
{file = "protobuf-3.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2"},
{file = "protobuf-3.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2"},
{file = "protobuf-3.19.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7"},
{file = "protobuf-3.19.4-cp38-cp38-win32.whl", hash = "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26"},
{file = "protobuf-3.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e"},
{file = "protobuf-3.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58"},
{file = "protobuf-3.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934"},
{file = "protobuf-3.19.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e"},
{file = "protobuf-3.19.4-cp39-cp39-win32.whl", hash = "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a"},
{file = "protobuf-3.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca"},
{file = "protobuf-3.19.4-py2.py3-none-any.whl", hash = "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616"},
{file = "protobuf-3.19.4.tar.gz", hash = "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pycodestyle = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
pyflakes = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
pyparsing = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pytest = [
{file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"},
{file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
win32-setctime = [
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
[tool.poetry]
name = "pybbgo"
name = "bbgo"
version = "0.1.0"
description = ""
authors = ["なるみ <narumi@maicoin.com>"]
authors = ["なるみ <weaper@gmail.com>"]
[tool.poetry.dependencies]
python = "^3.8"
@ -10,8 +10,10 @@ click = "^8.0.4"
loguru = "^0.6.0"
grpcio = "^1.44.0"
grpcio-tools = "^1.44.0"
flake8 = "^4.0.1"
[tool.poetry.dev-dependencies]
pytest = "^7.0.1"
[build-system]
requires = ["poetry-core>=1.0.0"]

View File

@ -1,6 +1,8 @@
[flake8]
max-line-length = 120
per-file-ignores = __init__.py: F401
# ignore =
exclude = bbgo_pb2.py, bbgo_pb2_grpc.py
[yapf]
based_on_style = google

67
python/tests/servicer.py Normal file
View File

@ -0,0 +1,67 @@
import random
import time
from bbgo import bbgo_pb2
from bbgo import bbgo_pb2_grpc
class TestServicer(bbgo_pb2_grpc.BBGOServicer):
def Subcribe(self, request, context):
i = 0
while True:
for subscription in request.subscriptions:
yield bbgo_pb2.SubscribeResponse(
channel=subscription.channel,
event=bbgo_pb2.Event.UPDATE,
exchange=subscription.exchange,
symbol=subscription.symbol + f'_{i}',
)
i += 1
time.sleep(random.random())
def SubcribeUserData(self, request, context):
i = 0
while True:
yield bbgo_pb2.SubscribeResponse(
channel=bbgo_pb2.Channel.USER,
event=bbgo_pb2.Event.ORDER_UPDATE,
exchange='max',
symbol=f'user_{i}',
)
i += 1
time.sleep(random.random())
def SubmitOrder(self, request, context):
submit_order = request.submit_order
order = bbgo_pb2.Order(
exchange=submit_order.exchange,
symbol=submit_order.symbol,
side=submit_order.side,
quantity=submit_order.quantity,
price=submit_order.price,
stop_price=submit_order.stop_price,
order_type=submit_order.order_type,
client_order_id=submit_order.client_order_id,
group_id=submit_order.group_id,
)
error = bbgo_pb2.Error(error_code=0, error_message='')
return bbgo_pb2.SubmitOrderResponse(order=order, error=error)
def CancelOrder(self, request, context):
pass
def QueryOrder(self, request, context):
pass
def QueryOrders(self, request, context):
pass
def QueryTrades(self, request, context):
pass
def QueryKLines(self, request, context):
pass

58
python/tests/test_grpc.py Normal file
View File

@ -0,0 +1,58 @@
from concurrent import futures
import grpc
import pytest
from bbgo import BBGO
from bbgo import bbgo_pb2
from bbgo import bbgo_pb2_grpc
from tests.servicer import TestServicer
@pytest.fixture
def grpc_address(host='[::]', port=50051):
return f'{host}:{port}'
@pytest.fixture
def bbgo(host='[::]', port=50051):
return BBGO(host, port)
@pytest.fixture
def grpc_channel(grpc_address):
return grpc.insecure_channel(grpc_address)
@pytest.fixture
def grpc_server(grpc_address, max_workers=1):
server = grpc.server(futures.ThreadPoolExecutor(max_workers))
servicer = TestServicer()
bbgo_pb2_grpc.add_BBGOServicer_to_server(servicer, server)
server.add_insecure_port(grpc_address)
server.start()
yield server
server.stop(grace=None)
def test_submit_order(bbgo, grpc_server):
exchange = 'max'
symbol = 'BTCUSDT'
side = bbgo_pb2.Side.BUY
quantity = 0.01
order_type = bbgo_pb2.OrderType.LIMIT
response = bbgo.submit_order(
exchange=exchange,
symbol=symbol,
side=side,
quantity=quantity,
order_type=order_type,
)
order = response.order
assert order.exchange == exchange
assert order.symbol == symbol
assert order.side == side
assert order.quantity == quantity