Skip to content

Commit

Permalink
Negative Risk (#66)
Browse files Browse the repository at this point in the history
* first pass

* fix tests, cleanup

* reverse types file change

* updates

* update TickSize type usage

* remove Optional

* updates

* adding test cases

* bump python-order-utils

---------

Co-authored-by: Rodrigo <[email protected]>
  • Loading branch information
mshrieve and poly-rodr authored Dec 7, 2023
1 parent b45628d commit 6c71a73
Show file tree
Hide file tree
Showing 8 changed files with 1,698 additions and 72 deletions.
40 changes: 28 additions & 12 deletions py_clob_client/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import logging

from .order_builder.builder import OrderBuilder
from .clob_types import ApiCreds, OrderType

from .headers.headers import create_level_1_headers, create_level_2_headers
from .signer import Signer

Expand Down Expand Up @@ -47,7 +45,10 @@
BalanceAllowanceParams,
OrderScoringParams,
TickSize,
CreateOrderOptions,
OrdersScoringParams,
OrderType,
PartialCreateOrderOptions,
)
from .exceptions import PolyException
from .http_helpers.helpers import (
Expand All @@ -60,7 +61,7 @@
add_order_scoring_params_to_url,
add_orders_scoring_params_to_url,
)
from py_order_utils.config import get_contract_config

