diff --git a/.Jules/palette.md b/.Jules/palette.md index 5de3a38..231399f 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,7 @@ ## 2024-05-22 - Visual Hierarchy in CLI Output **Learning:** Adding color-coded indicators (Green/Red) and emojis (💰, 📉) in CLI tools significantly reduces cognitive load when parsing financial data streams. It transforms a wall of text into a scannable narrative. **Action:** For data-heavy CLI applications, always implement a semantic color system and visual anchors (icons/emojis) for key events. + +## 2024-05-23 - Accessibility and Control in CLI Tools +**Learning:** While color and emojis improve scannability, they can be distracting or inaccessible (e.g., for color-blind users or automated parsing). Providing `--no-color` and `--quiet` flags is essential for accessibility and flexibility. +**Action:** Always include flags to disable visual enhancements and suppress verbose output in CLI tools to respect user preferences and support automation. diff --git a/.gitignore b/.gitignore index d4fb281..2e049dd 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,8 @@ # debug information files *.dwo + +# Python +__pycache__/ +*.pyc +.pytest_cache/ diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index 82df43f..6706418 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,5 +1,6 @@ import numpy as np import pandas as pd +import argparse class Colors: HEADER = '\033[95m' @@ -9,6 +10,15 @@ class Colors: ENDC = '\033[0m' BOLD = '\033[1m' + @classmethod + def disable(cls): + cls.HEADER = '' + cls.BLUE = '' + cls.GREEN = '' + cls.RED = '' + cls.ENDC = '' + cls.BOLD = '' + 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. @@ -49,7 +59,7 @@ def generate_trading_signals(signals): signals['positions'] = signals['signal'].diff().shift(1) return signals -def simulate_trading(signals, initial_cash=10000): +def simulate_trading(signals, initial_cash=10000, quiet=False): """ Simulates trading based on signals and prints a daily ledger. """ @@ -59,7 +69,8 @@ def simulate_trading(signals, initial_cash=10000): portfolio['btc'] = 0.0 portfolio['total_value'] = float(initial_cash) - print(f"{Colors.HEADER}{Colors.BOLD}------ Daily Trading Ledger ------{Colors.ENDC}") + if not quiet: + print(f"{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'] @@ -81,13 +92,27 @@ def simulate_trading(signals, initial_cash=10000): portfolio.loc[i, 'btc'] = 0 portfolio.loc[i, 'total_value'] = portfolio.loc[i, 'cash'] + portfolio.loc[i, 'btc'] * row['price'] - print(f"Day {i}: Portfolio Value: ${portfolio.loc[i, 'total_value']:.2f}, Cash: ${portfolio.loc[i, 'cash']:.2f}, BTC: {portfolio.loc[i, 'btc']:.4f}") + if not quiet: + print(f"Day {i}: Portfolio Value: ${portfolio.loc[i, 'total_value']:.2f}, Cash: ${portfolio.loc[i, 'cash']:.2f}, BTC: {portfolio.loc[i, 'btc']:.4f}") return portfolio if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Bitcoin Trading Simulation') + parser.add_argument('--days', type=int, default=60, help='Number of days to simulate') + parser.add_argument('--initial-cash', type=float, default=10000, help='Initial cash in portfolio') + parser.add_argument('--initial-price', type=float, default=50000, help='Initial Bitcoin price') + parser.add_argument('--volatility', type=float, default=0.02, help='Volatility of price changes') + parser.add_argument('--quiet', '-q', action='store_true', help='Suppress daily portfolio logs') + parser.add_argument('--no-color', action='store_true', help='Disable colored output') + + args = parser.parse_args() + + if args.no_color: + Colors.disable() + # Simulate prices - prices = simulate_bitcoin_prices() + prices = simulate_bitcoin_prices(days=args.days, initial_price=args.initial_price, volatility=args.volatility) # Calculate moving averages signals = calculate_moving_averages(prices) @@ -96,11 +121,11 @@ def simulate_trading(signals, initial_cash=10000): signals = generate_trading_signals(signals) # Simulate trading - portfolio = simulate_trading(signals) + portfolio = simulate_trading(signals, initial_cash=args.initial_cash, quiet=args.quiet) # Final portfolio performance final_value = portfolio['total_value'].iloc[-1] - initial_cash = 10000 + initial_cash = args.initial_cash profit = final_value - initial_cash # Compare with buy and hold strategy diff --git a/test_simulation.py b/test_simulation.py new file mode 100644 index 0000000..05a5106 --- /dev/null +++ b/test_simulation.py @@ -0,0 +1,32 @@ +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(): + prices = simulate_bitcoin_prices(days=10, initial_price=100, volatility=0.01) + assert len(prices) == 10 + assert isinstance(prices, pd.Series) + assert prices.iloc[0] == 100 + +def test_calculate_moving_averages(): + prices = pd.Series([10, 11, 12, 13, 14, 15, 16, 17, 18, 19], name='Price') + signals = calculate_moving_averages(prices, short_window=3, long_window=5) + assert 'short_mavg' in signals.columns + assert 'long_mavg' in signals.columns + assert len(signals) == 10 + +def test_generate_trading_signals(): + # create dummy signals dataframe + signals = pd.DataFrame({ + 'price': [100, 101, 102, 103, 104], + 'short_mavg': [100, 101, 102, 103, 104], + 'long_mavg': [99, 100, 101, 102, 103] + }) + # Here short > long, so signal should be 1.0 (buy) + + signals_with_logic = generate_trading_signals(signals) + assert 'signal' in signals_with_logic.columns + assert 'positions' in signals_with_logic.columns + # Check if logic is applied (dummy check) + assert (signals_with_logic['signal'] == 1.0).all()