Skip to content
Raimbek AlishSoftware engineering

Case study · Team project · QuackHacks 2025

Winner, Polymarket Track — QuackHacks 2025

Poly Predictor Kit

Understanding markets means understanding both the numbers and the conversation.

A Chrome extension and three local Python services that read a Polymarket event: a Gemini summary of what its prices imply, a classifier's read of whether its comments are emotional or rational, and a check for lopsided risk.

Raimbek's roleDeveloped the market summarization and comment-classification pipelines. Team project: Devpost lists 6 creators.

One event, three readingsConceptual illustration

A Polymarket event

  1. Prices
    1. Gamma API
    2. Prompt
    3. Gemini
    4. AI Trade Insight
  2. Prices
    1. Gamma API
    2. Rules
    3. Steamroller warning
  3. Comments
    1. Gamma API
    2. Classifier
    3. Emotional or rational share

Project overview

Winner, Polymarket Track — QuackHacks 2025. One Chrome extension reads a market's numbers with Gemini and its conversation with a TF-IDF and logistic-regression classifier. Raimbek developed both of those pipelines; the extension and the risk check are part of the team's architecture.

Explore the system map Raimbek's contributionSource on GitHub

Raimbek's contribution
The Gemini-powered market summarization and TF-IDF / logistic-regression comment-classification pipelines.
Built with
Python · Flask · Gemini API · Polymarket Gamma API · scikit-learn · Snowflake (Snowpark) · Chrome extension
Status
Built in November 2025 at QuackHacks. Runs locally: the extension calls three Flask services on ports 5000–5002. Not deployed.

Raimbek's contribution

The summarizer and the classifier.

Developed the Gemini-powered market summarization and TF-IDF / Logistic Regression comment-classification pipelines as part of the team project that won the Polymarket Track at QuackHacks 2025.

The numbers

Market summarization pipeline

Finds a Polymarket event through the Gamma API, keeps up to three of its markets, reads their prices as probabilities and asks Gemini for a short insight under fixed rules.

Code: poly_event_ai_summarizer.py · commit 5f34cdf

How the summarizer works

The conversation

Comment-classification pipeline

Gemini labels training comments as emotional, rational or spam; TF-IDF features and logistic regression learn those labels; at runtime the saved model reads an event's comments without calling Gemini.

Code: geminiAutoLabelAssigner.py · train_model_snowflake.py · main_api.py

How the classifier works

The team's architecture

Poly Predictor Kit is a team project. These parts are credited to the team:

  • The Chrome extension and its popup, which call all three services.
  • The Flask service that wraps the summarizer for the extension.
  • The steamroller detector, the risk rule explained under The risk shape.
  • A web front page, not connected to the services.

Devpost listsUmar Turdumambetov · Bekzat Uraimov · Shamil Khamrayev · Akan Abdireshov · Zhanbolot Eraliev · Raimbek Alish

Team from Devpost, read on 2026-10-24 Public record

What it does

Three buttons, three readings of one event.

Open any Polymarket event, open the extension, and choose one of three buttons. Each calls its own Python service on the same machine and shows the answer as text.

  1. The numbers

    “Poly AI Summarizer”

    The event's markets and prices become a structured prompt; Gemini writes a short, hedged summary of what they imply.

    What comes back3–6 sentences of plain text: what current prices imply, one or two risks that could change the odds, both sides of the view, and a reminder that it is not financial advice.

    Raimbek's pipeline

  2. The conversation

    “Emotional Damage Predictor”

    Every comment on the event is labeled by a saved scikit-learn model; the result is the share of emotional against rational comments.

    Its message, as the code writes itFrom comments provided it seems like the event might be based on Emotions: N%

    Raimbek's pipeline

  3. The numbers

    “Steamroller Check”

    A rule, not a model: flags an outcome whose price leaves a tiny upside and a huge downside.

    Its message, as the code writes itSIDE looks like a steamroller trade. Current implied probability is about P%. At this price, one loss wipes roughly N average wins. There are about D days left until resolution.

    Team component

Button names and message formats from popup.html and the services Verified in code

System map

Follow an event through the code

Each line is a pipeline in the repository and each station one of its stages. Choose a station to see what it takes, what it does and what it gives, with a link to its code.

Conceptual illustration Verified in code

  • The numbers: prices and volumes
  • The conversation: comments
  • Stage of Raimbek's pipelines

Market insight

Raimbek's pipeline

The numbers · the “Poly AI Summarizer” button

Market insight · stage 4 of 6

Structured prompt

Takes
The event and its markets.
Does
Parses outcome prices, which the Gamma API stores as JSON strings, and writes each as a price and an implied percentage beside its question, status and volume.
Gives
A plain-text prompt with the market data and the writing rules.

