Arena Documentation

Everything you need to build, test, and submit a prediction model.

1. What Is the Arena?

Moon Dev Arena Beta is a prediction contest. You write a Python function that predicts whether BTC will go up or down. That's it. Your model competes in two competitions simultaneously: a weekly competition where predictions are made every hour, and a daily competition where predictions are made every 5 minutes.

This is not a trading bot competition. Your code never touches an exchange, never places orders, never manages risk. You just predict a direction.

The entire contest in one sentence:

Each round (hourly for the weekly competition, every 5 minutes for the daily), our server gives your predict(data) function a dict of live market data from 4 sources, and your function returns "long", "short", or "skip".

The model with the highest score at the end of the weekly competition wins $1,795 Moon Dev credit (one win per person — nothing in the Moon Dev shop costs more than $1,795, so a second win would have nothing to redeem against). The daily competition winner receives $50 USDC (winner pulled at 11 AM ET each day — send us your Ethereum or Solana USDC address on the claim page).

1b. The Two Competitions

Every model you submit competes in two competitions simultaneously. There is no extra step — when you submit your model, it is automatically entered into both the weekly and daily competitions.

Weekly Competition

1-hour prediction intervals

  • Your model predicts BTC direction once per hour
  • Competition runs for ~1 full week
  • Winner gets $1,795 Moon Dev credit
  • One win per person — nothing in the shop costs more than $1,795
  • ~168 rounds across the entire week
  • Price is checked 1 hour later to determine correct/wrong

Daily Competition

5-minute prediction intervals

  • Same model predicts BTC direction every 5 minutes
  • New competition every day — resets at 11 AM ET
  • Winner gets $50 USDC
  • Send us your Ethereum or Solana USDC address on the claim page — we send $50 USDC
  • 288 rounds across a single day
  • Price is checked 5 minutes later to determine correct/wrong

Strategy Consideration

The same predict(data) function runs on both timeframes. A model that works great on 1-hour moves might not work as well on 5-minute moves, and vice versa. That's part of the challenge. You get 15 model slots — you could dedicate some to 1-hour strategies and some to 5-minute strategies, or build one model that works on both. Every model you upload competes on both timeframes automatically, so think about which approach gives you the best edge.

There are no code differences between 1-hour and 5-minute models. The data dict is identical, the sandbox is identical, the predict() function signature is identical. The only difference is how often your model runs and how quickly the prediction is resolved.

2. How It Works

1

You write a Python file with a predict() function

One file. One function. Returns "long", "short", or "skip".

2

You upload it once

Go to Submit, log in with your display name + secret key, upload your .py file.

3

On schedule, our server runs your model automatically

We fetch 4 data sources of live BTC data (price ticks, liquidations, and positions near liquidation), package it into a single Python dict, and pass it to your predict(data) function inside a secure Docker container with no internet access.

4

After each round, we check if you were right

If BTC went up and you said "long": +1 point. If it went down and you said "long": -1 point. If you said "skip": 0 points.

Data flow diagram:

OUR SERVER (has internet):
  ├─ Fetches BTC price ticks from the Moon Dev API (last 4h)
  ├─ Fetches Hyperliquid liquidation events (last 24h)
  ├─ Fetches positions near liquidation (current snapshot)
  ├─ Fetches Binance liquidation events (last 24h)
  └─ Packages all 4 sources + 2 metadata fields into one dict → writes data.json

THE DICT HAS EXACTLY 6 KEYS:
  symbol, timestamp, ticks, hl_liquidations, positions_near_liq, binance_liquidations
  ⚠️ NO other keys exist. No orderflow, no funding_rate, no candles, no sentiment.

DOCKER CONTAINER (no internet, read-only, 512MB, 30s timeout):
  ├─ Reads /input/data.json
  ├─ Imports /input/model.py (your code)
  ├─ Calls predict(data)
  ├─ Your function returns "long", "short", or "skip"
  └─ Result saved → container destroyed

3. The predict() Function

Your entire submission is a single Python file containing one function:

minimal_model.py

