diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..ed370ff --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,10 @@ +# Bitcoin Trading Simulation Contributors + +We'd like to thank the following individuals for their contributions to this project: + +- **EiJackGH** - Initial project setup and core simulation logic. +- **aidasofialily-cmd** - Enhanced simulation features and Bitcoin price data integration. +- **Jules** - AI Assistant and software engineer, assisting in codebase maintenance and improvements. + +--- +Special thanks to all the developers who help maintain and improve this open-source tool! diff --git a/README.md b/README.md index 4cd8db6..efa68f3 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,14 @@ Run the test suite using `pytest`: ```bash pytest ``` + +## Contributors + +The Bitcoin Trading Simulation is built and maintained by a team of dedicated developers. Check out the full list of [Contributors](CONTRIBUTORS.md). + +## Support the Project + +If you find this simulation helpful, please consider: +- 🌟 Starring the repository on GitHub. +- 💬 Sharing your feedback and feature requests. +- 🛠️ Contributing to the codebase via Pull Requests. diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index 0d46e73..bb0aced 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -96,22 +96,22 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): 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: + # Buy signal: If signal is 1 (Golden Cross) and we don't have BTC + if row['signal'] == 1.0 and portfolio.loc[i, 'btc'] == 0: 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'] - 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 + if btc_to_buy > 0: + portfolio.loc[i, 'btc'] += btc_to_buy + portfolio.loc[i, 'cash'] -= btc_to_buy * row['price'] 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 + print(f"{Colors.GREEN}🟢 Day {i}: Buy {btc_to_buy:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}") + + # Sell signal: If signal is -1 (Death Cross) and we have BTC + elif row['signal'] == -1.0 and portfolio.loc[i, 'btc'] > 0: + cash_received = portfolio.loc[i, 'btc'] * row['price'] + portfolio.loc[i, 'cash'] += cash_received + 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'] @@ -148,12 +148,20 @@ def countdown(quiet=False): parser.add_argument("--volatility", type=float, default=0.02, help="Price volatility") parser.add_argument("--quiet", action="store_true", help="Suppress daily portfolio log") parser.add_argument("--no-color", action="store_true", help="Disable colored output") + parser.add_argument("--contributors", action="store_true", help="Display the project contributor team") args = parser.parse_args() if args.no_color: Colors.disable() + if args.contributors: + print(f"\n{Colors.HEADER}{Colors.BOLD}--- Bitcoin Trading Simulation Team ---{Colors.ENDC}") + print(f"{Colors.BLUE}- EiJackGH{Colors.ENDC}") + print(f"{Colors.BLUE}- aidasofialily-cmd{Colors.ENDC}") + print(f"{Colors.BLUE}- Jules (AI Software Engineer){Colors.ENDC}") + sys.exit(0) + # Simulate prices prices = simulate_bitcoin_prices(days=args.days, initial_price=args.initial_price, volatility=args.volatility) @@ -225,3 +233,4 @@ def print_line(label, value_str, color=Colors.ENDC): print_line("vs Buy & Hold:", f"{vs_sign}${abs(vs_buy_hold):,.2f}", vs_color) print(f"{Colors.HEADER}{Colors.BOLD}╚{border}╝{Colors.ENDC}") + print(f"\n{Colors.BLUE}🌟 Love this project? Consider starring the repository on GitHub!{Colors.ENDC}") diff --git a/requirements.txt b/requirements.txt index cfaa995..26c9a85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ numpy pandas requests -venv +pytest diff --git a/test_bitcoin_trading.py b/test_bitcoin_trading.py index 6c480ea..18daa0d 100644 --- a/test_bitcoin_trading.py +++ b/test_bitcoin_trading.py @@ -31,6 +31,7 @@ def test_simulate_trading_quiet_mode(capsys): """Test that quiet mode suppresses output.""" signals = pd.DataFrame(index=range(5)) signals['price'] = [100.0, 101.0, 102.0, 103.0, 104.0] + signals['signal'] = [0.0] * 5 signals['positions'] = [0.0] * 5 simulate_trading(signals, initial_cash=1000, quiet=True) @@ -43,6 +44,7 @@ def test_simulate_trading_verbose_mode(capsys): """Test that verbose mode prints daily ledger.""" signals = pd.DataFrame(index=range(2)) signals['price'] = [100.0, 101.0] + signals['signal'] = [0.0, 0.0] signals['positions'] = [0.0, 0.0] simulate_trading(signals, initial_cash=1000, quiet=False) diff --git a/test_simulation.py b/test_simulation.py index 8fddb57..93254b0 100644 --- a/test_simulation.py +++ b/test_simulation.py @@ -1,6 +1,9 @@ import pandas as pd import numpy as np -from bitcoin_trading_simulation import simulate_bitcoin_prices, calculate_moving_averages, generate_trading_signals +from bitcoin_trading_simulation import ( + simulate_bitcoin_prices, calculate_moving_averages, + generate_trading_signals, simulate_trading +) def test_simulate_bitcoin_prices(): @@ -33,3 +36,33 @@ def test_generate_trading_signals(): assert 'positions' in signals.columns # Check that positions are calculated (not all nan, though first might be) assert signals['positions'].isin([0, 1, -1, 2, -2, np.nan]).any() + + +def test_initial_buy_signal(): + """Verify that a buy signal on the very first day is executed.""" + data = { + 'price': [100, 105, 110], + 'signal': [1.0, 1.0, 1.0] # Constant buy signal from day 0 + } + signals = pd.DataFrame(data) + portfolio = simulate_trading(signals, initial_cash=1000, quiet=True) + + # On day 0, it should have bought BTC + assert portfolio.loc[0, 'btc'] == 1000 / 100 + assert portfolio.loc[0, 'cash'] == 0 + + +def test_initial_sell_signal(): + """Verify that a sell signal on the first day is handled (though usually we start with cash).""" + # Start with some BTC and a sell signal + data = { + 'price': [100, 90, 80], + 'signal': [-1.0, -1.0, -1.0] + } + signals = pd.DataFrame(data) + # Manually inject BTC into the simulation for testing if possible, + # but simulate_trading initializes btc to 0. + # So we test if it stays at 0 and doesn't crash. + portfolio = simulate_trading(signals, initial_cash=1000, quiet=True) + assert portfolio.loc[0, 'btc'] == 0 + assert portfolio.loc[0, 'cash'] == 1000