├── scripts ├── bash │ ├── install.sh │ ├── start-dev.sh │ ├── run-docker.sh │ ├── build-docker.sh │ └── run-docker-dev.sh └── python │ ├── setup.py │ ├── server.py │ └── cli.py ├── .env.example ├── docs ├── images │ └── cli.png └── EXAMPLE.md ├── .github ├── ISSUE_TEMPLATE │ ├── feature-request---.md │ └── bug-report---.md ├── workflows │ ├── docker-image.yml │ ├── greetings.yml │ ├── python-app.yml │ └── dependency-review.yml └── REPOSITORY_SETUP.md ├── agents ├── connectors │ ├── search.py │ ├── news.py │ └── chroma.py ├── application │ ├── cron.py │ ├── creator.py │ ├── trade.py │ ├── executor.py │ └── prompts.py ├── utils │ ├── utils.py │ └── objects.py └── polymarket │ ├── gamma.py │ └── polymarket.py ├── tests └── test.py ├── .pre-commit-config.yaml ├── Dockerfile ├── .gitignore ├── LICENSE.md ├── GITHUB_SETUP_SUMMARY.txt ├── pyproject.toml ├── requirements.txt ├── CONTRIBUTING.md └── README.md /scripts/bash/install.sh: -------------------------------------------------------------------------------- 1 | pip install -r requirements.txt 2 | -------------------------------------------------------------------------------- /scripts/bash/start-dev.sh: -------------------------------------------------------------------------------- 1 | python setup.py 2 | fastapi dev server.py 3 | -------------------------------------------------------------------------------- /scripts/bash/run-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --rm -it polymarket-agents:latest 3 | -------------------------------------------------------------------------------- /scripts/python/setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | -------------------------------------------------------------------------------- /scripts/bash/build-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -f Dockerfile --tag polymarket-agents:latest . 3 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | POLYGON_WALLET_PRIVATE_KEY="" 2 | OPENAI_API_KEY="" 3 | TAVILY_API_KEY="" 4 | NEWSAPI_API_KEY="" 5 | -------------------------------------------------------------------------------- /docs/images/cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlackSky-Jose/PolyMarket-trading-AI-model/HEAD/docs/images/cli.png -------------------------------------------------------------------------------- /scripts/bash/run-docker-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --rm -it -v $(pwd):/home polymarket-agents:latest bash 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request---.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature request \U0001F680" 3 | about: Suggest an idea 4 | labels: enhancement 5 | 6 | --- 7 | 8 | ## Summary 9 | Brief explanation of the feature. 10 | 11 | ### Basic example 12 | Include a basic example or links here. 13 | 14 | ### Motivation 15 | Why are we doing this? What use cases does it support? What is the expected outcome 16 | ? 17 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile --tag polymarket-agents:$(date +%s) 19 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: "Welcome to Polymarket Agents. Thank you for filing your first issue." 16 | pr-message: "Welcome to Polymarket Agents. Thank you for creating your first PR. Cheers!" 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report---.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug report \U0001F41E" 3 | about: Create a bug report 4 | labels: bug 5 | 6 | --- 7 | 8 | ## Describe the bug 9 | A clear and concise description of what the bug is. 10 | 11 | ### Steps to reproduce 12 | Steps to reproduce the behavior. 13 | 14 | ### Expected behavior 15 | A clear and concise description of what you expected to happen. 16 | 17 | ### Environment 18 | - OS: [e.g. Arch Linux] 19 | - Other details that you think may affect. 20 | 21 | ### Additional context 22 | Add any other context about the problem here 23 | . 24 | -------------------------------------------------------------------------------- /agents/connectors/search.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from dotenv import load_dotenv 4 | from tavily import TavilyClient 5 | 6 | load_dotenv() 7 | 8 | openai_api_key = os.getenv("OPEN_API_KEY") 9 | tavily_api_key = os.getenv("TAVILY_API_KEY") 10 | 11 | # Step 1. Instantiating your TavilyClient 12 | tavily_client = TavilyClient(api_key=tavily_api_key) 13 | 14 | # Step 2. Executing a context search query 15 | context = tavily_client.get_search_context(query="Will Biden drop out of the race?") 16 | # Step 3. That's it! You now have a context string that you can feed directly into your RAG Application 17 | -------------------------------------------------------------------------------- /agents/application/cron.py: -------------------------------------------------------------------------------- 1 | from agents.application.trade import Trader 2 | 3 | import time 4 | 5 | from scheduler import Scheduler 6 | from scheduler.trigger import Monday 7 | 8 | 9 | class Scheduler: 10 | def __init__(self) -> None: 11 | self.trader = Trader() 12 | self.schedule = Scheduler() 13 | 14 | def start(self) -> None: 15 | while True: 16 | self.schedule.exec_jobs() 17 | time.sleep(1) 18 | 19 | 20 | class TradingAgent(Scheduler): 21 | def __init__(self) -> None: 22 | super() 23 | self.trader = Trader() 24 | self.weekly(Monday(), self.trader.one_best_trade) 25 | -------------------------------------------------------------------------------- /scripts/python/server.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from fastapi import FastAPI 3 | 4 | app = FastAPI() 5 | 6 | 7 | @app.get("/") 8 | def read_root(): 9 | return {"Hello": "World"} 10 | 11 | 12 | @app.get("/items/{item_id}") 13 | def read_item(item_id: int, q: Union[str, None] = None): 14 | return {"item_id": item_id, "q": q} 15 | 16 | 17 | @app.get("/trades/{trade_id}") 18 | def read_trade(trade_id: int, q: Union[str, None] = None): 19 | return {"trade_id": trade_id, "q": q} 20 | 21 | 22 | @app.get("/markets/{market_id}") 23 | def read_market(market_id: int, q: Union[str, None] = None): 24 | return {"market_id": market_id, "q": q} 25 | 26 | 27 | # post new prompt 28 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | % python test/test.py 3 | ... 4 | ---------------------------------------------------------------------- 5 | Ran 3 tests in 0.000s 6 | 7 | OK 8 | """ 9 | 10 | import unittest 11 | 12 | 13 | class TestStringMethods(unittest.TestCase): 14 | def test_upper(self): 15 | self.assertEqual("foo".upper(), "FOO") 16 | 17 | def test_isupper(self): 18 | self.assertTrue("FOO".isupper()) 19 | self.assertFalse("Foo".isupper()) 20 | 21 | def test_split(self): 22 | s = "hello world" 23 | self.assertEqual(s.split(), ["hello", "world"]) 24 | # check that s.split fails when the separator is not a string 25 | with self.assertRaises(TypeError): 26 | s.split(2) 27 | 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.6.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: check-added-large-files 9 | - id: check-json 10 | - id: check-toml 11 | - id: check-merge-conflict 12 | - id: debug-statements 13 | - id: mixed-line-ending 14 | 15 | - repo: https://github.com/astral-sh/ruff-pre-commit 16 | rev: v0.6.0 17 | hooks: 18 | - id: ruff 19 | args: [--fix, --exit-non-zero-on-fix] 20 | - id: ruff-format 21 | 22 | - repo: https://github.com/pre-commit/mirrors-mypy 23 | rev: v1.11.0 24 | hooks: 25 | - id: mypy 26 | additional_dependencies: [types-all] 27 | args: [--ignore-missing-imports] 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim 2 | 3 | # Set environment variables 4 | ENV PYTHONUNBUFFERED=1 \ 5 | PYTHONDONTWRITEBYTECODE=1 \ 6 | PIP_NO_CACHE_DIR=1 \ 7 | PIP_DISABLE_PIP_VERSION_CHECK=1 8 | 9 | # Install system dependencies 10 | RUN apt-get update && apt-get install -y --no-install-recommends \ 11 | gcc \ 12 | g++ \ 13 | && rm -rf /var/lib/apt/lists/* 14 | 15 | # Create app directory 16 | WORKDIR /app 17 | 18 | # Copy dependency files 19 | COPY pyproject.toml ./ 20 | 21 | # Install Python dependencies 22 | RUN pip install --upgrade pip setuptools wheel && \ 23 | pip install -e ".[dev]" && \ 24 | pip cache purge 25 | 26 | # Copy application code 27 | COPY . . 28 | 29 | # Set Python path 30 | ENV PYTHONPATH=/app 31 | 32 | # Expose port (if running server) 33 | EXPOSE 8000 34 | 35 | # Default command 36 | CMD ["python", "-m", "scripts.python.cli"] 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | 23 | # Virtual environments 24 | .venv/ 25 | venv/ 26 | ENV/ 27 | env/ 28 | 29 | # IDE 30 | .vscode/ 31 | .idea/ 32 | *.swp 33 | *.swo 34 | *~ 35 | 36 | # Environment variables 37 | .env 38 | .env.local 39 | .env.*.local 40 | 41 | # Logs 42 | *.log 43 | logs/ 44 | 45 | # Database 46 | *.db 47 | *.sqlite 48 | *.sqlite3 49 | 50 | # Local RAG databases 51 | local_db/ 52 | local_db_events/ 53 | local_db_markets/ 54 | 55 | # Testing 56 | .pytest_cache/ 57 | .coverage 58 | htmlcov/ 59 | .tox/ 60 | .hypothesis/ 61 | 62 | # mypy 63 | .mypy_cache/ 64 | .dmypy.json 65 | dmypy.json 66 | 67 | # Ruff 68 | .ruff_cache/ 69 | 70 | # OS 71 | .DS_Store 72 | Thumbs.db 73 | 74 | # Project specific 75 | *.json.bak 76 | *.csv 77 | -------------------------------------------------------------------------------- /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Set up Python 3.9 23 | uses: actions/setup-python@v3 24 | with: 25 | python-version: "3.9" 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install black pytest 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Lint with black 32 | run: | 33 | black . 34 | - name: Test with unittest 35 | run: | 36 | python -m unittest discover 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Polymarket 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /agents/utils/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def parse_camel_case(key) -> str: 5 | output = "" 6 | for char in key: 7 | if char.isupper(): 8 | output += " " 9 | output += char.lower() 10 | else: 11 | output += char 12 | return output 13 | 14 | 15 | def preprocess_market_object(market_object: dict) -> dict: 16 | description = market_object["description"] 17 | 18 | for k, v in market_object.items(): 19 | if k == "description": 20 | continue 21 | if isinstance(v, bool): 22 | description += ( 23 | f' This market is{" not" if not v else ""} {parse_camel_case(k)}.' 24 | ) 25 | 26 | if k in ["volume", "liquidity"]: 27 | description += f" This market has a current {k} of {v}." 28 | print("\n\ndescription:", description) 29 | 30 | market_object["description"] = description 31 | 32 | return market_object 33 | 34 | 35 | def preprocess_local_json(file_path: str, preprocessor_function: function) -> None: 36 | with open(file_path, "r+") as open_file: 37 | data = json.load(open_file) 38 | 39 | output = [] 40 | for obj in data: 41 | preprocessed_json = preprocessor_function(obj) 42 | output.append(preprocessed_json) 43 | 44 | split_path = file_path.split(".") 45 | new_file_path = split_path[0] + "_preprocessed." + split_path[1] 46 | with open(new_file_path, "w+") as output_file: 47 | json.dump(output, output_file) 48 | 49 | 50 | def metadata_func(record: dict, metadata: dict) -> dict: 51 | print("record:", record) 52 | print("meta:", metadata) 53 | for k, v in record.items(): 54 | metadata[k] = v 55 | 56 | del metadata["description"] 57 | del metadata["events"] 58 | 59 | return metadata 60 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable 6 | # packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 10 | name: 'Dependency review' 11 | on: 12 | pull_request: 13 | branches: [ "main" ] 14 | 15 | # If using a dependency submission action in this workflow this permission will need to be set to: 16 | # 17 | # permissions: 18 | # contents: write 19 | # 20 | # https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api 21 | permissions: 22 | contents: read 23 | # Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option 24 | pull-requests: write 25 | 26 | jobs: 27 | dependency-review: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: 'Checkout repository' 31 | uses: actions/checkout@v4 32 | - name: 'Dependency Review' 33 | uses: actions/dependency-review-action@v4 34 | # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. 35 | with: 36 | comment-summary-in-pr: always 37 | # fail-on-severity: moderate 38 | # deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later 39 | # retry-on-snapshot-warnings: true 40 | -------------------------------------------------------------------------------- /agents/application/creator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from agents.application.executor import Executor as Agent 4 | from agents.polymarket.gamma import GammaMarketClient as Gamma 5 | from agents.polymarket.polymarket import Polymarket 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class Creator: 11 | def __init__(self): 12 | self.polymarket = Polymarket() 13 | self.gamma = Gamma() 14 | self.agent = Agent() 15 | def one_best_market(self): 16 | """ 17 | one_best_trade is a strategy that evaluates all events, markets, and orderbooks 18 | 19 | leverages all available information sources accessible to the autonomous agent 20 | 21 | then executes that trade without any human intervention 22 | 23 | """ 24 | try: 25 | events = self.polymarket.get_all_tradeable_events() 26 | logger.info(f"1. FOUND {len(events)} EVENTS") 27 | 28 | filtered_events = self.agent.filter_events_with_rag(events) 29 | logger.info(f"2. FILTERED {len(filtered_events)} EVENTS") 30 | 31 | markets = self.agent.map_filtered_events_to_markets(filtered_events) 32 | logger.info(f"3. FOUND {len(markets)} MARKETS") 33 | 34 | filtered_markets = self.agent.filter_markets(markets) 35 | logger.info(f"4. FILTERED {len(filtered_markets)} MARKETS") 36 | 37 | best_market = self.agent.source_best_market_to_create(filtered_markets) 38 | logger.info(f"5. IDEA FOR NEW MARKET {best_market}") 39 | return best_market 40 | 41 | except Exception as e: 42 | logger.error(f"Error in one_best_market: {e}", exc_info=True) 43 | logger.info("Retrying...") 44 | self.one_best_market() 45 | 46 | def maintain_positions(self): 47 | pass 48 | 49 | def incentive_farm(self): 50 | pass 51 | if __name__ == "__main__": 52 | c = Creator() 53 | c.one_best_market() 54 | -------------------------------------------------------------------------------- /GITHUB_SETUP_SUMMARY.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | GITHUB REPOSITORY DESCRIPTION & SETUP 3 | ================================================================================ 4 | 5 | 📋 COPY THIS FOR GITHUB REPOSITORY DESCRIPTION: 6 | -------------------------------------------------------------------------------- 7 | 8 | 🤖 AI-powered autonomous trading agents for Polymarket prediction markets. Built with Python 3.12+, LangChain, and modern tooling. Features RAG, superforecasting, and full API integration. 9 | 10 | ================================================================================ 11 | 📌 RECOMMENDED TOPICS/TAGS: 12 | -------------------------------------------------------------------------------- 13 | 14 | polymarket 15 | prediction-markets 16 | ai-trading 17 | trading-bot 18 | langchain 19 | openai 20 | rag 21 | superforecasting 22 | python 23 | fastapi 24 | web3 25 | blockchain 26 | defi 27 | automated-trading 28 | llm 29 | ai-agents 30 | machine-learning 31 | cryptocurrency 32 | ethereum 33 | polygon 34 | 35 | ================================================================================ 36 | ✅ WHAT'S BEEN UPDATED: 37 | -------------------------------------------------------------------------------- 38 | 39 | 1. ✅ README.md - Enhanced with compelling description and modern formatting 40 | 2. ✅ GITHUB_DESCRIPTION.md - Contains all description options 41 | 3. ✅ .github/REPOSITORY_SETUP.md - Complete setup guide 42 | 43 | ================================================================================ 44 | 🚀 QUICK SETUP STEPS: 45 | -------------------------------------------------------------------------------- 46 | 47 | 1. Go to your GitHub repository 48 | 2. Click "Settings" → "General" 49 | 3. Paste the description above into the "Description" field 50 | 4. Scroll to "Topics" and add the tags listed above 51 | 5. Save changes 52 | 53 | ================================================================================ 54 | 55 | -------------------------------------------------------------------------------- /.github/REPOSITORY_SETUP.md: -------------------------------------------------------------------------------- 1 | # GitHub Repository Setup Guide 2 | 3 | ## Repository Description (Short) 4 | Copy this into the GitHub repository description field: 5 | 6 | ``` 7 | 🤖 AI-powered autonomous trading agents for Polymarket prediction markets. Built with Python 3.12+, LangChain, and modern tooling. Features RAG, superforecasting, and full API integration. 8 | ``` 9 | 10 | ## Topics/Tags 11 | Add these topics to your repository (Settings → Topics): 12 | 13 | ``` 14 | polymarket 15 | prediction-markets 16 | ai-trading 17 | trading-bot 18 | langchain 19 | openai 20 | rag 21 | superforecasting 22 | python 23 | fastapi 24 | web3 25 | blockchain 26 | defi 27 | automated-trading 28 | llm 29 | ai-agents 30 | machine-learning 31 | cryptocurrency 32 | ethereum 33 | polygon 34 | ``` 35 | 36 | ## Website URL (Optional) 37 | If you have a website or documentation: 38 | ``` 39 | https://github.com/BlackSky-Jose/PolyMarket-AI-agent-trading 40 | ``` 41 | 42 | ## Social Preview 43 | The README.md already includes a logo image at `docs/images/cli.png`. Make sure this path exists for the social preview. 44 | 45 | ## Repository Settings Recommendations 46 | 47 | ### General Settings 48 | - ✅ **Features**: Enable Issues, Discussions, Projects, Wiki (as needed) 49 | - ✅ **Pull Requests**: Allow merge commits, squash merging, or rebase merging 50 | - ✅ **Archive this repository**: Unchecked (unless archiving) 51 | 52 | ### Security Settings 53 | - ✅ Enable **Dependency graph** 54 | - ✅ Enable **Dependabot alerts** 55 | - ✅ Enable **Dependabot security updates** 56 | - ✅ Enable **Code scanning** (optional, but recommended) 57 | 58 | ### Actions Settings 59 | - ✅ Allow all actions and reusable workflows (or configure as needed) 60 | 61 | ## README Badges 62 | The README already includes badges. Make sure the shield URLs point to your repository: 63 | - Update `contributors-url`, `forks-url`, `stars-url`, `issues-url`, `license-url` if needed 64 | 65 | ## Quick Setup Checklist 66 | - [ ] Update repository description 67 | - [ ] Add topics/tags 68 | - [ ] Verify README displays correctly 69 | - [ ] Set up branch protection rules (optional) 70 | - [ ] Configure GitHub Actions (if using CI/CD) 71 | - [ ] Add repository to GitHub Topics 72 | - [ ] Pin important repositories (if applicable) 73 | 74 | -------------------------------------------------------------------------------- /agents/connectors/news.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import os 3 | 4 | from newsapi import NewsApiClient 5 | 6 | from agents.utils.objects import Article 7 | 8 | 9 | class News: 10 | def __init__(self) -> None: 11 | self.configs = { 12 | "language": "en", 13 | "country": "us", 14 | "top_headlines": "https://newsapi.org/v2/top-headlines?country=us&apiKey=", 15 | "base_url": "https://newsapi.org/v2/", 16 | } 17 | 18 | self.categories = { 19 | "business", 20 | "entertainment", 21 | "general", 22 | "health", 23 | "science", 24 | "sports", 25 | "technology", 26 | } 27 | 28 | self.API = NewsApiClient(os.getenv("NEWSAPI_API_KEY")) 29 | 30 | def get_articles_for_cli_keywords(self, keywords) -> "list[Article]": 31 | query_words = keywords.split(",") 32 | all_articles = self.get_articles_for_options(query_words) 33 | article_objects: list[Article] = [] 34 | for _, articles in all_articles.items(): 35 | for article in articles: 36 | article_objects.append(Article(**article)) 37 | return article_objects 38 | 39 | def get_top_articles_for_market(self, market_object: dict) -> "list[Article]": 40 | return self.API.get_top_headlines( 41 | language="en", country="usa", q=market_object["description"] 42 | ) 43 | 44 | def get_articles_for_options( 45 | self, 46 | market_options: "list[str]", 47 | date_start: datetime = None, 48 | date_end: datetime = None, 49 | ) -> "list[Article]": 50 | 51 | all_articles = {} 52 | # Default to top articles if no start and end dates are given for search 53 | if not date_start and not date_end: 54 | for option in market_options: 55 | response_dict = self.API.get_top_headlines( 56 | q=option.strip(), 57 | language=self.configs["language"], 58 | country=self.configs["country"], 59 | ) 60 | articles = response_dict["articles"] 61 | all_articles[option] = articles 62 | else: 63 | for option in market_options: 64 | response_dict = self.API.get_everything( 65 | q=option.strip(), 66 | language=self.configs["language"], 67 | country=self.configs["country"], 68 | from_param=date_start, 69 | to=date_end, 70 | ) 71 | articles = response_dict["articles"] 72 | all_articles[option] = articles 73 | 74 | return all_articles 75 | 76 | def get_category(self, market_object: dict) -> str: 77 | news_category = "general" 78 | market_category = market_object["category"] 79 | if market_category in self.categories: 80 | news_category = market_category 81 | return news_category 82 | -------------------------------------------------------------------------------- /agents/application/trade.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import shutil 3 | from pathlib import Path 4 | 5 | from agents.application.executor import Executor as Agent 6 | from agents.polymarket.gamma import GammaMarketClient as Gamma 7 | from agents.polymarket.polymarket import Polymarket 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class Trader: 13 | def __init__(self): 14 | self.polymarket = Polymarket() 15 | self.gamma = Gamma() 16 | self.agent = Agent() 17 | 18 | def pre_trade_logic(self) -> None: 19 | self.clear_local_dbs() 20 | 21 | def clear_local_dbs(self) -> None: 22 | """Clear local database directories.""" 23 | for db_dir in ["local_db_events", "local_db_markets"]: 24 | db_path = Path(db_dir) 25 | if db_path.exists(): 26 | try: 27 | shutil.rmtree(db_path) 28 | logger.info(f"Cleared {db_dir}") 29 | except OSError as e: 30 | logger.warning(f"Failed to clear {db_dir}: {e}") 31 | 32 | def one_best_trade(self) -> None: 33 | """ 34 | 35 | one_best_trade is a strategy that evaluates all events, markets, and orderbooks 36 | 37 | leverages all available information sources accessible to the autonomous agent 38 | 39 | then executes that trade without any human intervention 40 | 41 | """ 42 | try: 43 | self.pre_trade_logic() 44 | 45 | events = self.polymarket.get_all_tradeable_events() 46 | logger.info(f"1. FOUND {len(events)} EVENTS") 47 | 48 | filtered_events = self.agent.filter_events_with_rag(events) 49 | logger.info(f"2. FILTERED {len(filtered_events)} EVENTS") 50 | 51 | markets = self.agent.map_filtered_events_to_markets(filtered_events) 52 | logger.info(f"3. FOUND {len(markets)} MARKETS") 53 | 54 | filtered_markets = self.agent.filter_markets(markets) 55 | logger.info(f"4. FILTERED {len(filtered_markets)} MARKETS") 56 | 57 | if not filtered_markets: 58 | logger.warning("No markets found after filtering") 59 | return 60 | 61 | market = filtered_markets[0] 62 | best_trade = self.agent.source_best_trade(market) 63 | logger.info(f"5. CALCULATED TRADE {best_trade}") 64 | 65 | amount = self.agent.format_trade_prompt_for_execution(best_trade) 66 | # Please refer to TOS before uncommenting: polymarket.com/tos 67 | # trade = self.polymarket.execute_market_order(market, amount) 68 | # logger.info(f"6. TRADED {trade}") 69 | 70 | except Exception as e: 71 | logger.error(f"Error in one_best_trade: {e}", exc_info=True) 72 | logger.info("Retrying...") 73 | self.one_best_trade() 74 | 75 | def maintain_positions(self): 76 | pass 77 | 78 | def incentive_farm(self): 79 | pass 80 | 81 | 82 | if __name__ == "__main__": 83 | t = Trader() 84 | t.one_best_trade() 85 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=68.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "poly-ai-trading-agent" 7 | version = "2.0.0" 8 | description = "AI trading agents for Polymarket prediction markets" 9 | readme = "README.md" 10 | requires-python = ">=3.12" 11 | license = {text = "MIT"} 12 | authors = [ 13 | {name = "Polymarket Agents Contributors"} 14 | ] 15 | keywords = ["polymarket", "trading", "ai", "prediction-markets", "blockchain"] 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Intended Audience :: Developers", 19 | "License :: OSI Approved :: MIT License", 20 | "Programming Language :: Python :: 3", 21 | "Programming Language :: Python :: 3.12", 22 | "Topic :: Office/Business :: Financial :: Investment", 23 | ] 24 | 25 | dependencies = [ 26 | "aiohttp>=3.11.0", 27 | "chromadb>=0.5.0", 28 | "fastapi>=0.115.0", 29 | "httpx>=0.27.0", 30 | "langchain>=0.3.0", 31 | "langchain-chroma>=0.1.2", 32 | "langchain-community>=0.3.0", 33 | "langchain-core>=0.3.0", 34 | "langchain-openai>=0.2.0", 35 | "langchain-text-splitters>=0.3.0", 36 | "langgraph>=0.2.0", 37 | "langsmith>=0.2.0", 38 | "newsapi-python>=0.2.7", 39 | "openai>=1.54.0", 40 | "pydantic>=2.9.0", 41 | "python-dotenv>=1.0.1", 42 | "py-clob-client>=0.17.5", 43 | "py-order-utils>=0.3.2", 44 | "tavily-python>=0.5.0", 45 | "typer>=0.12.0", 46 | "uvicorn[standard]>=0.32.0", 47 | "web3>=6.20.0", 48 | ] 49 | 50 | [project.optional-dependencies] 51 | dev = [ 52 | "ruff>=0.6.0", 53 | "mypy>=1.11.0", 54 | "pytest>=8.3.0", 55 | "pytest-asyncio>=0.24.0", 56 | "pre-commit>=3.8.0", 57 | "types-requests>=2.32.0", 58 | ] 59 | 60 | [project.scripts] 61 | poly-agent = "scripts.python.cli:app" 62 | 63 | [tool.ruff] 64 | target-version = "py312" 65 | line-length = 100 66 | select = [ 67 | "E", # pycodestyle errors 68 | "W", # pycodestyle warnings 69 | "F", # pyflakes 70 | "I", # isort 71 | "B", # flake8-bugbear 72 | "C4", # flake8-comprehensions 73 | "UP", # pyupgrade 74 | "ARG", # flake8-unused-arguments 75 | "SIM", # flake8-simplify 76 | ] 77 | ignore = [ 78 | "E501", # line too long (handled by formatter) 79 | "B008", # do not perform function calls in argument defaults 80 | "ARG001", # unused function argument (common in overrides) 81 | ] 82 | 83 | [tool.ruff.per-file-ignores] 84 | "__init__.py" = ["F401"] 85 | "tests/*" = ["ARG", "S101"] 86 | 87 | [tool.ruff.isort] 88 | known-first-party = ["agents", "scripts"] 89 | 90 | [tool.mypy] 91 | python_version = "3.12" 92 | warn_return_any = true 93 | warn_unused_configs = true 94 | disallow_untyped_defs = false 95 | disallow_incomplete_defs = false 96 | check_untyped_defs = true 97 | no_implicit_optional = true 98 | warn_redundant_casts = true 99 | warn_unused_ignores = true 100 | warn_no_return = true 101 | strict_optional = true 102 | ignore_missing_imports = true 103 | 104 | [[tool.mypy.overrides]] 105 | module = [ 106 | "chromadb.*", 107 | "langchain.*", 108 | "langchain_community.*", 109 | "langchain_core.*", 110 | "langchain_openai.*", 111 | "newsapi.*", 112 | "py_clob_client.*", 113 | "py_order_utils.*", 114 | "tavily.*", 115 | ] 116 | ignore_missing_imports = true 117 | 118 | [tool.pytest.ini_options] 119 | testpaths = ["tests"] 120 | python_files = ["test_*.py"] 121 | python_classes = ["Test*"] 122 | python_functions = ["test_*"] 123 | asyncio_mode = "auto" 124 | 125 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohappyeyeballs==2.3.4 2 | aiohttp==3.10.0 3 | aiosignal==1.3.1 4 | annotated-types==0.7.0 5 | anyio==4.4.0 6 | asgiref==3.8.1 7 | asttokens==2.4.1 8 | async-timeout==4.0.3 9 | attrs==23.2.0 10 | backoff==2.2.1 11 | bcrypt==4.2.0 12 | bitarray==2.9.2 13 | build==1.2.1 14 | cachetools==5.4.0 15 | certifi==2024.7.4 16 | cfgv==3.4.0 17 | charset-normalizer==3.3.2 18 | chroma-hnswlib==0.7.6 19 | chromadb==0.5.5 20 | ckzg==1.0.2 21 | click==8.1.7 22 | coloredlogs==15.0.1 23 | cytoolz==0.12.3 24 | dataclasses-json==0.6.7 25 | Deprecated==1.2.14 26 | devtools==0.12.2 27 | distlib==0.3.8 28 | distro==1.9.0 29 | dnspython==2.6.1 30 | eip712-structs==1.1.0 31 | email_validator==2.2.0 32 | eth-account==0.13.1 33 | eth-hash==0.7.0 34 | eth-keyfile==0.8.1 35 | eth-keys==0.5.1 36 | eth-rlp==2.1.0 37 | eth-typing==4.4.0 38 | eth-utils==4.1.1 39 | eth_abi==5.1.0 40 | exceptiongroup==1.2.2 41 | executing==2.0.1 42 | fastapi==0.111.0 43 | fastapi-cli==0.0.4 44 | filelock==3.15.4 45 | flatbuffers==24.3.25 46 | frozenlist==1.4.1 47 | fsspec==2024.6.1 48 | google-auth==2.32.0 49 | googleapis-common-protos==1.63.2 50 | grpcio==1.65.2 51 | h11==0.14.0 52 | hexbytes==1.2.1 53 | httpcore==1.0.5 54 | httptools==0.6.1 55 | httpx==0.27.0 56 | huggingface-hub==0.24.5 57 | humanfriendly==10.0 58 | identify==2.6.0 59 | idna==3.7 60 | importlib_metadata==8.0.0 61 | importlib_resources==6.4.0 62 | iniconfig==2.0.0 63 | Jinja2==3.1.4 64 | jq==1.7.0 65 | jsonpatch==1.33 66 | jsonpointer==3.0.0 67 | jsonschema==4.23.0 68 | jsonschema-specifications==2023.12.1 69 | kubernetes==30.1.0 70 | langchain==0.2.11 71 | langchain-chroma==0.1.2 72 | langchain-community==0.2.10 73 | langchain-core==0.2.26 74 | langchain-openai==0.1.19 75 | langchain-text-splitters==0.2.2 76 | langchainhub==0.1.20 77 | langgraph==0.1.17 78 | langsmith==0.1.94 79 | lru-dict==1.3.0 80 | markdown-it-py==3.0.0 81 | MarkupSafe==2.1.5 82 | marshmallow==3.21.3 83 | mdurl==0.1.2 84 | mmh3==4.1.0 85 | monotonic==1.6 86 | mpmath==1.3.0 87 | multidict==6.0.5 88 | mypy-extensions==1.0.0 89 | newsapi-python==0.2.7 90 | nodeenv==1.9.1 91 | numpy==1.26.4 92 | oauthlib==3.2.2 93 | onnxruntime==1.18.1 94 | openai==1.37.1 95 | opentelemetry-api==1.26.0 96 | opentelemetry-exporter-otlp-proto-common==1.26.0 97 | opentelemetry-exporter-otlp-proto-grpc==1.26.0 98 | opentelemetry-instrumentation==0.47b0 99 | opentelemetry-instrumentation-asgi==0.47b0 100 | opentelemetry-instrumentation-fastapi==0.47b0 101 | opentelemetry-proto==1.26.0 102 | opentelemetry-sdk==1.26.0 103 | opentelemetry-semantic-conventions==0.47b0 104 | opentelemetry-util-http==0.47b0 105 | orjson==3.10.6 106 | overrides==7.7.0 107 | packaging==24.1 108 | parsimonious==0.10.0 109 | platformdirs==4.2.2 110 | pluggy==1.5.0 111 | poly_eip712_structs==0.0.1 112 | posthog==3.5.0 113 | pre-commit==3.8.0 114 | protobuf==4.25.4 115 | py_clob_client==0.17.5 116 | py_order_utils==0.3.2 117 | pyasn1==0.6.0 118 | pyasn1_modules==0.4.0 119 | pycryptodome==3.20.0 120 | pydantic==2.8.2 121 | pydantic_core==2.20.1 122 | Pygments==2.18.0 123 | PyPika==0.48.9 124 | pyproject_hooks==1.1.0 125 | pysha3==1.0.2 126 | pytest==8.3.2 127 | python-dateutil==2.9.0.post0 128 | python-dotenv==1.0.1 129 | python-multipart==0.0.9 130 | pyunormalize==15.1.0 131 | PyYAML==6.0.1 132 | referencing==0.35.1 133 | regex==2024.7.24 134 | requests==2.32.3 135 | requests-oauthlib==2.0.0 136 | rich==13.7.1 137 | rlp==4.0.1 138 | rpds-py==0.19.1 139 | rsa==4.9 140 | scheduler==0.8.7 141 | shellingham==1.5.4 142 | six==1.16.0 143 | sniffio==1.3.1 144 | SQLAlchemy==2.0.31 145 | starlette==0.37.2 146 | sympy==1.13.1 147 | tavily-python==0.3.5 148 | tenacity==8.5.0 149 | tiktoken==0.7.0 150 | tokenizers==0.19.1 151 | tomli==2.0.1 152 | toolz==0.12.1 153 | tqdm==4.66.4 154 | typeguard==4.3.0 155 | typer==0.12.3 156 | types-requests==2.32.0.20240712 157 | typing-inspect==0.9.0 158 | typing_extensions==4.12.2 159 | ujson==5.10.0 160 | urllib3==2.2.2 161 | uvicorn==0.30.3 162 | uvloop==0.19.0 163 | virtualenv==20.26.3 164 | watchfiles==0.22.0 165 | web3==6.11.0 166 | websocket-client==1.8.0 167 | websockets==12.0 168 | wrapt==1.16.0 169 | yarl==1.9.4 170 | zipp==3.19.2 171 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ### Welcome and Thanks! 4 | 5 | > First off, thank you for considering contributing to our Decentralized AI Agent for Polymarket! It's contributors like you that help make this project a powerful tool for prediction markets. 6 | 7 | ### Why Follow These Guidelines? 8 | 9 | > Following these guidelines shows that you respect the time and effort of the developers working on this project. In return, we’ll address your contributions efficiently, review your changes, and help you finalize your pull requests. 10 | 11 | ### Types of Contributions We Welcome 12 | 13 | We value all kinds of contributions! Whether it’s improving our documentation, triaging bugs, or enhancing our algorithms, your contributions are greatly appreciated. 14 | 15 | > This project is open source, and we’re thrilled to receive contributions from the community. You can help by writing tutorials, reporting bugs, suggesting new features, or contributing code to improve our AI agent. 16 | 17 | ### Contributions We Do Not Seek 18 | 19 | To streamline the process, please avoid these types of contributions: 20 | 21 | > Please do not use the issue tracker for general support questions. For help, consider joining our the Polymarket Discord or referring to the [Polymarket API documentation](https://polymarket.com/docs). 22 | 23 | # Ground Rules 24 | 25 | ### Setting Expectations 26 | 27 | We ask that all contributors adhere to the following standards: 28 | 29 | > Responsibilities 30 | > * Ensure compatibility with multiple environments where our AI agent operates. 31 | > * Create issues for significant changes and gather community feedback. 32 | > * Keep changes modular and focused. 33 | > * Foster an inclusive environment and welcome contributors from diverse backgrounds. 34 | 35 | # Your First Contribution 36 | 37 | ### Getting Started 38 | 39 | New to contributing? Here’s how you can start: 40 | 41 | > Begin by exploring our beginner-friendly issues, which are tagged as [good first issue](#). These are smaller tasks that are great for newcomers. 42 | 43 | ### Bonus: Helpful Resources 44 | 45 | Here are some tutorials to guide you through your first open source contribution: 46 | 47 | > New to open source? Check out [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 48 | 49 | ### Next Steps? 50 | 51 | > At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat: 52 | > 53 | > If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge. 54 | 55 | # Getting Started 56 | 57 | ### Submitting Your Contribution 58 | 59 | Follow these steps to submit your contributions: 60 | 61 | * Fork the repository and make changes in your fork. 62 | * Ensure your code follows our [style guide](#) and passes all tests. 63 | * Sign the Contributor License Agreement (CLA), if required. 64 | * Submit a pull request with a detailed description of your changes. 65 | 66 | > For significant changes: 67 | > 1. Fork the repo and make your changes. 68 | > 2. Ensure your changes align with the project’s coding standards. 69 | > 3. Sign the Contributor License Agreement if applicable. 70 | > 4. Open a pull request and describe the changes you’ve made. 71 | 72 | ### For Small or “Obvious” Fixes 73 | 74 | For minor fixes, such as typos or documentation improvements, you can: 75 | 76 | > Submit a simple patch without a CLA if the change does not involve significant new functionality. Examples include: 77 | > * Typographical corrections 78 | > * Simple formatting changes 79 | > * Non-functional code adjustments 80 | 81 | # How to Report a Bug 82 | 83 | ### Security Disclosures 84 | 85 | If you discover a security issue, please report it privately: 86 | 87 | > If you find a security vulnerability, do NOT open an issue. Instead, email Polymarket directly. 88 | 89 | ### Filing a Bug Report 90 | 91 | When reporting a bug, please provide the following details: 92 | 93 | > When filing a bug, include: 94 | > 1. The version of the AI agent you are using. 95 | > 2. Your operating system and environment. 96 | > 3. Steps to reproduce the issue. 97 | > 4. Expected behavior. 98 | > 5. Actual behavior observed. 99 | 100 | # How to Suggest a Feature or Enhancement 101 | 102 | ### Project Goals and Philosophy 103 | 104 | Before suggesting a feature, understand our project goals: 105 | 106 | > Our AI agent aims to leverage decentralized mechanisms to provide accurate predictions within Polymarket's markets. We are focused on enhancing market analysis and prediction accuracy. 107 | 108 | ### Suggesting a Feature 109 | 110 | If you have an idea for a new feature: 111 | 112 | > Open an issue on GitHub with a detailed description of the feature, including its purpose, benefits, and any proposed implementation details. 113 | 114 | # Code Review Process 115 | 116 | ### Review and Acceptance 117 | 118 | Our review process is as follows: 119 | 120 | > The core team must approve the PR. 121 | -------------------------------------------------------------------------------- /agents/connectors/chroma.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | 5 | from langchain_openai import OpenAIEmbeddings 6 | from langchain_community.document_loaders import JSONLoader 7 | from langchain_community.vectorstores.chroma import Chroma 8 | 9 | from agents.polymarket.gamma import GammaMarketClient 10 | from agents.utils.objects import SimpleEvent, SimpleMarket 11 | 12 | 13 | class PolymarketRAG: 14 | def __init__(self, local_db_directory=None, embedding_function=None) -> None: 15 | self.gamma_client = GammaMarketClient() 16 | self.local_db_directory = local_db_directory 17 | self.embedding_function = embedding_function 18 | 19 | def load_json_from_local( 20 | self, json_file_path=None, vector_db_directory="./local_db" 21 | ) -> None: 22 | loader = JSONLoader( 23 | file_path=json_file_path, jq_schema=".[].description", text_content=False 24 | ) 25 | loaded_docs = loader.load() 26 | 27 | embedding_function = OpenAIEmbeddings(model="text-embedding-3-small") 28 | Chroma.from_documents( 29 | loaded_docs, embedding_function, persist_directory=vector_db_directory 30 | ) 31 | 32 | def create_local_markets_rag(self, local_directory="./local_db") -> None: 33 | all_markets = self.gamma_client.get_all_current_markets() 34 | 35 | if not os.path.isdir(local_directory): 36 | os.mkdir(local_directory) 37 | 38 | local_file_path = f"{local_directory}/all-current-markets_{time.time()}.json" 39 | 40 | with open(local_file_path, "w+") as output_file: 41 | json.dump(all_markets, output_file) 42 | 43 | self.load_json_from_local( 44 | json_file_path=local_file_path, vector_db_directory=local_directory 45 | ) 46 | 47 | def query_local_markets_rag( 48 | self, local_directory=None, query=None 49 | ) -> "list[tuple]": 50 | embedding_function = OpenAIEmbeddings(model="text-embedding-3-small") 51 | local_db = Chroma( 52 | persist_directory=local_directory, embedding_function=embedding_function 53 | ) 54 | response_docs = local_db.similarity_search_with_score(query=query) 55 | return response_docs 56 | 57 | def events(self, events: "list[SimpleEvent]", prompt: str) -> "list[tuple]": 58 | # create local json file 59 | local_events_directory: str = "./local_db_events" 60 | if not os.path.isdir(local_events_directory): 61 | os.mkdir(local_events_directory) 62 | local_file_path = f"{local_events_directory}/events.json" 63 | dict_events = [x.dict() for x in events] 64 | with open(local_file_path, "w+") as output_file: 65 | json.dump(dict_events, output_file) 66 | 67 | # create vector db 68 | def metadata_func(record: dict, metadata: dict) -> dict: 69 | 70 | metadata["id"] = record.get("id") 71 | metadata["markets"] = record.get("markets") 72 | 73 | return metadata 74 | 75 | loader = JSONLoader( 76 | file_path=local_file_path, 77 | jq_schema=".[]", 78 | content_key="description", 79 | text_content=False, 80 | metadata_func=metadata_func, 81 | ) 82 | loaded_docs = loader.load() 83 | embedding_function = OpenAIEmbeddings(model="text-embedding-3-small") 84 | vector_db_directory = f"{local_events_directory}/chroma" 85 | local_db = Chroma.from_documents( 86 | loaded_docs, embedding_function, persist_directory=vector_db_directory 87 | ) 88 | 89 | # query 90 | return local_db.similarity_search_with_score(query=prompt) 91 | 92 | def markets(self, markets: "list[SimpleMarket]", prompt: str) -> "list[tuple]": 93 | # create local json file 94 | local_events_directory: str = "./local_db_markets" 95 | if not os.path.isdir(local_events_directory): 96 | os.mkdir(local_events_directory) 97 | local_file_path = f"{local_events_directory}/markets.json" 98 | with open(local_file_path, "w+") as output_file: 99 | json.dump(markets, output_file) 100 | 101 | # create vector db 102 | def metadata_func(record: dict, metadata: dict) -> dict: 103 | 104 | metadata["id"] = record.get("id") 105 | metadata["outcomes"] = record.get("outcomes") 106 | metadata["outcome_prices"] = record.get("outcome_prices") 107 | metadata["question"] = record.get("question") 108 | metadata["clob_token_ids"] = record.get("clob_token_ids") 109 | 110 | return metadata 111 | 112 | loader = JSONLoader( 113 | file_path=local_file_path, 114 | jq_schema=".[]", 115 | content_key="description", 116 | text_content=False, 117 | metadata_func=metadata_func, 118 | ) 119 | loaded_docs = loader.load() 120 | embedding_function = OpenAIEmbeddings(model="text-embedding-3-small") 121 | vector_db_directory = f"{local_events_directory}/chroma" 122 | local_db = Chroma.from_documents( 123 | loaded_docs, embedding_function, persist_directory=vector_db_directory 124 | ) 125 | 126 | # query 127 | return local_db.similarity_search_with_score(query=prompt) 128 | -------------------------------------------------------------------------------- /scripts/python/cli.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | from typing import Optional 4 | 5 | import typer 6 | from rich.console import Console 7 | from rich.pretty import pprint 8 | 9 | from agents.polymarket.polymarket import Polymarket 10 | from agents.connectors.chroma import PolymarketRAG 11 | from agents.connectors.news import News 12 | from agents.application.trade import Trader 13 | from agents.application.executor import Executor 14 | from agents.application.creator import Creator 15 | 16 | # Configure logging 17 | logging.basicConfig( 18 | level=logging.INFO, 19 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 20 | handlers=[logging.StreamHandler(sys.stdout)] 21 | ) 22 | 23 | app = typer.Typer(help="Polymarket AI Trading Agent CLI") 24 | console = Console() 25 | 26 | # Initialize clients lazily to avoid import-time errors 27 | _polymarket: Optional[Polymarket] = None 28 | _newsapi_client: Optional[News] = None 29 | _polymarket_rag: Optional[PolymarketRAG] = None 30 | 31 | 32 | def get_polymarket() -> Polymarket: 33 | global _polymarket 34 | if _polymarket is None: 35 | _polymarket = Polymarket() 36 | return _polymarket 37 | 38 | 39 | def get_news() -> News: 40 | global _newsapi_client 41 | if _newsapi_client is None: 42 | _newsapi_client = News() 43 | return _newsapi_client 44 | 45 | 46 | def get_rag() -> PolymarketRAG: 47 | global _polymarket_rag 48 | if _polymarket_rag is None: 49 | _polymarket_rag = PolymarketRAG() 50 | return _polymarket_rag 51 | @app.command() 52 | def get_all_markets(limit: int = 5, sort_by: str = "spread") -> None: 53 | """ 54 | Query Polymarket's markets 55 | """ 56 | console.print(f"[cyan]Fetching {limit} markets, sorted by {sort_by}[/cyan]") 57 | pm = get_polymarket() 58 | markets = pm.get_all_markets() 59 | markets = pm.filter_markets_for_trading(markets) 60 | if sort_by == "spread": 61 | markets = sorted(markets, key=lambda x: x.spread, reverse=True) 62 | markets = markets[:limit] 63 | console.print("[green]Markets:[/green]") 64 | pprint(markets) 65 | 66 | 67 | @app.command() 68 | def get_trending_markets(limit: int = 10) -> None: 69 | """ 70 | Get trending markets sorted by 24-hour volume 71 | """ 72 | console.print(f"[cyan]Fetching {limit} trending markets (sorted by 24h volume)[/cyan]") 73 | pm = get_polymarket() 74 | markets = pm.get_trending_markets(limit=limit) 75 | console.print(f"[green]Found {len(markets)} trending markets:[/green]") 76 | pprint(markets) 77 | 78 | 79 | @app.command() 80 | def get_relevant_news(keywords: str) -> None: 81 | """ 82 | Use NewsAPI to query the internet 83 | """ 84 | console.print(f"[cyan]Fetching news for keywords: {keywords}[/cyan]") 85 | news = get_news() 86 | articles = news.get_articles_for_cli_keywords(keywords) 87 | console.print(f"[green]Found {len(articles)} articles:[/green]") 88 | pprint(articles) 89 | 90 | 91 | @app.command() 92 | def get_all_events(limit: int = 5, sort_by: str = "number_of_markets") -> None: 93 | """ 94 | Query Polymarket's events 95 | """ 96 | console.print(f"[cyan]Fetching {limit} events, sorted by {sort_by}[/cyan]") 97 | pm = get_polymarket() 98 | events = pm.get_all_events() 99 | events = pm.filter_events_for_trading(events) 100 | if sort_by == "number_of_markets": 101 | events = sorted(events, key=lambda x: len(x.markets.split(",")), reverse=True) 102 | events = events[:limit] 103 | console.print(f"[green]Found {len(events)} events:[/green]") 104 | pprint(events) 105 | 106 | 107 | @app.command() 108 | def create_local_markets_rag(local_directory: str) -> None: 109 | """ 110 | Create a local markets database for RAG 111 | """ 112 | console.print(f"[cyan]Creating local RAG database in {local_directory}[/cyan]") 113 | rag = get_rag() 114 | rag.create_local_markets_rag(local_directory=local_directory) 115 | console.print("[green]RAG database created successfully![/green]") 116 | 117 | 118 | @app.command() 119 | def query_local_markets_rag(vector_db_directory: str, query: str) -> None: 120 | """ 121 | RAG over a local database of Polymarket's events 122 | """ 123 | console.print(f"[cyan]Querying RAG database: {query}[/cyan]") 124 | rag = get_rag() 125 | response = rag.query_local_markets_rag( 126 | local_directory=vector_db_directory, query=query 127 | ) 128 | console.print("[green]RAG Results:[/green]") 129 | pprint(response) 130 | 131 | 132 | @app.command() 133 | def ask_superforecaster(event_title: str, market_question: str, outcome: str) -> None: 134 | """ 135 | Ask a superforecaster about a trade 136 | """ 137 | console.print(f"[cyan]Event: {event_title}[/cyan]") 138 | console.print(f"[cyan]Question: {market_question}[/cyan]") 139 | console.print(f"[cyan]Outcome: {outcome}[/cyan]") 140 | executor = Executor() 141 | response = executor.get_superforecast( 142 | event_title=event_title, market_question=market_question, outcome=outcome 143 | ) 144 | console.print("[green]Superforecaster Response:[/green]") 145 | console.print(response) 146 | 147 | 148 | @app.command() 149 | def create_market() -> None: 150 | """ 151 | Format a request to create a market on Polymarket 152 | """ 153 | console.print("[cyan]Generating market idea...[/cyan]") 154 | c = Creator() 155 | market_description = c.one_best_market() 156 | console.print("[green]Market Description:[/green]") 157 | console.print(market_description) 158 | 159 | 160 | @app.command() 161 | def ask_llm(user_input: str) -> None: 162 | """ 163 | Ask a question to the LLM and get a response. 164 | """ 165 | console.print(f"[cyan]Querying LLM: {user_input}[/cyan]") 166 | executor = Executor() 167 | response = executor.get_llm_response(user_input) 168 | console.print("[green]LLM Response:[/green]") 169 | console.print(response) 170 | 171 | 172 | @app.command() 173 | def ask_polymarket_llm(user_input: str) -> None: 174 | """ 175 | What types of markets do you want trade? 176 | """ 177 | console.print(f"[cyan]Querying Polymarket LLM: {user_input}[/cyan]") 178 | executor = Executor() 179 | response = executor.get_polymarket_llm(user_input=user_input) 180 | console.print("[green]LLM + current markets&events response:[/green]") 181 | console.print(response) 182 | 183 | 184 | @app.command() 185 | def run_autonomous_trader() -> None: 186 | """ 187 | Let an autonomous system trade for you. 188 | """ 189 | console.print("[yellow]⚠️ Starting autonomous trader...[/yellow]") 190 | console.print("[yellow]⚠️ Please review Terms of Service: https://polymarket.com/tos[/yellow]") 191 | trader = Trader() 192 | trader.one_best_trade() 193 | console.print("[green]Autonomous trading completed![/green]") 194 | 195 | 196 | if __name__ == "__main__": 197 | app() 198 | -------------------------------------------------------------------------------- /docs/EXAMPLE.md: -------------------------------------------------------------------------------- 1 | (.venv) $ python -i application/trade.py 2 | 3 | 1. FOUND 27 EVENTS 4 | 5 | ... prompting ... You are an AI assistant for analyzing prediction markets. 6 | You will be provided with json output for api data from Polymarket. 7 | Polymarket is an online prediction market that lets users bet on the outcome of future events in a wide range of topics, like sports, politics, and pop culture. 8 | Get accurate real-time probabilities of the events that matter most to you. 9 | 10 | Filter these events for the ones you will be best at trading on profitably. 11 | 12 | 13 | 14 | 2. FILTERED 4 EVENTS 15 | https://gamma-api.polymarket.com/markets/500715 16 | https://gamma-api.polymarket.com/markets/500716 17 | https://gamma-api.polymarket.com/markets/500717 18 | https://gamma-api.polymarket.com/markets/500344 19 | https://gamma-api.polymarket.com/markets/500924 20 | https://gamma-api.polymarket.com/markets/500925 21 | 22 | 3. FOUND 6 MARKETS 23 | 24 | 25 | ... prompting ... You are an AI assistant for analyzing prediction markets. 26 | You will be provided with json output for api data from Polymarket. 27 | Polymarket is an online prediction market that lets users bet on the outcome of future events in a wide range of topics, like sports, politics, and pop culture. 28 | Get accurate real-time probabilities of the events that matter most to you. 29 | 30 | Filter these markets for the ones you will be best at trading on profitably. 31 | 32 | 33 | 34 | 4. FILTERED 4 MARKETS 35 | 36 | ... prompting ... 37 | You are a Superforecaster tasked with correctly predicting the likelihood of events. 38 | Use the following systematic process to develop an accurate prediction for the following 39 | question=`Court temporarily allows Texas to arrest migrants?` and description=`On March 20 a 3-panel judge heard arguments on whether Texas should temporarily be permitted to enforce its immigration law which allows state officials to arrest people they suspect of entering the country illegally. 40 | 41 | This market will resolve to "Yes" if the SB4 Texas immigration law is permitted to go into effect by the 3 judge panel of the US 5th Circuit Court of Appeals before the court has officially ruled on the law's legality. Otherwise this market will resolve to "No". 42 | 43 | If no ruling is issued by the 3-panel judge before the appeal process starts (currently scheduled for April 3), this market will resolve to "No." 44 | 45 | The primary resolution source for this market will be official information from the US government, however a consensus of credible reporting will also be used.` combination. 46 | 47 | Here are the key steps to use in your analysis: 48 | 49 | 1. Breaking Down the Question: 50 | - Decompose the question into smaller, more manageable parts. 51 | - Identify the key components that need to be addressed to answer the question. 52 | 2. Gathering Information: 53 | - Seek out diverse sources of information. 54 | - Look for both quantitative data and qualitative insights. 55 | - Stay updated on relevant news and expert analyses. 56 | 3. Consider Base Rates: 57 | - Use statistical baselines or historical averages as a starting point. 58 | - Compare the current situation to similar past events to establish a benchmark probability. 59 | 4. Identify and Evaluate Factors: 60 | - List factors that could influence the outcome. 61 | - Assess the impact of each factor, considering both positive and negative influences. 62 | - Use evidence to weigh these factors, avoiding over-reliance on any single piece of information. 63 | 5. Think Probabilistically: 64 | - Express predictions in terms of probabilities rather than certainties. 65 | - Assign likelihoods to different outcomes and avoid binary thinking. 66 | - Embrace uncertainty and recognize that all forecasts are probabilistic in nature. 67 | 68 | Given these steps produce a statement on the probability of outcome=`['Yes', 'No']` occurring. 69 | 70 | Give your response in the following format: 71 | 72 | I believe Court temporarily allows Texas to arrest migrants? has a likelihood `` for outcome of ``. 73 | 74 | 75 | result: I believe Court temporarily allows Texas to arrest migrants? has a likelihood `0.3` for outcome of `Yes`. 76 | 77 | ... prompting ... You are an AI assistant for analyzing prediction markets. 78 | You will be provided with json output for api data from Polymarket. 79 | Polymarket is an online prediction market that lets users bet on the outcome of future events in a wide range of topics, like sports, politics, and pop culture. 80 | Get accurate real-time probabilities of the events that matter most to you. 81 | 82 | Imagine yourself as the top trader on Polymarket, dominating the world of information markets with your keen insights and strategic acumen. You have an extraordinary ability to analyze and interpret data from diverse sources, turning complex information into profitable trading opportunities. 83 | You excel in predicting the outcomes of global events, from political elections to economic developments, using a combination of data analysis and intuition. Your deep understanding of probability and statistics allows you to assess market sentiment and make informed decisions quickly. 84 | Every day, you approach Polymarket with a disciplined strategy, identifying undervalued opportunities and managing your portfolio with precision. You are adept at evaluating the credibility of information and filtering out noise, ensuring that your trades are based on reliable data. 85 | Your adaptability is your greatest asset, enabling you to thrive in a rapidly changing environment. You leverage cutting-edge technology and tools to gain an edge over other traders, constantly seeking innovative ways to enhance your strategies. 86 | In your journey on Polymarket, you are committed to continuous learning, staying informed about the latest trends and developments in various sectors. Your emotional intelligence empowers you to remain composed under pressure, making rational decisions even when the stakes are high. 87 | Visualize yourself consistently achieving outstanding returns, earning recognition as the top trader on Polymarket. You inspire others with your success, setting new standards of excellence in the world of information markets. 88 | 89 | 90 | 91 | You made the following prediction for a market: I believe Court temporarily allows Texas to arrest migrants? has a likelihood `0.3` for outcome of `Yes`. 92 | 93 | The current outcomes $['Yes', 'No'] prices are: $['0.17', '0.83'] 94 | 95 | Given your prediction, respond with a genius trade in the format: 96 | ` 97 | price:'price_on_the_orderbook', 98 | size:'percentage_of_total_funds', 99 | side: BUY or SELL, 100 | ` 101 | 102 | Your trade should approximate price using the likelihood in your prediction. 103 | 104 | Example response: 105 | 106 | RESPONSE``` 107 | price:0.5, 108 | size:0.1, 109 | side:BUY, 110 | ``` 111 | 112 | 113 | 114 | result: ``` 115 | price:0.3, 116 | size:0.2, 117 | side: BUY, 118 | ``` 119 | 120 | 5. CALCULATED TRADE ``` 121 | price:0.3, 122 | size:0.2, 123 | side: BUY, 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /agents/utils/objects.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import Optional, Union 3 | from pydantic import BaseModel 4 | 5 | 6 | class Trade(BaseModel): 7 | id: int 8 | taker_order_id: str 9 | market: str 10 | asset_id: str 11 | side: str 12 | size: str 13 | fee_rate_bps: str 14 | price: str 15 | status: str 16 | match_time: str 17 | last_update: str 18 | outcome: str 19 | maker_address: str 20 | owner: str 21 | transaction_hash: str 22 | bucket_index: str 23 | maker_orders: list[str] 24 | type: str 25 | 26 | 27 | class SimpleMarket(BaseModel): 28 | id: int 29 | question: str 30 | # start: str 31 | end: str 32 | description: str 33 | active: bool 34 | # deployed: Optional[bool] 35 | funded: bool 36 | # orderMinSize: float 37 | # orderPriceMinTickSize: float 38 | rewardsMinSize: float 39 | rewardsMaxSpread: float 40 | # volume: Optional[float] 41 | spread: float 42 | outcomes: str 43 | outcome_prices: str 44 | clob_token_ids: Optional[str] 45 | 46 | 47 | class ClobReward(BaseModel): 48 | id: str # returned as string in api but really an int? 49 | conditionId: str 50 | assetAddress: str 51 | rewardsAmount: float # only seen 0 but could be float? 52 | rewardsDailyRate: int # only seen ints but could be float? 53 | startDate: str # yyyy-mm-dd formatted date string 54 | endDate: str # yyyy-mm-dd formatted date string 55 | 56 | 57 | class Tag(BaseModel): 58 | id: str 59 | label: Optional[str] = None 60 | slug: Optional[str] = None 61 | forceShow: Optional[bool] = None # missing from current events data 62 | createdAt: Optional[str] = None # missing from events data 63 | updatedAt: Optional[str] = None # missing from current events data 64 | _sync: Optional[bool] = None 65 | 66 | 67 | class PolymarketEvent(BaseModel): 68 | id: str # "11421" 69 | ticker: Optional[str] = None 70 | slug: Optional[str] = None 71 | title: Optional[str] = None 72 | startDate: Optional[str] = None 73 | creationDate: Optional[str] = ( 74 | None # fine in market event but missing from events response 75 | ) 76 | endDate: Optional[str] = None 77 | image: Optional[str] = None 78 | icon: Optional[str] = None 79 | active: Optional[bool] = None 80 | closed: Optional[bool] = None 81 | archived: Optional[bool] = None 82 | new: Optional[bool] = None 83 | featured: Optional[bool] = None 84 | restricted: Optional[bool] = None 85 | liquidity: Optional[float] = None 86 | volume: Optional[float] = None 87 | reviewStatus: Optional[str] = None 88 | createdAt: Optional[str] = None # 2024-07-08T01:06:23.982796Z, 89 | updatedAt: Optional[str] = None # 2024-07-15T17:12:48.601056Z, 90 | competitive: Optional[float] = None 91 | volume24hr: Optional[float] = None 92 | enableOrderBook: Optional[bool] = None 93 | liquidityClob: Optional[float] = None 94 | _sync: Optional[bool] = None 95 | commentCount: Optional[int] = None 96 | # markets: list[str, 'Market'] # forward reference Market defined below - TODO: double check this works as intended 97 | markets: Optional[list[Market]] = None 98 | tags: Optional[list[Tag]] = None 99 | cyom: Optional[bool] = None 100 | showAllOutcomes: Optional[bool] = None 101 | showMarketImages: Optional[bool] = None 102 | 103 | 104 | class Market(BaseModel): 105 | id: int 106 | question: Optional[str] = None 107 | conditionId: Optional[str] = None 108 | slug: Optional[str] = None 109 | resolutionSource: Optional[str] = None 110 | endDate: Optional[str] = None 111 | liquidity: Optional[float] = None 112 | startDate: Optional[str] = None 113 | image: Optional[str] = None 114 | icon: Optional[str] = None 115 | description: Optional[str] = None 116 | outcome: Optional[list] = None 117 | outcomePrices: Optional[list] = None 118 | volume: Optional[float] = None 119 | active: Optional[bool] = None 120 | closed: Optional[bool] = None 121 | marketMakerAddress: Optional[str] = None 122 | createdAt: Optional[str] = None # date type worth enforcing for dates? 123 | updatedAt: Optional[str] = None 124 | new: Optional[bool] = None 125 | featured: Optional[bool] = None 126 | submitted_by: Optional[str] = None 127 | archived: Optional[bool] = None 128 | resolvedBy: Optional[str] = None 129 | restricted: Optional[bool] = None 130 | groupItemTitle: Optional[str] = None 131 | groupItemThreshold: Optional[int] = None 132 | questionID: Optional[str] = None 133 | enableOrderBook: Optional[bool] = None 134 | orderPriceMinTickSize: Optional[float] = None 135 | orderMinSize: Optional[int] = None 136 | volumeNum: Optional[float] = None 137 | liquidityNum: Optional[float] = None 138 | endDateIso: Optional[str] = None # iso format date = None 139 | startDateIso: Optional[str] = None 140 | hasReviewedDates: Optional[bool] = None 141 | volume24hr: Optional[float] = None 142 | clobTokenIds: Optional[list] = None 143 | umaBond: Optional[int] = None # returned as string from api? 144 | umaReward: Optional[int] = None # returned as string from api? 145 | volume24hrClob: Optional[float] = None 146 | volumeClob: Optional[float] = None 147 | liquidityClob: Optional[float] = None 148 | acceptingOrders: Optional[bool] = None 149 | negRisk: Optional[bool] = None 150 | commentCount: Optional[int] = None 151 | _sync: Optional[bool] = None 152 | events: Optional[list[PolymarketEvent]] = None 153 | ready: Optional[bool] = None 154 | deployed: Optional[bool] = None 155 | funded: Optional[bool] = None 156 | deployedTimestamp: Optional[str] = None # utc z datetime string 157 | acceptingOrdersTimestamp: Optional[str] = None # utc z datetime string, 158 | cyom: Optional[bool] = None 159 | competitive: Optional[float] = None 160 | pagerDutyNotificationEnabled: Optional[bool] = None 161 | reviewStatus: Optional[str] = None # deployed, draft, etc. 162 | approved: Optional[bool] = None 163 | clobRewards: Optional[list[ClobReward]] = None 164 | rewardsMinSize: Optional[int] = ( 165 | None # would make sense to allow float but we'll see 166 | ) 167 | rewardsMaxSpread: Optional[float] = None 168 | spread: Optional[float] = None 169 | 170 | 171 | class ComplexMarket(BaseModel): 172 | id: int 173 | condition_id: str 174 | question_id: str 175 | tokens: Union[str, str] 176 | rewards: str 177 | minimum_order_size: str 178 | minimum_tick_size: str 179 | description: str 180 | category: str 181 | end_date_iso: str 182 | game_start_time: str 183 | question: str 184 | market_slug: str 185 | min_incentive_size: str 186 | max_incentive_spread: str 187 | active: bool 188 | closed: bool 189 | seconds_delay: int 190 | icon: str 191 | fpmm: str 192 | name: str 193 | description: Union[str, None] = None 194 | price: float 195 | tax: Union[float, None] = None 196 | 197 | 198 | class SimpleEvent(BaseModel): 199 | id: int 200 | ticker: str 201 | slug: str 202 | title: str 203 | description: str 204 | end: str 205 | active: bool 206 | closed: bool 207 | archived: bool 208 | restricted: bool 209 | new: bool 210 | featured: bool 211 | restricted: bool 212 | markets: str 213 | 214 | 215 | class Source(BaseModel): 216 | id: Optional[str] 217 | name: Optional[str] 218 | 219 | 220 | class Article(BaseModel): 221 | source: Optional[Source] 222 | author: Optional[str] 223 | title: Optional[str] 224 | description: Optional[str] 225 | url: Optional[str] 226 | urlToImage: Optional[str] 227 | publishedAt: Optional[str] 228 | content: Optional[str] 229 | -------------------------------------------------------------------------------- /agents/polymarket/gamma.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import json 3 | import logging 4 | from typing import Optional 5 | 6 | from agents.polymarket.polymarket import Polymarket 7 | from agents.utils.objects import Market, PolymarketEvent, ClobReward, Tag 8 | 9 | logger = logging.getLogger(__name__) 10 | class GammaMarketClient: 11 | def __init__(self): 12 | self.gamma_url = "https://gamma-api.polymarket.com" 13 | self.gamma_markets_endpoint = self.gamma_url + "/markets" 14 | self.gamma_events_endpoint = self.gamma_url + "/events" 15 | def parse_pydantic_market(self, market_object: dict) -> Market: 16 | try: 17 | if "clobRewards" in market_object: 18 | clob_rewards: list[ClobReward] = [] 19 | for clob_rewards_obj in market_object["clobRewards"]: 20 | clob_rewards.append(ClobReward(**clob_rewards_obj)) 21 | market_object["clobRewards"] = clob_rewards 22 | 23 | if "events" in market_object: 24 | events: list[PolymarketEvent] = [] 25 | for market_event_obj in market_object["events"]: 26 | events.append(self.parse_nested_event(market_event_obj)) 27 | market_object["events"] = events 28 | 29 | # These two fields below are returned as stringified lists from the api 30 | if "outcomePrices" in market_object: 31 | market_object["outcomePrices"] = json.loads( 32 | market_object["outcomePrices"] 33 | ) 34 | if "clobTokenIds" in market_object: 35 | market_object["clobTokenIds"] = json.loads( 36 | market_object["clobTokenIds"] 37 | ) 38 | 39 | return Market(**market_object) 40 | except Exception as err: 41 | logger.error(f"[parse_market] Caught exception: {err}") 42 | logger.debug(f"Exception while handling object: {market_object}") 43 | raise 44 | 45 | # Event parser for events nested under a markets api response 46 | def parse_nested_event(self, event_object: dict) -> PolymarketEvent: 47 | logger.debug(f"[parse_nested_event] called with: {event_object}") 48 | try: 49 | if "tags" in event_object: 50 | tags: list[Tag] = [] 51 | for tag in event_object["tags"]: 52 | tags.append(Tag(**tag)) 53 | event_object["tags"] = tags 54 | 55 | return PolymarketEvent(**event_object) 56 | except Exception as err: 57 | logger.error(f"[parse_nested_event] Caught exception: {err}") 58 | logger.debug(f"Exception while handling object: {event_object}") 59 | raise 60 | 61 | def parse_pydantic_event(self, event_object: dict) -> PolymarketEvent: 62 | try: 63 | if "tags" in event_object: 64 | tags: list[Tag] = [] 65 | for tag in event_object["tags"]: 66 | tags.append(Tag(**tag)) 67 | event_object["tags"] = tags 68 | return PolymarketEvent(**event_object) 69 | except Exception as err: 70 | logger.error(f"[parse_pydantic_event] Caught exception: {err}") 71 | logger.debug(f"Exception while handling object: {event_object}") 72 | raise 73 | 74 | def get_markets( 75 | self, querystring_params={}, parse_pydantic=False, local_file_path=None 76 | ) -> "list[Market]": 77 | if parse_pydantic and local_file_path is not None: 78 | raise Exception( 79 | 'Cannot use "parse_pydantic" and "local_file" params simultaneously.' 80 | ) 81 | 82 | response = httpx.get(self.gamma_markets_endpoint, params=querystring_params) 83 | if response.status_code == 200: 84 | data = response.json() 85 | if local_file_path is not None: 86 | with open(local_file_path, "w+") as out_file: 87 | json.dump(data, out_file) 88 | elif not parse_pydantic: 89 | return data 90 | else: 91 | markets: list[Market] = [] 92 | for market_object in data: 93 | markets.append(self.parse_pydantic_market(market_object)) 94 | return markets 95 | else: 96 | logger.error(f"Error response returned from API: HTTP {response.status_code}") 97 | response.raise_for_status() 98 | raise RuntimeError(f"API request failed with status {response.status_code}") 99 | 100 | def get_events( 101 | self, querystring_params={}, parse_pydantic=False, local_file_path=None 102 | ) -> "list[PolymarketEvent]": 103 | if parse_pydantic and local_file_path is not None: 104 | raise Exception( 105 | 'Cannot use "parse_pydantic" and "local_file" params simultaneously.' 106 | ) 107 | 108 | response = httpx.get(self.gamma_events_endpoint, params=querystring_params) 109 | if response.status_code == 200: 110 | data = response.json() 111 | if local_file_path is not None: 112 | with open(local_file_path, "w+") as out_file: 113 | json.dump(data, out_file) 114 | elif not parse_pydantic: 115 | return data 116 | else: 117 | events: list[PolymarketEvent] = [] 118 | for market_event_obj in data: 119 | events.append(self.parse_pydantic_event(market_event_obj)) 120 | return events 121 | else: 122 | logger.error(f"Error response returned from API: HTTP {response.status_code}") 123 | response.raise_for_status() 124 | raise RuntimeError(f"API request failed with status {response.status_code}") 125 | 126 | def get_all_markets(self, limit=2) -> "list[Market]": 127 | return self.get_markets(querystring_params={"limit": limit}) 128 | 129 | def get_all_events(self, limit=2) -> "list[PolymarketEvent]": 130 | return self.get_events(querystring_params={"limit": limit}) 131 | 132 | def get_current_markets(self, limit=4) -> "list[Market]": 133 | return self.get_markets( 134 | querystring_params={ 135 | "active": True, 136 | "closed": False, 137 | "archived": False, 138 | "limit": limit, 139 | } 140 | ) 141 | 142 | def get_all_current_markets(self, limit=100) -> "list[Market]": 143 | offset = 0 144 | all_markets = [] 145 | while True: 146 | params = { 147 | "active": True, 148 | "closed": False, 149 | "archived": False, 150 | "limit": limit, 151 | "offset": offset, 152 | } 153 | market_batch = self.get_markets(querystring_params=params) 154 | all_markets.extend(market_batch) 155 | 156 | if len(market_batch) < limit: 157 | break 158 | offset += limit 159 | 160 | return all_markets 161 | 162 | def get_current_events(self, limit=4) -> "list[PolymarketEvent]": 163 | return self.get_events( 164 | querystring_params={ 165 | "active": True, 166 | "closed": False, 167 | "archived": False, 168 | "limit": limit, 169 | } 170 | ) 171 | 172 | def get_clob_tradable_markets(self, limit=2) -> "list[Market]": 173 | return self.get_markets( 174 | querystring_params={ 175 | "active": True, 176 | "closed": False, 177 | "archived": False, 178 | "limit": limit, 179 | "enableOrderBook": True, 180 | } 181 | ) 182 | 183 | def get_market(self, market_id: int) -> dict: 184 | url = f"{self.gamma_markets_endpoint}/{market_id}" 185 | logger.debug(f"Fetching market from: {url}") 186 | response = httpx.get(url) 187 | response.raise_for_status() 188 | return response.json() 189 | 190 | 191 | if __name__ == "__main__": 192 | gamma = GammaMarketClient() 193 | market = gamma.get_market("253123") 194 | poly = Polymarket() 195 | object = poly.map_api_to_market(market) 196 | -------------------------------------------------------------------------------- /agents/application/executor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import ast 4 | import re 5 | import logging 6 | import math 7 | from typing import List, Dict, Any 8 | 9 | from dotenv import load_dotenv 10 | from langchain_core.messages import HumanMessage, SystemMessage 11 | from langchain_openai import ChatOpenAI 12 | from agents.polymarket.gamma import GammaMarketClient as Gamma 13 | from agents.connectors.chroma import PolymarketRAG as Chroma 14 | from agents.utils.objects import SimpleEvent, SimpleMarket 15 | from agents.application.prompts import Prompter 16 | from agents.polymarket.polymarket import Polymarket 17 | 18 | logger = logging.getLogger(__name__) 19 | 20 | def retain_keys(data, keys_to_retain): 21 | if isinstance(data, dict): 22 | return { 23 | key: retain_keys(value, keys_to_retain) 24 | for key, value in data.items() 25 | if key in keys_to_retain 26 | } 27 | elif isinstance(data, list): 28 | return [retain_keys(item, keys_to_retain) for item in data] 29 | else: 30 | return data 31 | 32 | class Executor: 33 | def __init__(self, default_model: str = 'gpt-4o-mini') -> None: 34 | load_dotenv() 35 | max_token_model: Dict[str, int] = { 36 | 'gpt-3.5-turbo-16k': 15000, 37 | 'gpt-4-1106-preview': 95000, 38 | 'gpt-4o-mini': 128000, 39 | 'gpt-4o': 128000, 40 | } 41 | self.token_limit = max_token_model.get(default_model, 128000) 42 | self.prompter = Prompter() 43 | self.openai_api_key = os.getenv("OPENAI_API_KEY") 44 | if not self.openai_api_key: 45 | logger.warning("OPENAI_API_KEY not found in environment") 46 | self.llm = ChatOpenAI( 47 | model=default_model, 48 | temperature=0, 49 | ) 50 | self.gamma = Gamma() 51 | self.chroma = Chroma() 52 | self.polymarket = Polymarket() 53 | 54 | def get_llm_response(self, user_input: str) -> str: 55 | system_message = SystemMessage(content=str(self.prompter.market_analyst())) 56 | human_message = HumanMessage(content=user_input) 57 | messages = [system_message, human_message] 58 | result = self.llm.invoke(messages) 59 | return result.content 60 | 61 | def get_superforecast( 62 | self, event_title: str, market_question: str, outcome: str 63 | ) -> str: 64 | messages = self.prompter.superforecaster( 65 | description=event_title, question=market_question, outcome=outcome 66 | ) 67 | result = self.llm.invoke(messages) 68 | return result.content 69 | 70 | 71 | def estimate_tokens(self, text: str) -> int: 72 | # This is a rough estimate. For more accurate results, consider using a tokenizer. 73 | return len(text) // 4 # Assuming average of 4 characters per token 74 | 75 | def process_data_chunk(self, data1: List[Dict[Any, Any]], data2: List[Dict[Any, Any]], user_input: str) -> str: 76 | system_message = SystemMessage( 77 | content=str(self.prompter.prompts_polymarket(data1=data1, data2=data2)) 78 | ) 79 | human_message = HumanMessage(content=user_input) 80 | messages = [system_message, human_message] 81 | result = self.llm.invoke(messages) 82 | return result.content 83 | 84 | 85 | def divide_list(self, original_list, i): 86 | # Calculate the size of each sublist 87 | sublist_size = math.ceil(len(original_list) / i) 88 | 89 | # Use list comprehension to create sublists 90 | return [original_list[j:j+sublist_size] for j in range(0, len(original_list), sublist_size)] 91 | 92 | def get_polymarket_llm(self, user_input: str) -> str: 93 | data1 = self.gamma.get_current_events() 94 | data2 = self.gamma.get_current_markets() 95 | 96 | combined_data = str(self.prompter.prompts_polymarket(data1=data1, data2=data2)) 97 | 98 | # Estimate total tokens 99 | total_tokens = self.estimate_tokens(combined_data) 100 | 101 | # Set a token limit (adjust as needed, leaving room for system and user messages) 102 | token_limit = self.token_limit 103 | if total_tokens <= token_limit: 104 | # If within limit, process normally 105 | return self.process_data_chunk(data1, data2, user_input) 106 | else: 107 | # If exceeding limit, process in chunks 108 | logger.info(f'Total tokens {total_tokens} exceeding LLM capacity, splitting into chunks') 109 | group_size = (total_tokens // token_limit) + 1 110 | useful_keys = [ 111 | 'id', 'questionID', 'description', 'liquidity', 'clobTokenIds', 112 | 'outcomes', 'outcomePrices', 'volume', 'startDate', 'endDate', 113 | 'question', 'events' 114 | ] 115 | data1 = retain_keys(data1, useful_keys) 116 | cut_1 = self.divide_list(data1, group_size) 117 | cut_2 = self.divide_list(data2, group_size) 118 | cut_data_12 = zip(cut_1, cut_2) 119 | 120 | results = [] 121 | 122 | for cut_data in cut_data_12: 123 | sub_data1 = cut_data[0] 124 | sub_data2 = cut_data[1] 125 | result = self.process_data_chunk(sub_data1, sub_data2, user_input) 126 | results.append(result) 127 | 128 | combined_result = " ".join(results) 129 | return combined_result 130 | def filter_events(self, events: "list[SimpleEvent]") -> str: 131 | prompt = self.prompter.filter_events(events) 132 | result = self.llm.invoke(prompt) 133 | return result.content 134 | 135 | def filter_events_with_rag(self, events: "list[SimpleEvent]") -> str: 136 | prompt = self.prompter.filter_events() 137 | logger.debug(f"Filtering events with RAG, prompt: {prompt[:100]}...") 138 | return self.chroma.events(events, prompt) 139 | 140 | def map_filtered_events_to_markets( 141 | self, filtered_events: "list[SimpleEvent]" 142 | ) -> "list[SimpleMarket]": 143 | markets = [] 144 | for e in filtered_events: 145 | data = json.loads(e[0].json()) 146 | market_ids = data["metadata"]["markets"].split(",") 147 | for market_id in market_ids: 148 | market_data = self.gamma.get_market(market_id) 149 | formatted_market_data = self.polymarket.map_api_to_market(market_data) 150 | markets.append(formatted_market_data) 151 | return markets 152 | 153 | def filter_markets(self, markets) -> "list[tuple]": 154 | prompt = self.prompter.filter_markets() 155 | logger.debug(f"Filtering markets, prompt: {prompt[:100]}...") 156 | return self.chroma.markets(markets, prompt) 157 | 158 | def source_best_trade(self, market_object) -> str: 159 | market_document = market_object[0].dict() 160 | market = market_document["metadata"] 161 | outcome_prices = ast.literal_eval(market["outcome_prices"]) 162 | outcomes = ast.literal_eval(market["outcomes"]) 163 | question = market["question"] 164 | description = market_document["page_content"] 165 | 166 | prompt = self.prompter.superforecaster(question, description, outcomes) 167 | logger.debug("Getting superforecast prediction") 168 | result = self.llm.invoke(prompt) 169 | content = result.content 170 | logger.debug(f"Superforecast result: {content[:200]}...") 171 | 172 | prompt = self.prompter.one_best_trade(content, outcomes, outcome_prices) 173 | logger.debug("Determining best trade") 174 | result = self.llm.invoke(prompt) 175 | content = result.content 176 | logger.debug(f"Best trade result: {content[:200]}...") 177 | return content 178 | 179 | def format_trade_prompt_for_execution(self, best_trade: str) -> float: 180 | data = best_trade.split(",") 181 | # price = re.findall("\d+\.\d+", data[0])[0] 182 | size = re.findall("\d+\.\d+", data[1])[0] 183 | usdc_balance = self.polymarket.get_usdc_balance() 184 | return float(size) * usdc_balance 185 | 186 | def source_best_market_to_create(self, filtered_markets) -> str: 187 | prompt = self.prompter.create_new_market(filtered_markets) 188 | logger.debug("Creating new market idea") 189 | result = self.llm.invoke(prompt) 190 | content = result.content 191 | return content 192 | -------------------------------------------------------------------------------- /agents/application/prompts.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from datetime import datetime 3 | 4 | 5 | class Prompter: 6 | 7 | def generate_simple_ai_trader(market_description: str, relevant_info: str) -> str: 8 | return f""" 9 | 10 | You are a trader. 11 | 12 | Here is a market description: {market_description}. 13 | 14 | Here is relevant information: {relevant_info}. 15 | 16 | Do you buy or sell? How much? 17 | """ 18 | 19 | def market_analyst(self) -> str: 20 | return f""" 21 | You are a market analyst that takes a description of an event and produces a market forecast. 22 | Assign a probability estimate to the event occurring described by the user 23 | """ 24 | 25 | def sentiment_analyzer(self, question: str, outcome: str) -> float: 26 | return f""" 27 | You are a political scientist trained in media analysis. 28 | You are given a question: {question}. 29 | and an outcome of yes or no: {outcome}. 30 | 31 | You are able to review a news article or text and 32 | assign a sentiment score between 0 and 1. 33 | 34 | """ 35 | 36 | def prompts_polymarket( 37 | self, data1: str, data2: str, market_question: str, outcome: str 38 | ) -> str: 39 | current_market_data = str(data1) 40 | current_event_data = str(data2) 41 | return f""" 42 | You are an AI assistant for users of a prediction market called Polymarket. 43 | Users want to place bets based on their beliefs of market outcomes such as political or sports events. 44 | 45 | Here is data for current Polymarket markets {current_market_data} and 46 | current Polymarket events {current_event_data}. 47 | 48 | Help users identify markets to trade based on their interests or queries. 49 | Provide specific information for markets including probabilities of outcomes. 50 | Give your response in the following format: 51 | 52 | I believe {market_question} has a likelihood {float} for outcome of {outcome}. 53 | """ 54 | 55 | def prompts_polymarket(self, data1: str, data2: str) -> str: 56 | current_market_data = str(data1) 57 | current_event_data = str(data2) 58 | return f""" 59 | You are an AI assistant for users of a prediction market called Polymarket. 60 | Users want to place bets based on their beliefs of market outcomes such as political or sports events. 61 | 62 | Here is data for current Polymarket markets {current_market_data} and 63 | current Polymarket events {current_event_data}. 64 | Help users identify markets to trade based on their interests or queries. 65 | Provide specific information for markets including probabilities of outcomes. 66 | """ 67 | 68 | def routing(self, system_message: str) -> str: 69 | return f"""You are an expert at routing a user question to the appropriate data source. System message: ${system_message}""" 70 | 71 | def multiquery(self, question: str) -> str: 72 | return f""" 73 | You're an AI assistant. Your task is to generate five different versions 74 | of the given user question to retreive relevant documents from a vector database. By generating 75 | multiple perspectives on the user question, your goal is to help the user overcome some of the limitations 76 | of the distance-based similarity search. 77 | Provide these alternative questions separated by newlines. Original question: {question} 78 | 79 | """ 80 | 81 | def read_polymarket(self) -> str: 82 | return f""" 83 | You are an prediction market analyst. 84 | """ 85 | 86 | def polymarket_analyst_api(self) -> str: 87 | return f"""You are an AI assistant for analyzing prediction markets. 88 | You will be provided with json output for api data from Polymarket. 89 | Polymarket is an online prediction market that lets users Bet on the outcome of future events in a wide range of topics, like sports, politics, and pop culture. 90 | Get accurate real-time probabilities of the events that matter most to you. """ 91 | 92 | def filter_events(self) -> str: 93 | return ( 94 | self.polymarket_analyst_api() 95 | + f""" 96 | 97 | Filter these events for the ones you will be best at trading on profitably. 98 | 99 | """ 100 | ) 101 | 102 | def filter_markets(self) -> str: 103 | return ( 104 | self.polymarket_analyst_api() 105 | + f""" 106 | 107 | Filter these markets for the ones you will be best at trading on profitably. 108 | 109 | """ 110 | ) 111 | 112 | def superforecaster(self, question: str, description: str, outcome: str) -> str: 113 | return f""" 114 | You are a Superforecaster tasked with correctly predicting the likelihood of events. 115 | Use the following systematic process to develop an accurate prediction for the following 116 | question=`{question}` and description=`{description}` combination. 117 | 118 | Here are the key steps to use in your analysis: 119 | 120 | 1. Breaking Down the Question: 121 | - Decompose the question into smaller, more manageable parts. 122 | - Identify the key components that need to be addressed to answer the question. 123 | 2. Gathering Information: 124 | - Seek out diverse sources of information. 125 | - Look for both quantitative data and qualitative insights. 126 | - Stay updated on relevant news and expert analyses. 127 | 3. Considere Base Rates: 128 | - Use statistical baselines or historical averages as a starting point. 129 | - Compare the current situation to similar past events to establish a benchmark probability. 130 | 4. Identify and Evaluate Factors: 131 | - List factors that could influence the outcome. 132 | - Assess the impact of each factor, considering both positive and negative influences. 133 | - Use evidence to weigh these factors, avoiding over-reliance on any single piece of information. 134 | 5. Think Probabilistically: 135 | - Express predictions in terms of probabilities rather than certainties. 136 | - Assign likelihoods to different outcomes and avoid binary thinking. 137 | - Embrace uncertainty and recognize that all forecasts are probabilistic in nature. 138 | 139 | Given these steps produce a statement on the probability of outcome=`{outcome}` occuring. 140 | 141 | Give your response in the following format: 142 | 143 | I believe {question} has a likelihood `{float}` for outcome of `{str}`. 144 | """ 145 | 146 | def one_best_trade( 147 | self, 148 | prediction: str, 149 | outcomes: List[str], 150 | outcome_prices: str, 151 | ) -> str: 152 | return ( 153 | self.polymarket_analyst_api() 154 | + f""" 155 | 156 | Imagine yourself as the top trader on Polymarket, dominating the world of information markets with your keen insights and strategic acumen. You have an extraordinary ability to analyze and interpret data from diverse sources, turning complex information into profitable trading opportunities. 157 | You excel in predicting the outcomes of global events, from political elections to economic developments, using a combination of data analysis and intuition. Your deep understanding of probability and statistics allows you to assess market sentiment and make informed decisions quickly. 158 | Every day, you approach Polymarket with a disciplined strategy, identifying undervalued opportunities and managing your portfolio with precision. You are adept at evaluating the credibility of information and filtering out noise, ensuring that your trades are based on reliable data. 159 | Your adaptability is your greatest asset, enabling you to thrive in a rapidly changing environment. You leverage cutting-edge technology and tools to gain an edge over other traders, constantly seeking innovative ways to enhance your strategies. 160 | In your journey on Polymarket, you are committed to continuous learning, staying informed about the latest trends and developments in various sectors. Your emotional intelligence empowers you to remain composed under pressure, making rational decisions even when the stakes are high. 161 | Visualize yourself consistently achieving outstanding returns, earning recognition as the top trader on Polymarket. You inspire others with your success, setting new standards of excellence in the world of information markets. 162 | 163 | """ 164 | + f""" 165 | 166 | You made the following prediction for a market: {prediction} 167 | 168 | The current outcomes ${outcomes} prices are: ${outcome_prices} 169 | 170 | Given your prediction, respond with a genius trade in the format: 171 | ` 172 | price:'price_on_the_orderbook', 173 | size:'percentage_of_total_funds', 174 | side: BUY or SELL, 175 | ` 176 | 177 | Your trade should approximate price using the likelihood in your prediction. 178 | 179 | Example response: 180 | 181 | RESPONSE``` 182 | price:0.5, 183 | size:0.1, 184 | side:BUY, 185 | ``` 186 | 187 | """ 188 | ) 189 | 190 | def format_price_from_one_best_trade_output(self, output: str) -> str: 191 | return f""" 192 | 193 | You will be given an input such as: 194 | 195 | ` 196 | price:0.5, 197 | size:0.1, 198 | side:BUY, 199 | ` 200 | 201 | Please extract only the value associated with price. 202 | In this case, you would return "0.5". 203 | 204 | Only return the number after price: 205 | 206 | """ 207 | 208 | def format_size_from_one_best_trade_output(self, output: str) -> str: 209 | return f""" 210 | 211 | You will be given an input such as: 212 | 213 | ` 214 | price:0.5, 215 | size:0.1, 216 | side:BUY, 217 | ` 218 | 219 | Please extract only the value associated with price. 220 | In this case, you would return "0.1". 221 | 222 | Only return the number after size: 223 | 224 | """ 225 | 226 | def create_new_market(self, filtered_markets: str) -> str: 227 | return f""" 228 | {filtered_markets} 229 | 230 | Invent an information market similar to these markets that ends in the future, 231 | at least 6 months after today, which is: {datetime.today().strftime('%Y-%m-%d')}, 232 | so this date plus 6 months at least. 233 | 234 | Output your format in: 235 | 236 | Question: "..."? 237 | Outcomes: A or B 238 | 239 | With ... filled in and A or B options being the potential results. 240 | For example: 241 | 242 | Question: "Will Kamala win" 243 | Outcomes: Yes or No 244 | 245 | """ 246 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Contributors][contributors-shield]][contributors-url] 3 | [![Forks][forks-shield]][forks-url] 4 | [![Stargazers][stars-shield]][stars-url] 5 | [![Issues][issues-shield]][issues-url] 6 | [![MIT License][license-shield]][license-url] 7 | 8 | 9 | 10 |
11 |
12 | 13 | Logo 14 | 15 | 16 |

