Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
6ec37fb
feat: improve CLI UX with colors, emojis and fix warnings
google-labs-jules[bot] Jan 13, 2026
78055d6
feat: add CLI arguments for configuration and accessibility
google-labs-jules[bot] Feb 6, 2026
de186f7
feat: add CLI arguments and accessibility options
google-labs-jules[bot] Feb 10, 2026
3d19360
feat: Add CLI arguments for configuration and accessibility
google-labs-jules[bot] Feb 11, 2026
806c9e6
feat: enhance CLI UX with argument parsing and accessibility options
google-labs-jules[bot] Feb 12, 2026
fc0fb49
Merge pull request #33 from EiJackGH/palette-cli-ux-improvements-8778…
EiJackGH Feb 14, 2026
1b473de
Add CodeQL analysis workflow configuration
EiJackGH Feb 14, 2026
0857f57
Merge branch 'main' into palette-cli-accessibility-18133986709458003954
EiJackGH Feb 14, 2026
bff9ec3
Merge pull request #38 from EiJackGH/palette-cli-accessibility-181339…
EiJackGH Feb 14, 2026
5a7a827
Merge branch 'main' into palette/cli-ux-improvements-1429745144804431…
EiJackGH Feb 14, 2026
a980225
Merge pull request #37 from EiJackGH/palette/cli-ux-improvements-1429…
EiJackGH Feb 14, 2026
cb2f491
Merge branch 'palette-cli-ux-enhancement-18424718808937523864' into main
EiJackGH Feb 14, 2026
0713774
Implement tests for Bitcoin price functions
EiJackGH Feb 14, 2026
74ed0fc
Merge branch 'main' into palette-cli-ux-improvements-1520168734396799…
EiJackGH Feb 16, 2026
f23a596
Merge pull request #9 from EiJackGH/palette-cli-ux-improvements-15201…
EiJackGH Feb 16, 2026
538a32c
Merge branch 'palette-cli-ux-improvements-3584131630186929809' into main
EiJackGH Feb 16, 2026
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: 3 additions & 7 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
## 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.
## 2024-05-23 - CLI UX Enhancement
**Learning:** Even in CLI apps, visual distinction (colors, emojis) significantly reduces cognitive load when scanning logs.
**Action:** Use ANSI colors and consistent emojis for key events (success/failure) in future CLI tools.
101 changes: 101 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '42 16 * * 6'

jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write

# required to fetch internal or private CodeQL packs
packages: read

# only required for workflows in private repositories
actions: read
contents: read

strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4

# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality

# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@
# Python
__pycache__/
*.pyc
.pytest_cache/
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ A Python-based CLI tool that simulates Bitcoin trading using a 'Golden Cross' mo

## Features

- **Price Simulation:** Uses Geometric Brownian Motion to simulate 60 days of Bitcoin prices.
- **Price Simulation:** Uses Geometric Brownian Motion to simulate Bitcoin prices.
- **Trading Strategy:** Implements a Golden Cross strategy (Short MA > Long MA = Buy, Short MA < Long MA = Sell).
- **Rich CLI Output:** features color-coded logs (Green for Buy/Profit, Red for Sell/Loss) and emojis for better readability.
- **Rich CLI Output:** Features color-coded logs (Green for Buy/Profit, Red for Sell/Loss) and emojis for better readability.
- **Performance metrics:** Compares the strategy's performance against a "Buy and Hold" approach.
- **Customizable:** Configure simulation parameters via command-line arguments.

## Installation

Expand All @@ -20,16 +21,36 @@ pip install -r requirements.txt

## Usage

Run the simulation script:
Run the simulation script with default settings (60 days, $10k initial cash):

```bash
python bitcoin_trading_simulation.py
```

### Options

Customize the simulation with the following arguments:

```bash
python bitcoin_trading_simulation.py --days 100 --initial-cash 5000 --initial-price 60000 --volatility 0.03
```