def predict(data):
    """
    Called automatically each round with live market data.
    Weekly competition: once per hour. Daily competition: every 5 minutes.

    Args:
        data (dict): 4 data sources of live BTC market data.
                     See Section 4 for details.

    Returns:
        str: "long", "short", or "skip"
    """
    return "long"

THE DATA DICT HAS EXACTLY 6 KEYS. NOTHING ELSE.

Your data dict contains only these keys:

# Metadata

data["symbol"] → str, always "BTC"

data["timestamp"] → int, unix ms

# 4 data sources (all are list[dict])

data["ticks"] → BTC price ticks (last 4h)

data["hl_liquidations"] → Hyperliquid liquidation events (last 24h)

data["positions_near_liq"] → positions close to liquidation

data["binance_liquidations"] → Binance liquidation events (last 24h)

There are NO other keys. No "orderflow", no "funding_rate", no "hlp_sentiment", no "candles", no "whale_positions", no "price". If you access a key not listed above, it will be None/empty and your model will skip every round. See Section 5 for the exact fields inside each data source.

Important details:

  • Return a string, not a dict. Just "long", "short", or "skip".
  • If your function crashes, that round counts as a skip (0 points).
  • Your function must finish within 30 seconds.
  • No state persists between rounds. Each hour is a fresh execution.
  • You can import any of the pre-installed libraries (numpy, pandas, ta-lib, etc.).
  • You cannot make network requests. The container has no internet.

Join Tomorrow's Zoom Call

Chat with Moon Dev directly about your Arena strategy, ask questions, and meet other algorithmic traders.

Reserve your spot →

4. Download Data & Backtest

The Big Picture

Your goal is to write a Python function that predicts whether BTC will go up or down in the next hour. To build a good model, you need past data with answers — what did the market look like at 2pm? What happened by 3pm? Did it go up or down?

That's exactly what we give you. We provide 60 days of historical data across 4 CSV files — price ticks, Hyperliquid liquidations, positions near liquidation, and Binance liquidations. Each CSV includes timestamps so you can align the data hourly and check: did BTC go up or down after each snapshot?

Download the CSVs, build your model logic, test it against the historical data, and iterate until you have an edge. Then submit it.

Where does all this data come from?

All 4 data sources come from the Moon Dev API (moondev.com/docs). You do not need an API key. You do not need to call any APIs yourself. The Arena server does all the data fetching for you, both during the live contest and in these historical files.

No API key needed — this competition is 100% free

All the data you need is downloadable right here on this page using the buttons below. Just sign in with your free Arena account (the display name and secret key you got when you signed up). No Moon Dev API key required. No paid subscription required.

If you do have a Moon Dev API key, you can also fetch data programmatically via curl — see the CLI section below the downloads.

These are ALL the keys in the data dict passed to your predict(data) function. There are no other keys. Do not use any key not listed here.

KeyTypeDescription
Metadata
symbolstrAlways "BTC"
timestampintUnix timestamp (ms) when this snapshot was collected
4 Data Sources
tickslist[dict]BTC price ticks from last 4 hours
hl_liquidationslist[dict]Hyperliquid liquidation events (last 24h)
positions_near_liqlist[dict]BTC positions close to liquidation price
binance_liquidationslist[dict]Binance liquidation events (last 24h)

Download Historical Data (4 CSVs)

60 days of historical data across 4 CSV files. Download individually or grab the zip with all 4. Sign in with your Arena account to access downloads.

Sign in to download your data package

Sign up for a free account at moondev.com/arena/signup first — you'll get a display name and secret key that you'll use to download data.

Enter the display name and secret key you got when you signed up. Don't have an account yet? Sign up free — it takes 10 seconds.

All 4 CSVs (zip)

Sign in to download

One zip file containing all 4 historical CSVs. This is the easiest way to get started.

btc_ticks.csv

Sign in to download

112K+ price ticks over 60 days. Fields: timestamp, price, datetime.

hl_liquidations.csv

Sign in to download

178K+ Hyperliquid liquidation events. Fields: timestamp, datetime, coin, side, liquidated_user, price, size, notional_value, mark_price, method.

