Skip to main content

Overview

Tournaments are time-bound forecasting competitions on Metaculus where participants compete for prizes and recognition. They provide a structured way to test forecasting skills on curated question sets.

Tournament Structure

Tournaments are implemented as a special type of Project (from projects/models.py:213-223):
class ProjectTypes(models.TextChoices):
    TOURNAMENT = "tournament"
    QUESTION_SERIES = "question_series"
    INDEX = "index"
    # ... other types
Question Series are similar to tournaments but typically without prizes and more focused on tracking predictions over time on a specific topic.

Key Tournament Fields

From projects/models.py:286-311, tournaments have specific time and prize configuration:
class Project(TimeStampedModel, TranslatedModel):
    type = models.CharField(max_length=32, choices=ProjectTypes.choices)
    
    # Tournament-specific fields
    prize_pool = models.DecimalField(
        default=None,
        decimal_places=2,
        max_digits=15,
        null=True,
        blank=True
    )
    
    start_date = models.DateTimeField(null=True, blank=True)
    
    close_date = models.DateTimeField(
        help_text=(
            "The date the tournament wraps up and prizes will be paid. "
            "All questions that should be included in the leaderboard must "
            "close and resolve before this date. "
            "This is displayed on the front end as the 'Winners announced date'"
        ),
    )
    
    forecasting_end_date = models.DateTimeField(
        help_text=(
            "The date the last scored question that counts for the tournament closes. "
            "The date shown is the latest of the Forecasting end date "
            "and the latest question close date closing and resolving before the Close date."
        ),
    )
    
    sign_up_fields = models.JSONField(
        default=list,
        blank=True,
        help_text="Used during tournament onboarding."
    )

Tournament Lifecycle

1

Announcement

Tournament is announced with rules, prize pool, and timeline. Type is set to TOURNAMENT.
2

Open for Forecasting

Questions become available between start_date and forecasting_end_date. Participants make predictions.
3

Forecasting Ends

Last question closes at forecasting_end_date. No more predictions accepted.
4

Resolution Period

Questions are resolved as outcomes become known. Must complete before close_date.
5

Winners Announced

Final scores calculated at close_date. Prize distribution announced.

Tournament Status

From projects/models.py:441-448, tournaments have an is_ongoing property:
@property
def is_ongoing(self):
    if self.type in (
        self.ProjectTypes.TOURNAMENT,
        self.ProjectTypes.QUESTION_SERIES,
        self.ProjectTypes.INDEX,
    ):
        return self.close_date > django_timezone.now() if self.close_date else True

Tournament Questions

Questions are associated with tournaments through the post-project relationship:

Primary Association

From projects/models.py:249-259:
post = models.ForeignKey(
    "posts.Post",
    on_delete=models.CASCADE,
    related_name="questions",
)

# Post has:
default_project = models.ForeignKey(Project, on_delete=models.CASCADE)
projects = models.ManyToManyField(Project, related_name="posts")
Questions can be:
  • Primary to the tournament: post.default_project = tournament
  • Tagged to the tournament: Tournament in post.projects

Leaderboards

Each tournament has a primary leaderboard that ranks participants.

Primary Leaderboard

From projects/models.py:240-246:
primary_leaderboard = models.ForeignKey(
    "scoring.Leaderboard",
    null=True,
    on_delete=models.SET_NULL,
    related_name="primary_project",
    blank=True,
)

Auto-Created Leaderboard

Tournaments automatically get a default leaderboard on creation (from projects/models.py:422-439):
def save(self, *args, **kwargs):
    creating = not self.pk
    super().save(*args, **kwargs)
    
    if creating and not self.primary_leaderboard and self.type in (
        self.ProjectTypes.TOURNAMENT,
        self.ProjectTypes.QUESTION_SERIES,
        self.ProjectTypes.COMMUNITY,
    ):
        from scoring.models import Leaderboard
        
        leaderboard = Leaderboard.objects.create(
            project=self,
            score_type=LeaderboardScoreTypes.PEER_TOURNAMENT,
        )
        Project.objects.filter(pk=self.pk).update(primary_leaderboard=leaderboard)
The default leaderboard uses PEER_TOURNAMENT scoring, which sums peer scores across all tournament questions.

Prize Pools

Tournament Prize Pool

The main prize pool for the tournament (from projects/models.py:287-289):
prize_pool = models.DecimalField(
    default=None, decimal_places=2, max_digits=15, null=True, blank=True
)

Leaderboard-Specific Prizes

Tournaments can have multiple leaderboards with different prize allocations (from scoring/models.py:184-201):
class Leaderboard(TimeStampedModel):
    prize_pool = models.DecimalField(
        help_text="""Optional. If not set, the Project's prize_pool will be used.
        If the Project has a prize pool, but this leaderboard has none, set this to 0.
        """,
    )
    
    minimum_prize_amount = models.DecimalField(
        default=50.00,
        decimal_places=2,
        max_digits=15,
        help_text="""The minimum amount a user can win in this leaderboard.
        Any remaining money is redistributed. Tournaments that close before June 2025
        will have a value of 0.00.
        """,
    )
Prizes are typically distributed to top performers. The exact distribution formula depends on tournament rules and leaderboard configuration.

Tournament Participation

Sign-Up Fields

Tournaments can require custom sign-up information (from projects/models.py:309-311):
sign_up_fields = models.JSONField(
    default=list, blank=True, help_text="Used during tournament onboarding."
)
Example sign-up fields:
[
  {"field": "affiliation", "label": "University or Organization", "required": true},
  {"field": "experience", "label": "Forecasting Experience", "required": false}
]

