diff --git a/.Jules/palette.md b/.Jules/palette.md index 5de3a38..b0c69b6 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 Customization in CLI Tools +**Learning:** While rich CLI output is engaging, it can be problematic for accessibility or automated parsing. Providing flags like `--no-color` and `--quiet` ensures the tool is versatile and inclusive. +**Action:** Always include `--no-color` and `--quiet` flags in CLI tools that use rich formatting. diff --git a/.gitignore b/.gitignore index d4fb281..907591b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ # debug information files *.dwo + +# Python +__pycache__/ +*.pyc diff --git a/README.md b/README.md index be561ef..d940914 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,21 @@ Run the simulation script: python bitcoin_trading_simulation.py ``` +### Options + +- `--days`: Number of days to simulate (default: 60) +- `--initial-cash`: Initial cash amount (default: 10000) +- `--initial-price`: Initial Bitcoin price (default: 50000) +- `--volatility`: Price volatility (default: 0.02) +- `--quiet`: Suppress daily ledger output +- `--no-color`: Disable colored output + +Example: + +```bash +python bitcoin_trading_simulation.py --days 30 --initial-cash 5000 --quiet +``` + ## Tests Run the test suite: diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index 82df43f..0e7b77f 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,5 +1,7 @@ import numpy as np import pandas as pd +import argparse + class Colors: HEADER = '\033[95m' @@ -9,6 +11,16 @@ 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. @@ -23,6 +35,7 @@ def simulate_bitcoin_prices(days=60, initial_price=50000, volatility=0.02): prices.append(prices[-1] + price_change) return pd.Series(prices, name='Price') + def calculate_moving_averages(prices, short_window=7, long_window=30): """ Calculates short and long moving averages for a given price series. @@ -33,6 +46,7 @@ def calculate_moving_averages(prices, short_window=7, long_window=30): signals['long_mavg'] = prices.rolling(window=long_window, min_periods=1, center=False).mean() return signals + def generate_trading_signals(signals): """ Generates trading signals based on the Golden Cross strategy. @@ -44,12 +58,13 @@ def generate_trading_signals(signals): signals.loc[signals['short_mavg'] > signals['long_mavg'], 'signal'] = 1.0 # A Death Cross (sell signal) signals.loc[signals['short_mavg'] < signals['long_mavg'], 'signal'] = -1.0 - + # We create 'positions' to represent the trading action: 1 for buy, -1 for sell, 0 for hold 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,11 +74,13 @@ 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'] - portfolio.loc[i, 'btc'] = portfolio.loc[i-1, 'btc'] + portfolio.loc[i, 'cash'] = portfolio.loc[i - 1, 'cash'] + portfolio.loc[i, 'btc'] = portfolio.loc[i - 1, 'btc'] # Buy signal if row['positions'] == 2.0: @@ -81,32 +98,49 @@ 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}, " + f"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 amount") + parser.add_argument("--initial-price", type=float, default=50000, help="Initial Bitcoin price") + parser.add_argument("--volatility", type=float, default=0.02, help="Price volatility") + parser.add_argument("--quiet", action="store_true", help="Suppress daily ledger output") + 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) - + # Generate trading signals 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 buy_and_hold_btc = 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}") diff --git a/test.py b/test.py deleted file mode 100644 index b326d87..0000000 --- a/test.py +++ /dev/null @@ -1,3 +0,0 @@ -# Filename: tests/test_sample.py -def test_example(): - assert 1 + 1 == 2 diff --git a/test_bitcoin_trading.py b/test_bitcoin_trading.py new file mode 100644 index 0000000..b1b9c66 --- /dev/null +++ b/test_bitcoin_trading.py @@ -0,0 +1,45 @@ +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_simulate_bitcoin_prices(self): + days = 10 + prices = simulate_bitcoin_prices(days=days) + self.assertIsInstance(prices, pd.Series) + self.assertEqual(len(prices), days) + + def test_calculate_moving_averages(self): + prices = simulate_bitcoin_prices(days=20) + signals = calculate_moving_averages(prices, short_window=5, long_window=10) + self.assertIsInstance(signals, pd.DataFrame) + self.assertIn('short_mavg', signals.columns) + self.assertIn('long_mavg', signals.columns) + + def test_generate_trading_signals(self): + prices = simulate_bitcoin_prices(days=20) + signals = calculate_moving_averages(prices, short_window=5, long_window=10) + signals = generate_trading_signals(signals) + self.assertIn('signal', signals.columns) + self.assertIn('positions', signals.columns) + + def test_simulate_trading(self): + prices = simulate_bitcoin_prices(days=20) + signals = calculate_moving_averages(prices, short_window=5, long_window=10) + signals = generate_trading_signals(signals) + portfolio = simulate_trading(signals, initial_cash=10000, quiet=True) + self.assertIsInstance(portfolio, pd.DataFrame) + self.assertIn('total_value', portfolio.columns) + self.assertIn('cash', portfolio.columns) + self.assertIn('btc', portfolio.columns) + self.assertEqual(len(portfolio), 20) + + +if __name__ == '__main__': + unittest.main()