Raimbek's pipelinePart of Raimbek's market summarization pipeline.Code: poly_event_ai_summarizer.py

Comment classifier

Raimbek's pipeline

The conversation · the “Emotional Damage Predictor” button

Steamroller check

Team component

The numbers · the “Steamroller Check” button

Read the system map as text

Market insight (the numbers, “Poly AI Summarizer” button; Raimbek's pipeline)

  1. Extension button. Reads the event's slug from the tab's address and posts it to the local insight service. Team component.
  2. Insight service. Passes the slug to the summarizer module and returns its text, or an error. Team component.
  3. Event and markets. Resolves the event through GET /events/slug/{slug}, /events/{id} or /public-search, then keeps up to three of its markets, open ones first.
  4. Structured prompt. Parses outcome prices, which the Gamma API stores as JSON strings, and writes each as a price and an implied percentage beside its question, status and volume.
  5. Gemini. Generates 3–6 sentences on what current prices imply, one or two risks that could move the odds, and both sides of the view. The prompt forbids buy or sell instructions and position sizes, and asks for a not-financial-advice reminder.
  6. AI Trade Insight. The popup shows it as returned. Team component.

Comment classifier (the conversation, “Emotional Damage Predictor” button; Raimbek's pipeline)

  1. Extension button. Posts the event's address to the local classifier service. Team component.
  2. Comments. Pages through GET /comments for the event, 100 at a time, and keeps each comment's text.
  3. Saved classifier. Loads the trained scikit-learn pipeline from comment_classifier.pkl and predicts one label per comment. No Gemini call happens here.
  4. Emotional or rational. Counts emotional and rational comments, ignores spam, and reports the larger share.

Steamroller check (the numbers, “Steamroller Check” button; a team component)

  1. Extension button. Calls the local steamroller service with the event's slug.
  2. Outcome prices. Fetches the event and takes its first active market's outcomes and prices.
  3. Risk rules. Per $1: the gain is 1 − p and the loss is p, so one loss erases p ÷ (1 − p) wins. A side is flagged when p is at least 0.9 and that factor at least 10; its score is weighted up when resolution is more than three days away.
  4. Risk message. Writes the side, its implied probability, how many wins one loss erases, and the days left.

Preparing the classifier (the conversation, before runtime; Raimbek's pipeline)

  1. Collect comments. Downloads every comment on the event.
  2. Gemini labels. Asks Gemini 2.5 Flash to label each comment EMOTIONAL, RATIONAL or SPAM from three short definitions, answering in JSON. These labels are training data; Gemini is not used when the extension runs.
  3. Snowflake table. Appends BODY and LABEL rows to a Snowflake table through Snowpark. Snowflake stores the training data; it does not train the model.
  4. Train. Splits 80/20 with the labels' proportions kept, fits TfidfVectorizer followed by LogisticRegression, and prints a classification report. No scores are published.
  5. Model file. Saves it with joblib. The runtime service loads this file. After each analysis that service also appends its own labels to the table and retrains, so later training data includes the model's own labels.

The numbers · Raimbek's part

Prices are read as probabilities before Gemini sees them.

The market summarization pipeline: the module that finds a Polymarket event through the Gamma API, turns its markets into a structured prompt and asks Gemini for a short insight.

  1. Find the event

    It accepts what a person would paste: an event's address, its slug, a numeric id or a search phrase. Each goes to the Gamma API through /events/slug/…, /events/… or /public-search.

  2. Keep what matters

    Up to three of the event's markets, open ones first, with their questions, status, volume and outcome prices.

  3. Read prices as probabilities

    The Gamma API returns outcomes and prices as JSON strings. The module parses them and writes each price beside its implied percentage.

    In the API response

    outcomes: '["Yes","No"]'outcomePrices: '["0.54","0.46"]'

    In the prompt

    Outcomes: Yes: 0.54 (~54.0%); No: 0.46 (~46.0%)
    Example values from the module's own docstring; the format is the code's Verified in code
  4. Ask with rules

    The prompt carries the event's title, status, end date and volume, then each market. It asks Gemini (gemini-2.5-flash, through the public REST API, with the key read from the environment) for 3–6 sentences under explicit rules:

    The prompt's instructions and rules, verbatim
    poly_event_ai_summarizer.pyVerified in code
    Using this information, write a short AI Trade Insight section in 3–6 sentences.
    
    Focus on:
    - What current prices roughly imply about market expectations.
    - 1–2 key scenarios or risk factors that could change the odds.
    - A balanced view that mentions both upside and downside perspectives.
    
    Rules:
    - Do NOT give explicit instructions like "you should buy" or "sell now".
    - Do NOT mention specific position sizes.
    - Explicitly remind the reader that this is NOT financial advice.
    

The module began as a command-line tool (commit 5f34cdf); the team then wrapped it in the Flask service that the extension calls, with the key loaded from a .env file.

The conversation · Raimbek's part

Gemini labels the training data. A small model reads the comments.

Raimbek developed the comment-classification pipeline. Its two halves run at different times, and only the first uses Gemini.

Before Teaching the classifier

  1. Collect comments Gamma API · one event
  2. Gemini labels Training labels only
  3. Snowflake table COMMENT_LABELS_DATA
  4. Train scikit-learn, locally
  5. Model file comment_classifier.pkl

comment_classifier.pkl is the bridge: the runtime service loads it

When the button is pressed Reading the comments

  1. Comments Gamma API · /comments
  2. Saved classifier TF-IDF + logistic regression
  3. Emotional or rational A share, spam left out

The three labels Verified in code

Gemini labels comments with these definitions, verbatim from its prompt. The trained model learns the same three classes.

EMOTIONAL
emotion-heavy, opinions, little/no reasoning.
RATIONAL
uses reasoning, facts, or structure to support claims.
SPAM
off-topic, self-promo, nonsense, low-effort, "gm", etc.

The model

A scikit-learn pipeline: TF-IDF features from the comment text, then logistic regression. It trains on an 80/20 split that keeps the labels' proportions and prints a classification report. No scores are published, so none are claimed here.

The training pipeline, verbatim
Emotional_Damage_Predictor/train_model_snowflake.pyVerified in code
    model = Pipeline([
        ("tfidf", TfidfVectorizer()),
        ("clf", LogisticRegression(max_iter=1000)),
    ])

Snowflake stores; scikit-learn trains. The labeled comments are appended to a Snowflake table through Snowpark and read back into pandas, and the model is fitted locally. The README's “Snowpark ML training” is not what the code does.

After each analysis, the runtime service appends its own labels to that table and retrains, so later training data includes the model's own labels, not only Gemini's.

The numbers · team component

A rule, not a model: how much one loss can erase.

The third button flags a “steamroller”: an outcome priced so high that winning pays little and losing costs nearly everything. It is arithmetic on the price, and part of the team's architecture.

An explanation, not a trading tool. The calculator below evaluates the team's rule for a price you choose. It uses no market data and recommends nothing.

Price, read as probability93%

Win per share
7¢
Lose per share
93¢
One loss erases
13 wins

The detector would flag this: at least 90%, and at least 10 wins erased by one loss.

Wins erased by one loss, against priceComputed in your browser

Computed in your browser from the team's detector formula, with no market data. It explains the rule; it is not advice. The detector also uses the days left to resolution to grade the risk; that part is not shown here.

Checked against the code

What the code shows, and what it does not.

README: “Snowflake (Snowpark ML training)” and “continuous retraining through Snowflake”.

Snowpark writes and reads the table of labeled comments; the model is trained locally with scikit-learn. The retraining runs inside each analysis request, on the classifier's own labels.

Devpost: the steamroller check “uses liquidity”.

The code uses implied probability, the wipeout factor and days to resolution. Liquidity is not read.

README: an example verdict, “The event looks 67% Emotional.”

An example of the message format. No classifier results or accuracy figures are published, and none are shown here.

A web front page (index.html) with three feature cards.

Its API addresses are placeholders, so it is not wired to the services. The working interface is the extension's popup.

Not claimed Not claimed

  • Model accuracy. The training script prints a report, but no scores are published.
  • Trading results, recommendations or advice. Nothing on this page trades or recommends; the summary's own prompt rules out buy or sell instructions.
  • A deployment. The services run locally on ports 5000–5002.
  • Running or sharing the trained classifier. This page explains it; it does not run it.

The team recorded a demo of the working extension: watch it on YouTube

Evidence

How this page knows what it says

What each evidence label means
Verified in code
Read in the public repository at commit 475fcd9: its Python services, extension and README.
Public record
From the project's Devpost page and the repository's commit history, read on 2026-10-24.
Conceptual illustration
Drawn to explain the architecture in the code. Not the extension's interface and not market data.
Computed in your browser
The steamroller detector's own formula, evaluated here for a probability you choose. No market data and no model.
Not claimed
Stated in the README or on Devpost but not supported by the code, or simply not measured.

What was not used

The trained classifier was not run, loaded or copied, and no market data, prices or comments appear on this page. The Devpost gallery holds only the team's logo, so there are no product screenshots here.