Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions AUDIT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Audit and Fix Plan — Polymarket MVP Phase 2 Live Path

Scope: Review all Phase 2 live-path changes, unify data paths, ensure safety gates, and prepare a clean, auditable path toward Phase 3 live pilot.

Summary of issues found (high level):
- Unused/legacy imports in Phase 2 live index (ingest-live import not used in runtime path).
- Inconsistent wallet/config gating across modules; added PHASE2_KILL_SWITCH and wallet-config checks, but some paths may still bypass gates.
- Live-bet path currently relies on sandbox logic but still retains a live bet call in some patches; ensure all live bets are gated behind edge checks and kill switches.
- Phase 2 live data ingestion tables in Supabase may not align with Phase 2 DB migrations; ensure table naming is consistent (polymarket_phase2_prices, polymarket_phase2_markets, polymarket_trades).
- Tests exist for edge logic but CI smoke tests are not fully wired; need CI to run a sandbox ingest + edge-run with no money.
- Wallet signer scaffolds are in place but not wired for a real wallet; ensure configuration guards are explicit and secure for future live path usage.
- Documentation gaps: Phase 2 gating protocol, how to enable live mode, and what to test in sandbox should be documented clearly in AUDIT and PR bodies.

Proposed fixes (high-level):
- Remove unused imports and dead code paths; ensure a clean baseline in main Phase 2 index.
- Strengthen gating: ensure PHASE2_KILL_SWITCH, PHASE2_LIVE_MODE, PHASE2_LIVE_WALLET_CONFIGURED are all checked in every live path, and log when a path is skipped.
- Align DB schema expectations with existing migrations and ensure the Phase 2 patch creates/uses the expected tables consistently.
- Add a robust, minimal test harness for Phase 2 edge logic with deterministic scenarios.
- Document Phase 2 live gating clearly in a single place (FIX_PLAN.md and PR body templates).
- Prepare Phase 3 Phase PR body with onboarding, compliance steps, and a test plan.

Acceptance criteria:
- [ ] Phase 2 index.js has no unused imports and all live paths are gated by a kill switch and wallet config check.
- [ ] All Phase 2 data ingestion and signal generation flows are auditable in Supabase with clear log entries.
- [ ] Phase 2 tests pass locally or in CI for edge logic; no money is involved.
- [ ] Phase 3 gating checklist exists in PR and Phase 3 patch is ready for review.

