diff --git a/lockdrop-calculations.ipynb b/lockdrop-calculations.ipynb index 9637137..66928e3 100644 --- a/lockdrop-calculations.ipynb +++ b/lockdrop-calculations.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 1, "id": "setup-imports", "metadata": {}, "outputs": [], @@ -26,12 +26,12 @@ "from lockdrop_calculations import (\n", " configure_matplotlib, print_constants_summary, calculate_dynamic_allocations,\n", " calculate_bonus_pools, calculate_final_allocations, generate_test_output,\n", - " print_analysis_tables, create_visualization, PENALTY_RATES, NUM_STARS, NUM_GALAXIES\n", + " print_analysis_tables, create_visualization, PENALTY_RATES, NUM_STARS, NUM_GALAXIES,\n", + " create_experiment_widgets, create_calculate_function, create_scenario_loader, create_export_function\n", ")\n", "import json\n", "from decimal import Decimal\n", "import pandas as pd\n", - "import ipywidgets as widgets\n", "from IPython.display import display, clear_output\n", "\n", "# Configure matplotlib\n", @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 2, "id": "show-constants", "metadata": {}, "outputs": [ @@ -53,24 +53,41 @@ "================================================================================\n", "\n", "šŸ”’ LOCKDROP ALLOCATION\n", - " Parameter Value\n", - "Total Supply (1 $Z per Urbit ID) 4,294,967,296\n", - " Lockdrop Allocation % 30.0%\n", - " Lockdrop Allocation ($Z) 1,288,490,188.8\n", + "+----------------------------------+-----------------+\n", + "| Parameter | Value |\n", + "+==================================+=================+\n", + "| Total Supply (1 $Z per Urbit ID) | 4,294,967,296 |\n", + "+----------------------------------+-----------------+\n", + "| Lockdrop Allocation % | 30.0% |\n", + "+----------------------------------+-----------------+\n", + "| Lockdrop Allocation ($Z) | 1,288,490,188.8 |\n", + "+----------------------------------+-----------------+\n", "\n", "⭐ URBIT POINTS DISTRIBUTION\n", - "Point Type Count Allocation %\n", - " Galaxies 256 0.39%\n", - " Stars 65,280 99.61%\n", - " Planets 4,294,901,760 0%\n", + "+--------------+---------------+----------------+\n", + "| Point Type | Count | Allocation % |\n", + "+==============+===============+================+\n", + "| Galaxies | 256 | 0.39% |\n", + "+--------------+---------------+----------------+\n", + "| Stars | 65,280 | 99.61% |\n", + "+--------------+---------------+----------------+\n", + "| Planets | 4,294,901,760 | 0% |\n", + "+--------------+---------------+----------------+\n", "\n", "ā±ļø LOCKDROP PARAMETERS\n", - " Parameter Value\n", - " Block Duration 2 seconds\n", - "Max Point Lock Duration (5 yrs) 157,788,000 seconds\n", - " Total Blocks 78,894,000\n", - " Star Allocation % 0.996093\n", - " Galaxy Allocation % 0.003906\n", + "+---------------------------------+---------------------+\n", + "| Parameter | Value |\n", + "+=================================+=====================+\n", + "| Block Duration | 2 seconds |\n", + "+---------------------------------+---------------------+\n", + "| Max Point Lock Duration (5 yrs) | 157,788,000 seconds |\n", + "+---------------------------------+---------------------+\n", + "| Total Blocks | 78,894,000 |\n", + "+---------------------------------+---------------------+\n", + "| Star Allocation % | 99.609375% |\n", + "+---------------------------------+---------------------+\n", + "| Galaxy Allocation % | 0.390625% |\n", + "+---------------------------------+---------------------+\n", "\n", "================================================================================\n" ] @@ -93,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 3, "id": "input-parameters", "metadata": {}, "outputs": [ @@ -101,6 +118,7 @@ "name": "stdout", "output_type": "stream", "text": [ + "==================================================\n", "šŸŽÆ PARTICIPATION INPUT CONTROLS\n", "==================================================\n", "\n", @@ -110,7 +128,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "79244d07df4e4ee6923dda69c1febc2e", + "model_id": "75120f519bd64aeb88163fdfb2f9aaed", "version_major": 2, "version_minor": 0 }, @@ -132,7 +150,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "4071b25411c44a67b26f5924a2507c0c", + "model_id": "83e348a914854ce0a4b3e75c58ecc94e", "version_major": 2, "version_minor": 0 }, @@ -146,7 +164,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1c785fe3828d4d5eb62a9f35755f29f8", + "model_id": "d851d648f30247e2b98b4bd57a101562", "version_major": 2, "version_minor": 0 }, @@ -157,18 +175,10 @@ "metadata": {}, "output_type": "display_data" }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "🌌 CALCULATIONS\n" - ] - }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bc3937e163a34ff58f215f94a36aee3e", + "model_id": "39b0b853b4bb4971b0ebbaa2fcd2e8e0", "version_major": 2, "version_minor": 0 }, @@ -181,159 +191,38 @@ } ], "source": [ - "# Define reasonable default values\n", - "DEFAULT_PARTICIPANTS = {\n", - " 'stars_1_years': 8000,\n", - " 'stars_2_years': 8000,\n", - " 'stars_3_years': 8000,\n", - " 'stars_4_years': 8000,\n", - " 'stars_5_years': 8000,\n", - " 'galaxies_1_years': 40,\n", - " 'galaxies_2_years': 40,\n", - " 'galaxies_3_years': 40,\n", - " 'galaxies_4_years': 40,\n", - " 'galaxies_5_years': 40\n", - "}\n", + "# Create experiment widgets using refactored function\n", + "widget_dict = create_experiment_widgets()\n", "\n", - "# Create input widgets\n", + "# Display widgets\n", "print(\"šŸŽÆ PARTICIPATION INPUT CONTROLS\")\n", "print(\"=\"*50)\n", "\n", - "# Star participation widgets\n", "print(\"\\n⭐ STAR PARTICIPATION\")\n", - "stars_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_1_years'], description='1 Year:', style={'description_width': '80px'})\n", - "stars_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_2_years'], description='2 Years:', style={'description_width': '80px'})\n", - "stars_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_3_years'], description='3 Years:', style={'description_width': '80px'})\n", - "stars_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_4_years'], description='4 Years:', style={'description_width': '80px'})\n", - "stars_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_5_years'], description='5 Years:', style={'description_width': '80px'})\n", - "\n", - "star_controls = widgets.VBox([stars_1_years, stars_2_years, stars_3_years, stars_4_years, stars_5_years])\n", - "display(star_controls)\n", + "display(widget_dict['star_controls'])\n", "\n", "print(\"\\n🌌 GALAXY PARTICIPATION\")\n", - "galaxies_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_1_years'], description='1 Year:', style={'description_width': '80px'})\n", - "galaxies_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_2_years'], description='2 Years:', style={'description_width': '80px'})\n", - "galaxies_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_3_years'], description='3 Years:', style={'description_width': '80px'})\n", - "galaxies_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_4_years'], description='4 Years:', style={'description_width': '80px'})\n", - "galaxies_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_5_years'], description='5 Years:', style={'description_width': '80px'})\n", - "\n", - "galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])\n", - "display(galaxy_controls)\n", + "display(widget_dict['galaxy_controls'])\n", "\n", "# Calculate button\n", - "calculate_button = widgets.Button(description='šŸ”„ Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'})\n", - "display(calculate_button)\n", + "display(widget_dict['calculate_button'])\n", "\n", "# Output area\n", - "print(\"\\n🌌 CALCULATIONS\")\n", - "output_area = widgets.Output()\n", - "display(output_area)" + "display(widget_dict['output_area'])" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 4, "id": "calculation-function", "metadata": {}, "outputs": [], "source": [ - "def calculate_and_display(button=None):\n", - " \"\"\"Calculate allocations and display results based on input parameters.\"\"\"\n", - " with output_area:\n", - " clear_output(wait=True)\n", - "\n", - " # Get current values from widgets\n", - " participation_counts = {\n", - " 'stars_1_years': stars_1_years.value,\n", - " 'stars_2_years': stars_2_years.value,\n", - " 'stars_3_years': stars_3_years.value,\n", - " 'stars_4_years': stars_4_years.value,\n", - " 'stars_5_years': stars_5_years.value,\n", - " 'galaxies_1_years': galaxies_1_years.value,\n", - " 'galaxies_2_years': galaxies_2_years.value,\n", - " 'galaxies_3_years': galaxies_3_years.value,\n", - " 'galaxies_4_years': galaxies_4_years.value,\n", - " 'galaxies_5_years': galaxies_5_years.value\n", - " }\n", - "\n", - " # Validate inputs\n", - " total_stars = sum(participation_counts[key] for key in participation_counts if 'stars' in key)\n", - " total_galaxies = sum(participation_counts[key] for key in participation_counts if 'galaxies' in key)\n", - "\n", - " if total_stars > NUM_STARS:\n", - " print(f\"āš ļø ERROR: Total stars ({total_stars:,}) exceeds maximum available ({NUM_STARS:,})\")\n", - " return\n", - "\n", - " if total_galaxies > NUM_GALAXIES:\n", - " print(f\"āš ļø ERROR: Total galaxies ({total_galaxies:,}) exceeds maximum available ({NUM_GALAXIES:,})\")\n", - " return\n", - "\n", - " if total_stars == 0 and total_galaxies == 0:\n", - " print(\"āš ļø ERROR: Must have at least some participants\")\n", - " return\n", - "\n", - " try:\n", - " # Perform calculations\n", - " print(\"šŸ”„ Calculating allocations...\\n\")\n", - "\n", - " allocation_data = calculate_dynamic_allocations(participation_counts)\n", - " bonus_data = calculate_bonus_pools(allocation_data)\n", - " final_data = calculate_final_allocations(allocation_data, bonus_data)\n", - "\n", - " # Display results\n", - " print_analysis_tables(allocation_data, bonus_data, final_data)\n", - "\n", - " # Show penalty rates for reference\n", - " print(\"\\nšŸ“‹ PENALTY SYSTEM REFERENCE\")\n", - " penalty_df = pd.DataFrame({\n", - " 'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],\n", - " 'Penalty Rate': [f\"{PENALTY_RATES[year]:.1%}\" for year in [5, 4, 3, 2, 1]],\n", - " 'Description': ['No penalty + Bonus', '20% penalty', '40% penalty', '60% penalty', '80% penalty']\n", - " })\n", - " print(penalty_df.to_string(index=False))\n", - "\n", - " # Create visualization\n", - " print(\"\\nšŸ“Š Generating visualization...\")\n", - " create_visualization(allocation_data, final_data)\n", - "\n", - " # Calculate participation insights\n", - " star_participation_rate = Decimal(total_stars) / NUM_STARS\n", - " galaxy_participation_rate = Decimal(total_galaxies) / NUM_GALAXIES\n", - "\n", - " print(\"\\n\" + \"=\"*80)\n", - " print(\"šŸŽÆ EXPERIMENT INSIGHTS\")\n", - " print(\"=\"*80)\n", - "\n", - " insights_df = pd.DataFrame({\n", - " 'Metric': [\n", - " 'Total Participants',\n", - " 'Star Participation Rate',\n", - " 'Galaxy Participation Rate',\n", - " '5-Year Star Bonus',\n", - " '5-Year Galaxy Bonus',\n", - " 'Highest Individual Allocation',\n", - " 'Lowest Individual Allocation'\n", - " ],\n", - " 'Value': [\n", - " f\"{total_stars + total_galaxies:,}\",\n", - " f\"{star_participation_rate:.1%}\",\n", - " f\"{galaxy_participation_rate:.1%}\",\n", - " f\"{float(bonus_data['bonus_per_star_5_years']):,.2f} $Z\" if participation_counts['stars_5_years'] > 0 else \"N/A (no 5Y stars)\",\n", - " f\"{float(bonus_data['bonus_per_galaxy_5_years']):,.2f} $Z\" if participation_counts['galaxies_5_years'] > 0 else \"N/A (no 5Y galaxies)\",\n", - " f\"{max(float(final_data['final_star_allocations'][5]), float(final_data['final_galaxy_allocations'][5])):,.2f} $Z\",\n", - " f\"{min(float(final_data['final_star_allocations'][1]), float(final_data['final_galaxy_allocations'][1])):,.2f} $Z\"\n", - " ]\n", - " })\n", - " print(insights_df.to_string(index=False))\n", - " print(\"\\n\" + \"=\"*80)\n", - "\n", - " except Exception as e:\n", - " print(f\"āŒ Error during calculation: {str(e)}\")\n", - " import traceback\n", - " traceback.print_exc()\n", + "# Create calculation function using refactored approach\n", + "calculate_and_display = create_calculate_function(widget_dict)\n", "\n", "# Connect button to calculation function\n", - "calculate_button.on_click(calculate_and_display)" + "widget_dict['calculate_button'].on_click(calculate_and_display)" ] }, { @@ -348,7 +237,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 5, "id": "preset-scenarios", "metadata": {}, "outputs": [ @@ -376,75 +265,23 @@ } ], "source": [ - "# Define preset scenarios\n", - "SCENARIOS = {\n", - " 'balanced': {\n", - " 'description': 'Balanced distribution across all lock periods',\n", - " 'params': {\n", - " 'stars_1_years': 8000, 'stars_2_years': 8000, 'stars_3_years': 8000, 'stars_4_years': 8000, 'stars_5_years': 8000,\n", - " 'galaxies_1_years': 40, 'galaxies_2_years': 40, 'galaxies_3_years': 40, 'galaxies_4_years': 40, 'galaxies_5_years': 40\n", - " }\n", - " },\n", - " 'five_year_focused': {\n", - " 'description': 'Most participants choose 5-year lock (maximum bonus scenario)',\n", - " 'params': {\n", - " 'stars_1_years': 2000, 'stars_2_years': 2000, 'stars_3_years': 3000, 'stars_4_years': 5000, 'stars_5_years': 28000,\n", - " 'galaxies_1_years': 10, 'galaxies_2_years': 10, 'galaxies_3_years': 20, 'galaxies_4_years': 30, 'galaxies_5_years': 130\n", - " }\n", - " },\n", - " 'short_term_focused': {\n", - " 'description': 'Most participants choose shorter locks (high penalty scenario)',\n", - " 'params': {\n", - " 'stars_1_years': 20000, 'stars_2_years': 15000, 'stars_3_years': 4000, 'stars_4_years': 800, 'stars_5_years': 200,\n", - " 'galaxies_1_years': 100, 'galaxies_2_years': 80, 'galaxies_3_years': 15, 'galaxies_4_years': 3, 'galaxies_5_years': 2\n", - " }\n", - " },\n", - " 'polarized': {\n", - " 'description': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',\n", - " 'params': {\n", - " 'stars_1_years': 25000, 'stars_2_years': 0, 'stars_3_years': 0, 'stars_4_years': 0, 'stars_5_years': 15000,\n", - " 'galaxies_1_years': 150, 'galaxies_2_years': 0, 'galaxies_3_years': 0, 'galaxies_4_years': 0, 'galaxies_5_years': 50\n", - " }\n", - " },\n", - " 'low_participation': {\n", - " 'description': 'Low overall participation scenario',\n", - " 'params': {\n", - " 'stars_1_years': 1000, 'stars_2_years': 800, 'stars_3_years': 600, 'stars_4_years': 400, 'stars_5_years': 200,\n", - " 'galaxies_1_years': 8, 'galaxies_2_years': 6, 'galaxies_3_years': 4, 'galaxies_4_years': 2, 'galaxies_5_years': 5\n", - " }\n", - " }\n", - "}\n", - "\n", - "def load_scenario(scenario_name):\n", - " \"\"\"Load a preset scenario into the input widgets.\"\"\"\n", - " if scenario_name not in SCENARIOS:\n", - " print(f\"āŒ Unknown scenario: {scenario_name}\")\n", - " return\n", - "\n", - " scenario = SCENARIOS[scenario_name]\n", - " params = scenario['params']\n", - "\n", - " # Update widget values\n", - " stars_1_years.value = params['stars_1_years']\n", - " stars_2_years.value = params['stars_2_years']\n", - " stars_3_years.value = params['stars_3_years']\n", - " stars_4_years.value = params['stars_4_years']\n", - " stars_5_years.value = params['stars_5_years']\n", - " galaxies_1_years.value = params['galaxies_1_years']\n", - " galaxies_2_years.value = params['galaxies_2_years']\n", - " galaxies_3_years.value = params['galaxies_3_years']\n", - " galaxies_4_years.value = params['galaxies_4_years']\n", - " galaxies_5_years.value = params['galaxies_5_years']\n", - "\n", - " print(f\"āœ… Loaded scenario: {scenario_name}\")\n", - " print(f\"šŸ“ Description: {scenario['description']}\")\n", - " print(\"\\nšŸ”„ Click 'Calculate Allocations' button above to see results!\")\n", + "# Create scenario loader using refactored function\n", + "load_scenario = create_scenario_loader(widget_dict)\n", "\n", "# Display available scenarios\n", "print(\"šŸ“‹ AVAILABLE PRESET SCENARIOS:\")\n", "print(\"=\"*50)\n", - "for name, scenario in SCENARIOS.items():\n", - " print(f\"\\n• {name}: {scenario['description']}\")\n", + "\n", + "scenarios = {\n", + " 'balanced': 'Balanced distribution across all lock periods',\n", + " 'five_year_focused': 'Most participants choose 5-year lock (maximum bonus scenario)',\n", + " 'short_term_focused': 'Most participants choose shorter locks (high penalty scenario)',\n", + " 'polarized': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',\n", + " 'low_participation': 'Low overall participation scenario'\n", + "}\n", + "\n", + "for name, description in scenarios.items():\n", + " print(f\"\\n• {name}: {description}\")\n", "\n", "print(\"\\nšŸŽÆ To load a scenario, call: load_scenario('scenario_name')\")\n", "print(\"\\nExample: load_scenario('five_year_focused')\")" @@ -452,24 +289,13 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 6, "id": "load-scenario-example", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "āœ… Loaded scenario: five_year_focused\n", - "šŸ“ Description: Most participants choose 5-year lock (maximum bonus scenario)\n", - "\n", - "šŸ”„ Click 'Calculate Allocations' button above to see results!\n" - ] - } - ], + "outputs": [], "source": [ "# Load the balanced scenario as default\n", - "load_scenario('five_year_focused')" + "# load_scenario('short_term_focused')" ] }, { @@ -479,12 +305,12 @@ "source": [ "## šŸ’¾ Export Results\n", "\n", - "Export your experimental results for use with the test suite:" + "Export your experimental results." ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 7, "id": "export-results", "metadata": {}, "outputs": [ @@ -501,50 +327,8 @@ } ], "source": [ - "def export_current_scenario(filename='lockdrop_allocations_experiment.json'):\n", - " \"\"\"Export current scenario results to JSON file.\"\"\"\n", - " # Get current values from widgets\n", - " participation_counts = {\n", - " 'stars_1_years': stars_1_years.value,\n", - " 'stars_2_years': stars_2_years.value,\n", - " 'stars_3_years': stars_3_years.value,\n", - " 'stars_4_years': stars_4_years.value,\n", - " 'stars_5_years': stars_5_years.value,\n", - " 'galaxies_1_years': galaxies_1_years.value,\n", - " 'galaxies_2_years': galaxies_2_years.value,\n", - " 'galaxies_3_years': galaxies_3_years.value,\n", - " 'galaxies_4_years': galaxies_4_years.value,\n", - " 'galaxies_5_years': galaxies_5_years.value\n", - " }\n", - "\n", - " try:\n", - " # Calculate allocations\n", - " allocation_data = calculate_dynamic_allocations(participation_counts)\n", - " bonus_data = calculate_bonus_pools(allocation_data)\n", - " final_data = calculate_final_allocations(allocation_data, bonus_data)\n", - "\n", - " # Generate test output\n", - " test_output = generate_test_output(final_data)\n", - "\n", - " # Add metadata\n", - " export_data = {\n", - " 'metadata': {\n", - " 'source': 'lockdrop-experiment.ipynb',\n", - " 'scenario': 'custom',\n", - " 'participation_counts': participation_counts\n", - " },\n", - " 'allocations': test_output\n", - " }\n", - "\n", - " # Save to file\n", - " with open(filename, 'w') as f:\n", - " json.dump(export_data, f, indent=2)\n", - "\n", - " print(f\"āœ… Results exported to: {filename}\")\n", - " print(f\"šŸ“Š Total participants: {sum(participation_counts.values()):,}\")\n", - "\n", - " except Exception as e:\n", - " print(f\"āŒ Export failed: {str(e)}\")\n", + "# Create export function using refactored approach\n", + "export_current_scenario = create_export_function(widget_dict)\n", "\n", "# Usage example\n", "print(\"šŸ’¾ Export your current scenario results:\")\n", @@ -554,39 +338,20 @@ ] }, { - "cell_type": "markdown", - "id": "tips-section", + "cell_type": "code", + "execution_count": 8, + "id": "3c93c9c2-9cec-4d2b-a2db-065602a3871c", "metadata": {}, + "outputs": [], "source": [ - "## šŸ’” Experimentation Tips\n", - "\n", - "### Interesting Things to Try:\n", - "\n", - "1. **Bonus Pool Impact**: Set most participants to 1-2 years and a few to 5 years to see massive bonuses\n", - "2. **Participation Rate Effects**: Try very low participation vs. high participation scenarios\n", - "3. **Star vs Galaxy Balance**: Experiment with different star/galaxy ratios\n", - "4. **Edge Cases**: What happens with only 5-year participants? Only 1-year?\n", - "5. **Realistic Scenarios**: Model expected real-world behavior patterns\n", - "\n", - "### Key Metrics to Watch:\n", - "\n", - "- **5-Year Bonus Multiplier**: How much extra do 5-year participants get?\n", - "- **Participation Rates**: What percentage of each point type participates?\n", - "- **Penalty Pool Size**: How much bonus is generated from penalties?\n", - "- **Allocation Spread**: Difference between highest and lowest allocations\n", - "\n", - "### Understanding the Math:\n", - "\n", - "- Penalties from shorter lock periods create bonus pools\n", - "- All bonus pools go to 5-year participants\n", - "- More penalty = more bonus for 5-year participants\n", - "- Lower participation = higher individual allocations" + "# Export allocation results from the experiment\n", + "# export_current_scenario(\"lockdrop_allocations_experiment.json\")" ] } ], "metadata": { "kernelspec": { - "display_name": "venv", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/lockdrop_calculations.py b/lockdrop_calculations.py index 59bb69b..16bf9c0 100644 --- a/lockdrop_calculations.py +++ b/lockdrop_calculations.py @@ -509,8 +509,8 @@ def print_final_allocations_and_verification(allocation_data, final_data): 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]:.12f}" for year in [5, 4, 3, 2, 1]], - 'Z per Block': [f"{star_z_per_block[year]:.12f}" 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") @@ -519,8 +519,8 @@ def print_final_allocations_and_verification(allocation_data, final_data): 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]:.12f}" for year in [5, 4, 3, 2, 1]], - 'Z per Block': [f"{galaxy_z_per_block[year]:.12f}" 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") @@ -534,14 +534,15 @@ def print_final_allocations_and_verification(allocation_data, final_data): verification_df = pd.DataFrame({ 'Category': ['Star Allocations', 'Galaxy Allocations', 'Combined'], - 'Calculated Total': [f"{total_stars_allocation:.12f} $Z", f"{total_galaxies_allocation:.12f} $Z", - f"{total_stars_allocation + total_galaxies_allocation:.12f} $Z"], - 'Expected Total': [f"{lockdrop_allocation_stars:.12f} $Z", f"{lockdrop_allocation_galaxies:.12f} $Z", - f"{LOCKDROP_ALLOCATION:.12f} $Z"], - 'Rounding Error': [f"{final_rounding_error_stars:.12f} $Z", f"{final_rounding_error_galaxies:.12f} $Z", - f"{final_rounding_error:.12f} $Z"] + '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) @@ -685,3 +686,297 @@ def load_watcher_events_data(generated_dir='generated'): except Exception as e: print(f"āš ļø Error loading watcher events: {e}") return None, False + + +# Experimental notebook widget and interaction functions +def create_experiment_widgets(): + """Create input widgets for the experimental notebook.""" + try: + import ipywidgets as widgets + except ImportError: + raise ImportError("ipywidgets is required for the experimental interface. Install with: pip install ipywidgets") + + # 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 + stars_1_years = widgets.IntText(value=default_participants['stars_1_years'], description='1 Year:', style={'description_width': '80px'}) + stars_2_years = widgets.IntText(value=default_participants['stars_2_years'], description='2 Years:', style={'description_width': '80px'}) + stars_3_years = widgets.IntText(value=default_participants['stars_3_years'], description='3 Years:', style={'description_width': '80px'}) + stars_4_years = widgets.IntText(value=default_participants['stars_4_years'], description='4 Years:', style={'description_width': '80px'}) + stars_5_years = widgets.IntText(value=default_participants['stars_5_years'], 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.IntText(value=default_participants['galaxies_1_years'], description='1 Year:', style={'description_width': '80px'}) + galaxies_2_years = widgets.IntText(value=default_participants['galaxies_2_years'], description='2 Years:', style={'description_width': '80px'}) + galaxies_3_years = widgets.IntText(value=default_participants['galaxies_3_years'], description='3 Years:', style={'description_width': '80px'}) + galaxies_4_years = widgets.IntText(value=default_participants['galaxies_4_years'], description='4 Years:', style={'description_width': '80px'}) + galaxies_5_years = widgets.IntText(value=default_participants['galaxies_5_years'], 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]) + + # Calculate button + calculate_button = widgets.Button(description='šŸ”„ Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'}) + + # Output area + output_area = widgets.Output() + + # Return all widgets as a dictionary + return { + 'star_controls': star_controls, + 'galaxy_controls': galaxy_controls, + 'calculate_button': calculate_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.""" + def calculate_and_display(button=None): + """Calculate allocations and display results based on input parameters.""" + try: + from IPython.display import clear_output + except ImportError: + raise ImportError("IPython is required for the experimental interface") + + 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)}") + import traceback + traceback.print_exc() + + return calculate_and_display + + +def get_preset_scenarios(): + """Get preset scenario definitions for experimentation.""" + return { + 'balanced': { + 'description': 'Balanced distribution across all lock periods', + 'params': { + '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 + } + }, + 'five_year_focused': { + 'description': 'Most participants choose 5-year lock (maximum bonus scenario)', + 'params': { + 'stars_1_years': 2000, 'stars_2_years': 2000, 'stars_3_years': 3000, 'stars_4_years': 5000, 'stars_5_years': 28000, + 'galaxies_1_years': 10, 'galaxies_2_years': 10, 'galaxies_3_years': 20, 'galaxies_4_years': 30, 'galaxies_5_years': 130 + } + }, + 'short_term_focused': { + 'description': 'Most participants choose shorter locks (high penalty scenario)', + 'params': { + 'stars_1_years': 20000, 'stars_2_years': 15000, 'stars_3_years': 4000, 'stars_4_years': 800, 'stars_5_years': 200, + 'galaxies_1_years': 100, 'galaxies_2_years': 80, 'galaxies_3_years': 15, 'galaxies_4_years': 3, 'galaxies_5_years': 2 + } + }, + 'polarized': { + 'description': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)', + 'params': { + 'stars_1_years': 25000, 'stars_2_years': 0, 'stars_3_years': 0, 'stars_4_years': 0, 'stars_5_years': 15000, + 'galaxies_1_years': 150, 'galaxies_2_years': 0, 'galaxies_3_years': 0, 'galaxies_4_years': 0, 'galaxies_5_years': 50 + } + }, + 'low_participation': { + 'description': 'Low overall participation scenario', + 'params': { + 'stars_1_years': 1000, 'stars_2_years': 800, 'stars_3_years': 600, 'stars_4_years': 400, 'stars_5_years': 200, + 'galaxies_1_years': 8, 'galaxies_2_years': 6, 'galaxies_3_years': 4, 'galaxies_4_years': 2, 'galaxies_5_years': 5 + } + } + } + + +def create_scenario_loader(widget_dict): + """Create scenario loading function for the experimental notebook.""" + def load_scenario(scenario_name): + """Load a preset scenario into the input widgets.""" + scenarios = get_preset_scenarios() + + if scenario_name not in scenarios: + print(f"āŒ Unknown scenario: {scenario_name}") + return + + scenario = scenarios[scenario_name] + params = scenario['params'] + widgets_map = widget_dict['widgets'] + + # Update widget values + widgets_map['stars_1_years'].value = params['stars_1_years'] + widgets_map['stars_2_years'].value = params['stars_2_years'] + widgets_map['stars_3_years'].value = params['stars_3_years'] + widgets_map['stars_4_years'].value = params['stars_4_years'] + widgets_map['stars_5_years'].value = params['stars_5_years'] + widgets_map['galaxies_1_years'].value = params['galaxies_1_years'] + widgets_map['galaxies_2_years'].value = params['galaxies_2_years'] + widgets_map['galaxies_3_years'].value = params['galaxies_3_years'] + widgets_map['galaxies_4_years'].value = params['galaxies_4_years'] + widgets_map['galaxies_5_years'].value = params['galaxies_5_years'] + + print(f"āœ… Loaded scenario: {scenario_name}") + print(f"šŸ“ Description: {scenario['description']}") + print("\nšŸ”„ Click 'Calculate Allocations' button above to see results!") + + return load_scenario + + +def create_export_function(widget_dict): + """Create export function for the experimental notebook.""" + def export_current_scenario(filename='lockdrop_allocations_experiment.json'): + """Export current scenario results to JSON file.""" + widgets_map = widget_dict['widgets'] + + # 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 + } + + 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-experiment.ipynb', + 'scenario': 'custom', + '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}") + print(f"šŸ“Š Total participants: {sum(participation_counts.values()):,}") + + except Exception as e: + print(f"āŒ Export failed: {str(e)}") + + return export_current_scenario