From a1547e90ffb0f0896802562997abef273ffe7a75 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 13:19:06 +0000 Subject: [PATCH 1/3] feat(cli): add input validation for trading simulation arguments - Validate `days`, `initial_cash`, `initial_price`, and `volatility` - Ensure numeric arguments are positive/non-negative - Print clear, color-coded error messages on failure - Remove accidentally committed `__pycache__` files This improves the CLI UX by preventing invalid simulations and providing helpful feedback to the user. Co-authored-by: EiJackGH <172181576+EiJackGH@users.noreply.github.com> --- .../bitcoin_trading_simulation.cpython-312.pyc | Bin 10887 -> 0 bytes .../test_bitcoin_trading.cpython-312.pyc | Bin 5175 -> 0 bytes bitcoin_trading_simulation.py | 14 ++++++++++++++ 3 files changed, 14 insertions(+) delete mode 100644 __pycache__/bitcoin_trading_simulation.cpython-312.pyc delete mode 100644 __pycache__/test_bitcoin_trading.cpython-312.pyc diff --git a/__pycache__/bitcoin_trading_simulation.cpython-312.pyc b/__pycache__/bitcoin_trading_simulation.cpython-312.pyc deleted file mode 100644 index 81dd6550442bb097e35393c0939f196966f2f006..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10887 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 VsiAK+g)gX{7fuC1?ZHm#{{V+s=IsCg diff --git a/__pycache__/test_bitcoin_trading.cpython-312.pyc b/__pycache__/test_bitcoin_trading.cpython-312.pyc deleted file mode 100644 index ecc297a91549969cdf54cd01999ccc2da4eaaba9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5175 zcmeHLU2GKB6~6Pkv+K2CZEUZ-HY`7-Y+#M~F$ocY1Z)B=EW|j3E<`lic&?gokYUvOKgEu^3qi5OCO{5#cpk}8ju>PNEK2akl~4^o^xks*Ear1 zr1Yu1mcMi7&OP_=-XKCn$^#OKG;D+EmlcOrKLg~@S?us!Z3NeVg-DB>1igCd};OMpII22^w(pkG%2 z1B3opz-j6-Ec>WCSC_kpbMB(dY^KMX2`A*?A26nk= z)Q)_jirH!dbhKqBjE_09qo?Tk!U{#?PpH4PjQZD8L3_icNTrwv|Ae( z*loj6sU9yy###mj9mz86_XqM@Ry)cT)(1`4+68cfd>h^}B9BxS>Y^Yj3iX>o1PisB zKm-bPC@Prvb;Wb&PZm*u$HJ)iEmTH{T|RzJ=kE*miU)t48zTLsgU z^(Md#@;J0(vir}%Y^Z%$E-1l=N_a*Ie_k_o{}^ zDUPv-FEnn(*T12%kgxudi@=LwDMt<9p~RH;mXNX|EX&7Y9#_{_%AQ>dP5-#!fdEMRS@#aZ>R@u3*t!cbzg3c7d&Z&`4)rgTgqoq+wW=1!zIT8yO|MEt2`~z7FB2PABbz!HEa;9O^ zP4GGvboc??k5&KZv!R2-^5g0aqr2|x znmGUEr7tecR_`756(XC5eZTQRq$_ON0=0@6rRjxCBF%Sqj_>>|KD@3_yKO`m2|Q84 zWA&3Fgu5rT;n6F1u3%#!6nz+q&4glq5A7V5pUK2u=|;`5x{2yJW!wL~2Ux)J-}N3n zFeEPV9=a7~rP~pHj1WWEiGbM%jRPE~Er2gKv}(0B{udYCoBw2jOw4sW&oHziQC#tYs`Q_Se@o_GJ}ujn*#! zet2`2=9HEd&HY;?c)Rje<#gSy$=*4o?NwXH_{y??zd&WJo5IF(WM%nVUgvc|7yBd~ z{_e}(R|(7bO0nM?0d58#+#}1~1jYP=<$Ul}wY2EUT6z$h{fU>&OR{3Mn3zG(f&}@c z9FMaUkZa2a;_*y2&Ti?hDc-3n#zZe9inejh&hwFrFC)iIh?>!}S!(xYQ)V`L)}Xyv zn$|K9^L-)@WvIqmODBaMes!WlPN;Slbwv(>n$;TAcuBi;>ZdcQ!t*Tz~t@tt->bhyM8C zH^MiUrh~~sWK$tj`@$zy2Oww$%R#e#V*8izFXFRGYst3uJvjgM<*zQ!DaZbo?2F<# z(sqOnfV?jrXZKteJ&tWh5Kbce1mO(8B3|oW;gxCU_kBN7nGzfP9L%s1qd%Jtey|dw z(+#^H)P3FjRr9QJl$@$~y!fRT@6oIe z9igqY)g>xld(!;`j2?p*Z$`Jlo0isA_&)e4Y>%EoU>C`0ti6rE{Pq<>YhNK$qkUF> z@Au;qbuPjr)=_{J=$t-%d0N${gT`V?1K^}?u#Zn({r&Z+>vPIm%pnu83P({@Oh_Rs zgbWc9xLgYBK~=@iTlWpc?3p$rY({ts;T?qc5YWEpd4MUxMrC;+#ruN+S_S(R;U<|E zeNuG3T98`i1ELh04~C>4&2L{X9hl!1klMNVFyiyDPde-lgk#_W(;ED=q{gZoB?pw? zak?juH#&T8YZ+a$+$$fcaT-^S@9f*oz4bAVbrd84_%+=1et>NvPDhhaEpNwB4!LJ@ zm}>BXgMSNs_?f_Q+#|B?5eYmZ{zs(d5efZ+T$mvjUWiGKJ3R5m!>#*gw(frczj|?p hpIzgL7#z<3O=u{Hp&KWDb^7DezdZ9yAYvn%>Oa%SLudd1 diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index 001e336..ce05889 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -1,4 +1,5 @@ import argparse +import sys import numpy as np import pandas as pd @@ -125,6 +126,19 @@ def simulate_trading(signals, initial_cash=10000, quiet=False): if args.no_color: Colors.disable() + if args.days <= 0: + print(f"{Colors.FAIL}Error: --days must be a positive integer.{Colors.ENDC}") + sys.exit(1) + if args.initial_cash <= 0: + print(f"{Colors.FAIL}Error: --initial-cash must be a positive amount.{Colors.ENDC}") + sys.exit(1) + if args.initial_price <= 0: + print(f"{Colors.FAIL}Error: --initial-price must be a positive amount.{Colors.ENDC}") + sys.exit(1) + if args.volatility < 0: + print(f"{Colors.FAIL}Error: --volatility must be non-negative.{Colors.ENDC}") + sys.exit(1) + # Simulate prices prices = simulate_bitcoin_prices(days=args.days, initial_price=args.initial_price, volatility=args.volatility) From bc95d94c34e36c653cccd006c5960dc7049215e2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:28:02 +0000 Subject: [PATCH 2/3] feat(cli): add friendly input validation for simulation parameters - Validates `days`, `initial_cash`, `initial_price`, and `volatility` arguments. - Prints color-coded error messages to stderr instead of crashing or allowing invalid simulation. - Exits with status code 1 on validation failure. - Adds `test_cli_args.py` to verify validation behavior. - Updates UX journal in `.Jules/palette.md`. Co-authored-by: EiJackGH <172181576+EiJackGH@users.noreply.github.com> --- .Jules/palette.md | 4 ++++ bitcoin_trading_simulation.py | 14 ++++++++++++++ test_cli_args.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 test_cli_args.py diff --git a/.Jules/palette.md b/.Jules/palette.md index 96bd45d..e0a5f9e 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -5,3 +5,7 @@ ## 2025-02-28 - Structured CLI Reports **Learning:** Dense numerical data in CLI output is hard to parse. Using ASCII box-drawing characters and alignment to create a "dashboard" or "invoice" style summary significantly improves readability and perceived quality. **Action:** When summarizing simulation or batch job results, always format the final report as a structured table or box rather than a list of print statements. + +## 2025-06-15 - CLI Input Hygiene +**Learning:** Default validation errors (stack traces, unhandled exceptions) break the "tool" illusion and feel amateur. Friendly, upfront validation with color-coded errors (e.g. "Error: Days must be positive") establishes trust and guides the user. +**Action:** Always validate CLI arguments explicitly before execution and use `sys.exit(1)` with a clear message. diff --git a/bitcoin_trading_simulation.py b/bitcoin_trading_simulation.py index cb07d20..d54bf40 100644 --- a/bitcoin_trading_simulation.py +++ b/bitcoin_trading_simulation.py @@ -142,6 +142,20 @@ def countdown(quiet=False): if args.no_color: Colors.disable() + # Input validation + if args.days <= 0: + print(f"{Colors.FAIL}Error: Days must be a positive integer.{Colors.ENDC}", file=sys.stderr) + sys.exit(1) + if args.initial_cash <= 0: + print(f"{Colors.FAIL}Error: Initial cash must be positive.{Colors.ENDC}", file=sys.stderr) + sys.exit(1) + if args.initial_price <= 0: + print(f"{Colors.FAIL}Error: Initial price must be positive.{Colors.ENDC}", file=sys.stderr) + sys.exit(1) + if args.volatility < 0: + print(f"{Colors.FAIL}Error: Volatility must be non-negative.{Colors.ENDC}", file=sys.stderr) + sys.exit(1) + # Simulate prices prices = simulate_bitcoin_prices(days=args.days, initial_price=args.initial_price, volatility=args.volatility) diff --git a/test_cli_args.py b/test_cli_args.py new file mode 100644 index 0000000..f7226ef --- /dev/null +++ b/test_cli_args.py @@ -0,0 +1,33 @@ +import subprocess +import sys +import pytest + +def run_simulation(args): + """Runs the simulation script with the given arguments.""" + cmd = [sys.executable, "bitcoin_trading_simulation.py"] + args + return subprocess.run(cmd, capture_output=True, text=True) + +def test_invalid_days(): + result = run_simulation(["--days", "-5"]) + assert result.returncode != 0, "Simulation should fail with negative days" + assert "days must be a positive integer" in result.stderr.lower() or "days must be a positive integer" in result.stdout.lower() + +def test_invalid_initial_cash(): + result = run_simulation(["--initial-cash", "-1000"]) + assert result.returncode != 0, "Simulation should fail with negative cash" + assert "initial cash must be positive" in result.stderr.lower() or "initial cash must be positive" in result.stdout.lower() + +def test_invalid_initial_price(): + result = run_simulation(["--initial-price", "-500"]) + assert result.returncode != 0, "Simulation should fail with negative price" + assert "initial price must be positive" in result.stderr.lower() or "initial price must be positive" in result.stdout.lower() + +def test_invalid_volatility(): + result = run_simulation(["--volatility", "-0.1"]) + assert result.returncode != 0, "Simulation should fail with negative volatility" + assert "volatility must be non-negative" in result.stderr.lower() or "volatility must be non-negative" in result.stdout.lower() + +def test_valid_run(): + # Run with quiet mode to speed up test + result = run_simulation(["--days", "10", "--quiet"]) + assert result.returncode == 0, "Valid simulation should succeed" From 223a04efddc73558f8e9bacb8bd550706825ed8b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:29:27 +0000 Subject: [PATCH 3/3] fix(ci): remove invalid Rust workflow - Deleted `.github/workflows/rust.yml` which was causing CI failures as this is a Python-only project. - The `rust.yml` workflow attempted to run `cargo build`, which failed because no `Cargo.toml` exists. - The existing `python-package.yml` workflow correctly handles testing and linting for this repository. Co-authored-by: EiJackGH <172181576+EiJackGH@users.noreply.github.com> --- .github/workflows/rust.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 9fd45e0..0000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Rust - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose