# HKJC Odds Steamers & Drifters (`astronomical_lizard/hkjc-odds-steamers-drifters`) Actor

Tracks real-time Hong Kong Jockey Club (HKJC) horse racing odds movements. Compares current Win and Place odds against persisted snapshots to flag Steamers (shortening odds) and Drifters (lengthening odds).

- **URL**: https://apify.com/astronomical\_lizard/hkjc-odds-steamers-drifters.md
- **Developed by:** [Alex Wong](https://apify.com/astronomical_lizard) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 odds checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## HKJC Odds Steamers & Drifters

Track real-time odds movements for Hong Kong Jockey Club (HKJC) horse races and automatically spot **Steamers** (odds getting shorter) and **Drifters** (odds getting longer).

***

### What this actor does

The actor watches the live WIN odds for a single race. On each run it:

1. Finds the current HKJC race meeting.
2. Selects the race you want to watch.
3. Fetches the latest race card and WIN/PLACE odds.
4. Compares the current odds with the odds it saved on a previous run.
5. Classifies every horse as a **STEAMER**, **DRIFTER**, or **NEUTRAL**.
6. Saves the results to a dataset and, if you want, sends alerts to a webhook.

It is designed to be run repeatedly — for example every minute — so you can see how odds move over time.

#### What are Steamers and Drifters?

- **STEAMER** — odds have shortened. The market thinks the horse is more likely to win. By default the actor flags this when WIN odds drop by 15% or more.
- **DRIFTER** — odds have lengthened. The market thinks the horse is less likely to win. By default the actor flags this when WIN odds rise by 20% or more.
- **NEUTRAL** — odds have not moved enough to trigger either threshold.

***

### When to use this actor

- You want to monitor odds movements during live HKJC betting.
- You want an automated alert when a horse becomes a steamer or drifter.
- You want to collect structured odds data into an Apify dataset for further analysis.

***

### Before you start

This actor works best when it is **scheduled to run repeatedly**. A single run only tells you the current odds. To see movement, the actor needs at least two snapshots to compare.

- The first run saves a baseline snapshot and exits.
- The second run compares the latest odds against that snapshot and writes dataset records.
- Every run after that updates the snapshot and keeps producing movement records.

If you only run it once, you will not see any `signal`, `odds_delta_pct`, or dataset output.

***

### How to set the input parameters

When you run the actor on Apify, you fill in a simple form. Here is what each field means and how to choose a value.

| Parameter | What it does | Recommended setting |
|-----------|--------------|---------------------|
| **Venue** | Free-text race venue. Use `ST` for Sha Tin, `HV` for Happy Valley, or any HKJC venue code such as `S1`, `S2`, `S3`, `H1`. Use `AUTO` to let the actor pick the active meeting. | Use `AUTO` unless you know the exact code. |
| **Race Number** | The race you want to watch (1–11). Leave empty to let the actor guess the current race from the Hong Kong clock. | Leave empty for live tracking, or enter a specific race number if you want to focus on one race. |
| **Baseline Lookback (minutes)** | How old a previous snapshot is allowed to be. The actor ignores snapshots older than twice this value. | `5` if you run every minute; `15` if you run every 10–15 minutes. |
| **Steamer Threshold (%)** | How much WIN odds must drop to flag a steamer. Must be negative. | `-15` means a 15% or bigger drop. |
| **Drifter Threshold (%)** | How much WIN odds must rise to flag a drifter. Must be positive. | `20` means a 20% or bigger rise. |
| **Webhook URL** | Optional. If set, every steamer/drifter record is sent to this URL as a JSON POST. | Leave empty if you only want dataset output. |
| **Key-Value Store Name** | The name of the Apify store where snapshots are saved between runs. | Keep the default `hkjc-odds-snapshots` unless you need to isolate different users or strategies. |

#### A few tips

- **Smaller steamer threshold** (for example `-10`) means more horses will be flagged as steamers.
- **Smaller drifter threshold** (for example `10`) means more horses will be flagged as drifters.
- If you set **Baseline Lookback** too low, a single missed or delayed run can leave you without a usable snapshot.
- If you set it too high, you may compare current odds against a snapshot that is no longer relevant.

***

### Example: track Sha Tin race 5

Suppose you want to watch race 5 at Sha Tin and get alerts when odds move sharply.

Fill in the input like this:

```json
{
  "venue": "ST",
  "race_number": 5,
  "baseline_minutes_ago": 5,
  "steamer_threshold_pct": -15,
  "drifter_threshold_pct": 20,
  "webhook_url": "https://hooks.your-site.com/hkjc-alerts",
  "store_name": "hkjc-odds-snapshots"
}
```

Then schedule the actor to run every minute. The first run will save the baseline odds. From the second run onward, you will see dataset records like this:

```json
{
  "timestamp": "2026-07-27T12:06:00Z",
  "venue": "ST",
  "race_number": 5,
  "horse_number": 12,
  "horse_name": "Twilight Calls (GB)",
  "jockey": "Saffie Osborne",
  "trainer": "Richard Spencer",
  "current_win_odds": 5.5,
  "baseline_win_odds": 6.7,
  "odds_delta_pct": -17.91,
  "signal": "STEAMER",
  "is_favorite": true
}
```

In this example, Twilight Calls’ WIN odds shortened from 6.7 to 5.5, a drop of about 18%, so the actor flags it as a **STEAMER** and sends it to the webhook.

***

### Best practice: schedule the actor every minute

To get a useful stream of odds movements, create an Apify task and schedule it to run every 1 minute.

1. Push the actor to your Apify account.
2. In the Apify Console, create a **task** from this actor.
3. Fill in the input parameters. A good starting point is:
   - Venue: `AUTO`
   - Race Number: leave empty
   - Baseline Lookback: `5`
   - Steamer Threshold: `-15`
   - Drifter Threshold: `20`
   - Webhook URL: your alert endpoint, or leave empty
4. Go to the task’s **Schedule** tab and add a schedule that runs every 1 minute.

With this setup the actor will automatically follow the meeting, infer the current race, and flag steamers/drifters as the market moves.

If you prefer to track a single race only, set **Race Number** to the race you care about and the actor will stay on that race for every run.

***

### Understanding the output

The actor writes one row per runner to the dataset, but only when it has a usable baseline snapshot to compare against.

#### Key output fields

| Field | Meaning |
|-------|---------|
| `timestamp` | UTC time of the current odds snapshot. |
| `venue` | `ST` for Sha Tin, `HV` for Happy Valley, or any HKJC venue code such as `S1`, `S2`, `H1`. |
| `race_number` | The race number. |
| `horse_number` | The saddle cloth / runner number. |
| `horse_name` | Name of the horse. |
| `jockey` | Jockey name. |
| `trainer` | Trainer name. |
| `current_win_odds` | Latest WIN odds. |
| `baseline_win_odds` | WIN odds from the previous snapshot. |
| `odds_delta_pct` | Percentage change in WIN odds. Negative = shorter, positive = longer. |
| `signal` | `STEAMER`, `DRIFTER`, or `NEUTRAL`. |
| `is_favorite` | `true` if this horse currently has the lowest WIN odds in the race. |

The `current_place_odds` and `current_quinella_odds` fields are also included. **Quinella odds are always null** because HKJC’s GraphQL API returns Quinella as two-horse combinations, not per-horse values.

***

### Sharing the actor across multiple users or strategies

The `store_name` parameter decides where snapshots are saved. Runs that use the same store name share the same snapshots.

- If two tasks track **different races**, they will not conflict because snapshot keys include the venue and race number.
- If two tasks track the **same race** at the same time, they can overwrite each other’s snapshots. To avoid this, give each user or strategy a unique `store_name`, for example `hkjc-odds-alice`, `hkjc-odds-strategy-b`, and so on.

***

### Common questions

**Why did the first run produce no dataset records?**\
The first run saves the baseline snapshot. You need a second run to compare against it.

**Why are all signals NEUTRAL?**\
Odds did not move enough to cross your thresholds, or the baseline is too recent. Try lowering the steamer/drifter thresholds or waiting longer between comparisons.

**Why is the race number wrong?**\
The automatic race detection uses fixed start times and 30-minute intervals. For precise control, set **Race Number** explicitly.

**Do I need to enter a date?**\
No. The actor asks HKJC for the current active meeting automatically.

***

### Data source

This actor reads from the official HKJC website.

# Actor input Schema

## `race_number` (type: `integer`):

Target race number to analyze (1-11). Leave empty to automatically detect the current or next upcoming race based on the HKJC schedule.

## `venue` (type: `string`):

Race venue. Use ST for Sha Tin, HV for Happy Valley, or any HKJC venue code such as S1, S2, S3, H1. Use AUTO to let the actor detect the active meeting.

## `baseline_minutes_ago` (type: `integer`):

How many minutes back to compare current odds against the most recent stored snapshot. A new snapshot is saved on every run.

## `steamer_threshold_pct` (type: `number`):

Percentage drop in odds required to flag a STEAMER. Must be negative (e.g. -15 means odds fell 15%% or more).

## `drifter_threshold_pct` (type: `number`):

Percentage rise in odds required to flag a DRIFTER (e.g. 20 means odds rose 20%% or more).

## `webhook_url` (type: `string`):

Optional HTTP endpoint to POST a JSON payload containing all STEAMER and DRIFTER alerts when thresholds are met.

## `store_name` (type: `string`):

Name of the Apify key-value store used to persist odds snapshots between runs. Use a unique name if multiple isolated users or strategies share the same Apify account.

## Actor input object example

```json
{
  "venue": "AUTO",
  "baseline_minutes_ago": 15,
  "steamer_threshold_pct": -15,
  "drifter_threshold_pct": 20,
  "store_name": "hkjc-odds-snapshots"
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("astronomical_lizard/hkjc-odds-steamers-drifters").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("astronomical_lizard/hkjc-odds-steamers-drifters").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call astronomical_lizard/hkjc-odds-steamers-drifters --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,astronomical_lizard/hkjc-odds-steamers-drifters"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Smtuu7xmANtIObSB6/builds/qGA1bHO2r7mh8p3BS/openapi.json
