diff --git a/.Jules/palette.md b/.Jules/palette.md index 5de3a38..37abfba 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. + +## 2026-01-25 - Command Line Flexibility +**Learning:** Hardcoded parameters in CLI tools frustrate users. Adding `argparse` with sensible defaults empowers users to explore the tool without editing code. +**Action:** Always implement command-line arguments for key simulation parameters. diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index 82df43f..3ee200e 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,3 +1,4 @@ +import argparse import numpy as np import pandas as pd @@ -49,7 +50,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 +60,9 @@ 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'] @@ -70,24 +73,37 @@ def simulate_trading(signals, initial_cash=10000): 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:>3}: 💰 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.RED}Day {i}: 📉 Sell {portfolio.loc[i, 'btc']:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") + if not quiet: + print(f"{Colors.RED}Day {i:>3}: 📉 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'] - 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:>3}: Portfolio Value: ${portfolio.loc[i, 'total_value']:>10.2f}, Cash: ${portfolio.loc[i, 'cash']:>10.2f}, BTC: {portfolio.loc[i, 'btc']:>8.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 (default: 60)") + parser.add_argument("--initial-cash", type=float, default=10000, help="Initial cash amount (default: 10000)") + parser.add_argument("--initial-price", type=float, default=50000, help="Initial Bitcoin price (default: 50000)") + parser.add_argument("--volatility", type=float, default=0.02, help="Price volatility (default: 0.02)") + parser.add_argument("--quiet", action="store_true", help="Suppress daily ledger output") + + args = parser.parse_args() + # 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 +112,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.py b/test.py index b326d87..cea6236 100644 --- a/test.py +++ b/test.py @@ -1,3 +1,42 @@ -# Filename: tests/test_sample.py -def test_example(): - assert 1 + 1 == 2 +import unittest +import pandas as pd +from bitcoin_trading_simulation import ( + simulate_bitcoin_prices, + calculate_moving_averages, + generate_trading_signals, + simulate_trading +) + +class TestBitcoinSimulation(unittest.TestCase): + def test_simulation_flow(self): + # 1. Simulate Prices + days = 10 + prices = simulate_bitcoin_prices(days=days) + self.assertEqual(len(prices), days) + self.assertTrue(isinstance(prices, pd.Series)) + + # 2. Moving Averages + signals = calculate_moving_averages(prices, short_window=2, long_window=5) + self.assertIn('short_mavg', signals.columns) + self.assertIn('long_mavg', signals.columns) + + # 3. Generate Signals + signals = generate_trading_signals(signals) + self.assertIn('positions', signals.columns) + + # 4. Simulate Trading (Normal) + portfolio = simulate_trading(signals, initial_cash=10000, quiet=True) + self.assertEqual(len(portfolio), days) + self.assertIn('total_value', portfolio.columns) + + def test_quiet_mode(self): + prices = simulate_bitcoin_prices(days=5) + signals = calculate_moving_averages(prices) + signals = generate_trading_signals(signals) + # Should not throw error + simulate_trading(signals, quiet=True) + # We capture stdout to verify silence? Maybe too complex for now. + # Just ensuring it runs is enough for "micro-UX". + +if __name__ == '__main__': + unittest.main()