Skip to content

Commit be83cea

Browse files
committed
[Tests][Exchange] Add polymarket tests
1 parent 5620cd4 commit be83cea

File tree

9 files changed

+223
-30
lines changed

9 files changed

+223
-30
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.4.239] - 2024-12-19
8+
### Added
9+
[Enum] Option Exchange Type
10+
[Exchanges] Polymarket tests
11+
712
## [2.4.238] - 2024-12-17
813
### Fixes
914
[CCXT] fix failed market fetch

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OctoBot-Trading [2.4.238](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
1+
# OctoBot-Trading [2.4.239](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
22
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/903b6b22bceb4661b608a86fea655f69)](https://app.codacy.com/gh/Drakkar-Software/OctoBot-Trading?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot-Trading&utm_campaign=Badge_Grade_Dashboard)
33
[![PyPI](https://img.shields.io/pypi/v/OctoBot-Trading.svg)](https://pypi.python.org/pypi/OctoBot-Trading/)
44
[![Coverage Status](https://coveralls.io/repos/github/Drakkar-Software/OctoBot-Trading/badge.svg?branch=master)](https://coveralls.io/github/Drakkar-Software/OctoBot-Trading?branch=master)

octobot_trading/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
# License along with this library.
1616

1717
PROJECT_NAME = "OctoBot-Trading"
18-
VERSION = "2.4.238" # major.minor.revision
18+
VERSION = "2.4.239" # major.minor.revision

octobot_trading/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ class ExchangeTypes(enum.Enum):
514514
SPOT = "spot"
515515
FUTURE = "future"
516516
MARGIN = "margin"
517+
OPTION = "option"
517518
UNKNOWN = "unknown"
518519

519520

tests_additional/real_exchanges/real_exchange_tester.py

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ async def inner_test_get_order_books(
171171
supports_custom_limit: bool,
172172
custom_limit: typing.Optional[int],
173173
max_empty_book_sides: int,
174+
supports_without_symbols_filter: bool,
174175
):
175176
async with (self.get_exchange_manager() as exchange_manager):
176177
# with symbols filter
@@ -205,36 +206,37 @@ async def inner_test_get_order_books(
205206
f"More empty book sides than expected: {max_empty_book_sides=} {empty_book_sides=}"
206207
)
207208
# without symbols filter
208-
books_by_symbol = await exchange_manager.exchange.get_order_books(symbols=None)
209-
empty_book_sides = []
210-
assert len(books_by_symbol) >= expected_missing_symbol_filter_books_min_count, (
211-
f"{len(books_by_symbol)=} NOT >= {expected_missing_symbol_filter_books_min_count=}"
212-
)
213-
for symbol, book in books_by_symbol.items():
214-
assert 0 < book[trading_enums.ExchangeConstantsOrderBookInfoColumns.TIMESTAMP.value] < ref_time, (
215-
f"Invalid {symbol} book timestamp value: "
216-
f"{book[trading_enums.ExchangeConstantsOrderBookInfoColumns.TIMESTAMP.value]}, {ref_time=}"
209+
if supports_without_symbols_filter:
210+
books_by_symbol = await exchange_manager.exchange.get_order_books(symbols=None)
211+
empty_book_sides = []
212+
assert len(books_by_symbol) >= expected_missing_symbol_filter_books_min_count, (
213+
f"{len(books_by_symbol)=} NOT >= {expected_missing_symbol_filter_books_min_count=}"
217214
)
218-
for side in [
219-
trading_enums.ExchangeConstantsOrderBookInfoColumns.BIDS,
220-
trading_enums.ExchangeConstantsOrderBookInfoColumns.ASKS
221-
]:
222-
if len(book[side.value]) == 0:
223-
empty_book_sides.append((symbol, side))
224-
else:
225-
assert min_book_orders_count <= len(book[side.value]) <= expected_max_orders_by_side, (
226-
f"Unexpected {symbol} {side.value} orders count: {len(book[side.value])}. Expected: "
227-
f"[{min_book_orders_count}:{expected_max_orders_by_side}]"
228-
)
229-
if len(empty_book_sides) > max_empty_book_sides:
230-
raise AssertionError(
231-
f"More empty book sides than expected: {max_empty_book_sides=} {len(empty_book_sides)=}"
215+
for symbol, book in books_by_symbol.items():
216+
assert 0 < book[trading_enums.ExchangeConstantsOrderBookInfoColumns.TIMESTAMP.value] < ref_time, (
217+
f"Invalid {symbol} book timestamp value: "
218+
f"{book[trading_enums.ExchangeConstantsOrderBookInfoColumns.TIMESTAMP.value]}, {ref_time=}"
219+
)
220+
for side in [
221+
trading_enums.ExchangeConstantsOrderBookInfoColumns.BIDS,
222+
trading_enums.ExchangeConstantsOrderBookInfoColumns.ASKS
223+
]:
224+
if len(book[side.value]) == 0:
225+
empty_book_sides.append((symbol, side))
226+
else:
227+
assert min_book_orders_count <= len(book[side.value]) <= expected_max_orders_by_side, (
228+
f"Unexpected {symbol} {side.value} orders count: {len(book[side.value])}. Expected: "
229+
f"[{min_book_orders_count}:{expected_max_orders_by_side}]"
230+
)
231+
if len(empty_book_sides) > max_empty_book_sides:
232+
raise AssertionError(
233+
f"More empty book sides than expected: {max_empty_book_sides=} {len(empty_book_sides)=}"
234+
)
235+
# with custom limit
236+
books_by_symbol = await exchange_manager.exchange.get_order_books(symbols=None, limit=custom_limit)
237+
self._ensure_book_custom_limit(
238+
books_by_symbol, supports_custom_limit, expected_max_orders_by_side, custom_limit
232239
)
233-
# with custom limit
234-
books_by_symbol = await exchange_manager.exchange.get_order_books(symbols=None, limit=custom_limit)
235-
self._ensure_book_custom_limit(
236-
books_by_symbol, supports_custom_limit, expected_max_orders_by_side, custom_limit
237-
)
238240

239241
def _ensure_book_custom_limit(
240242
self,

tests_additional/real_exchanges/test_hitbtc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ async def test_get_order_books(self):
133133
True,
134134
10,
135135
140,
136+
True
136137
)
137138

138139
async def test_get_recent_trades(self):

tests_additional/real_exchanges/test_hollaex.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ async def test_get_order_books(self):
137137
False,
138138
None,
139139
1,
140+
True
140141
)
141142

142143
async def test_get_recent_trades(self):
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Drakkar-Software OctoBot-Trading
2+
# Copyright (c) Drakkar-Software, All rights reserved.
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation; either
7+
# version 3.0 of the License, or (at your option) any later version.
8+
#
9+
# This library is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
# Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public
15+
# License along with this library.
16+
import pytest
17+
18+
from octobot_commons.enums import TimeFrames, PriceIndexes
19+
from octobot_trading.enums import ExchangeConstantsMarketStatusColumns as Ecmsc, \
20+
ExchangeConstantsOrderBookInfoColumns as Ecobic, ExchangeConstantsOrderColumns as Ecoc, \
21+
ExchangeConstantsTickersColumns as Ectc
22+
from tests_additional.real_exchanges.real_exchange_tester import RealExchangeTester
23+
import octobot_trading.enums as trading_enums
24+
# required to catch async loop context exceptions
25+
from tests import event_loop
26+
27+
try:
28+
import tentacles.Trading.Exchange.polymarket.ccxt.polymarket_async
29+
except ImportError:
30+
pytest.skip(
31+
reason=(
32+
"Polymarket tentacle is not installed, skipping TestPolymarketRealExchangeTester"
33+
)
34+
)
35+
36+
# All test coroutines will be treated as marked.
37+
pytestmark = pytest.mark.asyncio
38+
39+
40+
class TestPolymarketRealExchangeTester(RealExchangeTester):
41+
EXCHANGE_NAME = "polymarket"
42+
SYMBOL = "will-bitcoin-replace-sha-256-before-2027/USDC"
43+
SYMBOL_2 = "will-the-us-confirm-that-aliens-exist-before-2027/USDC"
44+
SYMBOL_3 = "10pt0-or-above-earthquake-before-2027/USDC"
45+
TIME_FRAME = TimeFrames.ONE_MINUTE
46+
MARKET_STATUS_TYPE = trading_enums.ExchangeTypes.OPTION.value
47+
48+
async def test_time_frames(self):
49+
time_frames = await self.time_frames()
50+
assert all(time_frame in time_frames for time_frame in (
51+
TimeFrames.ONE_MINUTE.value,
52+
TimeFrames.ONE_HOUR.value,
53+
TimeFrames.SIX_HOURS.value,
54+
TimeFrames.ONE_DAY.value,
55+
TimeFrames.ONE_WEEK.value,
56+
))
57+
58+
async def test_active_symbols(self):
59+
await self.inner_test_active_symbols(18412, 18412)
60+
61+
async def test_get_market_status(self):
62+
for market_status in await self.get_market_statuses():
63+
self.ensure_required_market_status_values(market_status)
64+
self.check_market_status_limits(market_status, has_price_limits=True)
65+
66+
async def test_get_symbol_prices(self):
67+
# without limit
68+
symbol_prices = await self.get_symbol_prices()
69+
assert len(symbol_prices) >= 1000
70+
# check candles order (oldest first)
71+
self.ensure_elements_order(symbol_prices, PriceIndexes.IND_PRICE_TIME.value)
72+
# check last candle is the current candle
73+
assert symbol_prices[-1][PriceIndexes.IND_PRICE_TIME.value] >= self.get_time() - self.get_allowed_time_delta()
74+
75+
# try with candles limit (used in candled updater)
76+
symbol_prices = await self.get_symbol_prices(limit=200)
77+
assert len(symbol_prices) == 200
78+
# check candles order (oldest first)
79+
self.ensure_elements_order(symbol_prices, PriceIndexes.IND_PRICE_TIME.value)
80+
# check last candle is the current candle
81+
assert symbol_prices[-1][PriceIndexes.IND_PRICE_TIME.value] >= self.get_time() - self.get_allowed_time_delta()
82+
83+
async def test_get_historical_symbol_prices(self):
84+
# try with since and limit (used in data collector)
85+
for limit in (50, None):
86+
symbol_prices = await self.get_symbol_prices(since=self.CANDLE_SINCE, limit=limit)
87+
if limit:
88+
assert len(symbol_prices) == limit
89+
else:
90+
assert len(symbol_prices) > 5
91+
# check candles order (oldest first)
92+
self.ensure_elements_order(symbol_prices, PriceIndexes.IND_PRICE_TIME.value)
93+
# check that fetched candles are historical candles
94+
max_candle_time = self.get_time_after_time_frames(self.CANDLE_SINCE_SEC, len(symbol_prices))
95+
assert max_candle_time <= self.get_time()
96+
with pytest.raises(AssertionError): # not supported
97+
for candle in symbol_prices:
98+
assert self.CANDLE_SINCE_SEC <= candle[PriceIndexes.IND_PRICE_TIME.value] <= max_candle_time
99+
100+
async def test_get_historical_ohlcv(self):
101+
await super().test_get_historical_ohlcv()
102+
103+
async def test_get_kline_price(self):
104+
kline_price = await self.get_kline_price()
105+
assert len(kline_price) == 1
106+
assert len(kline_price[0]) == 6
107+
kline_start_time = kline_price[0][PriceIndexes.IND_PRICE_TIME.value]
108+
# assert kline is the current candle
109+
assert kline_start_time >= self.get_time() - self.get_allowed_time_delta()
110+
111+
async def test_get_order_book(self):
112+
order_book = await self.get_order_book()
113+
assert 0 < order_book[Ecobic.TIMESTAMP.value] < self._get_ref_order_book_timestamp()
114+
assert len(order_book[Ecobic.ASKS.value]) >= 5
115+
assert len(order_book[Ecobic.ASKS.value][0]) == 2
116+
assert len(order_book[Ecobic.BIDS.value]) >= 5
117+
assert len(order_book[Ecobic.BIDS.value][0]) == 2
118+
119+
async def test_get_order_books(self):
120+
await self.inner_test_get_order_books(
121+
True,
122+
1000, # asked symbols
123+
100, # up to 100 orders
124+
0, # from 0 orders
125+
False,
126+
None,
127+
0,
128+
False
129+
)
130+
131+
async def test_get_recent_trades(self):
132+
recent_trades = await self.get_recent_trades()
133+
assert len(recent_trades) == 50
134+
# check trades order (oldest first)
135+
self.ensure_elements_order(recent_trades, Ecoc.TIMESTAMP.value)
136+
137+
async def test_get_price_ticker(self):
138+
ticker = await self.get_price_ticker()
139+
self._check_ticker(ticker, self.SYMBOL, check_content=True)
140+
141+
async def test_get_all_currencies_price_ticker(self):
142+
tickers = await self.get_all_currencies_price_ticker()
143+
for symbol, ticker in tickers.items():
144+
self._check_ticker(ticker, symbol)
145+
146+
@staticmethod
147+
def _check_ticker(ticker, symbol, check_content=False):
148+
assert ticker[Ectc.SYMBOL.value] == symbol
149+
assert all(key in ticker for key in (
150+
Ectc.HIGH.value,
151+
Ectc.LOW.value,
152+
Ectc.BID.value,
153+
Ectc.BID_VOLUME.value,
154+
Ectc.ASK.value,
155+
Ectc.ASK_VOLUME.value,
156+
Ectc.OPEN.value,
157+
Ectc.CLOSE.value,
158+
Ectc.LAST.value,
159+
Ectc.PREVIOUS_CLOSE.value
160+
))
161+
if check_content:
162+
assert ticker[Ectc.HIGH.value] is None
163+
assert ticker[Ectc.LOW.value] is None
164+
assert ticker[Ectc.BID.value]
165+
assert ticker[Ectc.BID_VOLUME.value] is None
166+
assert ticker[Ectc.ASK.value]
167+
assert ticker[Ectc.ASK_VOLUME.value] is None
168+
assert ticker[Ectc.OPEN.value] is None
169+
assert ticker[Ectc.CLOSE.value]
170+
assert ticker[Ectc.LAST.value]
171+
assert ticker[Ectc.PREVIOUS_CLOSE.value] is None
172+
assert ticker[Ectc.BASE_VOLUME.value]
173+
assert ticker[Ectc.TIMESTAMP.value]
174+
RealExchangeTester.check_ticker_typing(
175+
ticker,
176+
check_open=False,
177+
check_high=False,
178+
check_low=False,
179+
check_close=True,
180+
check_base_volume=True,
181+
check_timestamp=True
182+
)

tests_additional/real_exchanges/test_upbit.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ async def test_get_order_books(self):
128128
False,
129129
10,
130130
10,
131+
True
131132
)
132133

133134
async def test_get_recent_trades(self):

0 commit comments

Comments
 (0)