Skip to content

Commit b8a32a2

Browse files
committed
[Tests] Add polymarket authenticated tests
1 parent 25dc5dc commit b8a32a2

File tree

3 files changed

+159
-10
lines changed

3 files changed

+159
-10
lines changed

additional_tests/exchanges_tests/.env.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ PHEMEX_KEY=
1616
PHEMEX_SECRET=
1717
PHEMEX_PASSWORD=
1818
PHEMEX_SANDBOXED=true
19+
20+
POLYMARKET_KEY=
21+
POLYMARKET_SECRET=
22+
POLYMARKET_PASSWORD=

additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class AbstractAuthenticatedExchangeTester:
7272
ORDER_SIZE = 10 # % of portfolio to include in test orders
7373
PORTFOLIO_TYPE_FOR_SIZE = trading_constants.CONFIG_PORTFOLIO_FREE
7474
CONVERTS_ORDER_SIZE_BEFORE_PUSHING_TO_EXCHANGES = False
75+
CONVERTS_ORDER_PRICE_BEFORE_PUSHING_TO_EXCHANGE = False
7576
ORDER_PRICE_DIFF = 20 # % of price difference compared to current price for limit and stop orders
7677
EXPECT_MISSING_ORDER_FEES_DUE_TO_ORDERS_TOO_OLD_FOR_RECENT_TRADES = False # when recent trades are limited and
7778
# closed orders fees are taken from recent trades
@@ -86,6 +87,7 @@ class AbstractAuthenticatedExchangeTester:
8687
OPEN_TIMEOUT = 15
8788
# if >0: retry fetching open/cancelled orders when created/cancelled orders are not synchronised instantly
8889
ORDER_IN_OPEN_AND_CANCELLED_ORDERS_TIMEOUT = 10
90+
ORDER_IMPACTS_PORTFOLIO_FREE_BALANCE = True
8991
CANCEL_TIMEOUT = 15
9092
EDIT_TIMEOUT = 15
9193
MIN_PORTFOLIO_SIZE = 1
@@ -598,7 +600,10 @@ async def inner_test_create_and_cancel_limit_orders(self, symbol=None, settlemen
598600
# )
599601
# # end debug tools
600602
open_orders = await self.get_open_orders(exchange_data)
601-
cancelled_orders = await self.get_cancelled_orders(exchange_data)
603+
try:
604+
cancelled_orders = await self.get_cancelled_orders(exchange_data)
605+
except trading_errors.NotSupported as err:
606+
cancelled_orders = []
602607
if self.CHECK_EMPTY_ACCOUNT:
603608
assert size >= trading_constants.ZERO if enable_min_size_check else size == trading_constants.ZERO
604609
assert open_orders == []
@@ -614,18 +619,22 @@ async def inner_test_create_and_cancel_limit_orders(self, symbol=None, settlemen
614619
self.check_created_limit_order(limit_order, price, size, side)
615620
assert await self.order_in_open_orders(open_orders, limit_order, symbol=symbol)
616621
await self.check_can_get_order(limit_order)
617-
# assert free portfolio amount is smaller than total amount
618-
balance = await self.get_portfolio()
619-
locked_currency = settlement_currency if side == trading_enums.TradeOrderSide.BUY else self.ORDER_CURRENCY
620-
assert balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE] < \
621-
balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL], (
622-
f"FALSE: {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE]} < {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL]}"
623-
)
622+
if self.ORDER_IMPACTS_PORTFOLIO_FREE_BALANCE:
623+
# assert free portfolio amount is smaller than total amount
624+
balance = await self.get_portfolio()
625+
locked_currency = settlement_currency if side == trading_enums.TradeOrderSide.BUY else self.ORDER_CURRENCY
626+
assert balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE] < \
627+
balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL], (
628+
f"FALSE: {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE]} < {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL]}"
629+
)
624630
finally:
625631
# don't leave buy_limit as open order
626632
await self.cancel_order(limit_order)
627633
assert await self.order_not_in_open_orders(open_orders, limit_order, symbol=symbol)
628-
assert await self.order_in_cancelled_orders(cancelled_orders, limit_order, symbol=symbol)
634+
try:
635+
assert await self.order_in_cancelled_orders(cancelled_orders, limit_order, symbol=symbol)
636+
except trading_errors.NotSupported as err:
637+
pass
629638

630639
async def inner_test_cancel_uncancellable_order(self):
631640
if self.UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE:
@@ -1340,7 +1349,13 @@ def _check_fetched_order_dicts(self, orders: list[dict]):
13401349