Owner: You (fintech development strategist) and the patching agent.
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
```

## Plan: Phase 1 – Polymarket MVP Data Ingestion (Zero-Cost)
- Create a new feature branch (feat/polymarket-mvp-phase1) with Phase 1 data ingestion scaffold
- Ingest mock Polymarket markets and daily prices into Supabase, using a lightweight Node.js app (polymarket-mvp-phase1)
- Add a minimal GitHub Actions workflow (workflows/polymarket-mvp-phase1.yaml) to schedule ingestion on CI using free tier
- Wire a basic data adapter (polymarket-mvp-phase1/feed.js) to generate sample market data for MVP
- Ensure env vars for Supabase and any mock Polymarket feed are documented in .env.local.example

## When Building Features
1. Always start with the TypeScript types/interfaces
2. Build the agent tool first, then the agent, then the API route, then the UI
Expand Down
32 changes: 32 additions & 0 deletions FIX_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Phase 2 Live Patch Fix Plan (auditable, incremental)

1) Phase 2 – Gate enforcement
- Ensure PHASE2_KILL_SWITCH, PHASE2_LIVE_MODE, PHASE2_LIVE_WALLET_CONFIGURED are checked on every live path
- Add logs when gates block progress

2) Phase 2 – Code Hygiene
- Remove unused imports (ingest-live) from polymarket-mvp-phase2-live/index.js
- Normalize environment variable handling; convert string booleans to booleans robustly

3) Phase 2 – Data path alignment
- Align live ingestion tables to existing naming conventions: polymarket_phase2_markets, polymarket_phase2_prices, polymarket_trades
- Ensure ingestion writes to these tables consistently

4) Phase 2 – Edge logic robustness
- Ensure edge and betFraction from edge.js are always numeric and clamped
- Add a small unit test to validate edge outputs (Yes/No) for a couple of market scenarios

5) Phase 2 – Tests and CI
- Add a small CI smoke test that runs ingestion and a single signal generation in sandbox mode
- Ensure tests don’t perform any live bets or external API calls

6) Phase 3 readiness
- Add Phase 3 governance checklist in PR body (privacy, compliance, wallet onboarding)
- Add a Phase 3 pilot plan with a safe, auditable live path gated behind compliance

Acceptance criteria for Phase 2 fixes:
- All gating variables defined and used consistently; no bypass paths
- No unused imports anywhere in Phase 2
- Data ingestion and signals are persisted in a stable schema
- Tests pass and CI can verify sandbox end-to-end
- Phase 3 patch will be ready for governance review
16 changes: 16 additions & 0 deletions polymarket-mvp-phase1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Polymarket MVP Phase 1 – Data Ingestion

Goal: Introduce a zero‑cost, cloud‑hosted data ingestion path that seeds a Phase 1 market list and daily prices into Supabase, enabling a downstream signal loop in Phase 2.

What this patch adds:
- A small Node.js app (polymarket-mvp-phase1) that upserts mock Polymarket markets and ingests daily prices into Supabase.
- A mock data feed (feed.js) for Phase 1 to stand in for live Polymarket data during MVP validation.
- A minimal ingest script (ingest.js) to show how to seed data programmatically.
- An Ingest workflow skeleton (workflows/polymarket-mvp-phase1.yaml) to schedule ingestion in CI (GitHub Actions) for free hosting scenarios.

Run locally (optional, for testing):
- npm i in polymarket-mvp-phase1 directory
- Copy .env.local.example to .env.local and fill in real values when you’re ready
- node index.js to seed markets and prices for the day

Note: This is Phase 1 of a multi‑phase MVP. Do not rely on this for trading; it’s for data ingestion testing and architecture validation only.
9 changes: 9 additions & 0 deletions polymarket-mvp-phase1/feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Simple mock market feed for Phase 1 MVP ingestion
function generateSampleMarkets() {
return [
{ market_id: 'POLY_PHASE1_MKT_1', name: 'Polymarket MVP Demo 1 Yes/No', yesPrice: 0.58, noPrice: 0.42 },
{ market_id: 'POLY_PHASE1_MKT_2', name: 'Polymarket MVP Demo 2 Yes/No', yesPrice: 0.32, noPrice: 0.68 }
]
}

module.exports = { generateSampleMarkets }
32 changes: 32 additions & 0 deletions polymarket-mvp-phase1/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require('dotenv').config()

const { createClient } = require('@supabase/supabase-js')
const feed = require('./feed')

const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

async function main() {
// Ensure markets exist
const markets = feed.generateSampleMarkets()
for (const m of markets) {
try {
await supabase.from('polymarket_phase1_markets').upsert([{ market_id: m.market_id, name: m.name }])
} catch (e) {
console.error('Failed to upsert market', m.market_id, e)
}
}
// Ingest today's prices for each market
const today = new Date().toISOString().slice(0, 10)
const prices = markets.map(m => ({ market_id: m.market_id, date: today, yes_price: m.yesPrice, no_price: m.noPrice }))
try {
await supabase.from('polymarket_phase1_prices').insert(prices)
} catch (e) {
console.error('Failed to insert prices', e)
}
console.log('Phase1 ingestion complete for', today)
}

main().catch(console.error)
21 changes: 21 additions & 0 deletions polymarket-mvp-phase1/ingest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require('dotenv').config()
const { createClient } = require('@supabase/supabase-js')
const feed = require('./feed')

const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

async function main() {
const markets = feed.generateSampleMarkets()
for (const m of markets) {
await supabase.from('polymarket_phase1_markets').upsert([{ market_id: m.market_id, name: m.name }])
}
const today = new Date().toISOString().slice(0, 10)
const prices = markets.map(m => ({ market_id: m.market_id, date: today, yes_price: m.yesPrice, no_price: m.noPrice }))
await supabase.from('polymarket_phase1_prices').insert(prices)
console.log('Phase1 ingest script executed for', today)
}

main().catch(console.error)
15 changes: 15 additions & 0 deletions polymarket-mvp-phase1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"+name+": "polymarket-mvp-phase1",
"version": "0.1.0",
"private": true,
"type": "commonjs",
"scripts": {
"start": "node index.js",
"ingest": "node ingest.js"
},
"dependencies": {
"dotenv": "^16.0.3",
"axios": "^1.5.0",
"@supabase/supabase-js": "^1.35.0"
}
}
16 changes: 16 additions & 0 deletions polymarket-mvp-phase2-live/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Polymarket MVP Phase 2 – Live Data + Phase 2 Live Path

Goal: Move Phase 1 ingestion scaffolding toward real data and a live-able trading loop, while keeping a safe, audited sandbox for testing. Phase 2 adds a live data adapter with a toggle for a live-mode that remains off by default and guarded by a kill switch.

What’s included:
- Live data ingestion path (data-live-feed.js) with a fallback to Phase 1 mock data
- Live trading scaffolding with a toggle (LIVE_MODE) to enable simulated live-execution pathways
- Live adapter stubs for wallet/signing (live-adapter.js) to show how real execution would map to a wallet
- Ingest and data routing hooks to Supabase Phase 2 tables

How to run:
- Set PHASE2_LIVE_MODE=true to enable live mode (requires wallet details and governance checks)
- Configure SUPABASE_* env vars and POLYMARKET_LIVE_FEED_ENDPOINT if you have a live feed
- Run: node index.js or npm run start in the phase 2 live folder

Safety: This is still sandboxed; money is not real unless you explicitly enable live mode with all compliance checks.
29 changes: 29 additions & 0 deletions polymarket-mvp-phase2-live/data-live-feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Live data adapter for Phase 2 (Polymarket live feed).
// If a live feed endpoint is configured, fetch from it; otherwise fallback to Phase 1 mock feed.
const axios = require('axios')

async function fetchLiveMarkets() {
const endpoint = process.env.POLYMARKET_LIVE_FEED_ENDPOINT
if (!endpoint) {
// Fallback to Phase 1 mock data (stable for now)
return [
{ market_id: 'POLY_PHASE2_MKT_1', name: 'Polymarket MVP Phase 2 Demo 1 Yes/No', yesPrice: 0.58, noPrice: 0.42 },
{ market_id: 'POLY_PHASE2_MKT_2', name: 'Polymarket MVP Phase 2 Demo 2 Yes/No', yesPrice: 0.32, noPrice: 0.68 }
]
}
try {
const r = await axios.get(endpoint)
const markets = (r.data.markets || []).map(mm => ({
market_id: mm.id || mm.market_id,
name: mm.name || mm.market,
yesPrice: mm.yesPrice != null ? mm.yesPrice : mm.yes_price,
noPrice: mm.noPrice != null ? mm.noPrice : mm.no_price
}))
return markets
} catch (e) {
console.error('Live feed fetch failed', e)
return []
}
}

module.exports = { fetchLiveMarkets }
14 changes: 14 additions & 0 deletions polymarket-mvp-phase2-live/edge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Edge and bet sizing engine for Phase 2 live path (sandboxed)
function clamp(n, min, max) {
return Math.max(min, Math.min(max, n))
}

function computeEdge(market, qModel, signal) {
const pYes = market.yesPrice
const edge = signal === 'Yes' ? Math.max(0, qModel - pYes) : (signal === 'No' ? Math.max(0, pYes - qModel) : 0)
// Simple bet fraction derived from edge; cap at 25%
const betFraction = clamp(edge * 1.5, 0, 0.25)
return { edge, betFraction }
}

module.exports = { computeEdge }
102 changes: 102 additions & 0 deletions polymarket-mvp-phase2-live/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require('dotenv').config()

const { createClient } = require('@supabase/supabase-js')
// ingestLive is no longer required at runtime in Phase 2 live path; ingestion is driven by data-live-feed and CI
const liveFeed = require('./data-live-feed')
const llmLive = require('./llm-live')
const promptModule = require('./prompt-live')
const liveAdapter = require('./live-adapter')
// Configure wallet for the live path if a wallet is provided in env (Phase 2 advance)
try {
require('./live-adapter').configureWalletFromEnvironment()
} catch (e) {
// ignore if not configured yet
}

const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY

let LIVE = (process.env.PHASE2_LIVE_MODE === 'true') || process.argv.includes('--live')
let balance = 10
const edgeModule = require('./edge')
const PHASE2_KILL_SWITCH = (process.env.PHASE2_KILL_SWITCH === 'ON')
const walletConfigured = (() => {
try {
const w = require('./wallet-signer').getWallet()
return !!w
} catch {
return (process.env.PHASE2_LIVE_WALLET_CONFIGURED === 'true')
}
})()
if (!LIVE) {
console.log('Phase 2 live mode is OFF. Running in paper/sandbox mode with live data as available.')
}

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

async function runCycle() {
// 1) fetch live markets (or fallback to Phase 1 mock)
let markets = await liveFeed.fetchLiveMarkets()
if (!markets || markets.length === 0) {
console.log('No live markets fetched; aborting cycle')
return
}
// 2) for each market, fetch live data and generate a signal via LLM
for (const m of markets) {
// Build a minimal prompt from market data
const prompt = promptModule.buildSignalPrompt ? promptModule.buildSignalPrompt(m, new Date(), { yesPrice: m.yesPrice, noPrice: m.noPrice }) : ''
const llmRaw = await llmLive.callLLM(prompt)
const signal = llmLive.parseSignalOutput(llmRaw)
if (signal) {
// Persist signal to Supabase
try {
await supabase.from('polymarket_signals').insert([{ market_id: m.market_id, signal: signal.signal, confidence: signal.confidence, reasoning: signal.reasoning, timestamp: new Date().toISOString() }])
} catch (e) {
console.error('DB write failed', e)
}
// Phase 2 live logic: compute edge and optionally place a sandbox live bet
const pModel = ((signal.confidence !== undefined ? signal.confidence : 50) / 100)
const edgeInfo = edgeModule.computeEdge(m, pModel, signal.signal)
if (LIVE && walletConfigured && edgeInfo && edgeInfo.edge > 0 && balance > 0 && !PHASE2_KILL_SWITCH) {
const betSize = Math.min(balance * edgeInfo.betFraction, balance)
let net = 0
if (signal.signal === 'Yes') {
const eventOccurs = Math.random() < pModel
const payout = betSize / m.yesPrice
net = eventOccurs ? payout - betSize : -betSize
} else if (signal.signal === 'No') {
const eventFalse = Math.random() < (1 - pModel)
const payout = betSize / (1 - m.yesPrice)
net = eventFalse ? payout - betSize : -betSize
}
balance += net
console.log(`[Phase2 Live Sandbox] market=${m.market_id} side=${signal.signal} bet=${betSize.toFixed(4)} net=${net.toFixed(4)} balance=${balance.toFixed(4)}`)
try {
await supabase.from('polymarket_trades').insert([{ market_id: m.market_id, side: signal.signal, bet: betSize, balance: balance, time: new Date().toISOString(), edge: edgeInfo.edge, bet_fraction: edgeInfo.betFraction }])
} catch (e) {
console.error('DB trade log failed', e)
}
} else if (LIVE && !walletConfigured) {
console.log('Live mode enabled but wallet not configured; skipping live bet for', m.market_id)
} else if (LIVE && PHASE2_KILL_SWITCH) {
console.log('Kill switch active; skipping live bets for', m.market_id)
} else if (LIVE) {
console.log('Edge not favorable or balance zero; skipping live bet for', m.market_id)
}
// Live bets are executed via the edge-based sandbox logic above when edge > 0.
} else {
console.log('LLM output invalid for market', m.market_id)
}
}
}

async function main() {
await runCycle()
if (LIVE) {
console.log('Live phase executed; to automate, configure CI to trigger this script on schedule')
} else {
console.log('Phase 2 sandbox cycle complete')
}
}

main().catch(console.error)
25 changes: 25 additions & 0 deletions polymarket-mvp-phase2-live/ingest-live.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require('dotenv').config()
const { createClient } = require('@supabase/supabase-js')
const feed = require('./data-live-feed')

const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

async function main() {
const markets = await feed.fetchLiveMarkets()
if (!markets || markets.length === 0) {
console.log('No markets to ingest')
return
}
for (const m of markets) {
await supabase.from('polymarket_phase2_markets').upsert([{ market_id: m.market_id, name: m.name }])
}
const today = new Date().toISOString().slice(0, 10)
const prices = markets.map(m => ({ market_id: m.market_id, date: today, yes_price: m.yesPrice, no_price: m.noPrice }))
await supabase.from('polymarket_phase2_prices').insert(prices)
console.log('Phase2 live ingestion complete for', today)
}

main().catch(console.error)
Loading