Follower Tracking

Tournaments track participant engagement (from projects/models.py:375-379):
followers_count = models.PositiveIntegerField(
    default=0, db_index=True, editable=False
)
forecasts_count = models.PositiveIntegerField(default=0, editable=False)
forecasters_count = models.PositiveIntegerField(default=0, editable=False)

Bot Participation

Tournaments can control how bots participate in leaderboards.

Bot Leaderboard Status

From projects/models.py:248-264:
class BotLeaderboardStatus(models.TextChoices):
    EXCLUDE_AND_HIDE = "exclude_and_hide"
    EXCLUDE_AND_SHOW = "exclude_and_show"
    INCLUDE = "include"
    BOTS_ONLY = "bots_only"

bot_leaderboard_status = models.CharField(
    max_length=32,
    choices=BotLeaderboardStatus.choices,
    default=BotLeaderboardStatus.EXCLUDE_AND_SHOW,
    help_text="""Sets the status of bots in any leaderboard associated with this project.
    exclude_and_hide: Bots excluded from ranks/prizes/medals and hidden from leaderboard.
    exclude_and_show: Bots excluded from ranks/prizes/medals but shown on leaderboard.
    include: Bots included in ranks/prizes/medals and shown on leaderboard.
    bots_only: Only Bots included in ranks/prizes/medals. Non-bots still shown.
    """,
)
Bots don’t appear on the leaderboard at all. Used when bots would be distracting.

Visibility and Discovery

Tournaments control their visibility across the platform.

Visibility Settings

From projects/models.py:229-362:
class Visibility(models.TextChoices):
    NORMAL = "normal"
    NOT_IN_MAIN_FEED = "not_in_main_feed"
    UNLISTED = "unlisted"

visibility = models.CharField(
    choices=Visibility.choices,
    default=Visibility.NOT_IN_MAIN_FEED,
    help_text=(
        "Sets the visibility of this project:\n"
        "Normal: Visible on main feed, contributes to global leaderboards/medals, "
        "lists the project in tournaments/question series page.\n"
        "Not In Main Feed: Not visible in main feed but searchable, doesn't contribute "
        "to global leaderboards/medals, lists in tournaments page.\n"
        "Unlisted: Not visible in main feed, not searchable, doesn't contribute "
        "to global leaderboards/medals. Default for newly created Tournaments/Question Series."
    ),
)

Homepage Display

From projects/models.py:364-368:
show_on_homepage = models.BooleanField(default=False, db_index=True)
show_on_services_page = models.BooleanField(
    default=False, db_index=True, help_text="Show project on the Services page."
)
New tournaments start as UNLISTED by default, allowing admins to populate questions before making them public.

Tournament Metadata

Branding and Description

From projects/models.py:266-279:
name = models.CharField(max_length=200)
slug = models.CharField(max_length=200, validators=[validate_alpha_slug])
subtitle = models.CharField(max_length=255, blank=True, default="")
description = models.TextField(blank=True, default="")
header_image = models.ImageField(null=True, blank=True)
header_logo = models.ImageField(null=True, blank=True)
emoji = models.CharField(max_length=10, default="", blank=True)

SEO Metadata

From projects/models.py:313-319:
html_metadata_json = models.JSONField(
    help_text="Custom JSON for HTML meta tags. Supported fields are: title, description",
    null=True,
    blank=True,
    default=None,
)

Forecast Flow

Tournaments can use an optimized forecasting interface (from projects/models.py:370-372):
forecasts_flow_enabled = models.BooleanField(
    default=True, help_text="Enables new forecast flow for tournaments"
)
This provides a streamlined UI for quickly making predictions across many tournament questions.

Permissions

Tournaments inherit the Projects permission system.

Default Permission

From projects/models.py:332-339:
default_permission = models.CharField(
    choices=ObjectPermission.choices,
    null=True,
    blank=True,
    default=ObjectPermission.FORECASTER,
    db_index=True,
)
Permission levels:
  • null: Private tournament (not accessible)
  • FORECASTER: Can view and make predictions
  • CURATOR: Can view, forecast, and help curate questions
  • ADMIN: Full control over tournament

Override Permissions

From projects/models.py:340 and 498-513:
override_permissions = models.ManyToManyField(User, through="ProjectUserPermission")

class ProjectUserPermission(TimeStampedModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    permission = models.CharField(choices=ObjectPermission.choices)
Individual users can have permissions that override the default.

Tournament Examples

Quarterly Tournament

3-month competition on current events with monthly question releases

Domain Tournament

Focused competition on AI, climate, economics, or other specific domain

Beginner Tournament

Lower-difficulty questions designed for new forecasters

Rapid Resolution

Short-term questions that resolve within days or weeks

Best Practices

Tournament scoring rewards coverage. Make initial forecasts on all questions early, even if they’re rough estimates.
Subscribe to tournament updates to know when new questions are added or when important information emerges.
You don’t need to be best at every question. Focus on questions where you have domain expertise.
Read comments from top forecasters to understand their reasoning and improve your own forecasts.
Tournament success requires well-calibrated probabilities, not just correct directional predictions.

API Reference

Projects API

Explore the full Projects/Tournaments API documentation

Projects

Understand the broader project system

Leaderboards

Learn how tournament rankings work

Scoring

Understand how tournament scores are calculated

Questions

Learn about question types and structure

Build docs developers (and LLMs) love