13411350
def check_created_limit_order(self, order, price, size, side):
13421351
self._check_order(order, size, side)
1343-
assert order.origin_price == price, f"{order.origin_price} != {price}"
1352+
if self.CONVERTS_ORDER_PRICE_BEFORE_PUSHING_TO_EXCHANGE:
1353+
# actual origin_price may vary due to price conversion
1354+
assert price * decimal.Decimal("0.8") <= order.origin_price <= price * decimal.Decimal("1.2"), (
1355+
f"FALSE: {price * decimal.Decimal('0.8')} <= {order.origin_price} <= {price * decimal.Decimal('1.2')}"
1356+
)
1357+
else:
1358+
assert order.origin_price == price, f"{order.origin_price} != {price}"
13441359
assert isinstance(order.filled_quantity, decimal.Decimal)
13451360
expected_type = personal_data.BuyLimitOrder \
13461361
if side is trading_enums.TradeOrderSide.BUY else personal_data.SellLimitOrder
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2+
# Copyright (c) 2025 Drakkar-Software, All rights reserved.
3+
#
4+
# OctoBot is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU General Public License
6+
# as published by the Free Software Foundation; either
7+
# version 3.0 of the License, or (at your option) any later version.
8+
#
9+
# OctoBot 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+
# General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public
15+
# License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16+
import pytest
17+
18+
from additional_tests.exchanges_tests import abstract_authenticated_exchange_tester
19+
20+
try:
21+
import tentacles.Trading.Exchange.polymarket.ccxt.polymarket_async
22+
except ImportError:
23+
pytest.skip(
24+
reason=(
25+
"Polymarket tentacle is not installed, skipping TestPolymarketAuthenticatedExchange"
26+
)
27+
)
28+
29+
# All test coroutines will be treated as marked.
30+
pytestmark = pytest.mark.asyncio
31+
32+
33+
class TestPolymarketAuthenticatedExchange(
34+
abstract_authenticated_exchange_tester.AbstractAuthenticatedExchangeTester
35+
):
36+
# enter exchange name as a class variable here
37+
EXCHANGE_NAME = "polymarket"
38+
ORDER_CURRENCY = "nothing-ever-happens-december" # "will-bitcoin-replace-sha-256-before-2027"
39+
SETTLEMENT_CURRENCY = "USDC"
40+
EXPIRATION_DATE = "251231" # "261231"
41+
SYMBOL = f"{ORDER_CURRENCY}/{SETTLEMENT_CURRENCY}:{SETTLEMENT_CURRENCY}-{EXPIRATION_DATE}"
42+
ORDER_SIZE = 10 # % of portfolio to include in test orders
43+
EXPECT_MISSING_FEE_IN_CANCELLED_ORDERS = False
44+
CONVERTS_ORDER_SIZE_BEFORE_PUSHING_TO_EXCHANGES = True
45+
CONVERTS_ORDER_PRICE_BEFORE_PUSHING_TO_EXCHANGE = True
46+
ORDER_IMPACTS_PORTFOLIO_FREE_BALANCE = False
47+
48+
async def test_get_portfolio(self):
49+
await super().test_get_portfolio()
50+
51+
async def test_get_portfolio_with_market_filter(self):
52+
# pass if not implemented
53+
pass
54+
55+
async def test_untradable_symbols(self):
56+
# pass if not implemented
57+
pass
58+
59+
async def test_get_max_orders_count(self):
60+
# pass if not implemented
61+
pass
62+
63+
async def test_get_account_id(self):
64+
# pass if not implemented
65+
pass
66+
67+
async def test_is_authenticated_request(self):
68+
await super().test_is_authenticated_request()
69+
70+
async def test_invalid_api_key_error(self):
71+
await super().test_invalid_api_key_error()
72+
73+
async def test_get_api_key_permissions(self):
74+
# pass if not implemented
75+
pass
76+
77+
async def test_missing_trading_api_key_permissions(self):
78+
pass
79+
80+
async def test_api_key_ip_whitelist_error(self):
81+
# pass if not implemented
82+
pass
83+
84+
async def test_get_not_found_order(self):
85+
await super().test_get_not_found_order()
86+
87+
async def test_is_valid_account(self):
88+
# pass if not implemented
89+
pass
90+
91+
async def test_get_special_orders(self):
92+
# pass if not implemented
93+
pass
94+
95+
async def test_create_and_cancel_limit_orders(self):
96+
await super().test_create_and_cancel_limit_orders()
97+
98+
async def test_create_and_fill_market_orders(self):
99+
await super().test_create_and_fill_market_orders()
100+
101+
async def test_get_my_recent_trades(self):
102+
await super().test_get_my_recent_trades()
103+
104+
async def test_get_closed_orders(self):
105+
# pass if not implemented
106+
pass
107+
108+
async def test_get_cancelled_orders(self):
109+
# pass if not implemented
110+
pass
111+
112+
async def test_create_and_cancel_stop_orders(self):
113+
# pass if not implemented
114+
pass
115+
116+
async def test_edit_limit_order(self):
117+
# pass if not implemented
118+
pass
119+
120+
async def test_edit_stop_order(self):
121+
# pass if not implemented
122+
pass
123+
124+
async def test_create_single_bundled_orders(self):
125+
# pass if not implemented
126+
pass
127+
128+
async def test_create_double_bundled_orders(self):
129+
# pass if not implemented
130+
pass

0 commit comments

Comments
 (0)