delete python

This commit is contained in:
narumi 2024-08-13 17:07:56 +08:00
parent a91685920e
commit cf42d9d0b0
50 changed files with 0 additions and 2657 deletions

View File

@ -1,48 +0,0 @@
name: Python
on:
push:
branches: [ main ]
paths:
- python
pull_request:
branches: [ main ]
paths:
- python
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 .

129
python/.gitignore vendored
View File

@ -1,129 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

View File

@ -1,36 +0,0 @@
# pybbgo
## Installation
```sh
cd <path/to/bbgo/python>
pip install .
```
## Usage
### Stream
```python
from loguru import logger
from bbgo import Stream
from bbgo.data import Event
from bbgo.handlers import UpdateHandler
class LogBook(UpdateHandler):
def handle(self, event: Event) -> None:
logger.info(event)
host = '127.0.0.1'
port = 50051
stream = Stream(host, port)
stream.subscribe('max', 'book', 'BTCUSDT', 'full')
stream.subscribe('max', 'book', 'ETHUSDT', 'full')
stream.add_event_handler(LogBook())
stream.start()
```

View File

@ -1,7 +0,0 @@
from . import enums
from . import handlers
from . import utils
from .services import MarketService
from .services import TradingService
from .services import UserDataService
from .stream import Stream

View File

@ -1,13 +0,0 @@
from .balance import Balance
from .depth import Depth
from .depth import PriceVolume
from .error import ErrorMessage
from .event import Event
from .event import MarketDataEvent
from .event import UserDataEvent
from .kline import KLine
from .order import Order
from .submit_order import SubmitOrder
from .subscription import Subscription
from .ticker import Ticker
from .trade import Trade

View File

@ -1,30 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
import bbgo_pb2
from ..utils import parse_number
@dataclass
class Balance:
exchange: str
currency: str
available: Decimal
locked: Decimal
borrowed: Decimal
@classmethod
def from_pb(cls, obj: bbgo_pb2.Balance) -> Balance:
return cls(
exchange=obj.exchange,
currency=obj.currency,
available=parse_number(obj.available),
locked=parse_number(obj.locked),
borrowed=parse_number(obj.borrowed),
)
def total(self) -> Decimal:
return self.available + self.locked

View File

@ -1,39 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
from typing import List
import bbgo_pb2
from ..utils import parse_number
@dataclass
class Depth:
exchange: str
symbol: str
asks: List[PriceVolume]
bids: List[PriceVolume]
@classmethod
def from_pb(cls, obj: bbgo_pb2.Depth):
return cls(
exchange=obj.exchange,
symbol=obj.symbol,
asks=[PriceVolume.from_pb(ask) for ask in obj.asks],
bids=[PriceVolume.from_pb(bid) for bid in obj.bids],
)
@dataclass
class PriceVolume:
price: Decimal
volume: Decimal
@classmethod
def from_pb(cls, obj: bbgo_pb2.PriceVolume):
return cls(
price=parse_number(obj.price),
volume=parse_number(obj.volume),
)

View File

@ -1,18 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
import bbgo_pb2
@dataclass
class ErrorMessage:
code: int
message: str
@classmethod
def from_pb(cls, obj: bbgo_pb2.Error) -> ErrorMessage:
return cls(
code=obj.error_code,
message=obj.error_message,
)

View File

@ -1,84 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import List
import bbgo_pb2
from ..enums import ChannelType
from ..enums import EventType
from ..utils import parse_time
from .balance import Balance
from .depth import Depth
from .error import ErrorMessage
from .kline import KLine
from .order import Order
from .ticker import Ticker
from .trade import Trade
@dataclass
class Event:
session: str
exchange: str
channel_type: ChannelType
event_type: EventType
@dataclass
class UserDataEvent(Event):
balances: List[Balance] = None
trades: List[Trade] = None
orders: List[Order] = None
@classmethod
def from_pb(cls, obj: bbgo_pb2.UserData) -> UserDataEvent:
return cls(
session=obj.session,
exchange=obj.exchange,
channel_type=ChannelType(obj.channel),
event_type=EventType(obj.event),
balances=[Balance.from_pb(balance) for balance in obj.balances],
trades=[Trade.from_pb(trade) for trade in obj.trades],
orders=[Order.from_pb(order) for order in obj.orders],
)
@dataclass
class MarketDataEvent(Event):
symbol: str
subscribed_at: datetime
error: ErrorMessage
depth: Depth = None
kline: KLine = None
ticker: Ticker = None
trades: List[Trade] = None
@classmethod
def from_pb(cls, obj: bbgo_pb2.MarketData) -> MarketDataEvent:
channel_type = ChannelType(obj.channel)
event = cls(
session=obj.session,
exchange=obj.exchange,
symbol=obj.symbol,
channel_type=channel_type,
event_type=EventType(obj.event),
subscribed_at=parse_time(obj.subscribed_at),
error=ErrorMessage.from_pb(obj.error),
)
if channel_type == ChannelType.BOOK:
event.depth = Depth.from_pb(obj.depth)
if channel_type == ChannelType.KLINE:
event.kline = KLine.from_pb(obj.kline)
if channel_type == ChannelType.TICKER:
event.ticker = Ticker.from_pb(obj.ticker)
if channel_type == ChannelType.TRADE:
event.trades = [Trade.from_pb(trade) for trade in obj.trades]
return event

View File

