Move lockdrop calculations to a module and add a experimentation notebook (#2)
- Refactor lockdrop calculations and other helper code from the simulation notebook to a python module - Add a notebook for experimenting with lockdrop calculations - Add widget buttons to run calculations and export results - Add a script a to setup and run the notebook Reviewed-on: #2 Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com> Co-committed-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
This commit is contained in:
parent
779b091ccd
commit
652c69cbee
120
EXPERIMENT.md
Normal file
120
EXPERIMENT.md
Normal file
@ -0,0 +1,120 @@
|
||||
# Lockdrop Allocation Experimentation
|
||||
|
||||
This guide explains how to use the interactive notebook to experiment with different lockdrop participation scenarios and understand how various distributions affect token allocations.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Python3](https://wiki.python.org/moin/BeginnersGuide/Download) `3.12.x` >= `python3 --version` >= `3.8.10` (the Python3 shipped in Ubuntu 20+ is good to go)
|
||||
- [pip](https://pip.pypa.io/en/stable/installation/) (Python package manager)
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Clone and Navigate to Directory**
|
||||
|
||||
```bash
|
||||
git clone git@git.vdb.to:LaconicNetwork/lockdrop-simulation.git
|
||||
cd lockdrop-simulation
|
||||
```
|
||||
|
||||
2. **Run the Experimentation Script**
|
||||
|
||||
```bash
|
||||
./run_experiment.sh
|
||||
```
|
||||
|
||||
This script will:
|
||||
- Automatically create a Python virtual environment
|
||||
- Install all required dependencies
|
||||
- Launch Jupyter notebook with the experimentation interface
|
||||
- Open your browser to the interactive notebook
|
||||
|
||||
3. **Use the Interactive Interface**
|
||||
|
||||
The notebook will open automatically in your browser. Execute it by clicking "Run All" from `Run` tab in order to start experimenting with different participation scenarios.
|
||||
|
||||
## Using the Interactive Interface
|
||||
|
||||
### Input Controls
|
||||
|
||||
- **Star Participation**: Adjust participation numbers for stars across 1-5 year lock durations
|
||||
- **Galaxy Participation**: Adjust participation numbers for galaxies across 1-5 year lock durations
|
||||
- **Preset Scenarios**: Use the dropdown to quickly load common scenarios:
|
||||
- **Balanced**: Even distribution across all lock periods
|
||||
- **Five-year focused**: Most participants choose maximum lock duration
|
||||
- **Short-term focused**: Most participants choose shorter lock durations
|
||||
- **Low participation**: Reduced overall participation scenario
|
||||
|
||||
### Action Buttons
|
||||
|
||||
- **🔄 Calculate Allocations**: Run calculations and display comprehensive analysis
|
||||
- **💾 Export Results**: Save experimental results to timestamped JSON file
|
||||
|
||||
## Understanding the Analysis Output
|
||||
|
||||
The notebook provides detailed analysis including:
|
||||
|
||||
### 1. Participation Distribution
|
||||
- Breakdown of participants by lock period
|
||||
- Participation rates for stars and galaxies
|
||||
- Total participant counts
|
||||
|
||||
### 2. Allocation Calculations
|
||||
- Base allocations accounting for actual participation
|
||||
- Quanta-adjusted allocations per participant
|
||||
- Calculations on rounding errors resulting from quanta usage
|
||||
|
||||
### 3. Penalty System Analysis
|
||||
- Impact of lock duration on allocations before bonuses
|
||||
- Penalty rates for each lock period
|
||||
- Allocation percentages relative to maximum
|
||||
|
||||
### 4. Bonus Pool Calculations
|
||||
- How penalties redistribute to 5-year participants
|
||||
- Bonus amounts per 5-year participant
|
||||
- Total bonus pool distribution
|
||||
|
||||
### 5. Final Allocations & Verification
|
||||
- Complete allocation breakdown by lock period
|
||||
- Z tokens per block calculations
|
||||
- Allocation verification and rounding error tracking
|
||||
|
||||
### 6. Visualizations
|
||||
- Participation distribution charts
|
||||
- Allocation comparison graphs
|
||||
- Lock period analysis plots
|
||||
|
||||
## Experimenting with Scenarios
|
||||
|
||||
### Quick Start with Presets
|
||||
|
||||
1. Select a preset from the dropdown (e.g., "Five-year focused")
|
||||
2. Click "🔄 Calculate Allocations"
|
||||
3. Review the analysis output
|
||||
4. Try different presets to compare scenarios
|
||||
|
||||
### Custom Scenarios
|
||||
|
||||
1. Manually adjust participation numbers
|
||||
2. Ensure total participants don't exceed maximum available (65,280 stars, 256 galaxies)
|
||||
3. Click "🔄 Calculate Allocations"
|
||||
4. Use "💾 Export Results" to save interesting configurations
|
||||
|
||||
## Exported Results
|
||||
|
||||
Export files contain:
|
||||
- **Metadata**: Timestamp, source notebook, participation parameters
|
||||
- **Allocations**: Complete calculation results in JSON format
|
||||
- **File naming**: `lockdrop_calculations_result_YYYYMMDD_HHMMSS.json`
|
||||
|
||||
These files can be used for:
|
||||
- Further analysis
|
||||
- Sharing experimental configurations
|
||||
- Testing different scenarios
|
||||
- Integration with other tools
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Import errors**: Ensure all dependencies are installed via `pip install -r requirements.txt`
|
||||
- **Widget display issues**: Restart notebook kernel and re-run cells
|
||||
- **Calculation errors**: Check that participation numbers don't exceed limits
|
||||
- **Export failures**: Ensure write permissions in the notebook directory
|
||||
17
README.md
17
README.md
@ -1,5 +1,22 @@
|
||||
# lockdrop-simulation
|
||||
|
||||
## Overview
|
||||
|
||||
This repository provides two main ways to work with lockdrop calculations:
|
||||
|
||||
### Interactive Experimentation
|
||||
|
||||
For independent experimentation with lockdrop allocation scenarios, see [EXPERIMENT.md](./EXPERIMENT.md). This allows you to:
|
||||
- Experiment with different participation distributions using an interactive Jupyter notebook
|
||||
- Test various scenarios (balanced, five-year focused, short-term focused, etc.)
|
||||
- Export timestamped results for analysis
|
||||
|
||||
### Full Simulation & Validation
|
||||
|
||||
Continue reading below for the complete simulation workflow that validates token distribution against a live zenithd node.
|
||||
|
||||
---
|
||||
|
||||
## Approach
|
||||
|
||||
The lockdrop simulation validates the Zenith Network's token distribution mechanism by creating a realistic test environment without requiring real Ethereum data or live participants.
|
||||
|
||||
File diff suppressed because one or more lines are too long
90
lockdrop-calculations.ipynb
Normal file
90
lockdrop-calculations.ipynb
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "experimental-header",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Z Token Lockdrop Distribution\n",
|
||||
"\n",
|
||||
"This notebook allows you to experiment with different participation distributions to see how they affect token allocations, bonus pools, and the penalty system.\n",
|
||||
"\n",
|
||||
"**Features:**\n",
|
||||
"- Adjust star and galaxy participation numbers by lock duration\n",
|
||||
"- Real-time calculation of allocations and bonus pools\n",
|
||||
"- Preset scenarios available in dropdown (balanced, five-year focused, short-term focused, low participation)\n",
|
||||
"- Visualization of results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "setup-imports",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Import shared calculation module\n",
|
||||
"from lockdrop import (\n",
|
||||
" configure_matplotlib, print_constants_summary,\n",
|
||||
" create_experimental_interface\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Configure matplotlib\n",
|
||||
"configure_matplotlib()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "show-constants",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Display core constants\n",
|
||||
"print_constants_summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "input-section-header",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎛️ Experimental Parameters\n",
|
||||
"\n",
|
||||
"Adjust the participation numbers below to experiment with different scenarios. Use the preset dropdown to quickly load common scenarios, or manually adjust the values for custom experiments."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "input-parameters",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create complete experimental interface\n",
|
||||
"create_experimental_interface()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
25
lockdrop/__init__.py
Normal file
25
lockdrop/__init__.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""
|
||||
Lockdrop calculations module.
|
||||
|
||||
This package provides a modular implementation of lockdrop token distribution calculations,
|
||||
penalty systems, bonus pools, and interactive experimental interfaces.
|
||||
"""
|
||||
|
||||
# Import all public functions for backward compatibility
|
||||
from .constants import *
|
||||
from .calculations import *
|
||||
from .display import (
|
||||
print_constants_summary,
|
||||
print_analysis_tables
|
||||
)
|
||||
from .visualization import *
|
||||
from .widgets import (
|
||||
create_experimental_interface
|
||||
)
|
||||
from .simulation import (
|
||||
run_simulation_analysis
|
||||
)
|
||||
|
||||
__version__ = "1.0.0"
|
||||
|
||||
# TODO: Use this package in zenithd notebooks
|
||||
185
lockdrop/calculations.py
Normal file
185
lockdrop/calculations.py
Normal file
@ -0,0 +1,185 @@
|
||||
"""
|
||||
Core lockdrop calculation functions.
|
||||
|
||||
This module contains the main mathematical functions for calculating token allocations,
|
||||
penalties, bonuses, and final distributions.
|
||||
"""
|
||||
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
from .constants import (
|
||||
TOKEN_PRECISION, LOCKDROP_ALLOCATION, STAR_ALLOCATION_PERCENT,
|
||||
GALAXY_ALLOCATION_PERCENT,
|
||||
LOCKDROP_DURATION_BLOCKS, PENALTY_RATES
|
||||
)
|
||||
|
||||
|
||||
def calculate_dynamic_allocations(participation_counts):
|
||||
"""
|
||||
Calculate allocations based on actual participation counts.
|
||||
|
||||
Args:
|
||||
participation_counts: Dict with keys like 'stars_1_years', 'stars_2_years', etc.
|
||||
"""
|
||||
# Extract participation data
|
||||
stars_counts = {year: Decimal(participation_counts[f'stars_{year}_years']) for year in range(1, 6)}
|
||||
galaxies_counts = {year: Decimal(participation_counts[f'galaxies_{year}_years']) for year in range(1, 6)}
|
||||
|
||||
total_stars_locked = sum(stars_counts.values())
|
||||
total_galaxies_locked = sum(galaxies_counts.values())
|
||||
|
||||
lockdrop_allocation_stars = LOCKDROP_ALLOCATION * STAR_ALLOCATION_PERCENT
|
||||
lockdrop_allocation_galaxies = LOCKDROP_ALLOCATION * GALAXY_ALLOCATION_PERCENT
|
||||
|
||||
# Dynamic allocations based on actual participation
|
||||
max_allocation_per_star = lockdrop_allocation_stars / total_stars_locked if total_stars_locked > 0 else Decimal('0')
|
||||
max_allocation_per_galaxy = lockdrop_allocation_galaxies / total_galaxies_locked if total_galaxies_locked > 0 else Decimal('0')
|
||||
|
||||
# Quanta calculation
|
||||
z_available_per_star_per_block = max_allocation_per_star / LOCKDROP_DURATION_BLOCKS
|
||||
z_available_per_galaxy_per_block = max_allocation_per_galaxy / LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
# Round down to 6 decimals
|
||||
adjusted_z_per_star_per_block = z_available_per_star_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
adjusted_z_per_galaxy_per_block = z_available_per_galaxy_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
|
||||
# Adjusted max allocation
|
||||
adjusted_max_allocation_per_star = adjusted_z_per_star_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
adjusted_max_allocation_per_galaxy = adjusted_z_per_galaxy_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
# Rounding errors
|
||||
rounding_error_per_star = max_allocation_per_star - adjusted_max_allocation_per_star
|
||||
rounding_error_per_galaxy = max_allocation_per_galaxy - adjusted_max_allocation_per_galaxy
|
||||
|
||||
total_rounding_error_stars = lockdrop_allocation_stars - (adjusted_max_allocation_per_star * total_stars_locked)
|
||||
total_rounding_error_galaxies = lockdrop_allocation_galaxies - (adjusted_max_allocation_per_galaxy * total_galaxies_locked)
|
||||
|
||||
return {
|
||||
'stars_counts': stars_counts,
|
||||
'galaxies_counts': galaxies_counts,
|
||||
'total_stars_locked': total_stars_locked,
|
||||
'total_galaxies_locked': total_galaxies_locked,
|
||||
'adjusted_max_allocation_per_star': adjusted_max_allocation_per_star,
|
||||
'adjusted_max_allocation_per_galaxy': adjusted_max_allocation_per_galaxy,
|
||||
'rounding_error_per_star': rounding_error_per_star,
|
||||
'rounding_error_per_galaxy': rounding_error_per_galaxy,
|
||||
'total_rounding_error_stars': total_rounding_error_stars,
|
||||
'total_rounding_error_galaxies': total_rounding_error_galaxies,
|
||||
'adjusted_z_per_star_per_block': adjusted_z_per_star_per_block,
|
||||
'adjusted_z_per_galaxy_per_block': adjusted_z_per_galaxy_per_block
|
||||
}
|
||||
|
||||
|
||||
def calculate_bonus_pools(allocation_data):
|
||||
"""Calculate bonus pools from penalties and rounding errors."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
total_rounding_error_stars = allocation_data['total_rounding_error_stars']
|
||||
total_rounding_error_galaxies = allocation_data['total_rounding_error_galaxies']
|
||||
|
||||
# Calculate penalty pools
|
||||
star_penalty_pool = Decimal('0')
|
||||
for years in [1, 2, 3, 4]:
|
||||
penalty = PENALTY_RATES[years]
|
||||
star_penalty_pool += adjusted_max_allocation_per_star * stars_counts[years] * penalty
|
||||
|
||||
galaxy_penalty_pool = Decimal('0')
|
||||
for years in [1, 2, 3, 4]:
|
||||
penalty = PENALTY_RATES[years]
|
||||
galaxy_penalty_pool += adjusted_max_allocation_per_galaxy * galaxies_counts[years] * penalty
|
||||
|
||||
star_penalty_pool = star_penalty_pool.quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
galaxy_penalty_pool = galaxy_penalty_pool.quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Add rounding errors to bonus pools
|
||||
star_bonus_pool_total = star_penalty_pool + total_rounding_error_stars
|
||||
galaxy_bonus_pool_total = galaxy_penalty_pool + total_rounding_error_galaxies
|
||||
|
||||
# Calculate bonus per 5-year participant
|
||||
stars_5_years = stars_counts[5]
|
||||
galaxies_5_years = galaxies_counts[5]
|
||||
|
||||
bonus_per_star_5_years = star_bonus_pool_total / stars_5_years if stars_5_years > 0 else Decimal('0')
|
||||
bonus_per_galaxy_5_years = galaxy_bonus_pool_total / galaxies_5_years if galaxies_5_years > 0 else Decimal('0')
|
||||
|
||||
return {
|
||||
'star_penalty_pool': star_penalty_pool,
|
||||
'galaxy_penalty_pool': galaxy_penalty_pool,
|
||||
'star_bonus_pool_total': star_bonus_pool_total,
|
||||
'galaxy_bonus_pool_total': galaxy_bonus_pool_total,
|
||||
'bonus_per_star_5_years': bonus_per_star_5_years,
|
||||
'bonus_per_galaxy_5_years': bonus_per_galaxy_5_years
|
||||
}
|
||||
|
||||
|
||||
def calculate_final_allocations(allocation_data, bonus_data):
|
||||
"""Calculate final allocations for all lock periods."""
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
bonus_per_star_5_years = bonus_data['bonus_per_star_5_years']
|
||||
bonus_per_galaxy_5_years = bonus_data['bonus_per_galaxy_5_years']
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
|
||||
# Calculate final allocations
|
||||
final_star_allocations = {}
|
||||
final_galaxy_allocations = {}
|
||||
|
||||
for years in range(1, 6):
|
||||
penalty = PENALTY_RATES[years]
|
||||
|
||||
if years == 5:
|
||||
# 5-year participants get base + bonus
|
||||
final_star_allocations[years] = (adjusted_max_allocation_per_star + bonus_per_star_5_years).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
final_galaxy_allocations[years] = (adjusted_max_allocation_per_galaxy + bonus_per_galaxy_5_years).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
else:
|
||||
# Other years get penalized amounts
|
||||
final_star_allocations[years] = (adjusted_max_allocation_per_star * (1 - penalty)).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
final_galaxy_allocations[years] = (adjusted_max_allocation_per_galaxy * (1 - penalty)).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Calculate Z per block for each lock period
|
||||
star_z_per_block = {}
|
||||
galaxy_z_per_block = {}
|
||||
|
||||
for years in range(1, 6):
|
||||
lock_duration_fraction = Decimal(years) / Decimal('5') # Fraction of full 5-year period
|
||||
effective_blocks = LOCKDROP_DURATION_BLOCKS * lock_duration_fraction
|
||||
|
||||
star_z_per_block[years] = (final_star_allocations[years] / effective_blocks).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
galaxy_z_per_block[years] = (final_galaxy_allocations[years] / effective_blocks).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Calculate total allocations for verification
|
||||
total_stars_allocation = sum(final_star_allocations[year] * stars_counts[year] for year in range(1, 6))
|
||||
total_galaxies_allocation = sum(final_galaxy_allocations[year] * galaxies_counts[year] for year in range(1, 6))
|
||||
|
||||
return {
|
||||
'final_star_allocations': final_star_allocations,
|
||||
'final_galaxy_allocations': final_galaxy_allocations,
|
||||
'star_z_per_block': star_z_per_block,
|
||||
'galaxy_z_per_block': galaxy_z_per_block,
|
||||
'total_stars_allocation': total_stars_allocation,
|
||||
'total_galaxies_allocation': total_galaxies_allocation
|
||||
}
|
||||
|
||||
|
||||
def generate_test_output(final_data):
|
||||
"""Generate JSON output for test validation."""
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
total_allocation = final_data['total_stars_allocation'] + final_data['total_galaxies_allocation']
|
||||
|
||||
# Convert to $sZ units (multiply by 10^8)
|
||||
output = {
|
||||
"stars": {
|
||||
f"{year}_years": int(final_star_allocations[year] * Decimal('1e8'))
|
||||
for year in range(1, 6)
|
||||
},
|
||||
"galaxies": {
|
||||
f"{year}_years": int(final_galaxy_allocations[year] * Decimal('1e8'))
|
||||
for year in range(1, 6)
|
||||
},
|
||||
"total": int(total_allocation * Decimal('1e8'))
|
||||
}
|
||||
|
||||
return output
|
||||
64
lockdrop/constants.py
Normal file
64
lockdrop/constants.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""
|
||||
Lockdrop calculation constants and configuration.
|
||||
|
||||
This module contains all constants used in lockdrop token distribution calculations.
|
||||
"""
|
||||
|
||||
from decimal import Decimal, ROUND_DOWN, getcontext
|
||||
|
||||
# Configure decimal precision
|
||||
getcontext().prec = 28
|
||||
getcontext().rounding = ROUND_DOWN
|
||||
|
||||
# Token Constants
|
||||
TOKEN_PRECISION = Decimal('0.00000001') # 8 decimal precision
|
||||
TOTAL_SUPPLY = 4294967296 # 1 $Z per Urbit ID
|
||||
LOCKDROP_ALLOCATION_PERCENT = Decimal('0.3')
|
||||
LOCKDROP_ALLOCATION = TOTAL_SUPPLY * LOCKDROP_ALLOCATION_PERCENT
|
||||
|
||||
# Urbit Point Counts
|
||||
NUM_GALAXIES = pow(2, 8)
|
||||
NUM_STARS = pow(2, 16) - pow(2, 8)
|
||||
NUM_PLANETS = pow(2, 32) - pow(2, 16)
|
||||
|
||||
# Allocation Distribution
|
||||
STAR_ALLOCATION_PERCENT = Decimal(NUM_STARS / pow(2, 16))
|
||||
GALAXY_ALLOCATION_PERCENT = 1 - STAR_ALLOCATION_PERCENT
|
||||
|
||||
# Lockdrop Duration (5 years including 1 leap year)
|
||||
LOCKDROP_DURATION_SECONDS = 5 * 365.25 * 24 * 60 * 60
|
||||
BLOCK_DURATION_SECONDS = 2
|
||||
LOCKDROP_DURATION_BLOCKS = int(LOCKDROP_DURATION_SECONDS / BLOCK_DURATION_SECONDS)
|
||||
|
||||
# Penalty Rates by Lock Period
|
||||
PENALTY_RATES = {
|
||||
5: Decimal('0'),
|
||||
4: Decimal('0.2'),
|
||||
3: Decimal('0.4'),
|
||||
2: Decimal('0.6'),
|
||||
1: Decimal('0.8')
|
||||
}
|
||||
|
||||
# Preset Scenarios for Experimentation
|
||||
SCENARIOS = {
|
||||
"balanced": {
|
||||
"description": 'Balanced distribution across all lock periods',
|
||||
"stars": {"1_year": 8000, "2_year": 8000, "3_year": 8000, "4_year": 8000, "5_year": 8000},
|
||||
"galaxies": {"1_year": 40, "2_year": 40, "3_year": 40, "4_year": 40, "5_year": 40}
|
||||
},
|
||||
"five_year_focused": {
|
||||
"description": 'Most participants choose 5-year lock (maximum bonus scenario)',
|
||||
"stars": {"1_year": 2000, "2_year": 2000, "3_year": 3000, "4_year": 5000, "5_year": 28000},
|
||||
"galaxies": {"1_year": 10, "2_year": 10, "3_year": 20, "4_year": 30, "5_year": 130}
|
||||
},
|
||||
"short_term_focused": {
|
||||
"description": 'Most participants choose shorter locks (high penalty scenario)',
|
||||
"stars": {"1_year": 20000, "2_year": 15000, "3_year": 4000, "4_year": 800, "5_year": 200},
|
||||
"galaxies": {"1_year": 100, "2_year": 80, "3_year": 15, "4_year": 3, "5_year": 2}
|
||||
},
|
||||
"low_participation": {
|
||||
"description": 'Low overall participation scenario',
|
||||
"stars": {"1_year": 1000, "2_year": 800, "3_year": 600, "4_year": 400, "5_year": 200},
|
||||
"galaxies": {"1_year": 8, "2_year": 6, "3_year": 4, "4_year": 2, "5_year": 5}
|
||||
}
|
||||
}
|
||||
265
lockdrop/display.py
Normal file
265
lockdrop/display.py
Normal file
@ -0,0 +1,265 @@
|
||||
"""
|
||||
Display and table formatting functions for lockdrop analysis.
|
||||
|
||||
This module contains functions for displaying analysis results in formatted tables
|
||||
and organizing the presentation of calculation results.
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
from decimal import Decimal
|
||||
from tabulate import tabulate
|
||||
from .constants import (
|
||||
PENALTY_RATES, LOCKDROP_ALLOCATION, STAR_ALLOCATION_PERCENT,
|
||||
GALAXY_ALLOCATION_PERCENT, LOCKDROP_DURATION_BLOCKS, TOTAL_SUPPLY,
|
||||
NUM_STARS, NUM_GALAXIES, NUM_PLANETS, LOCKDROP_ALLOCATION_PERCENT
|
||||
)
|
||||
|
||||
|
||||
def print_table_with_borders(df, title=""):
|
||||
"""Print DataFrame with borders using tabulate."""
|
||||
if title:
|
||||
print(f"\n{title}")
|
||||
print(tabulate(df, headers='keys', tablefmt='grid', showindex=False))
|
||||
|
||||
|
||||
def print_constants_summary():
|
||||
"""Display core constants as formatted tables."""
|
||||
print("=" * 80)
|
||||
print("📊 $Z LOCKDROP DISTRIBUTION - CORE CONSTANTS")
|
||||
print("=" * 80)
|
||||
|
||||
# Lockdrop Allocation Table
|
||||
lockdrop_df = pd.DataFrame({
|
||||
'Parameter': ['Total Supply (1 $Z per Urbit ID)', 'Lockdrop Allocation %', 'Lockdrop Allocation ($Z)'],
|
||||
'Value': [f"{TOTAL_SUPPLY:,}", f"{LOCKDROP_ALLOCATION_PERCENT:.1%}", f"{LOCKDROP_ALLOCATION:,.1f}"]
|
||||
})
|
||||
print_table_with_borders(lockdrop_df, "🔒 LOCKDROP ALLOCATION")
|
||||
|
||||
# Points Distribution Table
|
||||
points_df = pd.DataFrame({
|
||||
'Point Type': ['Galaxies', 'Stars', 'Planets (excl.)'],
|
||||
'Count': [f"{NUM_GALAXIES:,}", f"{NUM_STARS:,}", f"{NUM_PLANETS:,}"],
|
||||
'Allocation %': [f"{GALAXY_ALLOCATION_PERCENT:.3%}", f"{STAR_ALLOCATION_PERCENT:.3%}", "0%"]
|
||||
})
|
||||
print_table_with_borders(points_df, "🌟 URBIT POINT DISTRIBUTION")
|
||||
|
||||
# Penalty Schedule Table
|
||||
penalty_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty Rate': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Token % of Max': ['100%', '80%', '60%', '40%', '20%']
|
||||
})
|
||||
print_table_with_borders(penalty_df, "⚖️ PENALTY SCHEDULE")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_allocation_calculations(allocation_data):
|
||||
"""Print basic allocation calculation tables."""
|
||||
lockdrop_allocation_stars = LOCKDROP_ALLOCATION * STAR_ALLOCATION_PERCENT
|
||||
lockdrop_allocation_galaxies = LOCKDROP_ALLOCATION * GALAXY_ALLOCATION_PERCENT
|
||||
|
||||
total_stars_locked = allocation_data['total_stars_locked']
|
||||
total_galaxies_locked = allocation_data['total_galaxies_locked']
|
||||
rounding_error_per_star = allocation_data['rounding_error_per_star']
|
||||
rounding_error_per_galaxy = allocation_data['rounding_error_per_galaxy']
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
adjusted_z_per_star_per_block = allocation_data['adjusted_z_per_star_per_block']
|
||||
adjusted_z_per_galaxy_per_block = allocation_data['adjusted_z_per_galaxy_per_block']
|
||||
total_rounding_error_stars = allocation_data['total_rounding_error_stars']
|
||||
total_rounding_error_galaxies = allocation_data['total_rounding_error_galaxies']
|
||||
|
||||
print("=" * 80)
|
||||
print("🔢 ALLOCATION CALCULATIONS")
|
||||
print("=" * 80)
|
||||
|
||||
# Raw Allocations Table
|
||||
raw_allocation_per_star = lockdrop_allocation_stars / total_stars_locked if total_stars_locked > 0 else Decimal('0')
|
||||
raw_allocation_per_galaxy = lockdrop_allocation_galaxies / total_galaxies_locked if total_galaxies_locked > 0 else Decimal('0')
|
||||
|
||||
raw_allocations_df = pd.DataFrame({
|
||||
'Point Type': ['Stars', 'Galaxies'],
|
||||
'Total Allocation ($Z)': [f"{lockdrop_allocation_stars:,.1f}", f"{lockdrop_allocation_galaxies:,.1f}"],
|
||||
'Max Per Point ($Z)': [f"{raw_allocation_per_star:,.6f}", f"{raw_allocation_per_galaxy:,.6f}"]
|
||||
})
|
||||
print_table_with_borders(raw_allocations_df, "💰 RAW PARTICIPANT ALLOCATIONS")
|
||||
|
||||
# Quanta Calculations Table
|
||||
raw_z_per_star_per_block = raw_allocation_per_star / LOCKDROP_DURATION_BLOCKS if total_stars_locked > 0 else Decimal('0')
|
||||
raw_z_per_galaxy_per_block = raw_allocation_per_galaxy / LOCKDROP_DURATION_BLOCKS if total_galaxies_locked > 0 else Decimal('0')
|
||||
|
||||
quanta_df = pd.DataFrame({
|
||||
'Point Type': ['Stars', 'Galaxies'],
|
||||
'Raw Z per Block': [f"{raw_z_per_star_per_block:.15f}", f"{raw_z_per_galaxy_per_block:.15f}"],
|
||||
'Adjusted Z per Block (q)': [f"{adjusted_z_per_star_per_block:.6f}", f"{adjusted_z_per_galaxy_per_block:.6f}"]
|
||||
})
|
||||
print_table_with_borders(quanta_df, "⚙️ QUANTA CALCULATION")
|
||||
|
||||
# Adjusted Allocations Table (after quanta calculation)
|
||||
percentage_rounding_error_stars = total_rounding_error_stars / TOTAL_SUPPLY * 100 if total_stars_locked > 0 else Decimal('0')
|
||||
percentage_rounding_error_galaxies = total_rounding_error_galaxies / TOTAL_SUPPLY * 100 if total_galaxies_locked > 0 else Decimal('0')
|
||||
|
||||
# Separate tables for better readability
|
||||
star_adjusted_df = pd.DataFrame({
|
||||
'Metric': ['Adjusted Max per Star', 'Rounding Error per Star', 'Total Rounding Error', 'Rounding Error %'],
|
||||
'Value': [f"{adjusted_max_allocation_per_star:,.6f} $Z", f"{rounding_error_per_star:.9f} $Z",
|
||||
f"{total_rounding_error_stars:,.6f} $Z", f"{percentage_rounding_error_stars:.8f}%"],
|
||||
'Note': ['Before bonus', 'Per star loss', 'Goes to bonus pool', 'Of total supply']
|
||||
})
|
||||
|
||||
galaxy_adjusted_df = pd.DataFrame({
|
||||
'Metric': ['Adjusted Max per Galaxy', 'Rounding Error per Galaxy', 'Total Rounding Error', 'Rounding Error %'],
|
||||
'Value': [f"{adjusted_max_allocation_per_galaxy:,.6f} $Z", f"{rounding_error_per_galaxy:.9f} $Z",
|
||||
f"{total_rounding_error_galaxies:,.6f} $Z", f"{percentage_rounding_error_galaxies:.8f}%"],
|
||||
'Note': ['Before bonus', 'Per galaxy loss', 'Goes to bonus pool', 'Of total supply']
|
||||
})
|
||||
|
||||
print("\n🎯 QUANTA ADJUSTED PARTICIPANT ALLOCATIONS")
|
||||
print_table_with_borders(star_adjusted_df, "⭐ Star Adjustments")
|
||||
print_table_with_borders(galaxy_adjusted_df, "🌌 Galaxy Adjustments")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_penalty_analysis(allocation_data):
|
||||
"""Print penalty system analysis using values before bonus calculations."""
|
||||
print("=" * 80)
|
||||
print("⚖️ PENALTY SYSTEM ANALYSIS")
|
||||
print("=" * 80)
|
||||
|
||||
# Use adjusted allocations (after penalties, before bonus distribution)
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
|
||||
penalty_analysis_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty Rate': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Star Allocation ($Z)': [f"{adjusted_max_allocation_per_star * (1 - PENALTY_RATES[year]):,.6f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Galaxy Allocation ($Z)': [f"{adjusted_max_allocation_per_galaxy * (1 - PENALTY_RATES[year]):,.6f}" for year in [5, 4, 3, 2, 1]],
|
||||
'vs Max Allocation': ['100%', '80%', '60%', '40%', '20%']
|
||||
})
|
||||
|
||||
print_table_with_borders(penalty_analysis_df, "📊 PENALTY ADJUSTED ALLOCATIONS (Before Bonus Distribution)")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_participation_summary(allocation_data):
|
||||
"""Print participation distribution summary."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
total_stars_locked = allocation_data['total_stars_locked']
|
||||
total_galaxies_locked = allocation_data['total_galaxies_locked']
|
||||
|
||||
# Consolidated participation and lock period distribution
|
||||
participation_df = pd.DataFrame({
|
||||
'Lock Period': ['1 Year', '2 Years', '3 Years', '4 Years', '5 Years', 'Total'],
|
||||
'Stars': [f"{stars_counts[year]:,}" for year in [1, 2, 3, 4, 5]] + [f"{total_stars_locked:,}"],
|
||||
'% of Total Stars': [f"{stars_counts[year]/NUM_STARS:.2%}" for year in [1, 2, 3, 4, 5]] + [f"{total_stars_locked/NUM_STARS:.1%}"],
|
||||
'Galaxies': [f"{galaxies_counts[year]:,}" for year in [1, 2, 3, 4, 5]] + [f"{total_galaxies_locked:,}"],
|
||||
'% of Total Galaxies': [f"{galaxies_counts[year]/NUM_GALAXIES:.2%}" for year in [1, 2, 3, 4, 5]] + [f"{total_galaxies_locked/NUM_GALAXIES:.1%}"],
|
||||
'Total': [f"{stars_counts[year] + galaxies_counts[year]:,}" for year in [1, 2, 3, 4, 5]] + [f"{total_stars_locked + total_galaxies_locked:,}"],
|
||||
})
|
||||
|
||||
print("=" * 80)
|
||||
print_table_with_borders(participation_df, "🎯 PARTICIPANTS LOCK PERIOD DISTRIBUTION")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_bonus_pool_calculations(allocation_data, bonus_data):
|
||||
"""Print detailed bonus pool calculations."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
total_rounding_error_stars = allocation_data['total_rounding_error_stars']
|
||||
total_rounding_error_galaxies = allocation_data['total_rounding_error_galaxies']
|
||||
|
||||
print("=" * 80)
|
||||
print("🎁 BONUS POOL CALCULATIONS")
|
||||
print("=" * 80)
|
||||
|
||||
# Star Bonus Pool Analysis
|
||||
star_bonus_df = pd.DataFrame({
|
||||
'Component': ['Penalty Pool', 'Rounding Error Bonus', 'Total Star Bonus Pool',
|
||||
'Recipients (5Y Stars)', 'Bonus per 5Y Star', 'Final 5Y Star Allocation'],
|
||||
'Value': [f"{bonus_data['star_penalty_pool']:,.6f} $Z", f"{total_rounding_error_stars:,.6f} $Z",
|
||||
f"{bonus_data['star_bonus_pool_total']:,.6f} $Z", f"{stars_counts[5]:,}",
|
||||
f"{bonus_data['bonus_per_star_5_years']:,.6f} $Z", f"{adjusted_max_allocation_per_star + bonus_data['bonus_per_star_5_years']:,.6f} $Z"]
|
||||
})
|
||||
print_table_with_borders(star_bonus_df, "⭐ STAR BONUS POOL ANALYSIS")
|
||||
|
||||
# Galaxy Bonus Pool Analysis
|
||||
galaxy_bonus_df = pd.DataFrame({
|
||||
'Component': ['Penalty Pool', 'Rounding Error Bonus', 'Total Galaxy Bonus Pool',
|
||||
'Recipients (5Y Galaxies)', 'Bonus per 5Y Galaxy', 'Final 5Y Galaxy Allocation'],
|
||||
'Value': [f"{bonus_data['galaxy_penalty_pool']:,.6f} $Z", f"{total_rounding_error_galaxies:,.6f} $Z",
|
||||
f"{bonus_data['galaxy_bonus_pool_total']:,.6f} $Z", f"{galaxies_counts[5]:,}",
|
||||
f"{bonus_data['bonus_per_galaxy_5_years']:,.6f} $Z", f"{adjusted_max_allocation_per_galaxy + bonus_data['bonus_per_galaxy_5_years']:,.6f} $Z"]
|
||||
})
|
||||
print_table_with_borders(galaxy_bonus_df, "🌌 GALAXY BONUS POOL ANALYSIS")
|
||||
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_final_allocations_and_verification(allocation_data, final_data):
|
||||
"""Print final allocations and verification tables."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
star_z_per_block = final_data['star_z_per_block']
|
||||
galaxy_z_per_block = final_data['galaxy_z_per_block']
|
||||
total_stars_allocation = final_data['total_stars_allocation']
|
||||
total_galaxies_allocation = final_data['total_galaxies_allocation']
|
||||
|
||||
print("=" * 80)
|
||||
print("✅ FINAL ALLOCATIONS & VERIFICATION")
|
||||
print("=" * 80)
|
||||
|
||||
# Star Final Allocations Table
|
||||
star_final_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Final Allocation ($Z)': [f"{final_star_allocations[year]:,.8f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Z per Block': [f"{star_z_per_block[year]:,.8f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Participants': [f"{stars_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
|
||||
})
|
||||
print_table_with_borders(star_final_df, "⭐ FINAL STAR ALLOCATIONS")
|
||||
|
||||
# Galaxy Final Allocations Table
|
||||
galaxy_final_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Final Allocation ($Z)': [f"{final_galaxy_allocations[year]:,.8f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Z per Block': [f"{galaxy_z_per_block[year]:,.8f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Participants': [f"{galaxies_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
|
||||
})
|
||||
print_table_with_borders(galaxy_final_df, "🌌 FINAL GALAXY ALLOCATIONS")
|
||||
|
||||
# Verification and Rounding Error Analysis
|
||||
lockdrop_allocation_stars = LOCKDROP_ALLOCATION * STAR_ALLOCATION_PERCENT
|
||||
lockdrop_allocation_galaxies = LOCKDROP_ALLOCATION * GALAXY_ALLOCATION_PERCENT
|
||||
final_rounding_error_stars = lockdrop_allocation_stars - total_stars_allocation
|
||||
final_rounding_error_galaxies = lockdrop_allocation_galaxies - total_galaxies_allocation
|
||||
final_rounding_error = final_rounding_error_stars + final_rounding_error_galaxies
|
||||
|
||||
verification_df = pd.DataFrame({
|
||||
'Category': ['Star Allocations', 'Galaxy Allocations', 'Combined'],
|
||||
'Calculated Total': [f"{total_stars_allocation:.8f} $Z", f"{total_galaxies_allocation:.8f} $Z",
|
||||
f"{total_stars_allocation + total_galaxies_allocation:.8f} $Z"],
|
||||
'Expected Total': [f"{lockdrop_allocation_stars:.8f} $Z", f"{lockdrop_allocation_galaxies:.8f} $Z",
|
||||
f"{LOCKDROP_ALLOCATION:.8f} $Z"],
|
||||
'Rounding Error': [f"{final_rounding_error_stars:.8f} $Z", f"{final_rounding_error_galaxies:.8f} $Z",
|
||||
f"{final_rounding_error:.8f} $Z"]
|
||||
})
|
||||
print_table_with_borders(verification_df, "🔍 ALLOCATION VERIFICATION")
|
||||
print("\n📝 NOTE: Final rounding errors go to Zenith Foundation")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_analysis_tables(allocation_data, bonus_data, final_data):
|
||||
"""Print comprehensive analysis tables - calls all the detailed sections."""
|
||||
print_participation_summary(allocation_data)
|
||||
print_allocation_calculations(allocation_data)
|
||||
print_penalty_analysis(allocation_data)
|
||||
print_bonus_pool_calculations(allocation_data, bonus_data)
|
||||
print_final_allocations_and_verification(allocation_data, final_data)
|
||||
129
lockdrop/simulation.py
Normal file
129
lockdrop/simulation.py
Normal file
@ -0,0 +1,129 @@
|
||||
"""
|
||||
Simulation-specific functions for processing zenith-stack generated data.
|
||||
|
||||
This module contains functions specifically for processing simulation data
|
||||
generated by the zenith-stack project, including watcher events and participant data.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import urbitob
|
||||
from decimal import Decimal
|
||||
from collections import defaultdict
|
||||
|
||||
from .constants import NUM_GALAXIES
|
||||
from .calculations import (
|
||||
calculate_dynamic_allocations, calculate_bonus_pools,
|
||||
calculate_final_allocations, generate_test_output
|
||||
)
|
||||
from .display import print_analysis_tables
|
||||
|
||||
|
||||
def simulation_load_watcher_events(file_path):
|
||||
"""Load and parse PointLockedEvent events from JSON file (simulation-specific)."""
|
||||
with open(file_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Filter for only PointLockedEvent events during loading
|
||||
point_locked_events = [
|
||||
event_data for event_data in data['data']['eventsInRange']
|
||||
if event_data['event']['__typename'] == 'PointLockedEvent'
|
||||
]
|
||||
|
||||
return point_locked_events
|
||||
|
||||
|
||||
def simulation_analyze_lockdrop_events(events):
|
||||
"""Analyze PointLockedEvent events and return participation statistics (simulation-specific)."""
|
||||
# Initialize counters
|
||||
lock_duration_counts = {
|
||||
'star': defaultdict(int),
|
||||
'galaxy': defaultdict(int)
|
||||
}
|
||||
|
||||
# Process events (already filtered to PointLockedEvent only)
|
||||
for event_data in events:
|
||||
point = event_data['event']['point']
|
||||
lock_period = event_data['event']['lock_period']
|
||||
|
||||
# Determine if it's a galaxy or star
|
||||
point_num = urbitob.patp_to_num(point)
|
||||
point_type = "galaxy" if point_num < NUM_GALAXIES else "star"
|
||||
|
||||
# Count by lock period
|
||||
lock_duration_counts[point_type][lock_period] += 1
|
||||
|
||||
# Extract counts for each year and point type
|
||||
result = {}
|
||||
for years in [1, 2, 3, 4, 5]:
|
||||
result[f'stars_{years}_years'] = lock_duration_counts['star'][years]
|
||||
result[f'galaxies_{years}_years'] = lock_duration_counts['galaxy'][years]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def run_simulation_analysis(generated_dir=None):
|
||||
"""Complete simulation analysis pipeline (simulation-specific)."""
|
||||
# Determine generated directory
|
||||
if generated_dir is None:
|
||||
generated_dir = os.getenv('GENERATED_DIR', 'generated')
|
||||
|
||||
# Load and analyze watcher events
|
||||
watcher_events_path = os.path.join(generated_dir, 'watcher-events.json')
|
||||
|
||||
print("=" * 80)
|
||||
print("📁 WATCHER EVENTS DATA SUMMARY")
|
||||
print("=" * 80)
|
||||
|
||||
try:
|
||||
point_locked_events = simulation_load_watcher_events(watcher_events_path)
|
||||
|
||||
if point_locked_events:
|
||||
total_events = len(point_locked_events)
|
||||
print(f"✅ Successfully loaded {total_events} PointLockedEvent records from watcher file")
|
||||
else:
|
||||
print("⚠️ No PointLockedEvent events found")
|
||||
return None
|
||||
|
||||
# Analyze events and convert to Decimal
|
||||
lock_stats = simulation_analyze_lockdrop_events(point_locked_events)
|
||||
participation_counts = {k: Decimal(v) for k, v in lock_stats.items()}
|
||||
|
||||
# Run calculations
|
||||
allocation_data = calculate_dynamic_allocations(participation_counts)
|
||||
bonus_data = calculate_bonus_pools(allocation_data)
|
||||
final_data = calculate_final_allocations(allocation_data, bonus_data)
|
||||
|
||||
# Print comprehensive analysis
|
||||
print_analysis_tables(allocation_data, bonus_data, final_data)
|
||||
|
||||
# Generate and save test output
|
||||
test_output = generate_test_output(final_data)
|
||||
allocations_output_file = 'lockdrop_allocations_notebook.json'
|
||||
|
||||
export_data = {
|
||||
'metadata': {
|
||||
'source': 'lockdrop-calculations-simulated.ipynb',
|
||||
'participation_counts': lock_stats
|
||||
},
|
||||
'allocations': test_output
|
||||
}
|
||||
|
||||
with open(allocations_output_file, 'w') as f:
|
||||
json.dump(export_data, f, indent=2)
|
||||
|
||||
print("=" * 80)
|
||||
print("💾 JSON OUTPUT GENERATED")
|
||||
print(f"✅ Final allocations saved to: {allocations_output_file}")
|
||||
print("=" * 80)
|
||||
|
||||
# Return data for visualization
|
||||
return allocation_data, final_data
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Watcher events file not found: {watcher_events_path}")
|
||||
print(" Make sure GENERATED_DIR environment variable points to the correct directory")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"❌ Error during simulation analysis: {e}")
|
||||
return None
|
||||
121
lockdrop/visualization.py
Normal file
121
lockdrop/visualization.py
Normal file
@ -0,0 +1,121 @@
|
||||
"""
|
||||
Visualization functions for lockdrop analysis.
|
||||
|
||||
This module contains functions for creating charts and plots to visualize
|
||||
lockdrop participation and allocation data.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
|
||||
|
||||
def configure_matplotlib():
|
||||
"""Configure matplotlib settings for consistent plots."""
|
||||
plt.style.use('seaborn-v0_8')
|
||||
sns.set_palette("husl")
|
||||
|
||||
plt.rcParams['figure.figsize'] = (12, 8)
|
||||
plt.rcParams['font.size'] = 11
|
||||
plt.rcParams['axes.titlesize'] = 14
|
||||
plt.rcParams['axes.labelsize'] = 12
|
||||
plt.rcParams['xtick.labelsize'] = 10
|
||||
plt.rcParams['ytick.labelsize'] = 10
|
||||
plt.rcParams['legend.fontsize'] = 10
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
|
||||
try:
|
||||
plt.rcParams['font.family'] = 'DejaVu Sans'
|
||||
except:
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Liberation Sans', 'Arial', 'Helvetica']
|
||||
|
||||
|
||||
def create_visualization(allocation_data, final_data):
|
||||
"""Create comprehensive visualization plots."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
|
||||
fig = plt.figure(figsize=(20, 10))
|
||||
gs = fig.add_gridspec(2, 3, hspace=0.35, wspace=0.35)
|
||||
|
||||
fig.suptitle('Lockdrop Analysis', fontsize=18, fontweight='bold', y=0.98)
|
||||
|
||||
# 1. Star Participation Distribution
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
star_years = [1, 2, 3, 4, 5]
|
||||
star_counts = [stars_counts[year] for year in star_years]
|
||||
|
||||
bars1 = ax1.bar(range(len(star_years)), star_counts,
|
||||
color=['#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728'], alpha=0.8)
|
||||
ax1.set_xlabel('Lock Period (Years)')
|
||||
ax1.set_ylabel('Number of Stars')
|
||||
ax1.set_title('Star Participation by Lock Period', fontweight='bold')
|
||||
ax1.set_xticks(range(len(star_years)))
|
||||
ax1.set_xticklabels(star_years)
|
||||
|
||||
for i, count in enumerate(star_counts):
|
||||
height = bars1[i].get_height()
|
||||
ax1.text(bars1[i].get_x() + bars1[i].get_width()/2., height + height*0.01,
|
||||
f'{count:,}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 2. Galaxy Participation Distribution
|
||||
ax2 = fig.add_subplot(gs[0, 1])
|
||||
galaxy_counts = [galaxies_counts[year] for year in star_years]
|
||||
|
||||
bars2 = ax2.bar(range(len(star_years)), galaxy_counts,
|
||||
color=['#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2'], alpha=0.8)
|
||||
ax2.set_xlabel('Lock Period (Years)')
|
||||
ax2.set_ylabel('Number of Galaxies')
|
||||
ax2.set_title('Galaxy Participation by Lock Period', fontweight='bold')
|
||||
ax2.set_xticks(range(len(star_years)))
|
||||
ax2.set_xticklabels(star_years)
|
||||
|
||||
for i, count in enumerate(galaxy_counts):
|
||||
height = bars2[i].get_height()
|
||||
ax2.text(bars2[i].get_x() + bars2[i].get_width()/2., height + height*0.01,
|
||||
f'{count:,}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 3. Star Allocations
|
||||
ax3 = fig.add_subplot(gs[0, 2])
|
||||
star_alloc_values = [float(final_star_allocations[year]) for year in star_years]
|
||||
|
||||
bars3 = ax3.bar(range(len(star_years)), star_alloc_values,
|
||||
color=['#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728'], alpha=0.8)
|
||||
ax3.set_xlabel('Lock Period (Years)')
|
||||
ax3.set_ylabel('Allocation per Star ($Z)')
|
||||
ax3.set_title('Star Allocations by Lock Period', fontweight='bold')
|
||||
ax3.set_xticks(range(len(star_years)))
|
||||
ax3.set_xticklabels(star_years)
|
||||
|
||||
for i, allocation in enumerate(star_alloc_values):
|
||||
height = bars3[i].get_height()
|
||||
ax3.text(bars3[i].get_x() + bars3[i].get_width()/2., height + height*0.01,
|
||||
f'{allocation:,.0f}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 4. Galaxy Allocations
|
||||
ax4 = fig.add_subplot(gs[1, 0])
|
||||
galaxy_alloc_values = [float(final_galaxy_allocations[year]) for year in star_years]
|
||||
|
||||
bars4 = ax4.bar(range(len(star_years)), galaxy_alloc_values,
|
||||
color=['#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2'], alpha=0.8)
|
||||
ax4.set_xlabel('Lock Period (Years)')
|
||||
ax4.set_ylabel('Allocation per Galaxy ($Z)')
|
||||
ax4.set_title('Galaxy Allocations by Lock Period', fontweight='bold')
|
||||
ax4.set_xticks(range(len(star_years)))
|
||||
ax4.set_xticklabels(star_years)
|
||||
|
||||
for i, allocation in enumerate(galaxy_alloc_values):
|
||||
height = bars4[i].get_height()
|
||||
ax4.text(bars4[i].get_x() + bars4[i].get_width()/2., height + height*0.01,
|
||||
f'{allocation:,.0f}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# Hide remaining subplots
|
||||
ax5 = fig.add_subplot(gs[1, 1])
|
||||
ax5.axis('off')
|
||||
ax6 = fig.add_subplot(gs[1, 2])
|
||||
ax6.axis('off')
|
||||
|
||||
plt.subplots_adjust(bottom=0.08, top=0.88, left=0.05, right=0.98)
|
||||
plt.show()
|
||||
304
lockdrop/widgets.py
Normal file
304
lockdrop/widgets.py
Normal file
@ -0,0 +1,304 @@
|
||||
"""
|
||||
Jupyter widget functions for interactive lockdrop experimentation.
|
||||
|
||||
This module contains functions for creating and managing interactive widgets
|
||||
for the experimental lockdrop notebook.
|
||||
"""
|
||||
|
||||
import json
|
||||
import traceback
|
||||
import ipywidgets as widgets
|
||||
from datetime import datetime
|
||||
from IPython.display import clear_output
|
||||
|
||||
from .constants import NUM_STARS, NUM_GALAXIES, SCENARIOS
|
||||
from .calculations import (
|
||||
calculate_dynamic_allocations, calculate_bonus_pools,
|
||||
calculate_final_allocations, generate_test_output
|
||||
)
|
||||
from .display import print_analysis_tables, print_table_with_borders
|
||||
from .visualization import create_visualization
|
||||
|
||||
|
||||
def create_experiment_widgets():
|
||||
"""Create input widgets for the experimental notebook."""
|
||||
# Define reasonable default values
|
||||
default_participants = {
|
||||
'stars_1_years': 8000,
|
||||
'stars_2_years': 8000,
|
||||
'stars_3_years': 8000,
|
||||
'stars_4_years': 8000,
|
||||
'stars_5_years': 8000,
|
||||
'galaxies_1_years': 40,
|
||||
'galaxies_2_years': 40,
|
||||
'galaxies_3_years': 40,
|
||||
'galaxies_4_years': 40,
|
||||
'galaxies_5_years': 40
|
||||
}
|
||||
|
||||
# Create input widgets
|
||||
print("="*50)
|
||||
|
||||
# Star participation widgets with bounds
|
||||
stars_1_years = widgets.BoundedIntText(value=default_participants['stars_1_years'], min=0, max=NUM_STARS, description='1 Year:', style={'description_width': '80px'})
|
||||
stars_2_years = widgets.BoundedIntText(value=default_participants['stars_2_years'], min=0, max=NUM_STARS, description='2 Years:', style={'description_width': '80px'})
|
||||
stars_3_years = widgets.BoundedIntText(value=default_participants['stars_3_years'], min=0, max=NUM_STARS, description='3 Years:', style={'description_width': '80px'})
|
||||
stars_4_years = widgets.BoundedIntText(value=default_participants['stars_4_years'], min=0, max=NUM_STARS, description='4 Years:', style={'description_width': '80px'})
|
||||
stars_5_years = widgets.BoundedIntText(value=default_participants['stars_5_years'], min=0, max=NUM_STARS, description='5 Years:', style={'description_width': '80px'})
|
||||
|
||||
star_controls = widgets.VBox([stars_1_years, stars_2_years, stars_3_years, stars_4_years, stars_5_years])
|
||||
|
||||
galaxies_1_years = widgets.BoundedIntText(value=default_participants['galaxies_1_years'], min=0, max=NUM_GALAXIES, description='1 Year:', style={'description_width': '80px'})
|
||||
galaxies_2_years = widgets.BoundedIntText(value=default_participants['galaxies_2_years'], min=0, max=NUM_GALAXIES, description='2 Years:', style={'description_width': '80px'})
|
||||
galaxies_3_years = widgets.BoundedIntText(value=default_participants['galaxies_3_years'], min=0, max=NUM_GALAXIES, description='3 Years:', style={'description_width': '80px'})
|
||||
galaxies_4_years = widgets.BoundedIntText(value=default_participants['galaxies_4_years'], min=0, max=NUM_GALAXIES, description='4 Years:', style={'description_width': '80px'})
|
||||
galaxies_5_years = widgets.BoundedIntText(value=default_participants['galaxies_5_years'], min=0, max=NUM_GALAXIES, description='5 Years:', style={'description_width': '80px'})
|
||||
|
||||
galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])
|
||||
|
||||
# Preset scenario dropdown
|
||||
scenario_dropdown = widgets.Dropdown(
|
||||
options=[('Select Preset', '')] + [(scenario['description'], k) for k, scenario in SCENARIOS.items()],
|
||||
description='Preset:',
|
||||
style={'description_width': '60px'},
|
||||
layout={'width': '400px'}
|
||||
)
|
||||
|
||||
# Calculate button
|
||||
calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '10px 5px'})
|
||||
|
||||
# Export button
|
||||
export_button = widgets.Button(description='💾 Export Results', button_style='info', layout={'width': '180px', 'margin': '10px 5px'})
|
||||
|
||||
# Output area
|
||||
output_area = widgets.Output()
|
||||
|
||||
# Set up preset selection handler
|
||||
def on_preset_change(change):
|
||||
if change['new'] and change['new'] != '':
|
||||
scenario = SCENARIOS[change['new']]
|
||||
|
||||
# Update widget values
|
||||
stars_1_years.value = scenario['stars']['1_year']
|
||||
stars_2_years.value = scenario['stars']['2_year']
|
||||
stars_3_years.value = scenario['stars']['3_year']
|
||||
stars_4_years.value = scenario['stars']['4_year']
|
||||
stars_5_years.value = scenario['stars']['5_year']
|
||||
galaxies_1_years.value = scenario['galaxies']['1_year']
|
||||
galaxies_2_years.value = scenario['galaxies']['2_year']
|
||||
galaxies_3_years.value = scenario['galaxies']['3_year']
|
||||
galaxies_4_years.value = scenario['galaxies']['4_year']
|
||||
galaxies_5_years.value = scenario['galaxies']['5_year']
|
||||
|
||||
scenario_dropdown.observe(on_preset_change, names='value')
|
||||
|
||||
# Set up export button handler
|
||||
def on_export_click(button=None):
|
||||
"""Handle export button click with timestamp."""
|
||||
# Generate timestamp for filename
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f'lockdrop_calculations_result_{timestamp}.json'
|
||||
|
||||
# Get current values from widgets
|
||||
participation_counts = {
|
||||
'stars_1_years': stars_1_years.value,
|
||||
'stars_2_years': stars_2_years.value,
|
||||
'stars_3_years': stars_3_years.value,
|
||||
'stars_4_years': stars_4_years.value,
|
||||
'stars_5_years': stars_5_years.value,
|
||||
'galaxies_1_years': galaxies_1_years.value,
|
||||
'galaxies_2_years': galaxies_2_years.value,
|
||||
'galaxies_3_years': galaxies_3_years.value,
|
||||
'galaxies_4_years': galaxies_4_years.value,
|
||||
'galaxies_5_years': galaxies_5_years.value
|
||||
}
|
||||
|
||||
try:
|
||||
# Calculate allocations
|
||||
allocation_data = calculate_dynamic_allocations(participation_counts)
|
||||
bonus_data = calculate_bonus_pools(allocation_data)
|
||||
final_data = calculate_final_allocations(allocation_data, bonus_data)
|
||||
|
||||
# Generate test output
|
||||
test_output = generate_test_output(final_data)
|
||||
|
||||
# Add metadata
|
||||
export_data = {
|
||||
'metadata': {
|
||||
'source': 'lockdrop-calculations.ipynb',
|
||||
'timestamp': timestamp,
|
||||
'participation_counts': participation_counts
|
||||
},
|
||||
'allocations': test_output
|
||||
}
|
||||
|
||||
# Save to file
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(export_data, f, indent=2)
|
||||
|
||||
print(f"✅ Results exported to: {filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Export failed: {str(e)}")
|
||||
|
||||
export_button.on_click(on_export_click)
|
||||
|
||||
# Return all widgets as a dictionary
|
||||
return {
|
||||
'star_controls': star_controls,
|
||||
'galaxy_controls': galaxy_controls,
|
||||
'scenario_dropdown': scenario_dropdown,
|
||||
'calculate_button': calculate_button,
|
||||
'export_button': export_button,
|
||||
'output_area': output_area,
|
||||
'widgets': {
|
||||
'stars_1_years': stars_1_years,
|
||||
'stars_2_years': stars_2_years,
|
||||
'stars_3_years': stars_3_years,
|
||||
'stars_4_years': stars_4_years,
|
||||
'stars_5_years': stars_5_years,
|
||||
'galaxies_1_years': galaxies_1_years,
|
||||
'galaxies_2_years': galaxies_2_years,
|
||||
'galaxies_3_years': galaxies_3_years,
|
||||
'galaxies_4_years': galaxies_4_years,
|
||||
'galaxies_5_years': galaxies_5_years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def create_calculate_function(widget_dict):
|
||||
"""Create the calculation and display function for the experimental notebook."""
|
||||
from decimal import Decimal
|
||||
import pandas as pd
|
||||
|
||||
def calculate_and_display(button=None):
|
||||
"""Calculate allocations and display results based on input parameters."""
|
||||
|
||||
widgets_map = widget_dict['widgets']
|
||||
output_area = widget_dict['output_area']
|
||||
|
||||
with output_area:
|
||||
clear_output(wait=True)
|
||||
|
||||
# Get current values from widgets
|
||||
participation_counts = {
|
||||
'stars_1_years': widgets_map['stars_1_years'].value,
|
||||
'stars_2_years': widgets_map['stars_2_years'].value,
|
||||
'stars_3_years': widgets_map['stars_3_years'].value,
|
||||
'stars_4_years': widgets_map['stars_4_years'].value,
|
||||
'stars_5_years': widgets_map['stars_5_years'].value,
|
||||
'galaxies_1_years': widgets_map['galaxies_1_years'].value,
|
||||
'galaxies_2_years': widgets_map['galaxies_2_years'].value,
|
||||
'galaxies_3_years': widgets_map['galaxies_3_years'].value,
|
||||
'galaxies_4_years': widgets_map['galaxies_4_years'].value,
|
||||
'galaxies_5_years': widgets_map['galaxies_5_years'].value
|
||||
}
|
||||
|
||||
# Validate inputs
|
||||
total_stars = sum(participation_counts[key] for key in participation_counts if 'stars' in key)
|
||||
total_galaxies = sum(participation_counts[key] for key in participation_counts if 'galaxies' in key)
|
||||
|
||||
if total_stars > NUM_STARS:
|
||||
print(f"⚠️ ERROR: Total stars ({total_stars:,}) exceeds maximum available ({NUM_STARS:,})")
|
||||
return
|
||||
|
||||
if total_galaxies > NUM_GALAXIES:
|
||||
print(f"⚠️ ERROR: Total galaxies ({total_galaxies:,}) exceeds maximum available ({NUM_GALAXIES:,})")
|
||||
return
|
||||
|
||||
if total_stars == 0 and total_galaxies == 0:
|
||||
print("⚠️ ERROR: Must have at least some participants")
|
||||
return
|
||||
|
||||
try:
|
||||
# Perform calculations
|
||||
print("🔄 Calculating allocations...\n")
|
||||
|
||||
allocation_data = calculate_dynamic_allocations(participation_counts)
|
||||
bonus_data = calculate_bonus_pools(allocation_data)
|
||||
final_data = calculate_final_allocations(allocation_data, bonus_data)
|
||||
|
||||
# Display results
|
||||
print_analysis_tables(allocation_data, bonus_data, final_data)
|
||||
|
||||
# Create visualization
|
||||
print("\n📊 Generating visualization...")
|
||||
create_visualization(allocation_data, final_data)
|
||||
|
||||
# Calculate participation insights
|
||||
star_participation_rate = Decimal(total_stars) / NUM_STARS
|
||||
galaxy_participation_rate = Decimal(total_galaxies) / NUM_GALAXIES
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("🎯 EXPERIMENT INSIGHTS")
|
||||
print("=" * 80)
|
||||
|
||||
insights_df = pd.DataFrame({
|
||||
'Metric': [
|
||||
'Total Participants',
|
||||
'Star Participation Rate',
|
||||
'Galaxy Participation Rate',
|
||||
'5-Year Star Bonus',
|
||||
'5-Year Galaxy Bonus',
|
||||
'Highest Individual Allocation',
|
||||
'Lowest Individual Allocation'
|
||||
],
|
||||
'Value': [
|
||||
f"{total_stars + total_galaxies:,}",
|
||||
f"{star_participation_rate:.1%}",
|
||||
f"{galaxy_participation_rate:.1%}",
|
||||
f"{float(bonus_data['bonus_per_star_5_years']):,.2f} $Z" if participation_counts['stars_5_years'] > 0 else "N/A (no 5Y stars)",
|
||||
f"{float(bonus_data['bonus_per_galaxy_5_years']):,.2f} $Z" if participation_counts['galaxies_5_years'] > 0 else "N/A (no 5Y galaxies)",
|
||||
f"{max(float(final_data['final_star_allocations'][5]), float(final_data['final_galaxy_allocations'][5])):,.2f} $Z",
|
||||
f"{min(float(final_data['final_star_allocations'][1]), float(final_data['final_galaxy_allocations'][1])):,.2f} $Z"
|
||||
]
|
||||
})
|
||||
print_table_with_borders(insights_df, "🎯 EXPERIMENT INSIGHTS")
|
||||
print("\n" + "="*80)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error during calculation: {str(e)}")
|
||||
traceback.print_exc()
|
||||
|
||||
return calculate_and_display
|
||||
|
||||
|
||||
def create_experimental_interface():
|
||||
"""Create complete experimental interface with widgets and handlers."""
|
||||
from IPython.display import display
|
||||
|
||||
# Create all widgets
|
||||
widget_dict = create_experiment_widgets()
|
||||
|
||||
# Create calculation function
|
||||
calculate_function = create_calculate_function(widget_dict)
|
||||
|
||||
# Wire up the calculate button
|
||||
widget_dict['calculate_button'].on_click(calculate_function)
|
||||
|
||||
# Display the interface - preset dropdown first
|
||||
preset_label = widgets.HTML('<h3>🎮 Preset Scenarios</h3>')
|
||||
preset_layout = widgets.VBox([preset_label, widget_dict['scenario_dropdown']])
|
||||
|
||||
# Then participation controls
|
||||
star_label = widgets.HTML('<h3>⭐ Star Participation</h3>')
|
||||
galaxy_label = widgets.HTML('<h3>🌌 Galaxy Participation</h3>')
|
||||
|
||||
input_layout = widgets.HBox([
|
||||
widgets.VBox([star_label, widget_dict['star_controls']]),
|
||||
widgets.VBox([galaxy_label, widget_dict['galaxy_controls']])
|
||||
])
|
||||
|
||||
# Action buttons
|
||||
controls_layout = widgets.HBox([
|
||||
widget_dict['calculate_button'],
|
||||
widget_dict['export_button']
|
||||
])
|
||||
|
||||
# Display in order: preset first, then participation controls, then buttons
|
||||
display(preset_layout)
|
||||
display(input_layout)
|
||||
display(controls_layout)
|
||||
display(widget_dict['output_area'])
|
||||
|
||||
return None
|
||||
@ -6,3 +6,4 @@ numpy
|
||||
urbitob
|
||||
tabulate
|
||||
requests
|
||||
ipywidgets
|
||||
|
||||
49
run_experiment.sh
Executable file
49
run_experiment.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Lockdrop Allocation Experimentation Launcher
|
||||
# This script automatically sets up the environment and launches the experimental notebook
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting Lockdrop Experimentation Environment..."
|
||||
|
||||
# Check if Python 3 is available
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "❌ Python 3 is required but not installed. Please install Python 3."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create virtual environment if it doesn't exist
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "📦 Creating Python virtual environment..."
|
||||
python3 -m venv venv
|
||||
fi
|
||||
|
||||
# Activate virtual environment
|
||||
echo "🔧 Activating virtual environment..."
|
||||
source venv/bin/activate
|
||||
|
||||
# Install/upgrade dependencies
|
||||
echo "📚 Installing dependencies..."
|
||||
pip install -q --upgrade pip
|
||||
pip install -q -r requirements.txt
|
||||
|
||||
# Check if Jupyter is working
|
||||
if ! command -v jupyter &> /dev/null; then
|
||||
echo "❌ Jupyter installation failed. Please check your Python environment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Environment ready!"
|
||||
echo ""
|
||||
echo "🎯 Opening lockdrop experimentation notebook..."
|
||||
echo " The notebook will open in your default browser at http://localhost:8888"
|
||||
echo ""
|
||||
echo "📝 To stop the notebook server later, press Ctrl+C in this terminal"
|
||||
echo ""
|
||||
|
||||
# Launch Jupyter notebook
|
||||
jupyter notebook lockdrop-calculations.ipynb
|
||||
|
||||
echo ""
|
||||
echo "👋 Experimentation session ended."
|
||||
@ -31,7 +31,8 @@ class BaseAllocationTest(unittest.TestCase):
|
||||
with open(f'{cls.generated_dir}/generated-participants.json', 'r') as f:
|
||||
cls.participants = json.load(f)
|
||||
with open('lockdrop_allocations_notebook.json', 'r') as f:
|
||||
cls.notebook_allocations = json.load(f)
|
||||
notebook_data = json.load(f)
|
||||
cls.notebook_allocations = notebook_data['allocations']
|
||||
|
||||
cls.points_by_duration = cls._get_first_points()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user