Incentive Payments: Price Lists, BDM Bids, Belief Elicitation and Prolific Bonuses
Pay participants by their answers in Potato: Holt & Laury price lists, BDM bids and binarized scoring, drawn after the study and paid as Prolific bonuses.
New in v2.12.0
Potato can pay participants according to their answers, as experimental economics studies do. Three annotation types collect the answers:
annotation_type | Asks for | Paid by |
|---|---|---|
price_list | Option A or B in each row of a multiple price list | mpl_random_row |
bdm | The most the participant would pay for something | bdm (Becker, DeGroot & Marschak, 1964) |
belief_elicitation | A probability, or a distribution of 100 points over bins | bsr, the binarized scoring rule (Hossain & Okui, 2013) |
The incentives: block says how each scheme is paid. After a participant finishes, Potato draws the random numbers, works out the payoff and writes a bonus file in Prolific's bulk format. It never pays anyone by itself.
When the draws happen
Nothing is drawn while the participant answers. The page never sees a price, a drawn row or a threshold, so none of them can influence an answer. The draws happen when you run the incentives export or call the admin route, and only for participants who have finished the study.
Each draw is seeded from the user ID, the rule name, the item or page the answer belongs to, and a private incentives.seed. Running the computation again on the same answers gives the same draws, adding an item leaves the draws for the other items unchanged, and every draw is written to bonuses.csv.
Price lists
The Holt & Laury (2002) list, with at most one switch point:
annotation_schemes:
- annotation_type: price_list
name: risk
description: For each decision, choose the option you prefer.
preset: holt_laury
single_switch: true
label_requirement: {required: true}The preset has ten rows. Option A pays $2.00 with probability p and $1.60 otherwise, option B pays $3.85 with probability p and $0.10 otherwise, and p runs 0.1, 0.2, ... 1.0. scale: 10 pays ten times those amounts.
A time-preference list sets an amount now against larger amounts later:
- annotation_type: price_list
name: patience
description: Which would you rather receive?
preset: time_preference
amount_now: 10
amounts_later: [10, 10.5, 11, 12, 13, 15]
delay_label: in 4 weeksRows can also be written by hand. a_pays and b_pays are what the incentive rule pays, either a number for a sure amount or [[probability, amount], ...] for a lottery, and without them the list can be shown but not paid:
- annotation_type: price_list
name: insurance
description: Choose one option in each row.
rows:
- {a: "$5 for sure", b: "50% chance of $12, otherwise $0",
a_pays: 5, b_pays: [[0.5, 12], [0.5, 0]]}
- {a: "$6 for sure", b: "50% chance of $12, otherwise $0",
a_pays: 6, b_pays: [[0.5, 12], [0.5, 0]]}| Key | Default | Meaning |
|---|---|---|
preset | none | holt_laury or time_preference. Give either preset or rows |
rows | none | At least two {a, b, a_pays, b_pays, a_after, b_after}. a_after and b_after name a payment delay, such as in 4 weeks |
scale | 1 | Multiplies every amount of a preset. Not allowed with rows |
currency | $ | Symbol printed before amounts |
single_switch | false | Keeps at most one switch from A to B |
option_labels | [Option A, Option B] | Column headings |
amount_now, amounts_later | none | time_preference only: the sure amount now, and one later amount per row |
delay_label, now_label | in 4 weeks, today | time_preference only: when each option pays |
With single_switch: true, choosing B in a row also chooses B in every later row, and choosing A also chooses A in every earlier row. The rows filled in this way flash briefly, so the participant sees what changed. Without it, each row is independent, and the answer records how many times the participant switched. Each row is one radio group, so the arrow keys and Tab work as they do in any radio group.
BDM bids
annotation_schemes:
- annotation_type: bdm
name: voucher
description: What is the most you would pay for a voucher worth $3.00?
min: 0
max: 5
step: 0.05
input: number
instructions: >-
You have $5.00 to spend. A price between $0.00 and $5.00 is drawn after
the study. If your bid is at least the price, you buy the voucher and pay
the price, not your bid; otherwise you keep the $5.00.
label_requirement: {required: true}max is required, and min defaults to 0. input: slider shows a slider instead of a number box, with a hollow thumb at the midpoint that fills once the participant moves it, so an untouched slider is not read as a bid. The draw range and the endowment live in the incentive rule, not in the scheme, so write the same numbers into instructions so that the participant knows the rule they are bidding under.
Belief elicitation
A probability:
annotation_schemes:
- annotation_type: belief_elicitation
name: forecast
description: How likely is it that this happens?
mode: point
left_label: Certainly not
right_label: Certainly
label_requirement: {required: true}A distribution:
- annotation_type: belief_elicitation
name: rainfall
description: How much rain will fall next week?
mode: bins
bins: ["0-9 mm", "10-19 mm", "20-29 mm", "30 mm or more"]
label_requirement: {required: true}In point mode, the slider runs from 0 to 100% and starts hollow at 50%, so an untouched slider is not read as a report of 50%. In bins mode, the participant types a whole number of points for each bin, and a running total shows how many points are left to place. Until the total is exactly 100, the question reports itself incomplete.
Configuring payment
incentives:
enabled: true
currency: USD
seed: "a private string, kept out of the repository"
pay: one_random_item # one_random_item | all | sum
max_bonus: 6.00
only_finished: true
rules:
- scheme: risk # a price_list scheme
method: mpl_random_row
- scheme: voucher # a bdm scheme
method: bdm
endowment: 5.00
item_value: 3.00
draw: {min: 0, max: 5, step: 0.25}
- scheme: forecast # a belief_elicitation scheme
method: bsr
prize: 2.00
outcome_field: outcome| Key | Default | Meaning |
|---|---|---|
enabled | false | Turns on the incentives export and the admin route |
currency | USD | Label written to bonuses.csv. Prolific pays in the study's own currency, so set this to match it |
seed | none | Private text mixed into every draw. Without it, anyone with the code could compute the draws before answering, and the export warns when it is missing or still the example's value |
pay | one_random_item | one_random_item pays one decision drawn at random from all of a participant's answered decisions. all pays the sum of every decision, and sum is another name for all |
max_bonus | none | Upper limit on one participant's bonus |
only_finished | true | Draws and pays only for participants who reached the end of the study |
rules | required | One entry per paid scheme, each with scheme (the scheme's name) and method |
A rule can name a scheme in annotation_schemes, answered once per item, or one on a phase page such as poststudy, answered once. potato validate checks that the scheme exists and has the right type for the method.
mpl_random_row
One row of the participant's price list is drawn at random, each row equally likely, and the option they chose in that row is played. A second draw u in [0, 1) picks the lottery's outcome, with outcomes laid end to end in the order listed. A list with an unanswered row is not paid, and a list with more than one switch is paid and marked multiple switching.
bdm
| Key | Meaning |
|---|---|
endowment | Money the participant holds for this decision, default 0. With draw, it must be at least draw.max, so a purchase never costs more than the participant was given |
draw | {min, max} for a price drawn uniformly and rounded to cents. Add step to draw one of min, min + step, ..., max |
price_field | Instead of draw, an item field holding a price fixed in advance |
item_value, value_field | What the object is worth in money to the participant who buys it, as a number or an item field. Default 0, for a physical object handed over separately |
A bid at or above the price buys at the price, not at the bid, so payoff = endowment - price + item value. A lower bid does not buy, and payoff = endowment. Because the price does not depend on the bid, bidding anything other than the object's value never helps the participant and sometimes costs them, which is the property that makes the procedure incentive-compatible.
bsr
| Key | Meaning |
|---|---|
prize | The amount paid on a win |
outcome_field | The item field that holds what happened: 1 or 0 (or true/false, yes/no) for a point report, and a bin label or 0-based bin index for a bins report |
outcome | Instead of outcome_field, one value for a question answered once, such as one on a phase page |
The prize is paid when the quadratic loss of the report is below a threshold drawn uniformly from [0, K]. For a point report p on a binary event, loss = (1{event} - p)² with K = 1. For a bins report, loss = Σ (1{bin happened} - p_bin)² with K = 2. A participant who wants the best chance of the prize does best by reporting what they believe, whatever their attitude to risk, because the only payoffs are the prize and nothing.
The true outcome has to be supplied before you compute payments, either in the data file under outcome_field or filled in once the events have happened. Potato strips outcome_field and price_field from the item data sent to the page, so keep them out of the item text too. A participant whose drawn decision has no outcome yet is listed as pending and left out of the Prolific file until the outcome is filled in.
Auditing the draws
bonuses.csv has one row per paid-for decision, with who it was, which rule paid which answer, the seed, every random number used (as JSON in draws), the payoff, whether it counts toward the bonus, the participant's total after max_bonus, and a note that says why a decision was not paid. To check a payment by hand, read the draws and recompute the payoff from the rules above. Reproducing the draws themselves needs the same answers, the same rule names and the same incentives.seed.
Paying through Prolific
Participants need a Prolific ID, so log them in from the study URL with login: {type: url_direct, url_argument: PROLIFIC_PID} or with crowdsourcing: {provider: prolific}.
From the export:
python -m potato.export --config config.yaml --format incentives --output ./bonuses/Upload bonuses_prolific.csv in Prolific's bulk bonus dialog. Each line is <participant_id>,<amount> with two decimals and no header. The export leaves out, with a warning each, participants owed nothing, participants without a Prolific ID, participants who have not finished, and participants whose chosen option pays later.
From the admin API, with a Prolific token configured:
# Dry run: nothing is sent to Prolific
curl -H "X-API-Key: $KEY" http://localhost:8000/admin/api/crowd/incentive_bonuses
# Pay: echo the dry run's prolific_total
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"pay": true, "expected_total": "12.40", "study_id": "<study id>"}' \
http://localhost:8000/admin/api/crowd/incentive_bonusesPaying needs "pay": true and an expected_total equal to the dry run's prolific_total. If an answer changed since the dry run, the totals differ and the request is refused with status 409.
Each paid batch is recorded in <task_dir>/incentive_payments.json. A batch that includes a participant an earlier batch paid is refused with status 409, which stops a double click or a second payment run from paying the same people twice. To pay only the participants no recorded batch has paid, add "skip_paid": true and set expected_total to the dry run's unpaid_total. To pay a recorded batch again on purpose, add "repeat": true.
Example
python potato/flask_server.py start examples/advanced/incentivized-elicitation/config.yaml -p 8000The example asks four belief questions, one per item and paid by bsr from each item's outcome, then a post-study page with a Holt & Laury price list and a BDM bid for a voucher worth $3.00.
Further Reading
- Experiment Flow - Conditions and screen-outs
- Running a Crowdsourced Study - Prolific logins, completion codes and bonuses
- Constant Sum - Point allocations that are not paid
- Online Behavioral Experiments - A full study in Potato
For implementation details, see the source documentation.