Polymarket Agents

17 | 18 |

19 | Trade autonomously on Polymarket using AI Agents 20 |
21 | Explore the docs » 22 |
23 |
24 | View Demo 25 | · 26 | Report Bug 27 | · 28 | Request Feature 29 |

30 |
31 | 32 | 33 | 34 | # Polymarket Agents 35 | 36 | > 🤖 **AI-powered autonomous trading agents for Polymarket prediction markets** 37 | 38 | Polymarket Agents is a modern, open-source framework for building sophisticated AI trading agents on Polymarket. Leverage state-of-the-art LLMs, RAG (Retrieval-Augmented Generation), and superforecasting methodologies to create intelligent trading systems that analyze markets, events, and news to make informed predictions. 39 | 40 | **Built with Python 3.12+** | **MIT Licensed** | **Production Ready** 41 | 42 | This code is free and publicly available under MIT License open source license ([terms of service](#terms-of-service))! 43 | 44 | ## Features 45 | 46 | - **AI-Powered Trading**: Autonomous trading agents using state-of-the-art LLMs 47 | - **Polymarket Integration**: Full API integration for markets, events, and trading 48 | - **RAG Support**: Local and remote RAG (Retrieval-Augmented Generation) for market analysis 49 | - **Data Sources**: Integration with news providers, web search, and betting services 50 | - **Superforecasting**: Advanced prediction capabilities using superforecaster methodologies 51 | - **Trending Markets**: Get trending markets sorted by 24-hour volume 52 | - **Modern Stack**: Built with Python 3.12+, LangChain, FastAPI, and modern tooling 53 | - **Docker Ready**: Containerized deployment for easy setup 54 | 55 | # Getting started 56 | 57 | This project requires **Python 3.12+**. 58 | 59 | ## Prerequisites 60 | 61 | - Python 3.12 or higher 62 | - pip (Python package manager) 63 | - (Optional) Docker for containerized deployment 64 | - (Optional) NewsAPI key for news integration 65 | - (Optional) Tavily API key for web search 66 | 67 | ## Installation 68 | 69 | 1. **Clone the repository** 70 | 71 | ```bash 72 | git clone https://github.com/BlackSky-Jose/PolyMarket-AI-agent-trading.git 73 | cd poly-ai-trading-agent 74 | ``` 75 | 76 | 2. **Create a virtual environment** 77 | 78 | Using `venv` (recommended): 79 | 80 | ```bash 81 | python -m venv .venv 82 | ``` 83 | 84 | Or using `uv` (faster): 85 | 86 | ```bash 87 | uv venv 88 | ``` 89 | 90 | 3. **Activate the virtual environment** 91 | 92 | - On Windows (PowerShell): 93 | 94 | ```powershell 95 | .venv\Scripts\Activate.ps1 96 | ``` 97 | 98 | - On Windows (CMD): 99 | 100 | ```cmd 101 | .venv\Scripts\activate.bat 102 | ``` 103 | 104 | - On macOS and Linux: 105 | 106 | ```bash 107 | source .venv/bin/activate 108 | ``` 109 | 110 | 4. **Install the project** 111 | 112 | Using pip: 113 | 114 | ```bash 115 | pip install -e . 116 | ``` 117 | 118 | Or with development dependencies: 119 | 120 | ```bash 121 | pip install -e ".[dev]" 122 | ``` 123 | 124 | Using uv (faster): 125 | 126 | ```bash 127 | uv pip install -e ".[dev]" 128 | ``` 129 | 130 | 5. **Set up environment variables** 131 | 132 | Create a `.env` file in the project root: 133 | 134 | ```bash 135 | # Copy example if available, or create new 136 | touch .env 137 | ``` 138 | 139 | Add the following environment variables: 140 | 141 | ```env 142 | # Required for trading 143 | POLYGON_WALLET_PRIVATE_KEY="your_private_key_here" 144 | 145 | # Required for AI features 146 | OPENAI_API_KEY="your_openai_api_key_here" 147 | 148 | # Optional - for news integration 149 | NEWSAPI_API_KEY="your_newsapi_key_here" 150 | 151 | # Optional - for web search 152 | TAVILY_API_KEY="your_tavily_key_here" 153 | ``` 154 | 155 | **Important Notes:** 156 | - Never commit your `.env` file to version control 157 | - Keep your `POLYGON_WALLET_PRIVATE_KEY` secure and never share it 158 | - You can get a NewsAPI key from [newsapi.org](https://newsapi.org/) 159 | - You can get a Tavily key from [tavily.com](https://tavily.com/) 160 | 161 | 6. **Load your wallet with USDC** (if trading) 162 | 163 | Before executing trades, ensure your Polygon wallet has sufficient USDC balance for trading. 164 | 165 | ## Usage 166 | 167 | ### Command Line Interface 168 | 169 | The CLI is the primary interface for interacting with Polymarket. All commands follow this format: 170 | 171 | ```bash 172 | python -m scripts.python.cli [options] 173 | ``` 174 | 175 | #### Getting Help 176 | 177 | To see all available commands: 178 | 179 | ```bash 180 | python -m scripts.python.cli --help 181 | ``` 182 | 183 | To get help for a specific command: 184 | 185 | ```bash 186 | python -m scripts.python.cli --help 187 | ``` 188 | 189 | ### Detailed Command Reference 190 | 191 | #### 1. Get All Markets 192 | 193 | Retrieve and display markets from Polymarket. 194 | 195 | **Command:** 196 | ```bash 197 | python -m scripts.python.cli get-all-markets [--limit LIMIT] [--sort-by SORT_BY] 198 | ``` 199 | 200 | **Parameters:** 201 | - `--limit` (optional): Number of markets to retrieve (default: 5) 202 | - `--sort-by` (optional): Sorting criterion (default: "spread") 203 | 204 | **Examples:** 205 | 206 | ```bash 207 | # Get 10 markets sorted by spread 208 | python -m scripts.python.cli get-all-markets --limit 10 --sort-by spread 209 | 210 | # Get 5 markets (default) 211 | python -m scripts.python.cli get-all-markets 212 | 213 | # Get 20 markets 214 | python -m scripts.python.cli get-all-markets --limit 20 215 | ``` 216 | 217 | **Output:** Displays a list of markets with details including: 218 | - Market ID 219 | - Question/Title 220 | - Description 221 | - Active status 222 | - Spread 223 | - Outcomes 224 | - Outcome prices 225 | 226 | #### 2. Get Trending Markets 227 | 228 | Get trending markets sorted by 24-hour volume. This is useful for finding the most active markets. 229 | 230 | **Command:** 231 | ```bash 232 | python -m scripts.python.cli get-trending-markets [--limit LIMIT] 233 | ``` 234 | 235 | **Parameters:** 236 | - `--limit` (optional): Number of trending markets to retrieve (default: 10) 237 | 238 | **Examples:** 239 | 240 | ```bash 241 | # Get top 10 trending markets 242 | python -m scripts.python.cli get-trending-markets 243 | 244 | # Get top 25 trending markets 245 | python -m scripts.python.cli get-trending-markets --limit 25 246 | 247 | # Get top 5 trending markets 248 | python -m scripts.python.cli get-trending-markets --limit 5 249 | ``` 250 | 251 | **Output:** Displays trending markets sorted by 24-hour volume, showing the most active markets first. 252 | 253 | #### 3. Get All Events 254 | 255 | Retrieve and display events from Polymarket. 256 | 257 | **Command:** 258 | ```bash 259 | python -m scripts.python.cli get-all-events [--limit LIMIT] [--sort-by SORT_BY] 260 | ``` 261 | 262 | **Parameters:** 263 | - `--limit` (optional): Number of events to retrieve (default: 5) 264 | - `--sort-by` (optional): Sorting criterion (default: "number_of_markets") 265 | 266 | **Examples:** 267 | 268 | ```bash 269 | # Get 10 events sorted by number of markets 270 | python -m scripts.python.cli get-all-events --limit 10 --sort-by number_of_markets 271 | 272 | # Get 5 events (default) 273 | python -m scripts.python.cli get-all-events 274 | ``` 275 | 276 | **Output:** Displays events with details including: 277 | - Event ID 278 | - Title 279 | - Description 280 | - Active status 281 | - Number of associated markets 282 | - End date 283 | 284 | #### 4. Get Relevant News 285 | 286 | Search for news articles related to specific keywords. Requires NewsAPI key. 287 | 288 | **Command:** 289 | ```bash 290 | python -m scripts.python.cli get-relevant-news 291 | ``` 292 | 293 | **Parameters:** 294 | - `keywords` (required): Comma-separated keywords to search for 295 | 296 | **Examples:** 297 | 298 | ```bash 299 | # Search for news about Bitcoin 300 | python -m scripts.python.cli get-relevant-news "Bitcoin,crypto" 301 | 302 | # Search for news about elections 303 | python -m scripts.python.cli get-relevant-news "election,president" 304 | 305 | # Search for multiple keywords 306 | python -m scripts.python.cli get-relevant-news "AI,technology,machine learning" 307 | ``` 308 | 309 | **Output:** Displays news articles with: 310 | - Title 311 | - Description 312 | - Source 313 | - URL 314 | - Published date 315 | 316 | #### 5. Ask LLM 317 | 318 | Query the LLM with any question. Useful for getting AI-powered insights. 319 | 320 | **Command:** 321 | ```bash 322 | python -m scripts.python.cli ask-llm "" 323 | ``` 324 | 325 | **Parameters:** 326 | - `user_input` (required): Your question or prompt 327 | 328 | **Examples:** 329 | 330 | ```bash 331 | # Ask about trading strategies 332 | python -m scripts.python.cli ask-llm "What are the best markets to trade?" 333 | 334 | # Ask for market analysis 335 | python -m scripts.python.cli ask-llm "Should I invest in prediction markets?" 336 | 337 | # Ask for general questions 338 | python -m scripts.python.cli ask-llm "Explain how prediction markets work" 339 | ``` 340 | 341 | **Output:** Returns AI-generated response based on your question. 342 | 343 | #### 6. Ask Polymarket LLM 344 | 345 | Query the LLM with context about current Polymarket markets and events. This provides more relevant answers based on actual market data. 346 | 347 | **Command:** 348 | ```bash 349 | python -m scripts.python.cli ask-polymarket-llm "" 350 | ``` 351 | 352 | **Parameters:** 353 | - `user_input` (required): Your question about Polymarket 354 | 355 | **Examples:** 356 | 357 | ```bash 358 | # Ask about current markets 359 | python -m scripts.python.cli ask-polymarket-llm "What types of markets are currently trending?" 360 | 361 | # Ask for trading recommendations 362 | python -m scripts.python.cli ask-polymarket-llm "Which markets should I focus on for trading?" 363 | 364 | # Ask about market opportunities 365 | python -m scripts.python.cli ask-polymarket-llm "What are the best opportunities right now?" 366 | ``` 367 | 368 | **Output:** Returns AI-generated response with context from current Polymarket data. 369 | 370 | #### 7. Ask Superforecaster 371 | 372 | Get superforecaster predictions for specific events and outcomes. Uses advanced prediction methodologies. 373 | 374 | **Command:** 375 | ```bash 376 | python -m scripts.python.cli ask-superforecaster 377 | ``` 378 | 379 | **Parameters:** 380 | - `event_title` (required): Title of the event 381 | - `market_question` (required): The market question 382 | - `outcome` (required): The outcome to predict 383 | 384 | **Examples:** 385 | 386 | ```bash 387 | # Get prediction for a specific outcome 388 | python -m scripts.python.cli ask-superforecaster "2024 US Election" "Who will win?" "Candidate A" 389 | 390 | # Get prediction for another market 391 | python -m scripts.python.cli ask-superforecaster "Bitcoin Price" "Will Bitcoin reach $100k?" "Yes" 392 | ``` 393 | 394 | **Output:** Returns superforecaster analysis and prediction probability. 395 | 396 | #### 8. Create Market 397 | 398 | Generate a market idea using AI. The system analyzes current events and markets to suggest new market opportunities. 399 | 400 | **Command:** 401 | ```bash 402 | python -m scripts.python.cli create-market 403 | ``` 404 | 405 | **Examples:** 406 | 407 | ```bash 408 | # Generate a market idea 409 | python -m scripts.python.cli create-market 410 | ``` 411 | 412 | **Output:** Displays a generated market description with details about the proposed market. 413 | 414 | #### 9. Create Local Markets RAG 415 | 416 | Create a local RAG (Retrieval-Augmented Generation) database for markets. This allows for faster local queries. 417 | 418 | **Command:** 419 | ```bash 420 | python -m scripts.python.cli create-local-markets-rag 421 | ``` 422 | 423 | **Parameters:** 424 | - `local_directory` (required): Directory path where the RAG database will be stored 425 | 426 | **Examples:** 427 | 428 | ```bash 429 | # Create RAG database in ./rag_db directory 430 | python -m scripts.python.cli create-local-markets-rag ./rag_db 431 | 432 | # Create RAG database in a specific path 433 | python -m scripts.python.cli create-local-markets-rag /path/to/rag/database 434 | ``` 435 | 436 | **Output:** Creates a local vector database for faster market queries. 437 | 438 | #### 10. Query Local Markets RAG 439 | 440 | Query your local RAG database for market information. 441 | 442 | **Command:** 443 | ```bash 444 | python -m scripts.python.cli query-local-markets-rag "" 445 | ``` 446 | 447 | **Parameters:** 448 | - `vector_db_directory` (required): Path to your RAG database directory 449 | - `query` (required): Your search query 450 | 451 | **Examples:** 452 | 453 | ```bash 454 | # Query the local RAG database 455 | python -m scripts.python.cli query-local-markets-rag ./rag_db "What are the best markets for trading?" 456 | 457 | # Search for specific topics 458 | python -m scripts.python.cli query-local-markets-rag ./rag_db "crypto markets" 459 | ``` 460 | 461 | **Output:** Returns relevant market information from your local database. 462 | 463 | #### 11. Run Autonomous Trader 464 | 465 | ⚠️ **WARNING**: This command executes real trades. Review Terms of Service before use. 466 | 467 | Run an autonomous trading agent that analyzes markets and executes trades automatically. 468 | 469 | **Command:** 470 | ```bash 471 | python -m scripts.python.cli run-autonomous-trader 472 | ``` 473 | 474 | **Examples:** 475 | 476 | ```bash 477 | # Run the autonomous trader 478 | python -m scripts.python.cli run-autonomous-trader 479 | ``` 480 | 481 | **Important Notes:** 482 | - This will execute real trades on Polymarket 483 | - Ensure you have sufficient USDC balance 484 | - Review the Terms of Service at https://polymarket.com/tos 485 | - Start with small amounts to test 486 | - Monitor the trades closely 487 | 488 | **Output:** The trader will: 489 | 1. Fetch all tradeable events 490 | 2. Filter events using RAG 491 | 3. Map events to markets 492 | 4. Filter markets 493 | 5. Calculate the best trade 494 | 6. Execute the trade (if enabled) 495 | 496 | ### Direct Script Execution 497 | 498 | You can also run the trading script directly without the CLI: 499 | 500 | ```bash 501 | python -m agents.application.trade 502 | ``` 503 | 504 | This will execute the `one_best_trade()` method directly. 505 | 506 | ### Docker Deployment 507 | 508 | Build and run with Docker for easy deployment: 509 | 510 | **Build the image:** 511 | ```bash 512 | docker build -t poly-ai-trading-agent . 513 | ``` 514 | 515 | **Run the container:** 516 | ```bash 517 | docker run --env-file .env poly-ai-trading-agent 518 | ``` 519 | 520 | **Or use the provided scripts:** 521 | 522 | On Linux/macOS: 523 | ```bash 524 | ./scripts/bash/build-docker.sh 525 | ./scripts/bash/run-docker-dev.sh 526 | ``` 527 | 528 | On Windows (PowerShell): 529 | ```powershell 530 | .\scripts\bash\build-docker.sh 531 | .\scripts\bash\run-docker-dev.sh 532 | ``` 533 | 534 | ### Usage Examples and Workflows 535 | 536 | #### Example 1: Research Trending Markets 537 | 538 | ```bash 539 | # Step 1: Get trending markets 540 | python -m scripts.python.cli get-trending-markets --limit 20 541 | 542 | # Step 2: Get news about specific topics 543 | python -m scripts.python.cli get-relevant-news "Bitcoin,crypto,blockchain" 544 | 545 | # Step 3: Ask AI for analysis 546 | python -m scripts.python.cli ask-polymarket-llm "What are the best crypto markets to trade right now?" 547 | ``` 548 | 549 | #### Example 2: Create a Local RAG Database 550 | 551 | ```bash 552 | # Step 1: Create the RAG database 553 | python -m scripts.python.cli create-local-markets-rag ./my_rag_db 554 | 555 | # Step 2: Query the database 556 | python -m scripts.python.cli query-local-markets-rag ./my_rag_db "What markets are related to technology?" 557 | ``` 558 | 559 | #### Example 3: Get Market Analysis 560 | 561 | ```bash 562 | # Step 1: Get all markets 563 | python -m scripts.python.cli get-all-markets --limit 10 564 | 565 | # Step 2: Get events 566 | python -m scripts.python.cli get-all-events --limit 5 567 | 568 | # Step 3: Ask for superforecaster prediction 569 | python -m scripts.python.cli ask-superforecaster "2024 Election" "Who will win?" "Candidate A" 570 | ``` 571 | 572 | ## Development 573 | 574 | ### Code Quality 575 | 576 | This project uses modern Python tooling: 577 | 578 | - **Ruff** - Fast Python linter and formatter 579 | - **mypy** - Static type checking 580 | - **pre-commit** - Git hooks for code quality 581 | 582 | **Set up pre-commit hooks:** 583 | ```bash 584 | pre-commit install 585 | ``` 586 | 587 | **Run linting and type checking:** 588 | ```bash 589 | ruff check . 590 | ruff format . 591 | mypy . 592 | ``` 593 | 594 | ### Testing 595 | 596 | Run tests: 597 | ```bash 598 | pytest 599 | ``` 600 | 601 | ## Architecture 602 | 603 | The Polymarket Agents architecture features modular components that can be maintained and extended by individual community members. 604 | 605 | ### APIs 606 | 607 | Polymarket Agents connectors standardize data sources and order types. 608 | 609 | - **`Chroma.py`**: Chroma DB for vectorizing news sources and other API data. Developers are able to add their own vector database implementations. 610 | 611 | - **`Gamma.py`**: Defines `GammaMarketClient` class, which interfaces with the Polymarket Gamma API to fetch and parse market and event metadata. Methods to retrieve current and tradable markets, as well as defined information on specific markets and events. 612 | 613 | - **`Polymarket.py`**: Defines a Polymarket class that interacts with the Polymarket API to retrieve and manage market and event data, and to execute orders on the Polymarket DEX. It includes methods for API key initialization, market and event data retrieval, and trade execution. The file also provides utility functions for building and signing orders, as well as examples for testing API interactions. 614 | 615 | - **`Objects.py`**: Data models using Pydantic; representations for trades, markets, events, and related entities. 616 | 617 | ### Scripts 618 | 619 | Files for managing your local environment, server set-up to run the application remotely, and CLI for end-user commands. 620 | 621 | **`cli.py`** is the primary user interface for the repo. Users can run various commands to interact with the Polymarket API, retrieve relevant news articles, query local data, send data/prompts to LLMs, and execute trades in Polymarkets. 622 | 623 | Commands follow this format: 624 | ```bash 625 | python -m scripts.python.cli [options] 626 | ``` 627 | 628 | ## Contributing 629 | 630 | If you would like to contribute to this project, please follow these steps: 631 | 632 | 1. Fork the repository. 633 | 2. Create a new branch. 634 | 3. Make your changes. 635 | 4. Submit a pull request. 636 | 637 | ### Code Quality 638 | 639 | Before making contributions, please: 640 | 641 | 1. Install development dependencies: `pip install -e ".[dev]"` 642 | 2. Set up pre-commit hooks: `pre-commit install` 643 | 3. Run linting: `ruff check . && ruff format .` 644 | 4. Run type checking: `mypy .` 645 | 646 | ## Prediction Markets Reading 647 | 648 | - Prediction Markets: Bottlenecks and the Next Major Unlocks, Mikey 0x: https://mirror.xyz/1kx.eth/jnQhA56Kx9p3RODKiGzqzHGGEODpbskivUUNdd7hwh0 649 | - The promise and challenges of crypto + AI applications, Vitalik Buterin: https://vitalik.eth.limo/general/2024/01/30/cryptoai.html 650 | - Superforecasting: How to Upgrade Your Company's Judgement, Schoemaker and Tetlock: https://hbr.org/2016/05/superforecasting-how-to-upgrade-your-companys-judgment 651 | 652 | ## Contact 653 | 654 | For questions, support, or inquiries: 655 | 656 | - **Twitter/X**: [@blacksky_jose](https://x.com/blacksky_jose) 657 | - **Telegram**: [@blacksky_jose](https://t.me/blacksky_jose) 658 | - **GitHub Issues**: [Open an issue](https://github.com/BlackSky-Jose/PolyMarket-AI-agent-trading.git/issues) 659 | 660 | Enjoy using the CLI application! If you encounter any issues, feel free to open an issue on the repository or reach out through the contact channels above. 661 | 662 | **Important Legal Notice**: Before using this software for trading, please review and comply with: 663 | - Polymarket Terms of Service 664 | - Your local jurisdiction's regulations regarding prediction markets 665 | - Cryptocurrency trading regulations in your area 666 | 667 | The developers of this software are not responsible for any losses incurred through the use of this trading agent. Use at your own risk. 668 | -------------------------------------------------------------------------------- /agents/polymarket/polymarket.py: -------------------------------------------------------------------------------- 1 | # core polymarket api 2 | # https://github.com/Polymarket/py-clob-client/tree/main/examples 3 | 4 | import os 5 | import pdb 6 | import time 7 | import ast 8 | import requests 9 | 10 | from dotenv import load_dotenv 11 | 12 | from web3 import Web3 13 | from web3.constants import MAX_INT 14 | from web3.middleware import geth_poa_middleware 15 | 16 | import httpx 17 | from py_clob_client.client import ClobClient 18 | from py_clob_client.clob_types import ApiCreds 19 | from py_clob_client.constants import AMOY, POLYGON 20 | from py_order_utils.builders import OrderBuilder 21 | from py_order_utils.model import OrderData 22 | from py_order_utils.signer import Signer 23 | from py_clob_client.clob_types import ( 24 | OrderArgs, 25 | MarketOrderArgs, 26 | OrderType, 27 | OrderBookSummary, 28 | ) 29 | from py_clob_client.order_builder.constants import BUY 30 | 31 | from agents.utils.objects import SimpleMarket, SimpleEvent 32 | 33 | load_dotenv() 34 | 35 | 36 | class Polymarket: 37 | def __init__(self) -> None: 38 | self.gamma_url = "https://gamma-api.polymarket.com" 39 | self.gamma_markets_endpoint = self.gamma_url + "/markets" 40 | self.gamma_events_endpoint = self.gamma_url + "/events" 41 | 42 | self.clob_url = "https://clob.polymarket.com" 43 | self.clob_auth_endpoint = self.clob_url + "/auth/api-key" 44 | 45 | self.chain_id = 137 # POLYGON 46 | self.private_key = os.getenv("POLYGON_WALLET_PRIVATE_KEY") 47 | self.polygon_rpc = "https://polygon-rpc.com" 48 | self.w3 = Web3(Web3.HTTPProvider(self.polygon_rpc)) 49 | 50 | self.exchange_address = "0x4bfb41d5b3570defd03c39a9a4d8de6bd8b8982e" 51 | self.neg_risk_exchange_address = "0xC5d563A36AE78145C45a50134d48A1215220f80a" 52 | 53 | self.erc20_approve = """[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"Blacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newRescuer","type":"address"}],"name":"RescuerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"UnBlacklisted","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"inputs":[],"name":"APPROVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BLACKLISTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DECREASE_ALLOWANCE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INCREASE_ALLOWANCE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"META_TRANSACTION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESCUER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAW_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"approveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"enum GasAbstraction.AuthorizationState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"blacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blacklisters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"decrement","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"decreaseAllowanceWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"increment","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"increaseAllowanceWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newName","type":"string"},{"internalType":"string","name":"newSymbol","type":"string"},{"internalType":"uint8","name":"newDecimals","type":"uint8"},{"internalType":"address","name":"childChainManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenContract","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescuers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newName","type":"string"},{"internalType":"string","name":"newSymbol","type":"string"}],"name":"updateMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"withdrawWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"}]""" 54 | self.erc1155_set_approval = """[{"inputs": [{ "internalType": "address", "name": "operator", "type": "address" },{ "internalType": "bool", "name": "approved", "type": "bool" }],"name": "setApprovalForAll","outputs": [],"stateMutability": "nonpayable","type": "function"}]""" 55 | 56 | self.usdc_address = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" 57 | self.ctf_address = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045" 58 | 59 | self.web3 = Web3(Web3.HTTPProvider(self.polygon_rpc)) 60 | self.web3.middleware_onion.inject(geth_poa_middleware, layer=0) 61 | 62 | self.usdc = self.web3.eth.contract( 63 | address=self.usdc_address, abi=self.erc20_approve 64 | ) 65 | self.ctf = self.web3.eth.contract( 66 | address=self.ctf_address, abi=self.erc1155_set_approval 67 | ) 68 | 69 | self._init_api_keys() 70 | self._init_approvals(False) 71 | 72 | def _init_api_keys(self) -> None: 73 | self.client = ClobClient( 74 | self.clob_url, key=self.private_key, chain_id=self.chain_id 75 | ) 76 | self.credentials = self.client.create_or_derive_api_creds() 77 | self.client.set_api_creds(self.credentials) 78 | # print(self.credentials) 79 | 80 | def _init_approvals(self, run: bool = False) -> None: 81 | if not run: 82 | return 83 | 84 | priv_key = self.private_key 85 | pub_key = self.get_address_for_private_key() 86 | chain_id = self.chain_id 87 | web3 = self.web3 88 | nonce = web3.eth.get_transaction_count(pub_key) 89 | usdc = self.usdc 90 | ctf = self.ctf 91 | 92 | # CTF Exchange 93 | raw_usdc_approve_txn = usdc.functions.approve( 94 | "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", int(MAX_INT, 0) 95 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 96 | signed_usdc_approve_tx = web3.eth.account.sign_transaction( 97 | raw_usdc_approve_txn, private_key=priv_key 98 | ) 99 | send_usdc_approve_tx = web3.eth.send_raw_transaction( 100 | signed_usdc_approve_tx.raw_transaction 101 | ) 102 | usdc_approve_tx_receipt = web3.eth.wait_for_transaction_receipt( 103 | send_usdc_approve_tx, 600 104 | ) 105 | print(usdc_approve_tx_receipt) 106 | 107 | nonce = web3.eth.get_transaction_count(pub_key) 108 | 109 | raw_ctf_approval_txn = ctf.functions.setApprovalForAll( 110 | "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", True 111 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 112 | signed_ctf_approval_tx = web3.eth.account.sign_transaction( 113 | raw_ctf_approval_txn, private_key=priv_key 114 | ) 115 | send_ctf_approval_tx = web3.eth.send_raw_transaction( 116 | signed_ctf_approval_tx.raw_transaction 117 | ) 118 | ctf_approval_tx_receipt = web3.eth.wait_for_transaction_receipt( 119 | send_ctf_approval_tx, 600 120 | ) 121 | print(ctf_approval_tx_receipt) 122 | 123 | nonce = web3.eth.get_transaction_count(pub_key) 124 | 125 | # Neg Risk CTF Exchange 126 | raw_usdc_approve_txn = usdc.functions.approve( 127 | "0xC5d563A36AE78145C45a50134d48A1215220f80a", int(MAX_INT, 0) 128 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 129 | signed_usdc_approve_tx = web3.eth.account.sign_transaction( 130 | raw_usdc_approve_txn, private_key=priv_key 131 | ) 132 | send_usdc_approve_tx = web3.eth.send_raw_transaction( 133 | signed_usdc_approve_tx.raw_transaction 134 | ) 135 | usdc_approve_tx_receipt = web3.eth.wait_for_transaction_receipt( 136 | send_usdc_approve_tx, 600 137 | ) 138 | print(usdc_approve_tx_receipt) 139 | 140 | nonce = web3.eth.get_transaction_count(pub_key) 141 | 142 | raw_ctf_approval_txn = ctf.functions.setApprovalForAll( 143 | "0xC5d563A36AE78145C45a50134d48A1215220f80a", True 144 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 145 | signed_ctf_approval_tx = web3.eth.account.sign_transaction( 146 | raw_ctf_approval_txn, private_key=priv_key 147 | ) 148 | send_ctf_approval_tx = web3.eth.send_raw_transaction( 149 | signed_ctf_approval_tx.raw_transaction 150 | ) 151 | ctf_approval_tx_receipt = web3.eth.wait_for_transaction_receipt( 152 | send_ctf_approval_tx, 600 153 | ) 154 | print(ctf_approval_tx_receipt) 155 | 156 | nonce = web3.eth.get_transaction_count(pub_key) 157 | 158 | # Neg Risk Adapter 159 | raw_usdc_approve_txn = usdc.functions.approve( 160 | "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296", int(MAX_INT, 0) 161 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 162 | signed_usdc_approve_tx = web3.eth.account.sign_transaction( 163 | raw_usdc_approve_txn, private_key=priv_key 164 | ) 165 | send_usdc_approve_tx = web3.eth.send_raw_transaction( 166 | signed_usdc_approve_tx.raw_transaction 167 | ) 168 | usdc_approve_tx_receipt = web3.eth.wait_for_transaction_receipt( 169 | send_usdc_approve_tx, 600 170 | ) 171 | print(usdc_approve_tx_receipt) 172 | 173 | nonce = web3.eth.get_transaction_count(pub_key) 174 | 175 | raw_ctf_approval_txn = ctf.functions.setApprovalForAll( 176 | "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296", True 177 | ).build_transaction({"chainId": chain_id, "from": pub_key, "nonce": nonce}) 178 | signed_ctf_approval_tx = web3.eth.account.sign_transaction( 179 | raw_ctf_approval_txn, private_key=priv_key 180 | ) 181 | send_ctf_approval_tx = web3.eth.send_raw_transaction( 182 | signed_ctf_approval_tx.raw_transaction 183 | ) 184 | ctf_approval_tx_receipt = web3.eth.wait_for_transaction_receipt( 185 | send_ctf_approval_tx, 600 186 | ) 187 | print(ctf_approval_tx_receipt) 188 | 189 | def get_all_markets(self) -> "list[SimpleMarket]": 190 | markets = [] 191 | res = httpx.get(self.gamma_markets_endpoint) 192 | if res.status_code == 200: 193 | for market in res.json(): 194 | try: 195 | market_data = self.map_api_to_market(market) 196 | markets.append(SimpleMarket(**market_data)) 197 | except Exception as e: 198 | print(e) 199 | pass 200 | return markets 201 | 202 | def filter_markets_for_trading(self, markets: "list[SimpleMarket]"): 203 | tradeable_markets = [] 204 | for market in markets: 205 | if market.active: 206 | tradeable_markets.append(market) 207 | return tradeable_markets 208 | 209 | def get_market(self, token_id: str) -> SimpleMarket: 210 | params = {"clob_token_ids": token_id} 211 | res = httpx.get(self.gamma_markets_endpoint, params=params) 212 | if res.status_code == 200: 213 | data = res.json() 214 | market = data[0] 215 | return self.map_api_to_market(market, token_id) 216 | 217 | def map_api_to_market(self, market, token_id: str = "") -> SimpleMarket: 218 | market = { 219 | "id": int(market["id"]), 220 | "question": market["question"], 221 | "end": market["endDate"], 222 | "description": market["description"], 223 | "active": market["active"], 224 | # "deployed": market["deployed"], 225 | "funded": market["funded"], 226 | "rewardsMinSize": float(market["rewardsMinSize"]), 227 | "rewardsMaxSpread": float(market["rewardsMaxSpread"]), 228 | # "volume": float(market["volume"]), 229 | "spread": float(market["spread"]), 230 | "outcomes": str(market["outcomes"]), 231 | "outcome_prices": str(market["outcomePrices"]), 232 | "clob_token_ids": str(market["clobTokenIds"]), 233 | } 234 | if token_id: 235 | market["clob_token_ids"] = token_id 236 | return market 237 | 238 | def get_all_events(self) -> "list[SimpleEvent]": 239 | events = [] 240 | res = httpx.get(self.gamma_events_endpoint) 241 | if res.status_code == 200: 242 | print(len(res.json())) 243 | for event in res.json(): 244 | try: 245 | print(1) 246 | event_data = self.map_api_to_event(event) 247 | events.append(SimpleEvent(**event_data)) 248 | except Exception as e: 249 | print(e) 250 | pass 251 | return events 252 | 253 | def map_api_to_event(self, event) -> SimpleEvent: 254 | description = event["description"] if "description" in event.keys() else "" 255 | return { 256 | "id": int(event["id"]), 257 | "ticker": event["ticker"], 258 | "slug": event["slug"], 259 | "title": event["title"], 260 | "description": description, 261 | "active": event["active"], 262 | "closed": event["closed"], 263 | "archived": event["archived"], 264 | "new": event["new"], 265 | "featured": event["featured"], 266 | "restricted": event["restricted"], 267 | "end": event["endDate"], 268 | "markets": ",".join([x["id"] for x in event["markets"]]), 269 | } 270 | 271 | def filter_events_for_trading( 272 | self, events: "list[SimpleEvent]" 273 | ) -> "list[SimpleEvent]": 274 | tradeable_events = [] 275 | for event in events: 276 | if ( 277 | event.active 278 | and not event.restricted 279 | and not event.archived 280 | and not event.closed 281 | ): 282 | tradeable_events.append(event) 283 | return tradeable_events 284 | 285 | def get_all_tradeable_events(self) -> "list[SimpleEvent]": 286 | all_events = self.get_all_events() 287 | return self.filter_events_for_trading(all_events) 288 | 289 | def get_sampling_simplified_markets(self) -> "list[SimpleEvent]": 290 | markets = [] 291 | raw_sampling_simplified_markets = self.client.get_sampling_simplified_markets() 292 | for raw_market in raw_sampling_simplified_markets["data"]: 293 | token_one_id = raw_market["tokens"][0]["token_id"] 294 | market = self.get_market(token_one_id) 295 | markets.append(market) 296 | return markets 297 | 298 | def get_orderbook(self, token_id: str) -> OrderBookSummary: 299 | return self.client.get_order_book(token_id) 300 | 301 | def get_orderbook_price(self, token_id: str) -> float: 302 | return float(self.client.get_price(token_id)) 303 | 304 | def get_address_for_private_key(self): 305 | account = self.w3.eth.account.from_key(str(self.private_key)) 306 | return account.address 307 | 308 | def build_order( 309 | self, 310 | market_token: str, 311 | amount: float, 312 | nonce: str = str(round(time.time())), # for cancellations 313 | side: str = "BUY", 314 | expiration: str = "0", # timestamp after which order expires 315 | ): 316 | signer = Signer(self.private_key) 317 | builder = OrderBuilder(self.exchange_address, self.chain_id, signer) 318 | 319 | buy = side == "BUY" 320 | side = 0 if buy else 1 321 | maker_amount = amount if buy else 0 322 | taker_amount = amount if not buy else 0 323 | order_data = OrderData( 324 | maker=self.get_address_for_private_key(), 325 | tokenId=market_token, 326 | makerAmount=maker_amount, 327 | takerAmount=taker_amount, 328 | feeRateBps="1", 329 | nonce=nonce, 330 | side=side, 331 | expiration=expiration, 332 | ) 333 | order = builder.build_signed_order(order_data) 334 | return order 335 | 336 | def execute_order(self, price, size, side, token_id) -> str: 337 | return self.client.create_and_post_order( 338 | OrderArgs(price=price, size=size, side=side, token_id=token_id) 339 | ) 340 | 341 | def execute_market_order(self, market, amount) -> str: 342 | token_id = ast.literal_eval(market[0].dict()["metadata"]["clob_token_ids"])[1] 343 | order_args = MarketOrderArgs( 344 | token_id=token_id, 345 | amount=amount, 346 | ) 347 | signed_order = self.client.create_market_order(order_args) 348 | print("Execute market order... signed_order ", signed_order) 349 | resp = self.client.post_order(signed_order, orderType=OrderType.FOK) 350 | print(resp) 351 | print("Done!") 352 | return resp 353 | 354 | def get_usdc_balance(self) -> float: 355 | balance_res = self.usdc.functions.balanceOf( 356 | self.get_address_for_private_key() 357 | ).call() 358 | return float(balance_res / 10e5) 359 | 360 | 361 | def test(): 362 | host = "https://clob.polymarket.com" 363 | key = os.getenv("POLYGON_WALLET_PRIVATE_KEY") 364 | print(key) 365 | chain_id = POLYGON 366 | 367 | # Create CLOB client and get/set API credentials 368 | client = ClobClient(host, key=key, chain_id=chain_id) 369 | client.set_api_creds(client.create_or_derive_api_creds()) 370 | 371 | creds = ApiCreds( 372 | api_key=os.getenv("CLOB_API_KEY"), 373 | api_secret=os.getenv("CLOB_SECRET"), 374 | api_passphrase=os.getenv("CLOB_PASS_PHRASE"), 375 | ) 376 | chain_id = AMOY 377 | client = ClobClient(host, key=key, chain_id=chain_id, creds=creds) 378 | 379 | print(client.get_markets()) 380 | print(client.get_simplified_markets()) 381 | print(client.get_sampling_markets()) 382 | print(client.get_sampling_simplified_markets()) 383 | print(client.get_market("condition_id")) 384 | 385 | print("Done!") 386 | 387 | 388 | def gamma(): 389 | url = "https://gamma-com" 390 | markets_url = url + "/markets" 391 | res = httpx.get(markets_url) 392 | code = res.status_code 393 | if code == 200: 394 | markets: list[SimpleMarket] = [] 395 | data = res.json() 396 | for market in data: 397 | try: 398 | market_data = { 399 | "id": int(market["id"]), 400 | "question": market["question"], 401 | # "start": market['startDate'], 402 | "end": market["endDate"], 403 | "description": market["description"], 404 | "active": market["active"], 405 | "deployed": market["deployed"], 406 | "funded": market["funded"], 407 | # "orderMinSize": float(market['orderMinSize']) if market['orderMinSize'] else 0, 408 | # "orderPriceMinTickSize": float(market['orderPriceMinTickSize']), 409 | "rewardsMinSize": float(market["rewardsMinSize"]), 410 | "rewardsMaxSpread": float(market["rewardsMaxSpread"]), 411 | "volume": float(market["volume"]), 412 | "spread": float(market["spread"]), 413 | "outcome_a": str(market["outcomes"][0]), 414 | "outcome_b": str(market["outcomes"][1]), 415 | "outcome_a_price": str(market["outcomePrices"][0]), 416 | "outcome_b_price": str(market["outcomePrices"][1]), 417 | } 418 | markets.append(SimpleMarket(**market_data)) 419 | except Exception as err: 420 | print(f"error {err} for market {id}") 421 | pdb.set_trace() 422 | else: 423 | raise Exception() 424 | 425 | 426 | def main(): 427 | # auth() 428 | # test() 429 | # gamma() 430 | print(Polymarket().get_all_events()) 431 | 432 | 433 | if __name__ == "__main__": 434 | load_dotenv() 435 | 436 | p = Polymarket() 437 | 438 | # k = p.get_api_key() 439 | # m = p.get_sampling_simplified_markets() 440 | 441 | # print(m) 442 | # m = p.get_market('11015470973684177829729219287262166995141465048508201953575582100565462316088') 443 | 444 | # t = m[0]['token_id'] 445 | # o = p.get_orderbook(t) 446 | # pdb.set_trace() 447 | 448 | """ 449 | 450 | (Pdb) pprint(o) 451 | OrderBookSummary( 452 | market='0x26ee82bee2493a302d21283cb578f7e2fff2dd15743854f53034d12420863b55', 453 | asset_id='11015470973684177829729219287262166995141465048508201953575582100565462316088', 454 | bids=[OrderSummary(price='0.01', size='600005'), OrderSummary(price='0.02', size='200000'), ... 455 | asks=[OrderSummary(price='0.99', size='100000'), OrderSummary(price='0.98', size='200000'), ... 456 | ) 457 | 458 | """ 459 | 460 | # https://polygon-rpc.com 461 | 462 | test_market_token_id = ( 463 | "101669189743438912873361127612589311253202068943959811456820079057046819967115" 464 | ) 465 | test_market_data = p.get_market(test_market_token_id) 466 | 467 | # test_size = 0.0001 468 | test_size = 1 469 | test_side = BUY 470 | test_price = float(ast.literal_eval(test_market_data["outcome_prices"])[0]) 471 | 472 | # order = p.execute_order( 473 | # test_price, 474 | # test_size, 475 | # test_side, 476 | # test_market_token_id, 477 | # ) 478 | 479 | # order = p.execute_market_order(test_price, test_market_token_id) 480 | 481 | balance = p.get_usdc_balance() 482 | --------------------------------------------------------------------------------