# Buyer-Seller-Appraiser Bargaining Experiment Built with **oTree 6.0.2**. ## Setup ```bash pip install otree==6.0.2 cd bargaining_experiment otree devserver ``` Open http://localhost:8000 and start a demo session with **3 participants**. --- ## Game Flow ### Stage 0 – Role Assignment (all players) Roles are randomly assigned: **Buyer**, **Seller**, **Appraiser**. --- ### Stage 1 – Practice Round (Buyer & Seller; Appraiser waits) - Each player sees a slider from 0–400. - They must drag the thumb to their assigned **target value** (Buyer: 250, Seller: 150). - The **Submit** button is disabled until the target is matched exactly. - The Appraiser sees a wait page. --- ### Stage 2 – Anonymous Offer Round (Buyer & Seller; Appraiser waits) - Each player privately enters a price between 0 and 400. - Neither can see the other's offer. - A slider is provided as a UX aid but a numeric input is also accepted. - **If Buyer price ≥ Seller price → instant deal** (average of the two prices). The game jumps directly to the Final Results page. - **Otherwise** → proceed to the Public Bargaining phase. --- ### Stage 3 – Public Bargaining (Buyer & Seller; Appraiser has own page) #### Buyer & Seller - Both see a **shared price line** with two coloured thumbs: - 🔵 **Blue** = Buyer's current position - 🔴 **Red** = Seller's current position - Initial positions are set to their anonymous offer values. - Each player controls **only their own thumb** via their personal slider below the line. - Positions broadcast to both players in real-time via oTree Live Pages (WebSocket). - A **green overlap zone** appears when the buyer position ≥ seller position, indicating a potential deal. **Round ends when ANY of the following occur:** | Condition | Timer | |-----------|-------| | Buyer pos ≥ Seller pos | Immediate deal | | No movement by either party for **10 seconds** | Idle timeout | | **60 seconds** elapsed | Round timer | Three on-screen timers are shown: - ⏱ **Time Remaining** (counts down from 60s, turns red below 10s) - ⏸ **Your Idle Time** (resets on any movement, turns red near 10s) - ✅ **Total Time** (counts up) All timing values (idle time, time in round, total time) are saved to the database. #### Appraiser - Sees their own price line (100–400) with their randomly assigned appraiser price shown as a purple marker. - Does **not** interact with the bargaining. - Waits for the bargaining phase to complete. --- ### Stage 4 – Final Results (all players) Displays: - Outcome (instant deal / bargaining deal / no deal) - Final price (if deal) - Total time, idle time, time in round - Appraiser's price (for Appraiser role) --- ## File Structure ``` bargaining_experiment/ ├── settings.py └── bargaining_game/ ├── __init__.py ← Models, pages, live methods └── templates/ └── bargaining_game/ ├── RoleAssignment.html ├── PracticeSlider.html ├── AnonymousOffer.html ├── InstantDealResult.html ├── BargainingPage.html ← Main real-time page ├── AppriserBargainingPage.html └── FinalResults.html ``` ## Key Implementation Notes - **Live Pages**: `BargainingPage` uses `live_method` in `__init__.py` with `liveSend` / `liveRecv` in JS for real-time position updates. - **Timing**: All timing is done client-side (JS `setInterval`) and reported to the server via `liveSend`. The server stores values in the Player/Group models. - **Broadcast**: Sending to player id `0` in the live_method return dict broadcasts to the whole group. - **Idle detection**: Resets `lastMoveTime` on every `input` event; the ticker compares elapsed time since last move.