positions_near_liq.csv

Sign in to download

82K+ position snapshots. Fields: timestamp, datetime, address, coin, direction, position_value, liquidation_price, entry_price, leverage, distance_pct.

binance_liquidations.csv

Sign in to download

115K+ Binance liquidation events. Fields: timestamp, symbol, side, price, avg_price, qty, usd_size.

template_model.py

Sign in to download

A working model with a built-in backtester. Modify the predict(data) function, then run python template_model.py to test it against the CSV data. It'll simulate hourly rounds and print your accuracy and score. When you're happy, upload it at moondev.com/arena/submit.

Step by Step

1

Download the zip (all 4 CSVs + template_model.py) and unzip into a folder

2

Run python template_model.py to see the built-in backtest run against the CSV data

3

Open the CSVs in pandas or a spreadsheet — explore the data you'll be working with

4

Edit the predict(data) function in template_model.py (or start fresh with your own .py file)

5

Run python template_model.py again to test your changes — iterate until you're beating 50%. You can also use the backtesting.py framework or any other backtesting tool with the CSV data.

6

Upload your .py file at moondev.com/arena/submit. Your file just needs a def predict(data) function that returns "long", "short", or "skip".

Example: Testing the Template

Terminal

# Unzip the download, then run the template to test it:
python template_model.py

# Output:
#   Loaded 112,000 ticks from btc_ticks.csv
#   Date range: 2026-01-15 00:00:00 to 2026-03-28 23:00:00
#
#   [+] 2026-01-15T04:00 | BTC $    97,018 |  -0.04% | pred=short actual=short
#   [-] 2026-01-15T05:00 | BTC $    96,992 |  +0.16% | pred=short actual=long
#   ...
#
#   Predictions:  42 (28 correct, 14 wrong)
#   Accuracy:     66.7%
#   Score:        +14 (correct - wrong)

The template includes a local backtester that runs when you execute it directly. It loads the CSV data, simulates hourly rounds, and tests your predict() function. Only the predict(data) function matters for submission — the backtest code at the bottom is just for your local testing.

The key insight

The CSV files are your training data. Use the price ticks to determine what BTC did each hour (up or down), then correlate that with the liquidation events and positions near liquidation from the same time window. Your job is to find patterns that predict the next move. You can use the built-in backtester, the backtesting.py framework, or any other tool to test your ideas against the data.

Data grows every hour

We rebuild the CSVs twice daily (6 AM and 6 PM UTC) with a rolling 60-day window. The more data in your CSVs, the better your backtest results will be. Re-download the zip anytime to get the latest data.

5. Data Reference (4 Sources)

COMPLETE DATA SCHEMA — ONLY THESE KEYS EXIST

The data dict passed to your predict(data) function contains exactly 6 keys: symbol, timestamp, ticks, hl_liquidations, positions_near_liq, and binance_liquidations. That's it. There is no other data. Do not try to access any key not documented on this page.

Every hour, your predict(data) function receives a dict with 4 data sources of live BTC market data, all from the Moon Dev API. Every model gets the exact same data.

data['symbol']str

Always 'BTC'.

"BTC"
data['timestamp']int

Unix timestamp in milliseconds (UTC).

1773489600000
data['ticks']list[dict]

BTC price ticks from the last 4 hours. Each tick has a unix millisecond timestamp and price.

[
  {"t": 1773475200000, "p": 70500.5},
  {"t": 1773475260000, "p": 70510.0},
  {"t": 1773475320000, "p": 70505.0},
  ...  // ~200+ ticks over 4 hours
]

Historical CSV: btc_ticks.csv — 112K+ ticks over 60 days. Same fields: timestamp, price, datetime.

Building Candles from Ticks (1-Hour, 5-Minute, any timeframe)

The Arena gives you raw ticks — not candles. But you can build OHLCV candles at any timeframe from the tick data using pandas. This works for both the Weekly (1-Hour) and Daily (5-Minute) competitions since both receive the same 4 hours of ticks.

import pandas as pd