from .constants import L0, L1, L1_AUTH_UNAVAILABLE, L2, L2_AUTH_UNAVAILABLE
from .utilities import (
parse_raw_orderbook_summary,
Expand Down Expand Up @@ -96,8 +97,7 @@ def __init__(
self.signer = Signer(key, chain_id) if key else None
self.creds = creds
self.mode = self._get_client_mode()
if chain_id:
self.contract_config = get_contract_config(chain_id)

if self.signer:
self.builder = OrderBuilder(
self.signer, sig_type=signature_type, funder=funder
Expand Down Expand Up @@ -263,15 +263,29 @@ def __resolve_tick_size(
tick_size = min_tick_size
return tick_size

def create_order(self, order_args: OrderArgs, tick_size: TickSize = None):
def create_order(
self, order_args: OrderArgs, options: PartialCreateOrderOptions = None
):
"""
Creates and signs an order
Level 2 Auth required
Level 1 Auth required
"""
self.assert_level_2_auth()
self.assert_level_1_auth()

tick_size = self.__resolve_tick_size(order_args.token_id, tick_size)
return self.builder.create_order(order_args, tick_size)
# add resolve_order_options, or similar
tick_size = self.__resolve_tick_size(
order_args.token_id,
options.tick_size if options else None,
)
neg_risk = options.neg_risk if options else False

return self.builder.create_order(
order_args,
CreateOrderOptions(
tick_size=tick_size,
neg_risk=neg_risk,
),
)

def post_order(self, order, orderType: OrderType = OrderType.GTC):
"""
Expand All @@ -286,11 +300,13 @@ def post_order(self, order, orderType: OrderType = OrderType.GTC):
)
return post("{}{}".format(self.host, POST_ORDER), headers=headers, data=body)

def create_and_post_order(self, order_args: OrderArgs):
def create_and_post_order(
self, order_args: OrderArgs, options: PartialCreateOrderOptions = None
):
"""
Utility function to create and publish an order
"""
ord = self.create_order(order_args)
ord = self.create_order(order_args, options)
return self.post_order(ord)

def cancel(self, order_id):
Expand Down
35 changes: 33 additions & 2 deletions py_clob_client/clob_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Any
from dataclasses import dataclass, asdict
from json import dumps
from typing import Literal
from typing import Literal, Optional

from .constants import ZERO_ADDRESS

Expand Down Expand Up @@ -143,11 +143,42 @@ class OrdersScoringParams:
TickSize = Literal["0.1", "0.01", "0.001", "0.0001"]


@dataclass
class CreateOrderOptions:
tick_size: TickSize
neg_risk: bool = False


@dataclass
class PartialCreateOrderOptions:
tick_size: Optional[TickSize] = None
neg_risk: bool = False


@dataclass
class RoundConfig:
price: float
size: float
amount: float


TickSizes: dict[str, TickSize]
@dataclass
class ContractConfig:
"""
Contract Configuration
"""

exchange: str
"""
The exchange contract responsible for matching orders
"""

collateral: str
"""
The ERC20 token used as collateral for the exchange's markets
"""

conditional_tokens: str
"""
The ERC1155 conditional tokens contract
"""
42 changes: 42 additions & 0 deletions py_clob_client/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from .clob_types import ContractConfig


def get_contract_config(chainID: int, neg_risk: bool = False) -> ContractConfig:
"""
Get the contract configuration for the chain
"""

CONFIG = {
137: ContractConfig(
exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E",
collateral="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
conditional_tokens="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045",
),
80001: ContractConfig(
exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E",
collateral="0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961",
conditional_tokens="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43",
),
}

NEG_RISK_CONFIG = {
137: ContractConfig(
exchange="0xC5d563A36AE78145C45a50134d48A1215220f80a",
collateral="0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
conditional_tokens="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045",
),
80001: ContractConfig(
exchange="0x87d1A0DdB4C63a6301916F02090A51a7241571e4",
collateral="0x2e8dcfe708d44ae2e406a1c02dfe2fa13012f961",
conditional_tokens="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43",
),
}

if neg_risk:
config = NEG_RISK_CONFIG.get(chainID)
else:
config = CONFIG.get(chainID)
if config is None:
raise Exception("Invalid chainID: ${}".format(chainID))

return config
40 changes: 22 additions & 18 deletions py_clob_client/order_builder/builder.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from py_order_utils.builders import OrderBuilder as UtilsOrderBuild
from py_order_utils.builders import OrderBuilder as UtilsOrderBuilder
from py_order_utils.signer import Signer as UtilsSigner
from py_order_utils.model import (
EOA,
OrderData,
SignedOrder,
BUY as UtilsBuy,
SELL as UtilsSell,
)
from py_order_utils.config import get_contract_config

from .helpers import (
to_token_decimals,
Expand All @@ -16,12 +16,11 @@
round_up,
)
from .constants import BUY, SELL

from ..config import get_contract_config
from ..signer import Signer
from ..clob_types import OrderArgs, TickSize, RoundConfig
from typing import Tuple
from ..clob_types import OrderArgs, CreateOrderOptions, TickSize, RoundConfig

ROUNDING_CONFIG: Tuple[TickSize, RoundConfig] = {
ROUNDING_CONFIG: dict[TickSize, RoundConfig] = {
"0.1": RoundConfig(price=1, size=2, amount=3),
"0.01": RoundConfig(price=2, size=2, amount=4),
"0.001": RoundConfig(price=3, size=2, amount=5),
Expand All @@ -40,13 +39,6 @@ def __init__(self, signer: Signer, sig_type=None, funder=None):
# Used for Polymarket proxy wallets and other smart contract wallets
# Defaults to the address of the signer
self.funder = funder if funder is not None else self.signer.address()
self.contract_config = self._get_contract_config(self.signer.get_chain_id())
self.order_builder = UtilsOrderBuild(
self.contract_config.exchange, self.signer.get_chain_id(), self.signer
)

def _get_contract_config(self, chain_id: int):
return get_contract_config(chain_id)

def get_order_amounts(
self, side: str, size: float, price: float, round_config: RoundConfig
Expand Down Expand Up @@ -82,23 +74,25 @@ def get_order_amounts(
else:
raise ValueError(f"order_args.side must be '{BUY}' or '{SELL}'")

def create_order(self, order_args: OrderArgs, tick_size: TickSize) -> SignedOrder:
def create_order(
self, order_args: OrderArgs, options: CreateOrderOptions
) -> SignedOrder:
"""
Creates and signs an order
"""
side, maker_amount, taker_amount = self.get_order_amounts(
order_args.side,
order_args.size,
order_args.price,
ROUNDING_CONFIG[tick_size],
ROUNDING_CONFIG[options.tick_size],
)

data = OrderData(
maker=self.funder,
taker=order_args.taker,
tokenId=order_args.token_id,
makerAmount=maker_amount,
takerAmount=taker_amount,
makerAmount=str(maker_amount),
takerAmount=str(taker_amount),
side=side,
feeRateBps=str(order_args.fee_rate_bps),
nonce=str(order_args.nonce),
Expand All @@ -107,4 +101,14 @@ def create_order(self, order_args: OrderArgs, tick_size: TickSize) -> SignedOrde
signatureType=self.sig_type,
)

return self.order_builder.build_signed_order(data)
contract_config = get_contract_config(
self.signer.get_chain_id(), options.neg_risk
)

order_builder = UtilsOrderBuilder(
contract_config.exchange,
self.signer.get_chain_id(),
UtilsSigner(key=self.signer.private_key),
)

return order_builder.build_signed_order(data)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ parsimonious==0.8.1
pluggy==1.0.0
protobuf==3.19.4
py==1.11.0
py-order-utils>=0.1.3
py-order-utils>=0.2.0
pycryptodome==3.14.0
pyparsing==3.0.7
pyrsistent==0.18.1
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="py_clob_client",
version="0.11.0",
version="0.12.0",
author="Polymarket Engineering",
author_email="[email protected]",
maintainer="Polymarket Engineering",
Expand Down
Loading

0 comments on commit 6c71a73

Please sign in to comment.