diff --git a/.Jules/palette.md b/.Jules/palette.md index 018831f..d9b8b02 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,3 @@ -## 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. +## 2024-05-22 - [CLI Dashboard Pattern] +**Learning:** Simple CLI outputs can be significantly improved by using ASCII box-drawing characters and padded alignment to create a "dashboard" feel. Users perceive this as more polished and professional than raw text streams. Also, ensure `--quiet` flags silence *all* intermediate output to respect user intent. +**Action:** Use Python f-string alignment (`<`, `>`, `^`) and box-drawing characters for final summary reports in CLI tools. diff --git a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc b/__pycache__/bitcoin_trading_simulation.cpython-312.pyc index 52abfc2..39de5c6 100644 Binary files a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc and b/__pycache__/bitcoin_trading_simulation.cpython-312.pyc differ diff --git a/bitcoin.py b/bitcoin.py new file mode 100644 index 0000000..54f7beb --- /dev/null +++ b/bitcoin.py @@ -0,0 +1,22 @@ +import requests + + +def calculate_value(amount, price): + """ + Calculates the value of the Bitcoin amount at the given price. + """ + return float(amount * price) + + +def get_bitcoin_price(): + """ + Fetches the current Bitcoin price from CoinDesk API. + """ + url = "https://api.coindesk.com/v1/bpi/currentprice.json" + response = requests.get(url) + + if response.status_code != 200: + raise ConnectionError("Failed to fetch Bitcoin price") + + data = response.json() + return data["bpi"]["USD"]["rate_float"] diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index c86be3e..31c5fff 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,38 +1,34 @@ 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' - RED = '\033[91m' + WARNING = '\033[93m' + FAIL = '\033[91m' + RED = FAIL # Alias for compatibility ENDC = '\033[0m' BOLD = '\033[1m' + UNDERLINE = '\033[4m' @classmethod def disable(cls): cls.HEADER = '' cls.BLUE = '' + cls.CYAN = '' cls.GREEN = '' + cls.WARNING = '' + cls.FAIL = '' cls.RED = '' 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 +83,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 +95,18 @@ 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 " + f"${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 " + f"${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'] @@ -149,17 +148,39 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): final_value = portfolio['total_value'].iloc[-1] initial_cash = args.initial_cash profit = final_value - initial_cash + roi = (profit / initial_cash) * 100 + + # Calculate trade stats + buys = len(signals[signals['positions'] == 2.0]) + sells = len(signals[signals['positions'] == -2.0]) + 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}") + + print(f"\n{Colors.HEADER}{Colors.BOLD}╔══════════════════════════════════════════╗{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}║ Final Portfolio Performance ║{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}╠══════════════════════════════════════════╣{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {'Initial Cash:':<25} ${initial_cash:>14,.2f} " + f"{Colors.HEADER}║{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {'Final Value:':<25} ${final_value:>14,.2f} " + f"{Colors.HEADER}║{Colors.ENDC}") + if profit >= 0: - print(f"{Colors.GREEN}💰 Profit/Loss: ${profit:.2f}{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {Colors.GREEN}💰 Profit/Loss: " + f"${profit:>14,.2f}{Colors.ENDC} {Colors.HEADER}║{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {Colors.GREEN} ROI: " + f"{roi:>14.2f}%{Colors.ENDC} {Colors.HEADER}║{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.HEADER}║{Colors.ENDC} {Colors.FAIL}📉 Profit/Loss: " + f"${profit:>14,.2f}{Colors.ENDC} {Colors.HEADER}║{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {Colors.FAIL} ROI: " + f"{roi:>14.2f}%{Colors.ENDC} {Colors.HEADER}║{Colors.ENDC}") + + print(f"{Colors.HEADER}╠══════════════════════════════════════════╣{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {'Trades (B/S):':<25} {f'{buys}/{sells}':>15} " + f"{Colors.HEADER}║{Colors.ENDC}") + print(f"{Colors.HEADER}║{Colors.ENDC} {'Buy & Hold:':<25} ${buy_and_hold_value:>14,.2f} " + f"{Colors.HEADER}║{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}╚══════════════════════════════════════════╝{Colors.ENDC}") 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_simulation.py b/test_simulation.py index 0f4f1f8..8fddb57 100644 --- a/test_simulation.py +++ b/test_simulation.py @@ -1,8 +1,8 @@ -import pytest import pandas as pd import numpy as np from bitcoin_trading_simulation import simulate_bitcoin_prices, calculate_moving_averages, generate_trading_signals + def test_simulate_bitcoin_prices(): days = 10 prices = simulate_bitcoin_prices(days=days, initial_price=50000) @@ -10,6 +10,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 +18,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 = {