ticks = data.get('ticks', [])
df = pd.DataFrame(ticks)
df['t'] = pd.to_datetime(df['t'], unit='ms')
df = df.set_index('t').sort_index()

# Build 1-hour candles (for weekly competition strategies)
candles_1h = df['p'].resample('1h').agg(
    open='first', high='max', low='min', close='last'
).dropna()

# Build 5-minute candles (for daily competition strategies)
candles_5m = df['p'].resample('5min').agg(
    open='first', high='max', low='min', close='last'
).dropna()

# Build any other timeframe you want (15m, 30m, etc.)
candles_15m = df['p'].resample('15min').agg(
    open='first', high='max', low='min', close='last'
).dropna()

# Example: use 5-minute candle closes for short-term momentum
closes = candles_5m['close'].values
if len(closes) >= 6:
    recent_trend = closes[-1] - closes[-6]  # last 30 min direction

Both competitions receive the same tick data. The difference is only how often your model runs (every hour vs every 5 minutes) and when the result is checked. Use whichever candle timeframe makes sense for your strategy.

data['hl_liquidations']list[dict]

Hyperliquid liquidation events from the last 24 hours. BTC only.

[
  {
    "timestamp": "2026-03-14T10:23:19Z",
    "coin": "BTC",
    "side": "long",         // "long" = a long got liquidated (bearish)
    "size": 0.5,
    "price": 70077.0,
    "notional_value": 35038.5
  },
  ...
]

Historical CSV: hl_liquidations.csv — 178K+ events over 60 days. Fields: timestamp, datetime, coin, side, liquidated_user, price, size, notional_value, mark_price, method.

data['positions_near_liq']list[dict]

Current BTC positions that are close to their liquidation price. Useful for predicting liquidation cascades.

[
  {
    "coin": "BTC",
    "direction": "LONG",
    "position_value": 500000,
    "leverage": 20,
    "distance_pct": 1.5,       // 1.5% away from liquidation
    "entry_price": 71000,
    "liquidation_price": 68500
  },
  ...
]

Historical CSV: positions_near_liq.csv — 82K+ snapshots over 60 days. Fields: timestamp, datetime, address, coin, direction, position_value, liquidation_price, entry_price, leverage, distance_pct.

data['binance_liquidations']list[dict]

Binance liquidation events from the last 24 hours. BTC only.

[
  {
    "timestamp": 1773450000000,
    "symbol": "BTC",
    "side": "SELL",           // "SELL" = a long got liquidated
    "price": 70100,
    "qty": 0.3,
    "usd_size": 21030
  },
  ...
]

Historical CSV: binance_liquidations.csv — 115K+ events over 60 days. Fields: timestamp, symbol, side, price, avg_price, qty, usd_size.

6. Example Models

Here are 4 complete, working models showing different strategies. Each one is a valid submission you could upload right now. Use them as starting points for your own model.

tick_momentum.py

"""
Simple momentum using price ticks.
If price trending up over last hour, go long.
"""

def predict(data):
    ticks = data.get('ticks', [])
    if len(ticks) < 20:
        return "skip"

    prices = [t['p'] for t in ticks]
    third = len(prices) // 3
    avg_start = sum(prices[:third]) / third
    avg_end = sum(prices[-third:]) / third
    pct = (avg_end - avg_start) / avg_start * 100

    if pct > 0.3:
        return "long"
    elif pct < -0.3:
        return "short"
    return "skip"

7. Scoring System

+1

Correct prediction

You said "long", BTC went up

-1

Wrong prediction

You said "long", BTC went down

0

Skip / Error / Crash

Returned "skip", crashed, or timed out

Minimum prediction requirement

You must make at least 2 predictions ("long" or "short") during the competition to be eligible for prizes. Skips and errors don't count toward this minimum. This prevents the strategy of never predicting to maintain a 0 score.

The winner is the model with the highest cumulative score at the end of the competition. Strategic use of "skip" is encouraged — if you're not confident, sitting out is better than guessing wrong. Scoring works the same way for both the 1-hour and 5-minute competitions — the only difference is the interval at which predictions are checked.