- `--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 portfolio log (only show final result)
- `--no-color`: Disable colored output (for accessibility or logging)

Example:
```bash
python bitcoin_trading_simulation.py --days 30 --quiet --no-color
```

## Tests

Run the test suite:
Run the test suite using `pytest`:

```bash
python test.py
pytest
```
Binary file not shown.
Binary file added __pycache__/test_bitcoin_trading.cpython-312.pyc
Binary file not shown.
64 changes: 42 additions & 22 deletions bitcoin_trading_simulation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import argparse
import numpy as np
import pandas as pd
import argparse


class Colors:
HEADER = '\033[95m'
BLUE = '\033[94m'
Expand All @@ -19,6 +21,18 @@ def disable(cls):
cls.ENDC = ''
cls.BOLD = ''


class Colors:
HEADER = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'

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.
Expand All @@ -33,6 +47,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.
Expand All @@ -43,6 +58,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.
Expand All @@ -54,11 +70,12 @@ 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, quiet=False):
"""
Simulates trading based on signals and prints a daily ledger.
Expand All @@ -71,6 +88,8 @@ def simulate_trading(signals, initial_cash=10000, quiet=False):

if not quiet:
print(f"{Colors.HEADER}{Colors.BOLD}------ Daily Trading Ledger ------{Colors.ENDC}")

print(f"\n{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']
Expand All @@ -81,30 +100,33 @@ def simulate_trading(signals, initial_cash=10000, quiet=False):
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}")
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
print(f"{Colors.RED}Day {i}: 📉 Sell {portfolio.loc[i, 'btc']:.4f} BTC at ${row['price']:.2f}{Colors.ENDC}")
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']

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}")

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 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')
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 portfolio log")
parser.add_argument("--no-color", action="store_true", help="Disable colored output")

args = parser.parse_args()

Expand All @@ -113,33 +135,31 @@ def simulate_trading(signals, initial_cash=10000, quiet=False):

# Simulate 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, initial_cash=args.initial_cash, quiet=args.quiet)

# Final portfolio performance
final_value = portfolio['total_value'].iloc[-1]
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_btc = args.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}")

if profit >= 0:
print(f"Profit/Loss: {Colors.GREEN}📈 ${profit:.2f}{Colors.ENDC}")
print(f"{Colors.GREEN}💰 Profit/Loss: ${profit:.2f}{Colors.ENDC}")
else:
print(f"Profit/Loss: {Colors.RED}📉 ${profit:.2f}{Colors.ENDC}")

print(f"{Colors.FAIL}📉 Profit/Loss: ${profit:.2f}{Colors.ENDC}")
print(f"Buy and Hold Strategy Value: ${buy_and_hold_value:.2f}")
print(f"{Colors.HEADER}-----------------------------------------{Colors.ENDC}")
3 changes: 0 additions & 3 deletions test.py

This file was deleted.

35 changes: 35 additions & 0 deletions test_bitcoin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import pytest
from unittest.mock import patch
from bitcoin import get_bitcoin_price, calculate_value

# Test 1: Verify the calculation logic
def test_calculate_value():
"""Ensure BTC to USD conversion math is correct."""
price = 50000.0
amount = 2.5
expected = 125000.0
assert calculate_value(amount, price) == expected

# Test 2: Verify handling of zero amount
def test_calculate_value_zero():
assert calculate_value(0, 50000.0) == 0.0

# Test 3: Mocking an API response
@patch('bitcoin.requests.get')
def test_get_bitcoin_price(mock_get):
"""Simulate a successful API response from CoinDesk or similar."""
# Mock the JSON return value
mock_get.return_value.json.return_value = {
"bpi": {"USD": {"rate_float": 62000.50}}
}
mock_get.return_value.status_code = 200

price = get_bitcoin_price()
assert price == 62000.50

# Test 4: Handling API failure
@patch('bitcoin.requests.get')
def test_get_price_api_error(mock_get):
mock_get.return_value.status_code = 404
with pytest.raises(ConnectionError):
get_bitcoin_price()
Loading
Loading