Skip to content

Commit 4c1fc5c

Browse files
committed
[Tests] Add polymarket authenticated tests
1 parent aca7d2d commit 4c1fc5c

File tree

3 files changed

+159
-8
lines changed

3 files changed

+159
-8
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 & 8 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
@@ -616,13 +618,22 @@ async def inner_test_create_and_cancel_limit_orders(self, symbol=None, settlemen
616618
assert await self.order_in_open_orders(open_orders, limit_order, symbol=symbol)
617619
await self.check_can_get_order(limit_order)
618620
await self.sleep_before_checking_portfolio()
619-
# assert free portfolio amount is smaller than total amount
620-
balance = await self.get_portfolio()
621-
locked_currency = settlement_currency if side == trading_enums.TradeOrderSide.BUY else self.ORDER_CURRENCY
622-
assert balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE] < \
623-
balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL], (
624-
f"FALSE: {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE]} < {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL]}"
625-
)
621+
if self.ORDER_IMPACTS_PORTFOLIO_FREE_BALANCE:
622+
# assert free portfolio amount is smaller than total amount
623+
balance = await self.get_portfolio()
624+
locked_currency = settlement_currency if side == trading_enums.TradeOrderSide.BUY else self.ORDER_CURRENCY
625+
assert balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE] < \
626+
balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL], (
627+
f"FALSE: {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE]} < {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL]}"
628+
)
629+
else:
630+
# assert free portfolio amount equals total amount when orders don't impact free balance
631+
balance = await self.get_portfolio()
632+
locked_currency = settlement_currency if side == trading_enums.TradeOrderSide.BUY else self.ORDER_CURRENCY
633+
assert balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE] == \
634+
balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL], (
635+
f"FALSE: {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_FREE]} == {balance[locked_currency][trading_constants.CONFIG_PORTFOLIO_TOTAL]}"
636+
)
626637
finally:
627638
# don't leave buy_limit as open order
628639
await self.cancel_order(limit_order)
@@ -1344,7 +1355,13 @@ def _check_fetched_order_dicts(self, orders: list[dict]):
13441355

13451356
def check_created_limit_order(self, order, price, size, side):
13461357
self._check_order(order, size, side)
1347-
assert order.origin_price == price, f"{order.origin_price} != {price}"
1358+
if self.CONVERTS_ORDER_PRICE_BEFORE_PUSHING_TO_EXCHANGE:
1359+
# actual origin_price may vary due to price conversion
1360+
assert price * decimal.Decimal("0.8") <= order.origin_price <= price * decimal.Decimal("1.2"), (
1361+
f"FALSE: {price * decimal.Decimal('0.8')} <= {order.origin_price} <= {price * decimal.Decimal('1.2')}"
1362+
)
1363+
else:
1364+
assert order.origin_price == price, f"{order.origin_price} != {price}"
13481365
assert isinstance(order.filled_quantity, decimal.Decimal)
13491366
expected_type = personal_data.BuyLimitOrder \
13501367
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 = "will-bitcoin-replace-sha-256-before-2027"
39+
SETTLEMENT_CURRENCY = "USDC"
40+
EXPIRATION_DATE = "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)