Skip to content

Commit ab8bb8f

Browse files
committed
Extracted common code from WatchXYZ event sources to a common base class.
1 parent 4d35439 commit ab8bb8f

6 files changed

Lines changed: 125 additions & 159 deletions

File tree

basana/external/ccxt/bars.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
# limitations under the License.
1616

1717
from typing import Any, Dict, List, Optional, Union
18-
import asyncio
1918
import datetime
2019
import logging
2120

2221
from . import helpers
23-
from basana.core import bar, event, logs
22+
from .watch_event_source import WatchEventSource
23+
from basana.core import bar, logs
2424
from basana.core.pair import Pair
2525

2626

@@ -44,48 +44,23 @@ def to_bar(self, pair: Pair, duration: datetime.timedelta):
4444
)
4545

4646

47-
class WatchOHLCVEventSource(event.FifoQueueEventSource, event.Producer):
47+
class WatchOHLCVEventSource(WatchEventSource):
4848
def __init__(self, cli: Any, pair: Pair, timeframe: str, params: Optional[Dict[str, Any]] = None):
4949
if timeframe not in cli.timeframes:
5050
raise ValueError(f"Invalid bar_duration: {timeframe}")
5151

52-
if not cli.has.get("watchOHLCV"):
53-
raise NotImplementedError("The exchange does not support watchOHLCV")
54-
55-
super().__init__(producer=self)
56-
self._cli = cli
57-
self._pair = pair
58-
self._symbol = helpers.pair_to_symbol(pair)
52+
super().__init__(cli, pair, "watchOHLCV", "unWatchOHLCV")
5953
self._timeframe = timeframe
6054
self._params = dict(params or {})
6155
self._duration_secs = self._cli.parse_timeframe(self._timeframe)
6256
self._last_candle: Optional[Candle] = None
6357

64-
async def initialize(self):
65-
await self._cli.load_markets()
66-
67-
async def main(self):
68-
while True:
69-
try:
70-
ohlcv = await self._cli.watch_ohlcv(self._symbol, self._timeframe, params=self._params)
71-
self._handle_ohlcv(ohlcv)
72-
except Exception as e:
73-
logger.error(logs.StructuredMessage(
74-
"Error watching OHLCV", pair=self._pair, timeframe=self._timeframe, error=e
75-
))
76-
# Yield to the event loop to allow other tasks to run.
77-
await asyncio.sleep(0)
78-
79-
async def finalize(self):
80-
if not self._cli.has.get("unWatchOHLCV"):
81-
return
58+
async def watch(self):
59+
ohlcv = await self._cli.watch_ohlcv(self._symbol, self._timeframe, params=self._params)
60+
self._handle_ohlcv(ohlcv)
8261

83-
try:
84-
await self._cli.un_watch_ohlcv(self._symbol, self._timeframe)
85-
except Exception as error:
86-
logger.error(logs.StructuredMessage(
87-
"Error unwatching OHLCV", pair=self._pair, timeframe=self._timeframe, error=error
88-
))
62+
async def unwatch(self):
63+
await self._cli.un_watch_ohlcv(self._symbol, self._timeframe)
8964

9065
def _handle_ohlcv(self, ohlcv: list):
9166
candles = list(map(Candle, ohlcv))

basana/external/ccxt/order_book.py

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@
1717
from dataclasses import dataclass
1818
from decimal import Decimal
1919
from typing import Any, Awaitable, Callable, Dict, List, Optional
20-
import asyncio
21-
import logging
2220

2321
from . import helpers
24-
from basana.core import dt, event, logs
22+
from .watch_event_source import WatchEventSource
23+
from basana.core import dt, event
2524
from basana.core.pair import Pair
2625

2726

28-
logger = logging.getLogger(__name__)
29-
30-
3127
@dataclass
3228
class Entry:
3329
#: The price.
@@ -75,47 +71,23 @@ def __init__(self, when, order_book: PartialOrderBook):
7571
self.order_book: PartialOrderBook = order_book
7672

7773

78-
class WatchOrderBookEventSource(event.FifoQueueEventSource, event.Producer):
74+
class WatchOrderBookEventSource(WatchEventSource):
7975
def __init__(
8076
self, cli: Any, pair: Pair, limit: Optional[int] = None,
8177
params: Optional[Dict[str, Any]] = None
8278
):
83-
if not cli.has.get("watchOrderBook"):
84-
raise NotImplementedError("The exchange does not support watchOrderBook")
85-
86-
super().__init__(producer=self)
87-
self._cli = cli
88-
self._pair = pair
89-
self._symbol = helpers.pair_to_symbol(pair)
79+
super().__init__(cli, pair, "watchOrderBook", "unWatchOrderBook")
9080
self._limit = limit
9181
self._params = dict(params or {})
9282