@ -1,42 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
import bbgo_pb2
from ..utils import parse_number
from ..utils import parse_time
@dataclass
class KLine:
exchange: str
symbol: str
open: Decimal
high: Decimal
low: Decimal
close: Decimal
volume: Decimal
session: str = None
start_time: datetime = None
end_time: datetime = None
quote_volume: Decimal = None
closed: bool = None
@classmethod
def from_pb(cls, obj: bbgo_pb2.KLine) -> KLine:
return cls(
exchange=obj.exchange,
symbol=obj.symbol,
open=parse_number(obj.open),
high=parse_number(obj.high),
low=parse_number(obj.low),
close=parse_number(obj.close),
volume=parse_number(obj.volume),
quote_volume=parse_number(obj.quote_volume),
start_time=parse_time(obj.start_time),
end_time=parse_time(obj.end_time),
closed=obj.closed,
)

View File

@ -1,47 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
import bbgo_pb2
from ..enums import OrderType
from ..enums import SideType
from ..utils import parse_number
from ..utils import parse_time
@dataclass
class Order:
exchange: str
symbol: str
order_id: str
side: SideType
order_type: OrderType
price: Decimal
stop_price: Decimal
status: str
quantity: Decimal
executed_quantity: Decimal
client_order_id: str
group_id: int
created_at: datetime
@classmethod
def from_pb(cls, obj: bbgo_pb2.Order) -> Order:
return cls(
exchange=obj.exchange,
symbol=obj.symbol,
order_id=obj.id,
side=SideType(obj.side),
order_type=OrderType(obj.order_type),
price=parse_number(obj.price),
stop_price=parse_number(obj.stop_price),
status=obj.status,
quantity=parse_number(obj.quantity),
executed_quantity=parse_number(obj.executed_quantity),
client_order_id=obj.client_order_id,
group_id=obj.group_id,
created_at=parse_time(obj.created_at),
)

View File

@ -1,37 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
import bbgo_pb2
from ..enums import OrderType
from ..enums import SideType
@dataclass
class SubmitOrder:
session: str
exchange: str
symbol: str
side: SideType
quantity: Decimal
order_type: OrderType
price: Decimal = None
stop_price: Decimal = None
client_order_id: str = None
group_id: int = None
def to_pb(self) -> bbgo_pb2.SubmitOrder:
return bbgo_pb2.SubmitOrder(
session=self.session,
exchange=self.exchange,
symbol=self.symbol,
side=self.side.value,
price=str(self.price or ""),
quantity=str(self.quantity or ""),
stop_price=str(self.stop_price or ""),
order_type=self.order_type.value,
client_order_id=self.client_order_id or "",
group_id=self.group_id or 0,
)

View File

@ -1,32 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
import bbgo_pb2
from ..enums import ChannelType
from ..enums import DepthType
@dataclass
class Subscription:
exchange: str
channel: ChannelType
symbol: str
depth: DepthType = None
interval: str = None
def to_pb(self) -> bbgo_pb2.Subscription:
subscription_pb = bbgo_pb2.Subscription(
exchange=self.exchange,
channel=self.channel.value,
symbol=self.symbol,
)
if self.depth is not None:
subscription_pb.depth = self.depth.value
if self.interval is not None:
subscription_pb.interval = self.interval
return subscription_pb

View File

@ -1,31 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
import bbgo_pb2
from ..utils import parse_number
@dataclass
class Ticker:
exchange: str
symbol: str
open: Decimal
high: Decimal
low: Decimal
close: Decimal
volume: Decimal
@classmethod
def from_pb(cls, obj: bbgo_pb2.KLine) -> Ticker:
return cls(
exchange=obj.exchange,
symbol=obj.symbol,
open=parse_number(obj.open),
high=parse_number(obj.high),
low=parse_number(obj.low),
close=parse_number(obj.close),
volume=parse_number(obj.volume),
)

View File

@ -1,42 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
import bbgo_pb2
from ..enums import SideType
from ..utils import parse_number
from ..utils import parse_time
@dataclass
class Trade:
session: str
exchange: str
symbol: str
trade_id: str
price: Decimal
quantity: Decimal
created_at: datetime
side: SideType
fee_currency: str
fee: Decimal
maker: bool
@classmethod
def from_pb(cls, obj: bbgo_pb2.Trade) -> Trade:
return cls(
session=obj.session,
exchange=obj.exchange,
symbol=obj.symbol,
trade_id=obj.id,
price=parse_number(obj.price),
quantity=parse_number(obj.quantity),
created_at=parse_time(obj.created_at),
side=SideType(obj.side),
fee_currency=obj.fee_currency,
fee=parse_number(obj.fee),
maker=obj.maker,
)

View File

@ -1,5 +0,0 @@
from .channel_type import ChannelType
from .depth_type import DepthType
from .event_type import EventType
from .order_type import OrderType
from .side_type import SideType

View File

@ -1,16 +0,0 @@
from __future__ import annotations
from enum import Enum
class ChannelType(Enum):
BOOK = 0
TRADE = 1
TICKER = 2
KLINE = 3
BALANCE = 4
ORDER = 5
@classmethod
def from_str(cls, s: str) -> ChannelType:
return {t.name.lower(): t for t in cls}[s.lower()]

View File

@ -1,16 +0,0 @@
from __future__ import annotations
from enum import Enum
# string depth = 4; // depth is for book, valid values are full, medium, 1, 5 and 20
class DepthType(Enum):
FULL = 'full'
MEDIUM = 'medium'
DEPTH_1 = '1'
DEPTH_5 = '5'
DEPTH_20 = '20'
@classmethod
def from_str(cls, s: str) -> DepthType:
return {t.name.lower(): t for t in cls}[s.lower()]

View File

@ -1,17 +0,0 @@
from __future__ import annotations
from enum import Enum
class EventType(Enum):
UNKNOWN = 0
SUBSCRIBED = 1
UNSUBSCRIBED = 2
SNAPSHOT = 3
UPDATE = 4
AUTHENTICATED = 5
ERROR = 99
@classmethod
def from_str(cls, s: str) -> EventType:
return {t.name.lower(): t for t in cls}[s.lower()]

