diff --git a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc b/__pycache__/bitcoin_trading_simulation.cpython-312.pyc deleted file mode 100644 index 52abfc2..0000000 Binary files a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc and /dev/null differ diff --git a/__pycache__/test_bitcoin_trading.cpython-312.pyc b/__pycache__/test_bitcoin_trading.cpython-312.pyc deleted file mode 100644 index ecc297a..0000000 Binary files a/__pycache__/test_bitcoin_trading.cpython-312.pyc and /dev/null differ diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index c86be3e..517632b 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,38 +1,35 @@ import argparse import numpy as np import pandas as pd -import argparse +import sys class Colors: HEADER = '\033[95m' BLUE = '\033[94m' + CYAN = '\033[96m' GREEN = '\033[92m' - RED = '\033[91m' + WARNING = '\033[93m' + FAIL = '\033[91m' + RED = '\033[91m' # 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 +84,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 +96,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 ${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'] @@ -130,6 +130,20 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): args = parser.parse_args() + # Input Validation + if args.days <= 0: + print(f"{Colors.FAIL}Error: --days must be a positive integer.{Colors.ENDC}") + sys.exit(1) + if args.initial_cash < 0: + print(f"{Colors.FAIL}Error: --initial-cash must be non-negative.{Colors.ENDC}") + sys.exit(1) + if args.initial_price <= 0: + print(f"{Colors.FAIL}Error: --initial-price must be positive.{Colors.ENDC}") + sys.exit(1) + if args.volatility < 0: + print(f"{Colors.FAIL}Error: --volatility must be non-negative.{Colors.ENDC}") + sys.exit(1) + if args.no_color: Colors.disable() @@ -149,17 +163,52 @@ 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 + + # Trade statistics + # Calculate actual trades based on portfolio changes + btc_diff = portfolio['btc'].diff().fillna(0) + total_buys = len(btc_diff[btc_diff > 0]) + total_sells = len(btc_diff[btc_diff < 0]) + total_trades = total_buys + total_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}") + buy_and_hold_roi = ((buy_and_hold_value - initial_cash) / initial_cash) * 100 + + comparison = ((final_value - buy_and_hold_value) / buy_and_hold_value) * 100 + + 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.BOLD}Initial Cash:{Colors.ENDC} ${initial_cash:,.2f}") + print(f"║ {Colors.BOLD}Final Portfolio Value:{Colors.ENDC} ${final_value:,.2f}") + if profit >= 0: - print(f"{Colors.GREEN}💰 Profit/Loss: ${profit:.2f}{Colors.ENDC}") + print( + f"║ {Colors.BOLD}Profit/Loss:{Colors.ENDC} " + f"{Colors.GREEN}💰 ${profit:,.2f} ({roi:+.2f}%){Colors.ENDC}" + ) + else: + print( + f"║ {Colors.BOLD}Profit/Loss:{Colors.ENDC} " + f"{Colors.FAIL}📉 ${profit:,.2f} ({roi:+.2f}%){Colors.ENDC}" + ) + + print(f"{Colors.HEADER}{Colors.BOLD}╠══════════════════════════════════════════════════════════╣{Colors.ENDC}") + print(f"║ {Colors.BOLD}Strategy vs. Buy & Hold:{Colors.ENDC}") + print(f"║ Strategy ROI: {roi:+.2f}%") + print(f"║ Buy & Hold ROI: {buy_and_hold_roi:+.2f}%") + + if comparison >= 0: + print(f"║ Performance vs B&H: {Colors.GREEN}{comparison:+.2f}% (Better){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"║ Performance vs B&H: {Colors.FAIL}{comparison:+.2f}% (Worse){Colors.ENDC}") + + print(f"{Colors.HEADER}{Colors.BOLD}╠══════════════════════════════════════════════════════════╣{Colors.ENDC}") + print(f"║ {Colors.BOLD}Trade Statistics:{Colors.ENDC}") + print(f"║ Total Trades: {total_trades}") + print(f"║ Buys: {Colors.GREEN}{total_buys}{Colors.ENDC}") + print(f"║ Sells: {Colors.FAIL}{total_sells}{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}╚══════════════════════════════════════════════════════════╝{Colors.ENDC}") diff --git a/test_bitcoin.py b/test_bitcoin.py deleted file mode 100644 index 163248c..0000000 --- a/test_bitcoin.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest -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.""" - price = 50000.0 - amount = 2.5 - 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): - """Simulate a successful API response from CoinDesk or similar.""" - # Mock the JSON return value - mock_get.return_value.json.return_value = { - "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): - mock_get.return_value.status_code = 404 - with pytest.raises(ConnectionError): - get_bitcoin_price() 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 = {