8. Sandbox Environment

Your model runs in a locked-down Docker container. This is for fairness (no one can scrape external data) and security (your code can't affect our servers).

Container specs

Network: None (completely offline)

Memory: 512 MB max

Timeout: 30 seconds

Filesystem: Read-only (except /tmp)

User: Non-root (sandboxuser)

Python: 3.11

File layout inside container

/input/data.json   ← market data (read-only)
/input/model.py    ← your code (read-only)
/output/result.json ← your prediction (written by runner)
/tmp/              ← 64MB writable scratch space
📹

Live Zoom tomorrow — algorithmic traders building Arena models together with Moon Dev

9. Available Libraries

These libraries are pre-installed in the sandbox. You can import any of them in your model.

Python 3.11

Full standard library (math, statistics, json, collections, itertools, etc.)

numpy 1.26.2

Array operations, linear algebra, statistics

pandas 2.1.3

DataFrames, time series analysis

TA-Lib 0.4.32

150+ technical indicators (RSI, MACD, Bollinger Bands, etc.)

scikit-learn 1.3.2

Machine learning (clustering, regression, classification)

scipy 1.11.4

Scientific computing, statistical functions, optimization

You cannot pip install additional packages. If you need a library that's not listed, implement the logic yourself using the available tools.

10. Testing Locally

The best way to test is to use the backtest script from Section 4 above. But if you just want a quick sanity check that your model runs without errors, here's a simple test:

quick_test.py

import json

# Load the sample data file you downloaded
with open("sample_round.json") as f:
    data = json.load(f)

# Import and run your model
from my_model import predict
result = predict(data)

print(f"Prediction: {result}")
assert result in ("long", "short", "skip"), f"Invalid: {result}"
print("Model works!")

Important: always use .get() with defaults

Some fields may occasionally be null if an upstream API is temporarily down. Use data.get("hl_liquidations", []) instead of data["hl_liquidations"] so your model doesn't crash. A model that crashes scores 0 for that round.

11. Rules

1.

One account per person.

2.

One submission per competition. Resubmitting replaces your previous model.

3.

Your file must contain a def predict(data) function.

4.

Return a string: "long", "short", or "skip".

5.

No internet access in the sandbox. Your model cannot make HTTP requests.

6.

Maximum file size: 500 KB. Must be a valid .py file.

7.

No malicious code. All submissions are reviewed before prize payout.

8.

You must make at least 2 predictions (long/short, not skip) to be eligible for prizes.

9.

30-second execution timeout. Keep your model efficient.

10.

The $1,795 weekly credit can only be won once per person — nothing in the Moon Dev shop costs more than $1,795, so a second win would have nothing to redeem against. Daily $50 prizes have no cap.

11.

US residents: $1,999 lifetime cap on total Arena winnings per person. This includes any combination of the $1,795 weekly credit plus any $50 USDC from daily 5-minute wins. Once you cross $1,999 in total winnings, no further prizes can be awarded. Non-US residents are not subject to this cap.

12.

Daily 5-minute prize is $50 USDC sent to your Ethereum or Solana USDC address. Winners submit a USDC address on the claim page after the daily pull.

13.

Moon Dev reserves the right to disqualify entries that violate the spirit of fair competition.

12. FAQ

Where does the data come from?

All 4 data sources come from the Moon Dev API — the same data layer that powers the rest of moondev.com. BTC price ticks, Hyperliquid liquidations, positions near liquidation, and Binance liquidations are all collected and served by the Moon Dev API. You don't need an API key or any external data source. The Arena server pulls it all for you. To explore the full API yourself, visit moondev.com/docs.

Do I need to sign up for the Moon Dev API to compete?

No. You don't need an API key. During the contest, the Arena server pulls a fresh snapshot from the Moon Dev API every hour and passes it directly to your predict(data) function inside the sandbox. You just write logic — we handle the data.

How much historical data do I get for backtesting?

We provide 60 days of historical data across 4 CSV files — BTC price ticks (112K+), Hyperliquid liquidations (178K+), positions near liquidation (82K+), and Binance liquidations (115K+). Download the zip from Section 4 above and run the included backtest.py script against your model locally.

How do I get candle data (OHLCV)? I only see raw ticks.

The Arena provides raw price ticks, not pre-built candles — but you can build candles at ANY timeframe from the ticks using pandas. Both the Weekly (1-Hour) and Daily (5-Minute) competitions receive the same 4 hours of tick data. To build 5-minute candles: df = pd.DataFrame(data["ticks"]); df["t"] = pd.to_datetime(df["t"], unit="ms"); candles = df.set_index("t")["p"].resample("5min").agg(open="first", high="max", low="min", close="last").dropna(). Change "5min" to "1h", "15min", or any other timeframe. See the "Building Candles from Ticks" section under Source 1 in the Data Schema for the full code example.

Can I use machine learning?

Yes! scikit-learn is pre-installed. You can train models on the tick data, cluster liquidation patterns, use regression — whatever you want. Just remember: no state persists between rounds, so you'd need to retrain from the data dict each hour (within 30 seconds).

Can I use TA-Lib?

Yes! TA-Lib 0.4.32 is pre-installed with all 150+ indicators. import talib and go wild. See the example models above for inspiration.

What if my model crashes?

That round counts as a skip (0 points). Your model runs again next hour as if nothing happened. Always use .get() with defaults to handle missing data gracefully.

Can I update my model during the competition?

Yes, resubmitting replaces your previous model. The new version runs starting the next hour. Previous scores are kept.

How is "up" vs "down" determined?

We compare the BTC mid price when your prediction was recorded vs the mid price one hour later. If the price increased (even by $0.01), the actual direction is "long". If it decreased, "short".

What if the price is exactly the same?

If the price is identical (rare), the actual direction is "short" (technical implementation detail). Don't worry about this edge case.

Can I import my own modules?

No. Your submission is a single .py file. All logic must be in that file. You can import any of the pre-installed libraries (numpy, pandas, talib, sklearn, scipy) and the Python standard library.

Can my model access the internet?

No. The Docker container has --network none. All the data you need is in the data dict passed to predict().

How many rounds are in a competition?

The 1-hour weekly competition has ~168 rounds (24 per day for ~7 days). The 5-minute daily competition has 288 rounds per day (one every 5 minutes, 24/7). Check the leaderboard for live round counts.

What are the two competition types?

The 1-Hour competition runs weekly with a $1,795 Moon Dev credit prize (winnable once per person — nothing in the Moon Dev shop costs more than $1,795). The 5-Minute competition runs daily with a $50 USDC prize, sent to your Ethereum or Solana USDC address. When you submit a model, it automatically competes in BOTH — no extra step needed. Daily 5-minute winner is pulled at 11 AM Eastern Time every day; winners submit a USDC address on the claim page.

Do I need to submit separately for the 5-minute competition?

No. Every model you submit is automatically entered into both the 1-hour and 5-minute competitions. You get 15 model slots — all 15 compete on both timeframes. You might want to build some models optimized for 5-minute moves and others for 1-hour moves, but that's up to you.

Should I build different models for 1-hour vs 5-minute?

That's a strategic decision. A model that catches 1-hour trends might miss 5-minute noise, and a model tuned for 5-minute scalps might not capture hourly direction well. You have 15 slots — experiment! Some contestants dedicate slots to each timeframe, others build one model that works on both. There are no code differences between the two — same data, same sandbox, same predict() function.

Is "skip" a good strategy?

Strategically, yes. If your model isn't confident, returning "skip" (0 points) is better than guessing wrong (-1 point). But you need at least 2 real predictions to be eligible for prizes.

Need Help Building Your Model?

Join the Zoom call tomorrow and build alongside Moon Dev and other algorithmic traders. Ask questions live, get feedback on your approach, and learn what data to focus on.

Join the Zoom Call →

Ready to Build?

Sign up for free, copy one of the example models above, modify it with your own logic, and upload.

Questions? Email us at [email protected]

Built with love by Moon Dev