From c3af480914e83d4d1f4845c6904cfbdbe052ed35 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:42:25 +0000 Subject: [PATCH 1/2] feat: enhance CLI UX and fix quiet mode logic - Consolidated redundant `Colors` classes into a single robust helper. - Fixed `quiet` mode to correctly suppress all daily logs and trade events. - Added a delightful final simulation report with trade statistics and box-drawing formatting. - Improved comparison metrics (Strategy vs. Buy & Hold return %). - Cleaned up redundant imports and duplicate headers. - Updated unit tests to align with revised color attributes and output logic. Co-authored-by: EiJackGH <172181576+EiJackGH@users.noreply.github.com> --- .Jules/palette.md | 20 ++++- ...bitcoin_trading_simulation.cpython-312.pyc | Bin 8406 -> 0 bytes bitcoin_trading_simulation.py | 71 ++++++++++++------ test_bitcoin_trading.py | 3 + 4 files changed, 67 insertions(+), 27 deletions(-) delete mode 100644 __pycache__/bitcoin_trading_simulation.cpython-312.pyc diff --git a/.Jules/palette.md b/.Jules/palette.md index 018831f..0a2e29b 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,17 @@ -## 2024-05-23 - CLI UX Enhancement -**Learning:** Even in CLI apps, visual distinction (colors, emojis) significantly reduces cognitive load when scanning logs. -**Action:** Use ANSI colors and consistent emojis for key events (success/failure) in future CLI tools. +# Palette's UX Learnings - Bitcoin Trading Simulation + +## CLI Delight +- **Visual Structure:** Using box-drawing characters (`┏`, `┃`, `┗`, `━`) helps separate the final report from the scrolling logs, making it easier for users to find the most important information. +- **Emoji Semantics:** + - `🟢` and `🔴` are excellent for quick status indication (Buy/Sell, Profit/Loss). + - `🛒` (Buy) and `🏷️` (Sell) add a touch of personality to trade statistics. + - `🚀` and `📉` provide immediate feedback on strategy performance relative to benchmarks. +- **Color Contrast:** Using `CYAN` for values and `BOLD` for headers improves readability in dense CLI output. + +## Accessibility +- **No-Color Mode:** Always respect the `--no-color` flag by providing a fallback that removes ANSI escape codes while maintaining structure through spacing and symbols. +- **Quiet Mode:** `quiet` flags should suppress high-volume output (like daily ledgers) but can still show the final result, as long as it's clearly documented. + +## Code Quality for UX +- **Consolidated Styling:** A single `Colors` class with a `disable()` method ensures consistent styling across the application and easier maintenance for future theme changes. +- **Data Clarity:** Providing "Strategy Return %" alongside absolute values helps users quickly grasp the magnitude of their performance without doing mental math. diff --git a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc b/__pycache__/bitcoin_trading_simulation.cpython-312.pyc deleted file mode 100644 index 52abfc2edcf23a102e58b9d12c1192ee1c250d1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8406 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 diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index c86be3e..b6a30bd 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,38 +1,36 @@ import argparse import numpy as np import pandas as pd -import argparse class Colors: HEADER = '\033[95m' BLUE = '\033[94m' + CYAN = '\033[96m' GREEN = '\033[92m' + YELLOW = '\033[93m' RED = '\033[91m' + FAIL = '\033[91m' + WARNING = '\033[93m' ENDC = '\033[0m' BOLD = '\033[1m' + UNDERLINE = '\033[4m' @classmethod def disable(cls): cls.HEADER = '' cls.BLUE = '' + cls.CYAN = '' cls.GREEN = '' + cls.YELLOW = '' cls.RED = '' + cls.FAIL = '' + cls.WARNING = '' cls.ENDC = '' cls.BOLD = '' + cls.UNDERLINE = '' -class Colors: - HEADER = '\033[95m' - BLUE = '\033[94m' - CYAN = '\033[96m' - GREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - def simulate_bitcoin_prices(days=60, initial_price=50000, volatility=0.02): """ Simulates Bitcoin prices for a given number of days using Geometric Brownian Motion. @@ -87,9 +85,8 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): portfolio['total_value'] = float(initial_cash) if not quiet: - print(f"{Colors.HEADER}{Colors.BOLD}------ Daily Trading Ledger ------{Colors.ENDC}") + print(f"\n{Colors.HEADER}{Colors.BOLD}------ Daily Trading Ledger ------{Colors.ENDC}") - print(f"\n{Colors.HEADER}{Colors.BOLD}------ Daily Trading Ledger ------{Colors.ENDC}") for i, row in signals.iterrows(): if i > 0: portfolio.loc[i, 'cash'] = portfolio.loc[i-1, 'cash'] @@ -100,14 +97,16 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): btc_to_buy = portfolio.loc[i, 'cash'] / row['price'] portfolio.loc[i, 'btc'] += btc_to_buy portfolio.loc[i, 'cash'] -= btc_to_buy * row['price'] - print(f"{Colors.GREEN}🟢 Day {i}: Buy {btc_to_buy:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") + if not quiet: + print(f"{Colors.GREEN}🟢 Day {i}: Buy {btc_to_buy:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") # Sell signal elif row['positions'] == -2.0: if portfolio.loc[i, 'btc'] > 0: cash_received = portfolio.loc[i, 'btc'] * row['price'] portfolio.loc[i, 'cash'] += cash_received - print(f"{Colors.FAIL}🔴 Day {i}: Sell {portfolio.loc[i, 'btc']:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") + if not quiet: + print(f"{Colors.FAIL}🔴 Day {i}: Sell {portfolio.loc[i, 'btc']:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") portfolio.loc[i, 'btc'] = 0 portfolio.loc[i, 'total_value'] = portfolio.loc[i, 'cash'] + portfolio.loc[i, 'btc'] * row['price'] @@ -150,16 +149,40 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): initial_cash = args.initial_cash profit = final_value - initial_cash + # Calculate trade stats + btc_diff = portfolio['btc'].diff() + buys = (btc_diff > 0).sum() + sells = (btc_diff < 0).sum() + total_trades = buys + sells + # Compare with buy and hold strategy buy_and_hold_btc = args.initial_cash / prices.iloc[0] buy_and_hold_value = buy_and_hold_btc * prices.iloc[-1] - - print(f"\n{Colors.HEADER}{Colors.BOLD}------ Final Portfolio Performance ------{Colors.ENDC}") - print(f"Initial Cash: ${initial_cash:.2f}") - print(f"Final Portfolio Value: ${final_value:.2f}") + + # Final Summary Report + print(f"\n{Colors.HEADER}{Colors.BOLD}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}┃ FINAL SIMULATION PERFORMANCE REPORT ┃{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{Colors.ENDC}") + + print(f" • Initial Investment: {Colors.CYAN}${initial_cash:,.2f}{Colors.ENDC}") + print(f" • Final Portfolio Value: {Colors.CYAN}${final_value:,.2f}{Colors.ENDC}") + if profit >= 0: - print(f"{Colors.GREEN}💰 Profit/Loss: ${profit:.2f}{Colors.ENDC}") + print(f" • Profit/Loss: {Colors.GREEN}🟢 +${profit:,.2f}{Colors.ENDC}") + else: + print(f" • Profit/Loss: {Colors.FAIL}🔴 -${abs(profit):,.2f}{Colors.ENDC}") + + print(f" • Strategy Return: {((final_value/initial_cash - 1) * 100):.2f}%") + print(f" • Buy & Hold Return: {((buy_and_hold_value/initial_cash - 1) * 100):.2f}%") + + print(f"\n{Colors.BOLD} --- Trade Statistics ---{Colors.ENDC}") + print(f" • Total Trades: {total_trades}") + print(f" • Buys: {buys} 🛒") + print(f" • Sells: {sells} 🏷️") + + print(f"\n{Colors.HEADER}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.ENDC}") + if final_value > buy_and_hold_value: + print(f"{Colors.GREEN}{Colors.BOLD} 🚀 Strategy OUTPERFORMED Buy & Hold!{Colors.ENDC}") else: - print(f"{Colors.FAIL}📉 Profit/Loss: ${profit:.2f}{Colors.ENDC}") - print(f"Buy and Hold Strategy Value: ${buy_and_hold_value:.2f}") - print(f"{Colors.HEADER}-----------------------------------------{Colors.ENDC}") + print(f"{Colors.YELLOW}{Colors.BOLD} 📉 Buy & Hold was more effective this time.{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.ENDC}") diff --git a/test_bitcoin_trading.py b/test_bitcoin_trading.py index e7eac6f..3e267f7 100644 --- a/test_bitcoin_trading.py +++ b/test_bitcoin_trading.py @@ -16,6 +16,7 @@ def reset_colors(): 'RED': Colors.RED, 'ENDC': Colors.ENDC, 'BOLD': Colors.BOLD, + 'FAIL': Colors.FAIL, } yield # Restore colors @@ -25,6 +26,7 @@ def reset_colors(): Colors.RED = original_colors['RED'] Colors.ENDC = original_colors['ENDC'] Colors.BOLD = original_colors['BOLD'] + Colors.FAIL = original_colors['FAIL'] def test_simulate_trading_quiet_mode(capsys): @@ -59,6 +61,7 @@ def test_colors_disable(reset_colors): assert Colors.HEADER == "" assert Colors.GREEN == "" assert Colors.RED == "" + assert Colors.FAIL == "" def test_simulation_integration(): From 3ec6bb326a89d4e719915fdbf03b27b6bfaff575 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:47:50 +0000 Subject: [PATCH 2/2] feat: enhance CLI UX, fix quiet mode, and resolve CI failures - Improved CLI report formatting with box-drawing characters and emojis. - Fixed `quiet` mode to correctly suppress all daily logs. - Consolidated `Colors` class and added missing color attributes. - Added `bitcoin.py` module and `requests` dependency to fix CI test failures. - Fixed PEP 8 linting errors (line length, blank lines, whitespace). - Updated test fixtures for better isolation. Co-authored-by: EiJackGH <172181576+EiJackGH@users.noreply.github.com> --- bitcoin.py | 17 +++++++++++++++++ bitcoin_trading_simulation.py | 5 ++++- requirements.txt | 1 + test_bitcoin.py | 6 +++++- test_bitcoin_trading.py | 13 +++++++++++-- test_simulation.py | 8 ++++++-- 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 bitcoin.py diff --git a/bitcoin.py b/bitcoin.py new file mode 100644 index 0000000..ad421dd --- /dev/null +++ b/bitcoin.py @@ -0,0 +1,17 @@ +import requests + + +def calculate_value(amount, price): + """Calculates the USD value of a given amount of BTC.""" + return amount * price + + +def get_bitcoin_price(): + """Fetches the current BTC price from an API.""" + # Simplified implementation for testing purposes + response = requests.get("https://api.coindesk.com/v1/bpi/currentprice.json") + if response.status_code == 200: + data = response.json() + return data["bpi"]["USD"]["rate_float"] + else: + raise ConnectionError("Failed to fetch price") diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index b6a30bd..1e4e819 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -106,7 +106,10 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): cash_received = portfolio.loc[i, 'btc'] * row['price'] portfolio.loc[i, 'cash'] += cash_received if not quiet: - print(f"{Colors.FAIL}🔴 Day {i}: Sell {portfolio.loc[i, 'btc']:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") + msg = (f"{Colors.FAIL}🔴 Day {i}: Sell " + f"{portfolio.loc[i, 'btc']:.4f} BTC at " + f"${row['price']:.2f}{Colors.ENDC}") + print(msg) portfolio.loc[i, 'btc'] = 0 portfolio.loc[i, 'total_value'] = portfolio.loc[i, 'cash'] + portfolio.loc[i, 'btc'] * row['price'] diff --git a/requirements.txt b/requirements.txt index 5da331c..4ad1501 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ numpy pandas +requests diff --git a/test_bitcoin.py b/test_bitcoin.py index 163248c..4785f33 100644 --- a/test_bitcoin.py +++ b/test_bitcoin.py @@ -2,6 +2,7 @@ from unittest.mock import patch from bitcoin import get_bitcoin_price, calculate_value + # Test 1: Verify the calculation logic def test_calculate_value(): """Ensure BTC to USD conversion math is correct.""" @@ -10,10 +11,12 @@ def test_calculate_value(): expected = 125000.0 assert calculate_value(amount, price) == expected + # Test 2: Verify handling of zero amount def test_calculate_value_zero(): assert calculate_value(0, 50000.0) == 0.0 + # Test 3: Mocking an API response @patch('bitcoin.requests.get') def test_get_bitcoin_price(mock_get): @@ -23,10 +26,11 @@ def test_get_bitcoin_price(mock_get): "bpi": {"USD": {"rate_float": 62000.50}} } mock_get.return_value.status_code = 200 - + price = get_bitcoin_price() assert price == 62000.50 + # Test 4: Handling API failure @patch('bitcoin.requests.get') def test_get_price_api_error(mock_get): diff --git a/test_bitcoin_trading.py b/test_bitcoin_trading.py index 3e267f7..a6651e7 100644 --- a/test_bitcoin_trading.py +++ b/test_bitcoin_trading.py @@ -12,21 +12,29 @@ def reset_colors(): original_colors = { 'HEADER': Colors.HEADER, 'BLUE': Colors.BLUE, + 'CYAN': Colors.CYAN, 'GREEN': Colors.GREEN, + 'YELLOW': Colors.YELLOW, 'RED': Colors.RED, + 'FAIL': Colors.FAIL, + 'WARNING': Colors.WARNING, 'ENDC': Colors.ENDC, 'BOLD': Colors.BOLD, - 'FAIL': Colors.FAIL, + 'UNDERLINE': Colors.UNDERLINE, } yield # Restore colors Colors.HEADER = original_colors['HEADER'] Colors.BLUE = original_colors['BLUE'] + Colors.CYAN = original_colors['CYAN'] Colors.GREEN = original_colors['GREEN'] + Colors.YELLOW = original_colors['YELLOW'] Colors.RED = original_colors['RED'] + Colors.FAIL = original_colors['FAIL'] + Colors.WARNING = original_colors['WARNING'] Colors.ENDC = original_colors['ENDC'] Colors.BOLD = original_colors['BOLD'] - Colors.FAIL = original_colors['FAIL'] + Colors.UNDERLINE = original_colors['UNDERLINE'] def test_simulate_trading_quiet_mode(capsys): @@ -62,6 +70,7 @@ def test_colors_disable(reset_colors): assert Colors.GREEN == "" assert Colors.RED == "" assert Colors.FAIL == "" + assert Colors.CYAN == "" def test_simulation_integration(): diff --git a/test_simulation.py b/test_simulation.py index 0f4f1f8..c4e1893 100644 --- a/test_simulation.py +++ b/test_simulation.py @@ -1,7 +1,9 @@ -import pytest import pandas as pd import numpy as np -from bitcoin_trading_simulation import simulate_bitcoin_prices, calculate_moving_averages, generate_trading_signals +from bitcoin_trading_simulation import ( + simulate_bitcoin_prices, calculate_moving_averages, generate_trading_signals +) + def test_simulate_bitcoin_prices(): days = 10 @@ -10,6 +12,7 @@ def test_simulate_bitcoin_prices(): assert isinstance(prices, pd.Series) assert prices.name == 'Price' + def test_calculate_moving_averages(): prices = pd.Series([100, 101, 102, 103, 104, 105, 106, 107, 108, 109], name='Price') signals = calculate_moving_averages(prices, short_window=3, long_window=5) @@ -17,6 +20,7 @@ def test_calculate_moving_averages(): assert 'long_mavg' in signals.columns assert not signals['short_mavg'].isnull().all() + def test_generate_trading_signals(): # Create dummy signals DataFrame data = {