View File

@ -1,16 +0,0 @@
from __future__ import annotations
from enum import Enum
class OrderType(Enum):
MARKET = 0
LIMIT = 1
STOP_MARKET = 2
STOP_LIMIT = 3
POST_ONLY = 4
IOC_LIMIT = 5
@classmethod
def from_str(cls, s: str) -> OrderType:
return {t.name.lower(): t for t in cls}[s.lower()]

View File

@ -1,12 +0,0 @@
from __future__ import annotations
from enum import Enum
class SideType(Enum):
BUY = 0
SELL = 1
@classmethod
def from_str(cls, s: str) -> SideType:
return {t.name.lower(): t for t in cls}[s.lower()]

View File

@ -1,21 +0,0 @@
from .balance import BalanceHandler
from .balance import BalanceSnapshotHandler
from .balance import BalanceUpdateHandler
from .book import BookSnapshotHandler
from .book import BookUpdateHandler
from .error import ErrorHandler
from .handler import Handler
from .kline import KLineHandler
from .kline import KLineSnapshotHandler
from .kline import KLineUpdateHandler
from .order import OrderHandler
from .order import OrderSnapshotHandler
from .order import OrderUpdateHandler
from .snapshot import SnapshotHandler
from .ticker import TickerHandler
from .ticker import TickerSnapshotHandler
from .ticker import TickerUpdateHandler
from .trade import TradeHandler
from .trade import TradeSnapshotHandler
from .trade import TradeUpdateHandler
from .update import UpdateHandler

View File

@ -1,31 +0,0 @@
from ..data import UserDataEvent
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class BalanceHandler(Handler):
def __call__(self, event: UserDataEvent) -> None:
if event.channel_type != ChannelType.BALANCE:
return
super(BalanceHandler, self).__call__(event)
class BalanceSnapshotHandler(BalanceHandler):
def __call__(self, event: UserDataEvent) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(BalanceSnapshotHandler, self).__call__(event)
class BalanceUpdateHandler(BalanceHandler):
def __call__(self, event: UserDataEvent) -> None:
if event.event_type != EventType.UPDATE:
return
super(BalanceUpdateHandler, self).__call__(event)

View File

