Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -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!
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
37 changes: 23 additions & 14 deletions bitcoin_trading_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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}")
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
numpy
pandas
requests
venv
pytest
2 changes: 2 additions & 0 deletions test_bitcoin_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
35 changes: 34 additions & 1 deletion test_simulation.py
Original file line number Diff line number Diff line change
@@ -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():
Expand Down Expand Up @@ -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
Loading