Implement initial project structure with logging and token fetching functionality
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
logs/
|
||||
__pycache__/
|
||||
@@ -0,0 +1,3 @@
|
||||
LOG_FOLDER = "logs/"
|
||||
GAMMA_API_URL = "https://gamma-api.polymarket.com"
|
||||
REQUEST_TIMEOUT = 5
|
||||
@@ -0,0 +1,20 @@
|
||||
import asyncio
|
||||
from utils.logger import setup_logging
|
||||
from utils.tokens import fetch_tokens
|
||||
|
||||
|
||||
async def main():
|
||||
logger = setup_logging()
|
||||
logger.info("Polymarket HFT Market Maker started")
|
||||
|
||||
up_token, down_token, market_slug = await fetch_tokens()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nMarket maker stopped by user")
|
||||
except Exception as e:
|
||||
print(f"Fatal error: {e}")
|
||||
exit(1)
|
||||
@@ -0,0 +1,27 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from config import LOG_FOLDER
|
||||
|
||||
|
||||
def setup_logging():
|
||||
logs_dir = Path(LOG_FOLDER)
|
||||
logs_dir.mkdir(exist_ok=True)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||
log_filename = logs_dir / f"polymarket_hft_{timestamp}.log"
|
||||
|
||||
log_format = "%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s"
|
||||
date_format = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format=log_format,
|
||||
datefmt=date_format,
|
||||
handlers=[
|
||||
logging.FileHandler(log_filename, encoding="utf-8"),
|
||||
logging.StreamHandler(),
|
||||
],
|
||||
)
|
||||
|
||||
return logging.getLogger(__name__)
|
||||
@@ -0,0 +1,18 @@
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
MARKET_INTERVAL_SECONDS = 900
|
||||
TIMEZONE = "US/Eastern"
|
||||
|
||||
|
||||
def get_market_slug(coin: str = "btc") -> str:
|
||||
|
||||
if not coin or not isinstance(coin, str):
|
||||
raise ValueError("Coin must be a non-empty string")
|
||||
|
||||
et_tz = pytz.timezone(TIMEZONE)
|
||||
now = datetime.now(et_tz)
|
||||
ts = int(now.timestamp())
|
||||
start = (ts // MARKET_INTERVAL_SECONDS) * MARKET_INTERVAL_SECONDS
|
||||
return f"{coin.lower()}-updown-15m-{start}"
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import json
|
||||
import logging
|
||||
from multiprocessing.util import get_logger
|
||||
from typing import Optional, Tuple
|
||||
import aiohttp
|
||||
from .slug import get_market_slug
|
||||
from config import GAMMA_API_URL, REQUEST_TIMEOUT
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def fetch_tokens(
|
||||
coin: str = "btc",
|
||||
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
||||
|
||||
if not coin or not isinstance(coin, str):
|
||||
raise ValueError("Coin must be a non-empty string")
|
||||
|
||||
try:
|
||||
slug = get_market_slug(coin)
|
||||
url = f"{GAMMA_API_URL}/events/slug/{slug}"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
url, timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT)
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
data = await response.json()
|
||||
return _extract_tokens(data, slug)
|
||||
else:
|
||||
logger.warning(f"API request failed with status {response.status}")
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
logger.error(f"Network error fetching tokens: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Invalid JSON response: {e}")
|
||||
except ValueError as e:
|
||||
logger.error(f"Invalid input: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error fetching tokens: {e}")
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def _extract_tokens(
|
||||
data: dict, slug: str
|
||||
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
||||
|
||||
try:
|
||||
if not isinstance(data, dict) or "markets" not in data:
|
||||
logger.error("Invalid response structure: missing 'markets' field")
|
||||
return None, None, None
|
||||
|
||||
markets = data["markets"]
|
||||
if not isinstance(markets, list) or len(markets) == 0:
|
||||
logger.error("Invalid response structure: 'markets' is empty or not a list")
|
||||
return None, None, None
|
||||
|
||||
market = markets[0]
|
||||
if not isinstance(market, dict) or "clobTokenIds" not in market:
|
||||
logger.error("Invalid market structure: missing 'clobTokenIds'")
|
||||
return None, None, None
|
||||
|
||||
clob_token_ids = market["clobTokenIds"]
|
||||
if not isinstance(clob_token_ids, str):
|
||||
logger.error("Invalid token IDs format: not a string")
|
||||
return None, None, None
|
||||
|
||||
try:
|
||||
token_list = json.loads(clob_token_ids)
|
||||
if not isinstance(token_list, list) or len(token_list) < 2:
|
||||
logger.error("Invalid token list format or insufficient tokens")
|
||||
return None, None, None
|
||||
|
||||
up_token = token_list[0]
|
||||
down_token = token_list[1]
|
||||
|
||||
if not (
|
||||
isinstance(up_token, str)
|
||||
and isinstance(down_token, str)
|
||||
and up_token.isdigit()
|
||||
and down_token.isdigit()
|
||||
):
|
||||
logger.error("Invalid token ID format")
|
||||
return None, None, None
|
||||
|
||||
logger.info(
|
||||
f"Successfully extracted tokens for market {slug}: \n\tUP : {up_token} \n\tDOWN : {down_token}"
|
||||
)
|
||||
return up_token, down_token, slug
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse token IDs JSON: {e}")
|
||||
return None, None, None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error extracting tokens: {e}")
|
||||
return None, None, None
|
||||
Reference in New Issue
Block a user