93-
async def initialize(self):
94-
await self._cli.load_markets()
95-
96-
async def main(self):
97-
while True:
98-
try:
99-
order_book = await self._cli.watch_order_book(
100-
self._symbol, self._limit, params=self._params
101-
)
102-
self._handle_order_book(order_book)
103-
except Exception as e:
104-
logger.error(logs.StructuredMessage(
105-
"Error watching order book", pair=self._pair, error=e
106-
))
107-
await asyncio.sleep(0)
108-
109-
async def finalize(self):
110-
if not self._cli.has.get("unWatchOrderBook"):
111-
return
112-
113-
try:
114-
await self._cli.un_watch_order_book(self._symbol, params=self._params)
115-
except Exception as error:
116-
logger.error(logs.StructuredMessage(
117-
"Error unwatching order book", pair=self._pair, error=error
118-
))
83+
async def watch(self):
84+
order_book = await self._cli.watch_order_book(
85+
self._symbol, self._limit, params=self._params
86+
)
87+
self._handle_order_book(order_book)
88+
89+
async def unwatch(self):
90+
await self._cli.un_watch_order_book(self._symbol, params=self._params)
11991

12092
def _handle_order_book(self, order_book: dict):
12193
timestamp = order_book.get("timestamp")

basana/external/ccxt/orders.py

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616

1717
from decimal import Decimal
1818
from typing import Any, Awaitable, Callable, Dict, List, Optional
19-
import asyncio
2019
import datetime
2120
import functools
22-
import logging
2321

2422
from . import helpers
25-
from basana.core import dt, event, logs
23+
from .watch_event_source import WatchEventSource
24+
from basana.core import dt, event
2625
from basana.core.enums import OrderOperation
2726
from basana.core.pair import Pair
2827

2928

30-
logger = logging.getLogger(__name__)
31-
32-
3329
class Order:
3430
def __init__(self, pair: Pair, raw: dict):
3531
#: The trading pair.
@@ -121,41 +117,17 @@ def __init__(self, when: datetime.datetime, order: Order):
121117
self.order: Order = order
122118

123119

124-
class WatchOrdersEventSource(event.FifoQueueEventSource, event.Producer):
120+
class WatchOrdersEventSource(WatchEventSource):
125121
def __init__(self, cli: Any, pair: Pair, params: Optional[Dict[str, Any]] = None):
126-
if not cli.has.get("watchOrders"):
127-
raise NotImplementedError("The exchange does not support watchOrders")
128-
129-
super().__init__(producer=self)
130-
self._cli = cli
131-
self._pair = pair
132-
self._symbol = helpers.pair_to_symbol(pair)
122+
super().__init__(cli, pair, "watchOrders", "unWatchOrders")
133123
self._params = dict(params or {})
134124

135-
async def initialize(self):
136-
await self._cli.load_markets()
137-
138-
async def main(self):
139-
while True:
140-
try:
141-
orders = await self._cli.watch_orders(self._symbol, params=self._params)
142-
self._handle_orders(orders)
143-
except Exception as e:
144-
logger.error(logs.StructuredMessage(
145-
"Error watching orders", pair=self._pair, error=e
146-
))
147-
await asyncio.sleep(0)
148-
149-
async def finalize(self):
150-
if not self._cli.has.get("unWatchOrders"):
151-
return
152-
153-
try:
154-
await self._cli.un_watch_orders(self._symbol, params=self._params)
155-
except Exception as error:
156-
logger.error(logs.StructuredMessage(
157-
"Error unwatching orders", pair=self._pair, error=error
158-
))
125+
async def watch(self):
126+
orders = await self._cli.watch_orders(self._symbol, params=self._params)
127+
self._handle_orders(orders)
128+
129+
async def unwatch(self):
130+
await self._cli.un_watch_orders(self._symbol, params=self._params)
159131

160132
def _handle_orders(self, raw_orders: List[dict]):
161133
orders = list(map(functools.partial(Order, self._pair), raw_orders))

basana/external/ccxt/trades.py

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@
1616

1717
from decimal import Decimal
1818
from typing import Any, Awaitable, Callable, Dict, List, Optional
19-
import asyncio
2019
import datetime
21-
import logging
2220

