Skip to content
42 changes: 42 additions & 0 deletions polymarket_bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# PolyMarket Finance Bot (Scaffold)

Khung xương bot tự động cho PolyMarket, dùng Kronos (`model.Kronos`, `model.KronosTokenizer`, `model.KronosPredictor`) để suy luận xác suất xu hướng ngắn hạn.

## Cấu trúc

- `data/collector.py`: lấy market snapshot + lịch sử giá (mock, sẵn sàng thay API thật)
- `model/kronos_adapter.py`: adapter cho Kronos inference
- `decision/engine.py`: EV + Kelly + risk caps
- `execution/executor.py`: place order abstraction + retry
- `monitoring/logger.py`: logging giao dịch và metrics
- `main.py`: orchestration pipeline end-to-end

## Chạy demo

### Cách 1 (khuyến nghị): từ thư mục root repo `Kronos/`

```bash
python -m polymarket_bot.main
```

### Cách 2: nếu đang đứng trong thư mục `Kronos/polymarket_bot/`

```bash
python -m main
```

## Kiểm tra compile

### Từ root repo `Kronos/`

```bash
python -m py_compile polymarket_bot/main.py polymarket_bot/data/collector.py polymarket_bot/model/kronos_adapter.py polymarket_bot/decision/engine.py polymarket_bot/execution/executor.py polymarket_bot/monitoring/logger.py polymarket_bot/config/settings.py
```

### Từ `Kronos/polymarket_bot/`

```bash
python -m py_compile main.py data/collector.py model/kronos_adapter.py decision/engine.py execution/executor.py monitoring/logger.py config/settings.py
```

> Mặc định đang chạy chế độ `dry_run=True`, không gửi lệnh thật.
1 change: 1 addition & 0 deletions polymarket_bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
1 change: 1 addition & 0 deletions polymarket_bot/config/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
23 changes: 23 additions & 0 deletions polymarket_bot/config/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from dataclasses import dataclass


@dataclass
class RiskConfig:
max_risk_per_trade: float = 0.02
max_daily_loss: float = 0.05
min_liquidity: float = 5000.0
min_probability: float = 0.68


@dataclass
class StrategyConfig:
kelly_fraction_cap: float = 0.25
fee_rate: float = 0.01


@dataclass
class RuntimeConfig:
dry_run: bool = True
device: str = "cpu"
lookback: int = 256
pred_len: int = 1
1 change: 1 addition & 0 deletions polymarket_bot/data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
31 changes: 31 additions & 0 deletions polymarket_bot/data/collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from __future__ import annotations

from dataclasses import dataclass
import random


@dataclass
class MarketState:
market_id: str
yes_price: float
no_price: float
liquidity: float
history_yes_price: list[float]


class PolyMarketCollector:
"""Mock collector; thay bằng PolyMarket API ở bước tích hợp thật."""

def fetch_market_state(self, market_id: str, lookback: int = 256) -> MarketState:
base = 0.55
history = [max(0.01, min(0.99, base + random.uniform(-0.03, 0.03))) for _ in range(lookback)]
yes_price = history[-1]
no_price = 1.0 - yes_price
liquidity = random.uniform(6_000, 20_000)
return MarketState(
market_id=market_id,
yes_price=yes_price,
no_price=no_price,
liquidity=liquidity,
history_yes_price=history,
)
1 change: 1 addition & 0 deletions polymarket_bot/decision/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
38 changes: 38 additions & 0 deletions polymarket_bot/decision/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

from dataclasses import dataclass

from polymarket_bot.config.settings import RiskConfig, StrategyConfig


@dataclass
class TradeDecision:
should_trade: bool
side: str | None
size_fraction: float
expected_value: float
reason: str


class DecisionEngine:
def __init__(self, risk: RiskConfig, strategy: StrategyConfig):
self.risk = risk
self.strategy = strategy

def evaluate(self, prob_yes: float, yes_price: float, liquidity: float) -> TradeDecision:
if liquidity < self.risk.min_liquidity:
return TradeDecision(False, None, 0.0, 0.0, "liquidity_too_low")

if prob_yes < self.risk.min_probability:
return TradeDecision(False, None, 0.0, 0.0, "probability_below_threshold")

b = (1 - yes_price) / yes_price
q = 1 - prob_yes
kelly_f = max(0.0, (b * prob_yes - q) / b) if b > 0 else 0.0
size = min(kelly_f, self.strategy.kelly_fraction_cap, self.risk.max_risk_per_trade)

ev = prob_yes * (1 - yes_price) - (1 - prob_yes) * yes_price - self.strategy.fee_rate
if ev <= 0 or size <= 0:
return TradeDecision(False, None, 0.0, ev, "non_positive_ev_or_size")