@ -1,31 +0,0 @@
from ..data import MarketDataEvent
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class BookHandler(Handler):
def __call__(self, event: MarketDataEvent) -> None:
if event.channel_type != ChannelType.BOOK:
return
super(BookHandler, self).__call__(event)
class BookSnapshotHandler(BookHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(BookSnapshotHandler, self).__call__(event)
class BookUpdateHandler(BookHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.UPDATE:
return
super(BookUpdateHandler, self).__call__(event)

View File

@ -1,12 +0,0 @@
from ..data import Event
from ..enums import EventType
from .handler import Handler
class ErrorHandler(Handler):
def __call__(self, event: Event) -> None:
if event.event_type != EventType.ERROR:
return
super(ErrorHandler, self).__call__(event)

View File

@ -1,10 +0,0 @@
from ..data import Event
class Handler(object):
def __call__(self, event: Event) -> None:
self.handle(event)
def handle(self, event: Event) -> None:
raise NotImplementedError

View File

@ -1,31 +0,0 @@
from ..data import MarketDataEvent
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class KLineHandler(Handler):
def __call__(self, event: MarketDataEvent) -> None:
if event.channel_type != ChannelType.KLINE:
return
super(KLineHandler, self).__call__(event)
class KLineSnapshotHandler(KLineHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(KLineSnapshotHandler, self).__call__(event)
class KLineUpdateHandler(KLineHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.UPDATE:
return
super(KLineUpdateHandler, self).__call__(event)

View File

@ -1,31 +0,0 @@
from ..data import UserDataEvent
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class OrderHandler(Handler):
def __call__(self, event: UserDataEvent) -> None:
if event.channel_type != ChannelType.ORDER:
return
super(OrderHandler, self).__call__(event)
class OrderSnapshotHandler(OrderHandler):
def __call__(self, event: UserDataEvent) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(OrderSnapshotHandler, self).__call__(event)
class OrderUpdateHandler(OrderHandler):
def __call__(self, event: UserDataEvent) -> None:
if event.event_type != EventType.UPDATE:
return
super(OrderUpdateHandler, self).__call__(event)

View File

@ -1,12 +0,0 @@
from ..data import Event
from ..enums import EventType
from .handler import Handler
class SnapshotHandler(Handler):
def __call__(self, event: Event) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(SnapshotHandler, self).__call__(event)

View File

@ -1,31 +0,0 @@
from ..data import MarketDataEvent
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class TickerHandler(Handler):
def __call__(self, event: MarketDataEvent) -> None:
if event.channel_type != ChannelType.TICKER:
return
super(TickerHandler, self).__call__(event)
class TickerSnapshotHandler(TickerHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(TickerSnapshotHandler, self).__call__(event)
class TickerUpdateHandler(TickerHandler):
def __call__(self, event: MarketDataEvent) -> None:
if event.event_type != EventType.UPDATE:
return
super(TickerUpdateHandler, self).__call__(event)

View File

@ -1,31 +0,0 @@
from ..data import Event
from ..enums import ChannelType
from ..enums import EventType
from .handler import Handler
class TradeHandler(Handler):
def __call__(self, event: Event) -> None:
if event.channel_type != ChannelType.TRADE:
return
super(TradeHandler, self).__call__(event)
class TradeSnapshotHandler(TradeHandler):
def __call__(self, event: Event) -> None:
if event.event_type != EventType.SNAPSHOT:
return
super(TradeSnapshotHandler, self).__call__(event)
class TradeUpdateHandler(TradeHandler):
def __call__(self, event: Event) -> None:
if event.event_type != EventType.UPDATE:
return
super(TradeUpdateHandler, self).__call__(event)

View File

@ -1,12 +0,0 @@
from ..data import Event
from ..enums import EventType
from .handler import Handler
class UpdateHandler(Handler):
def __call__(self, event: Event) -> None:
if event.event_type != EventType.UPDATE:
return
super(UpdateHandler, self).__call__(event)

View File

@ -1,179 +0,0 @@
from __future__ import annotations
from typing import Iterator
from typing import List
from loguru import logger
import bbgo_pb2
import bbgo_pb2_grpc
from .data import ErrorMessage
from .data import KLine
from .data import MarketDataEvent
from .data import Order
from .data import SubmitOrder
from .data import Subscription
from .data import UserDataEvent
from .enums import OrderType
from .enums import SideType
from .utils import get_insecure_channel
class UserDataService(object):
stub: bbgo_pb2_grpc.UserDataServiceStub
def __init__(self, host: str, port: int) -> None:
self.stub = bbgo_pb2_grpc.UserDataServiceStub(get_insecure_channel(host, port))
def subscribe(self, session: str) -> Iterator[UserDataEvent]:
request = bbgo_pb2.UserDataRequest(session)
response_iter = self.stub.Subscribe(request)
for response in response_iter:
yield UserDataEvent.from_pb(response)
class MarketService(object):
stub: bbgo_pb2_grpc.MarketDataServiceStub
def __init__(self, host: str, port: int) -> None:
self.stub = bbgo_pb2_grpc.MarketDataServiceStub(get_insecure_channel(host, port))
def subscribe(self, subscriptions: List[Subscription]) -> Iterator[MarketDataEvent]:
request = bbgo_pb2.SubscribeRequest(subscriptions=[s.to_pb() for s in subscriptions])
response_iter = self.stub.Subscribe(request)
for response in response_iter:
yield MarketDataEvent.from_pb(response)
def query_klines(self,
exchange: str,
symbol: str,
limit: int = 30,
interval: str = '1m',
start_time: int = None,
end_time: int = None) -> List[KLine]:
request = bbgo_pb2.QueryKLinesRequest(exchange=exchange,
symbol=symbol,
limit=limit,
interval=interval,
start_time=start_time,
end_time=end_time)
response = self.stub.QueryKLines(request)
klines = []
for kline in response.klines:
klines.append(KLine.from_pb(kline))
error = ErrorMessage.from_pb(response.error)
if error.code != 0:
logger.error(error.message)
return klines
class TradingService(object):
stub: bbgo_pb2_grpc.TradingServiceStub
def __init__(self, host: str, port: int) -> None:
self.stub = bbgo_pb2_grpc.TradingServiceStub(get_insecure_channel(host, port))
def submit_order(self,
session: str,
exchange: str,
symbol: str,
side: str,
quantity: float,
order_type: str,
price: float = None,
stop_price: float = None,
client_order_id: str = None,
group_id: int = None) -> Order:
submit_order = SubmitOrder(session=session,
exchange=exchange,
symbol=symbol,
side=SideType.from_str(side),
quantity=quantity,
order_type=OrderType.from_str(order_type),
price=price,
stop_price=stop_price,
client_order_id=client_order_id,
group_id=group_id)
request = bbgo_pb2.SubmitOrderRequest(session=session, submit_orders=[submit_order.to_pb()])
response = self.stub.SubmitOrder(request)
order = Order.from_pb(response.orders[0])
error = ErrorMessage.from_pb(response.error)
if error.code != 0:
logger.error(error.message)
return order
def cancel_order(self, session: str, order_id: int = None, client_order_id: int = None) -> Order:
request = bbgo_pb2.CancelOrderRequest(
session=session,
id=order_id or "",
client_order_id=client_order_id or "",
)
response = self.stub.CancelOrder(request)
order = Order.from_pb(response.order)
error = ErrorMessage.from_pb(response.error)
if error.code != 0:
logger.error(error.message)
return order
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

View File

@ -1,80 +0,0 @@
import asyncio
from typing import Callable
from typing import List
import grpc
import bbgo_pb2
import bbgo_pb2_grpc
from bbgo.enums import ChannelType
from bbgo.enums import DepthType
from .data import Event
from .data import MarketDataEvent
from .data import Subscription
from .data import UserDataEvent
class Stream(object):
subscriptions: List[Subscription]
def __init__(self, host: str, port: int):
self.host = host
self.port = port
self.subscriptions = []
self.sessions = []
self.event_handlers = []
def subscribe(self, exchange: str, channel: str, symbol: str, depth: str = None, interval: str = None):
subscription = Subscription(exchange=exchange, channel=ChannelType.from_str(channel), symbol=symbol)
if depth is not None:
subscription.depth = DepthType(depth)
if interval is not None:
subscription.interval = interval
self.subscriptions.append(subscription)
def subscribe_user_data(self, session: str):
self.sessions.append(session)
def add_event_handler(self, event_handler: Callable) -> None:
self.event_handlers.append(event_handler)
def fire_event_handlers(self, event: Event) -> None:
for event_handler in self.event_handlers:
event_handler(event)
@property
def address(self):
return f'{self.host}:{self.port}'
async def _subscribe_market_data(self):
async with grpc.aio.insecure_channel(self.address) as channel:
stub = bbgo_pb2_grpc.MarketDataServiceStub(channel)
request = bbgo_pb2.SubscribeRequest(subscriptions=[s.to_pb() for s in self.subscriptions])
async for response in stub.Subscribe(request):
event = MarketDataEvent.from_pb(response)
self.fire_event_handlers(event)
async def _subscribe_user_data(self, session: str):
async with grpc.aio.insecure_channel(self.address) as channel:
stub = bbgo_pb2_grpc.UserDataServiceStub(channel)
request = bbgo_pb2.UserDataRequest(session=session)
async for response in stub.Subscribe(request):
event = UserDataEvent.from_pb(response)
self.fire_event_handlers(event)
def start(self):
coroutines = [self._subscribe_market_data()]
for session in self.sessions:
coroutines.append(self._subscribe_user_data(session))
group = asyncio.gather(*coroutines)
loop = asyncio.get_event_loop()
loop.run_until_complete(group)
loop.close()

View File

@ -1,7 +0,0 @@
from .convert import parse_number
from .convert import parse_time
from .grpc_utils import get_credentials_from_env
from .grpc_utils import get_grpc_cert_file_from_env
from .grpc_utils import get_grpc_key_file_from_env
from .grpc_utils import get_insecure_channel
from .grpc_utils import get_insecure_channel_from_env

View File

@ -1,20 +0,0 @@
from datetime import datetime
from decimal import Decimal
from typing import Union
def parse_number(s: Union[str, float]) -> Decimal:
if s is None:
return 0
if s == "":
return 0
return Decimal(s)
def parse_time(t: Union[str, int]) -> datetime:
if isinstance(t, str):
t = int(t)
return datetime.fromtimestamp(t / 1000)

View File

@ -1,43 +0,0 @@
import os
import 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 get_insecure_channel(host: str, port: int) -> grpc.Channel:
address = f'{host}:{port}'
return grpc.insecure_channel(address)
def get_insecure_channel_from_env() -> grpc.Channel:
host = os.environ.get('BBGO_GRPC_HOST') or '127.0.0.1'
port = os.environ.get('BBGO_GRPC_PORT') or 50051
address = get_insecure_channel(host, port)
return grpc.insecure_channel(address)

File diff suppressed because one or more lines are too long

View File

@ -1,354 +0,0 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import bbgo_pb2 as bbgo__pb2
class MarketDataServiceStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Subscribe = channel.unary_stream(
'/bbgo.MarketDataService/Subscribe',
request_serializer=bbgo__pb2.SubscribeRequest.SerializeToString,
response_deserializer=bbgo__pb2.MarketData.FromString,
)
self.QueryKLines = channel.unary_unary(
'/bbgo.MarketDataService/QueryKLines',
request_serializer=bbgo__pb2.QueryKLinesRequest.SerializeToString,
response_deserializer=bbgo__pb2.QueryKLinesResponse.FromString,
)
class MarketDataServiceServicer(object):
"""Missing associated documentation comment in .proto file."""
def Subscribe(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def QueryKLines(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_MarketDataServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'Subscribe': grpc.unary_stream_rpc_method_handler(
servicer.Subscribe,
request_deserializer=bbgo__pb2.SubscribeRequest.FromString,
response_serializer=bbgo__pb2.MarketData.SerializeToString,
),
'QueryKLines': grpc.unary_unary_rpc_method_handler(
servicer.QueryKLines,
request_deserializer=bbgo__pb2.QueryKLinesRequest.FromString,
response_serializer=bbgo__pb2.QueryKLinesResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'bbgo.MarketDataService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class MarketDataService(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def Subscribe(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/bbgo.MarketDataService/Subscribe',
bbgo__pb2.SubscribeRequest.SerializeToString,
bbgo__pb2.MarketData.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def QueryKLines(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.MarketDataService/QueryKLines',
bbgo__pb2.QueryKLinesRequest.SerializeToString,
bbgo__pb2.QueryKLinesResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
class UserDataServiceStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Subscribe = channel.unary_stream(
'/bbgo.UserDataService/Subscribe',
request_serializer=bbgo__pb2.UserDataRequest.SerializeToString,
response_deserializer=bbgo__pb2.UserData.FromString,
)
class UserDataServiceServicer(object):
"""Missing associated documentation comment in .proto file."""
def Subscribe(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_UserDataServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'Subscribe': grpc.unary_stream_rpc_method_handler(
servicer.Subscribe,
request_deserializer=bbgo__pb2.UserDataRequest.FromString,
response_serializer=bbgo__pb2.UserData.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'bbgo.UserDataService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class UserDataService(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def Subscribe(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/bbgo.UserDataService/Subscribe',
bbgo__pb2.UserDataRequest.SerializeToString,
bbgo__pb2.UserData.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
class TradingServiceStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.SubmitOrder = channel.unary_unary(
'/bbgo.TradingService/SubmitOrder',
request_serializer=bbgo__pb2.SubmitOrderRequest.SerializeToString,
response_deserializer=bbgo__pb2.SubmitOrderResponse.FromString,
)
self.CancelOrder = channel.unary_unary(
'/bbgo.TradingService/CancelOrder',
request_serializer=bbgo__pb2.CancelOrderRequest.SerializeToString,
response_deserializer=bbgo__pb2.CancelOrderResponse.FromString,
)
self.QueryOrder = channel.unary_unary(
'/bbgo.TradingService/QueryOrder',
request_serializer=bbgo__pb2.QueryOrderRequest.SerializeToString,
response_deserializer=bbgo__pb2.QueryOrderResponse.FromString,
)
self.QueryOrders = channel.unary_unary(
'/bbgo.TradingService/QueryOrders',
request_serializer=bbgo__pb2.QueryOrdersRequest.SerializeToString,
response_deserializer=bbgo__pb2.QueryOrdersResponse.FromString,
)
self.QueryTrades = channel.unary_unary(
'/bbgo.TradingService/QueryTrades',
request_serializer=bbgo__pb2.QueryTradesRequest.SerializeToString,
response_deserializer=bbgo__pb2.QueryTradesResponse.FromString,
)
class TradingServiceServicer(object):
"""Missing associated documentation comment in .proto file."""
def SubmitOrder(self, request, context):
"""request-response
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def CancelOrder(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def QueryOrder(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def QueryOrders(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def QueryTrades(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_TradingServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'SubmitOrder': grpc.unary_unary_rpc_method_handler(
servicer.SubmitOrder,
request_deserializer=bbgo__pb2.SubmitOrderRequest.FromString,
response_serializer=bbgo__pb2.SubmitOrderResponse.SerializeToString,
),
'CancelOrder': grpc.unary_unary_rpc_method_handler(
servicer.CancelOrder,
request_deserializer=bbgo__pb2.CancelOrderRequest.FromString,
response_serializer=bbgo__pb2.CancelOrderResponse.SerializeToString,
),
'QueryOrder': grpc.unary_unary_rpc_method_handler(
servicer.QueryOrder,
request_deserializer=bbgo__pb2.QueryOrderRequest.FromString,
response_serializer=bbgo__pb2.QueryOrderResponse.SerializeToString,
),
'QueryOrders': grpc.unary_unary_rpc_method_handler(
servicer.QueryOrders,
request_deserializer=bbgo__pb2.QueryOrdersRequest.FromString,
response_serializer=bbgo__pb2.QueryOrdersResponse.SerializeToString,
),
'QueryTrades': grpc.unary_unary_rpc_method_handler(
servicer.QueryTrades,
request_deserializer=bbgo__pb2.QueryTradesRequest.FromString,
response_serializer=bbgo__pb2.QueryTradesResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'bbgo.TradingService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class TradingService(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def SubmitOrder(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.TradingService/SubmitOrder',
bbgo__pb2.SubmitOrderRequest.SerializeToString,
bbgo__pb2.SubmitOrderResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def CancelOrder(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.TradingService/CancelOrder',
bbgo__pb2.CancelOrderRequest.SerializeToString,
bbgo__pb2.CancelOrderResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def QueryOrder(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.TradingService/QueryOrder',
bbgo__pb2.QueryOrderRequest.SerializeToString,
bbgo__pb2.QueryOrderResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def QueryOrders(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.TradingService/QueryOrders',
bbgo__pb2.QueryOrdersRequest.SerializeToString,
bbgo__pb2.QueryOrdersResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def QueryTrades(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/bbgo.TradingService/QueryTrades',
bbgo__pb2.QueryTradesRequest.SerializeToString,
bbgo__pb2.QueryTradesResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

View File

@ -1,24 +0,0 @@
import click
from bbgo import MarketService
@click.command()
@click.option('--host', default='127.0.0.1')
@click.option('--port', default=50051)
def main(host, port):
service = MarketService(host, port)
klines = service.query_klines(
exchange='binance',
symbol='BTCUSDT',
interval='1m',
limit=10,
)
for kline in klines:
print(kline)
if __name__ == '__main__':
main()

View File

@ -1,28 +0,0 @@
import click
from loguru import logger
from bbgo import Stream
from bbgo.data import Event
from bbgo.handlers import UpdateHandler
class LogBook(UpdateHandler):
def handle(self, event: Event) -> None:
logger.info(event)
@click.command()
@click.option('--host', default='127.0.0.1')
@click.option('--port', default=50051)
def main(host, port):
stream = Stream(host, port)
stream.subscribe('max', 'book', 'BTCUSDT', 'full')
stream.subscribe('max', 'book', 'ETHUSDT', 'full')
stream.subscribe_user_data('max')
stream.add_event_handler(LogBook())
stream.start()
if __name__ == '__main__':
main()

View File

@ -1,25 +0,0 @@
import click
from loguru import logger
from bbgo import MarketService
from bbgo.data import Subscription
from bbgo.enums import ChannelType
from bbgo.enums import DepthType
@click.command()
@click.option('--host', default='127.0.0.1')
@click.option('--port', default=50051)
def main(host, port):
subscriptions = [
Subscription('binance', ChannelType.BOOK, symbol='BTCUSDT', depth=DepthType.FULL),
]
service = MarketService(host, port)
response_iter = service.subscribe(subscriptions)
for response in response_iter:
logger.info(response)
if __name__ == '__main__':
main()

View File

@ -1,24 +0,0 @@
import grpc
from loguru import logger
import bbgo_pb2
import bbgo_pb2_grpc
from bbgo.data import UserDataEvent
def main():
host = '127.0.0.1'
port = 50051
address = f'{host}:{port}'
channel = grpc.insecure_channel(address)
stub = bbgo_pb2_grpc.UserDataServiceStub(channel)
request = bbgo_pb2.UserDataRequest(session='max')
response_iter = stub.Subscribe(request)
for response in response_iter:
event = UserDataEvent.from_pb(response)
logger.info(event)
if __name__ == '__main__':
main()

406
python/poetry.lock generated
View File

@ -1,406 +0,0 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
[[package]]
name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
[package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
[[package]]
name = "click"
version = "8.0.4"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.6"
files = [
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.6"
files = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
[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.53.0"
description = "HTTP/2-based RPC framework"
optional = false
python-versions = ">=3.7"
files = [
{file = "grpcio-1.53.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:752d2949b40e12e6ad3ed8cc552a65b54d226504f6b1fb67cab2ccee502cc06f"},
{file = "grpcio-1.53.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8a48fd3a7222be226bb86b7b413ad248f17f3101a524018cdc4562eeae1eb2a3"},
{file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f3e837d29f0e1b9d6e7b29d569e2e9b0da61889e41879832ea15569c251c303a"},
{file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef7d30242409c3aa5839b501e877e453a2c8d3759ca8230dd5a21cda029f046"},
{file = "grpcio-1.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6f90698b5d1c5dd7b3236cd1fa959d7b80e17923f918d5be020b65f1c78b173"},
{file = "grpcio-1.53.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a96c3c7f564b263c5d7c0e49a337166c8611e89c4c919f66dba7b9a84abad137"},
{file = "grpcio-1.53.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ee81349411648d1abc94095c68cd25e3c2812e4e0367f9a9355be1e804a5135c"},
{file = "grpcio-1.53.0-cp310-cp310-win32.whl", hash = "sha256:fdc6191587de410a184550d4143e2b24a14df495c86ca15e59508710681690ac"},
{file = "grpcio-1.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:658ffe1e39171be00490db5bd3b966f79634ac4215a1eb9a85c6cd6783bf7f6e"},
{file = "grpcio-1.53.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:1b172e6d497191940c4b8d75b53de82dc252e15b61de2951d577ec5b43316b29"},
{file = "grpcio-1.53.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:82434ba3a5935e47908bc861ce1ebc43c2edfc1001d235d6e31e5d3ed55815f7"},
{file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:1c734a2d4843e4e14ececf5600c3c4750990ec319e1299db7e4f0d02c25c1467"},
{file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a2ead3de3b2d53119d473aa2f224030257ef33af1e4ddabd4afee1dea5f04c"},
{file = "grpcio-1.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34d6e905f071f9b945cabbcc776e2055de1fdb59cd13683d9aa0a8f265b5bf9"},
{file = "grpcio-1.53.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eaf8e3b97caaf9415227a3c6ca5aa8d800fecadd526538d2bf8f11af783f1550"},
{file = "grpcio-1.53.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:da95778d37be8e4e9afca771a83424f892296f5dfb2a100eda2571a1d8bbc0dc"},
{file = "grpcio-1.53.0-cp311-cp311-win32.whl", hash = "sha256:e4f513d63df6336fd84b74b701f17d1bb3b64e9d78a6ed5b5e8a198bbbe8bbfa"},
{file = "grpcio-1.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:ddb2511fbbb440ed9e5c9a4b9b870f2ed649b7715859fd6f2ebc585ee85c0364"},
{file = "grpcio-1.53.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:2a912397eb8d23c177d6d64e3c8bc46b8a1c7680b090d9f13a640b104aaec77c"},
{file = "grpcio-1.53.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:55930c56b8f5b347d6c8c609cc341949a97e176c90f5cbb01d148d778f3bbd23"},
{file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:6601d812105583948ab9c6e403a7e2dba6e387cc678c010e74f2d6d589d1d1b3"},
{file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c705e0c21acb0e8478a00e7e773ad0ecdb34bd0e4adc282d3d2f51ba3961aac7"},
{file = "grpcio-1.53.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba074af9ca268ad7b05d3fc2b920b5fb3c083da94ab63637aaf67f4f71ecb755"},
{file = "grpcio-1.53.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:14817de09317dd7d3fbc8272864288320739973ef0f4b56bf2c0032349da8cdf"},
{file = "grpcio-1.53.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c7ad9fbedb93f331c2e9054e202e95cf825b885811f1bcbbdfdc301e451442db"},
{file = "grpcio-1.53.0-cp37-cp37m-win_amd64.whl", hash = "sha256:dad5b302a4c21c604d88a5d441973f320134e6ff6a84ecef9c1139e5ffd466f6"},
{file = "grpcio-1.53.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:fa8eaac75d3107e3f5465f2c9e3bbd13db21790c6e45b7de1756eba16b050aca"},
{file = "grpcio-1.53.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:104a2210edd3776c38448b4f76c2f16e527adafbde171fc72a8a32976c20abc7"},
{file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:dbc1ba968639c1d23476f75c356e549e7bbf2d8d6688717dcab5290e88e8482b"},
{file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95952d3fe795b06af29bb8ec7bbf3342cdd867fc17b77cc25e6733d23fa6c519"},
{file = "grpcio-1.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f144a790f14c51b8a8e591eb5af40507ffee45ea6b818c2482f0457fec2e1a2e"},
{file = "grpcio-1.53.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0698c094688a2dd4c7c2f2c0e3e142cac439a64d1cef6904c97f6cde38ba422f"},
{file = "grpcio-1.53.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6b6d60b0958be711bab047e9f4df5dbbc40367955f8651232bfdcdd21450b9ab"},
{file = "grpcio-1.53.0-cp38-cp38-win32.whl", hash = "sha256:1948539ce78805d4e6256ab0e048ec793956d54787dc9d6777df71c1d19c7f81"},
{file = "grpcio-1.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:df9ba1183b3f649210788cf80c239041dddcb375d6142d8bccafcfdf549522cd"},
{file = "grpcio-1.53.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:19caa5b7282a89b799e63776ff602bb39604f7ca98db6df27e2de06756ae86c3"},
{file = "grpcio-1.53.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:b5bd026ac928c96cc23149e6ef79183125542062eb6d1ccec34c0a37e02255e7"},
{file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:7dc8584ca6c015ad82e186e82f4c0fe977394588f66b8ecfc4ec873285314619"},
{file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2eddaae8af625e45b5c8500dcca1043264d751a6872cde2eda5022df8a336959"},
{file = "grpcio-1.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5fb6f3d7824696c1c9f2ad36ddb080ba5a86f2d929ef712d511b4d9972d3d27"},
{file = "grpcio-1.53.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8270d1dc2c98ab57e6dbf36fa187db8df4c036f04a398e5d5e25b4e01a766d70"},
{file = "grpcio-1.53.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:976a7f24eb213e8429cab78d5e120500dfcdeb01041f1f5a77b17b9101902615"},
{file = "grpcio-1.53.0-cp39-cp39-win32.whl", hash = "sha256:9c84a481451e7174f3a764a44150f93b041ab51045aa33d7b5b68b6979114e48"},
{file = "grpcio-1.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:6beb84f83360ff29a3654f43f251ec11b809dcb5524b698d711550243debd289"},
{file = "grpcio-1.53.0.tar.gz", hash = "sha256:a4952899b4931a6ba12951f9a141ef3e74ff8a6ec9aa2dc602afa40f63595e33"},
]
[package.extras]
protobuf = ["grpcio-tools (>=1.53.0)"]
[[package]]
name = "grpcio-tools"
version = "1.44.0"
description = "Protobuf code generator for gRPC"
optional = false
python-versions = ">=3.6"
files = [
{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"},
]
[package.dependencies]
grpcio = ">=1.44.0"
protobuf = ">=3.5.0.post1,<4.0dev"
setuptools = "*"
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
optional = false
python-versions = "*"
files = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
[[package]]
name = "loguru"
version = "0.6.0"
description = "Python logging made (stupidly) simple"
optional = false
python-versions = ">=3.5"
files = [
{file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
{file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
]
[package.dependencies]
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = "*"
files = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.6"
files = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
[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"
optional = false
python-versions = ">=3.6"
files = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "protobuf"
version = "3.19.4"
description = "Protocol Buffers"
optional = false
python-versions = ">=3.5"
files = [
{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"},
]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
[[package]]
name = "pyparsing"
version = "3.0.7"
description = "Python parsing module"
optional = false
python-versions = ">=3.6"
files = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "7.0.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"},
{file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"},
]
[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 = "setuptools"
version = "68.0.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"},
{file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "win32-setctime"
version = "1.1.0"
description = "A small Python utility to set file creation time on Windows"
optional = false
python-versions = ">=3.5"
files = [
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
[package.extras]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "bfda359d4e023f07cd8df05859450215e9f560f50b4a77a8aa8436ac42a74fe3"

View File

@ -1,25 +0,0 @@
[tool.poetry]
name = "bbgo"
version = "0.1.9"
description = ""
authors = ["なるみ <weaper@gmail.com>"]
packages = [
{ include = "bbgo" },
{ include = "bbgo_pb2.py" },
{ include = "bbgo_pb2_grpc.py" },
]
[tool.poetry.dependencies]
python = "^3.8"
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"]
build-backend = "poetry.core.masonry.api"

View File

@ -1,14 +0,0 @@
[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
column_limit = 120
[isort]
not_skip = __init__.py
line_length = 120
force_single_line = True

View File

@ -1,82 +0,0 @@
from decimal import Decimal
import bbgo_pb2
from bbgo.data import Balance
from bbgo.data import ErrorMessage
from bbgo.data import KLine
from bbgo.utils import parse_time
def test_balance_from_pb():
exchange = 'max'
currency = 'BTCUSDT'
available = '3.1415926'
locked = '2.7182818'
borrowed = '0.1234567'
balance_pb = bbgo_pb2.Balance(
exchange=exchange,
currency=currency,
available=available,
locked=locked,
borrowed=borrowed,
)
balance = Balance.from_pb(balance_pb)
assert balance.exchange == exchange
assert balance.currency == currency
assert balance.available == Decimal(available)
assert balance.locked == Decimal(locked)
assert balance.borrowed == Decimal(borrowed)
def test_kline_from_pb():
exchange = "binance"
symbol = "BTCUSDT"
open = "39919.31"
high = "39919.32"
low = "39919.31"
close = "39919.31"
volume = "0.27697"
quote_volume = "11056.4530226"
start_time = 1649833260000
end_time = 1649833319999
closed = True
kline_pb = bbgo_pb2.KLine(exchange=exchange,
symbol=symbol,
open=open,
high=high,
low=low,
close=close,
volume=volume,
quote_volume=quote_volume,
start_time=start_time,
end_time=end_time,
closed=closed)
kline = KLine.from_pb(kline_pb)
assert kline.exchange == exchange
assert kline.symbol == symbol
assert kline.open == Decimal(open)
assert kline.high == Decimal(high)
assert kline.low == Decimal(low)
assert kline.close == Decimal(close)
assert kline.volume == Decimal(volume)
assert kline.quote_volume == Decimal(quote_volume)
assert kline.start_time == parse_time(start_time)
assert kline.end_time == parse_time(end_time)
assert closed == closed
def test_order_from_pb():
error_code = 123
error_message = "error message 123"
error_pb = bbgo_pb2.Error(error_code=error_code, error_message=error_message)
error = ErrorMessage.from_pb(error_pb)
assert error.code == error_code
assert error.message == error_message

View File

@ -1,15 +0,0 @@
from bbgo.enums import ChannelType
def test_channel_type_from_str():
m = {
'book': ChannelType.BOOK,
'trade': ChannelType.TRADE,
'ticker': ChannelType.TICKER,
'kline': ChannelType.KLINE,
'balance': ChannelType.BALANCE,
'order': ChannelType.ORDER,
}
for k, v in m.items():
assert ChannelType.from_str(k) == v

View File

@ -1,19 +0,0 @@
from decimal import Decimal
from bbgo.utils import parse_number
from bbgo.utils import parse_time
def test_parse_time():
t = 1650610080000
d = parse_time(t)
assert d.timestamp() == t / 1000
def test_parse_float():
assert parse_number(None) == 0
assert parse_number("") == 0
s = "3.14159265358979"
assert parse_number(s) == Decimal(s)