2321
from . import helpers
24-
from basana.core import event, logs
22+
from .watch_event_source import WatchEventSource
23+
from basana.core import event
2524
from basana.core.pair import Pair
2625

2726

28-
logger = logging.getLogger(__name__)
29-
30-
3127
class Trade:
3228
def __init__(self, pair: Pair, raw: dict):
3329
#: The trading pair.
@@ -81,42 +77,17 @@ def __init__(self, when: datetime.datetime, trade: Trade):
8177
self.trade: Trade = trade
8278

8379

84-
class WatchTradesEventSource(event.FifoQueueEventSource, event.Producer):
80+
class WatchTradesEventSource(WatchEventSource):
8581
def __init__(self, cli: Any, pair: Pair, params: Optional[Dict[str, Any]] = None):
86-
if not cli.has.get("watchTrades"):
87-
raise NotImplementedError("The exchange does not support watchTrades")
88-
89-
super().__init__(producer=self)
90-
self._cli = cli
91-
self._pair = pair
92-
self._symbol = helpers.pair_to_symbol(pair)
82+
super().__init__(cli, pair, "watchTrades", "unWatchTrades")
9383
self._params = dict(params or {})
9484

95-
async def initialize(self):
96-
await self._cli.load_markets()
97-
98-
async def main(self):
99-
while True:
100-
try:
101-
trades = await self._cli.watch_trades(self._symbol, params=self._params)
102-
self._handle_trades(trades)
103-
except Exception as e:
104-
logger.error(logs.StructuredMessage(
105-
"Error watching trades", pair=self._pair, error=e
106-
))
107-
# Yield to the event loop to allow other tasks to run.
108-
await asyncio.sleep(0)
109-
110-
async def finalize(self):
111-
if not self._cli.has.get("unWatchTrades"):
112-
return
113-
114-
try:
115-
await self._cli.un_watch_trades(self._symbol)
116-
except Exception as error:
117-
logger.error(logs.StructuredMessage(
118-
"Error unwatching trades", pair=self._pair, error=error
119-
))
85+
async def watch(self):
86+
trades = await self._cli.watch_trades(self._symbol, params=self._params)
87+
self._handle_trades(trades)
88+
89+
async def unwatch(self):
90+
await self._cli.un_watch_trades(self._symbol)
12091

12192
def _handle_trades(self, trades: List[dict]):
12293
trades.sort(key=lambda trade: int(trade["timestamp"]))
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Basana
2+
#
3+
# Copyright 2022 Gabriel Martin Becedillas Ruiz
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from typing import Any
18+
import abc
19+
import asyncio
20+
import logging
21+
22+
from . import helpers
23+
from basana.core import event, logs
24+
from basana.core.pair import Pair
25+
26+
27+
logger = logging.getLogger(__name__)
28+
29+
30+
class WatchEventSource(event.FifoQueueEventSource, event.Producer):
31+
def __init__(self, cli: Any, pair: Pair, watch_capability: str, unwatch_capability: str):
32+
if not cli.has.get(watch_capability):
33+
raise NotImplementedError(f"The exchange does not support {watch_capability}")
34+
35+
super().__init__(producer=self)
36+
self._cli = cli
37+
self._pair = pair
38+
self._symbol = helpers.pair_to_symbol(pair)
39+
self._watch_capability = watch_capability
40+
self._unwatch_capability = unwatch_capability
41+
self.err_sleep = 0.1
42+
43+
async def initialize(self):
44+
await self._cli.load_markets()
45+
46+
async def main(self):
47+
while True:
48+
sleep = 0
49+
try:
50+
await self.watch()
51+
except Exception as e:
52+
logger.error(logs.StructuredMessage(
53+
f"Error during {self._watch_capability}", pair=self._pair, error=e
54+
))
55+
sleep = self.err_sleep
56+
# Yield to the event loop to allow other tasks to run.
57+
await asyncio.sleep(sleep)
58+
59+
async def finalize(self):
60+
if not self._cli.has.get(self._unwatch_capability):
61+
return
62+
63+
try:
64+
await self.unwatch()
65+
except Exception as e:
66+
logger.error(logs.StructuredMessage(
67+
f"Error during {self._unwatch_capability}", pair=self._pair, error=e
68+
))
69+
70+
@abc.abstractmethod
71+
async def watch(self):
72+
raise NotImplementedError()
73+
74+
@abc.abstractmethod
75+
async def unwatch(self):
76+
raise NotImplementedError()

0 commit comments

Comments
 (0)