return TradeDecision(True, "YES", size, ev, "ok")
1 change: 1 addition & 0 deletions polymarket_bot/execution/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
28 changes: 28 additions & 0 deletions polymarket_bot/execution/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from __future__ import annotations

import time


class ExecutionEngine:
def __init__(self, dry_run: bool = True, max_retries: int = 3):
self.dry_run = dry_run
self.max_retries = max_retries

def place_order(self, market_id: str, side: str, size_fraction: float) -> dict:
for attempt in range(1, self.max_retries + 1):
try:
if self.dry_run:
return {
"status": "simulated_filled",
"market_id": market_id,
"side": side,
"size_fraction": size_fraction,
"attempt": attempt,
}
# TODO: gọi API thật của PolyMarket tại đây
raise NotImplementedError("Live execution not implemented yet")
except Exception as exc: # retry policy
if attempt == self.max_retries:
return {"status": "failed", "error": str(exc), "attempt": attempt}
time.sleep(0.5 * attempt)
return {"status": "failed", "error": "unknown"}
56 changes: 56 additions & 0 deletions polymarket_bot/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from __future__ import annotations

<<<<<<< codex/create-project-instructions-for-poly-market-finance-47wtli
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bro, you committed with merge conflict placeholders??

import sys
from pathlib import Path


# Allow running via `python main.py` when cwd is `Kronos/polymarket_bot`.
if __package__ is None or __package__ == "":
repo_root = Path(__file__).resolve().parents[1]
if str(repo_root) not in sys.path:
sys.path.insert(0, str(repo_root))

=======
>>>>>>> master
from polymarket_bot.config.settings import RiskConfig, RuntimeConfig, StrategyConfig
from polymarket_bot.data.collector import PolyMarketCollector
from polymarket_bot.decision.engine import DecisionEngine
from polymarket_bot.execution.executor import ExecutionEngine
from polymarket_bot.model.kronos_adapter import KronosAdapter
from polymarket_bot.monitoring.logger import TradeLogger


def run_once(market_id: str = "demo_market_1") -> dict:
runtime = RuntimeConfig()
collector = PolyMarketCollector()
predictor = KronosAdapter(device=runtime.device)
decision_engine = DecisionEngine(RiskConfig(), StrategyConfig())
executor = ExecutionEngine(dry_run=runtime.dry_run)
logger = TradeLogger()

state = collector.fetch_market_state(market_id=market_id, lookback=runtime.lookback)
prob_yes = predictor.predict_yes_probability(state.history_yes_price, pred_len=runtime.pred_len)
decision = decision_engine.evaluate(prob_yes=prob_yes, yes_price=state.yes_price, liquidity=state.liquidity)

result = {
"market_id": state.market_id,
"prob_yes": prob_yes,
"yes_price": state.yes_price,
"liquidity": state.liquidity,
"decision": decision.__dict__,
}

if decision.should_trade:
result["execution"] = executor.place_order(
market_id=state.market_id,
side=decision.side or "YES",
size_fraction=decision.size_fraction,
)

logger.log(result)
return result


if __name__ == "__main__":
print(run_once())
1 change: 1 addition & 0 deletions polymarket_bot/model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
26 changes: 26 additions & 0 deletions polymarket_bot/model/kronos_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations

import pandas as pd

from model import Kronos, KronosPredictor, KronosTokenizer


class KronosAdapter:
"""Adapter gói inference từ shiyu-coder/Kronos."""

def __init__(self, device: str = "cpu", max_context: int = 512):
self.device = device
self.tokenizer = KronosTokenizer.from_pretrained("NeoQuasar/Kronos-Tokenizer-base")
self.model = Kronos.from_pretrained("NeoQuasar/Kronos-small")
self.predictor = KronosPredictor(self.model, self.tokenizer, device=device, max_context=max_context)

def predict_yes_probability(self, price_series: list[float], pred_len: int = 1) -> float:
df = pd.DataFrame({"close": price_series})
pred = self.predictor.predict(df, x_timestamp=None, pred_len=pred_len)
current = float(price_series[-1])
next_price = float(pred[0]) if len(pred) else current

# Heuristic mapping price delta -> probability adjustment
delta = next_price - current
p = max(0.01, min(0.99, current + delta))
return p
1 change: 1 addition & 0 deletions polymarket_bot/monitoring/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""PolyMarket bot package."""
17 changes: 17 additions & 0 deletions polymarket_bot/monitoring/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import annotations

import json
from datetime import datetime, timezone


class TradeLogger:
def __init__(self, log_path: str = "polymarket_bot_trades.log"):
self.log_path = log_path

def log(self, payload: dict) -> None:
record = {
"ts": datetime.now(timezone.utc).isoformat(),
**payload,
}
with open(self.log_path, "a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")