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
zcmcIqYfKwimaekP#>O_7ry(|>AOtXv5FVWbLLR(BNN5t0bjKla8&?4aV?)`H#(^F0
zsHHKZ-fc45t2mR5;>_%*9d)#Ev@5k+J5n>dBTZKOr>dc4cTIkzcUBtZUwSgTE6tzX
zb8oo};gRX?nXQQXI_I8y@44rm``zRGyTOo2z!!3cUB4?Kh~J=!{Ap8#mrpbVaf@IG
zhIA7)DJN|TTvysC1<^+^iuVXc`LROkVN)?DnT+>Ho0{ohG>~U7+V=>XhUsK
wS3EJ=$}GgWNe9fGL_`a3((?`Fu_APL55Yeb`b>oIEBMy8gny+hnp+@YA7
zUB^;yjP;$ysl#hQ^V@kXo8<4zGe$B8z{@tl=uJ!mbC4}&8t+hd6|#56r@x750!qnN
z3cHsfraIWvzFJ
zE!uBmE4Q^BWvjQe{r}Ic9jt)fW(76haW~pnxu?7vN3jNuu}9NZP?O#dWR*&`?N8i|
zQ7RUwC+Vvcn@1S$D5vOnq@$w|J-U38oBN+k^4rx;|3LC{J0nt!^
zW>kP=)PQ3$Y+6QR%Ve}R9jj+D-y>wNYcnk9ta?%1<#l^GUqEHP()z|U&RV4Gkd!q^
zS+kTiP6sISm4i5y$}Q8*6!yW>MDs(~;pO-7{1|ePuww#Ba4pS^Yw~&|S_*ZfI8#U|
zRfD@r`fxw>E47SQYJdTyZ-KBX21UZE71gJDI=Xw#i&W>Ci#;OMb-80uRP~+j=@}F=
z-s(6%*gx1OQoSAhXGE%Ju)7QR-aOMSYA+5#r!)P7JyudwI^8}Wnmql7@&?DuOv9+l
z@ASGnc0cD}T%K{e&ow>gcKBUh&w-f*F@tgW9HVYFkR@sOK&qYt5_}zy1c_$_rjYXe
z%w%2xWEwfkFNB&lSy%*_PHudarD#t``B#RQXpw`~EKy^(dmPiO-7aR@?bBXn&W*}?
zyM1=f;g%>I(h!Ff!)Z~1pfsmLi5?{elw_eK8xm3HbUS>$Y1TjCWw=~uc|dS^h>9l9
zp>4ql+5^0l_lTsy6zomv3WD8IqAv{gB(sWweW|v5L_mqLIN1M8W&B#9RheHZ33U!G
zNz#$v=j|vN`d5PZ?T!3@==&=0;R$EjDu9I6f$eJ#Yy}{Fa!I*Jj+0B2KW$fw(28zs
z`Sf-ZSHT3ARE%Ot?N8g=WZJqwV(Obr+mYM?wx?K3-$zRsi*UjaPFhqiDiID?%lTDk^t$~{TqeFsJO(H`M%5^dC$4|-A$RkG(E
zCamhfwM-hmVaX_2AKfXLD?P)xoKPL}aygS}4;xhpN|-6gg)zo*5%ffOOdK|T@6pw;DhnX0YGjLAAtXa!YyLM
z>?hNv4TRLUNbF?h4@udy;P4QOaB-kR2fRC&|02mKv1z5ud=lw&%O_tcAW8iiNs4bJ
zDFaJVf+=ePXR*a~IowX>YjA)m#%~d*DNCmi$kzD)+|n7IU`WTNpsn~_rJi
zh4QIxo49
zR@&vG$5V0{`uYTPL^33se$|v|Cz1%GJ4U_pEN0um;N^jQtlOO)ss6hTHOjiZ*Iz%>
zZ)H7-YDpdAQp1ZATpN_Qqmal=pW)*rYG=GY@aSHT4-T5r?RA2qca4o9zYXr&N^%|0
zM}k+9a_mMt_>7813uizt&=9+uBVBJ7^3&
zI(8jTow98Or&uB1|K*0Jm;$Vc!q@f0dk9v-n0Dn#%@^4cQ?x7BCavDxWKk=M_YD-x
z9WL+UKp(!jHy%Fn&7Hon_wM}!Ixo-xbsOCYcp;!X&@vX#(w!H&XosJ!_=EC5)0o_6HPz?Cf0FuSw9&)p
z>JLy@M1C7R1n_vw>vnnROK6I1bVWd0M|Z*eQ8A+q$!(*pSs1)>$c`k~svdK>Az~n^
z#@t>9JgTsEqz8WnV!|N;biauj7l2}T7y7t9L;xQvg%Ye9e3OfM`2mNOrkb5|v3>*s
zQu7i?m#Bp9VkW55?)TaOB#8#ZYv))e3$Iefhn~|238PQK1jh8^Ew!iIRp9A|Ki_k>
z5ikhO$-AY$sgE4~=-B;Z>&C`}@rYnN63<&R9t-v-i|L3-D7FUsL*oKf__U}b>|QIX
zh3Xi9hoor_lvqH^PfIHzBWtBiP@NVi%U3$%Q;lIo@!_Fc$8H{5Y54ofk81DNMoRbt
zL+j_>N}L-J&W-RxSNW`K>l%A9w>XhoF65Rcax0eAAu`mQ)agUBKYSxub?~F5`%AHN
zvDwGN@ygg0p{gU)|H0Yiv*EJvrLa}dS0pPNKAOHi9qWiW9(TnHVyA`56KR%<;c7v@
z573D|J9I+$8@_Qw>OCqz;%E*FH+!*VMXFNV56rT*8PU;I%!lCDyeqSUY
z=o=y5Xoo@9^cB&}*l^tRctoh~5cD0;@Km4k;iX$6H%C?mBi3j~v^hE!v#l!mmhRR5
zCrw}tG=XwK)^HA&j~k>S1}>WCFYCj|O%d|!P0(>j6fT`@p(=$7H8A?%10hFxpM
z>gZX)*b4bmW5M4Q-ZS4ehYgX-(b=doW{MYoR`O}d>g<#KtM3Y}L;Sf5{E>_N#cRBM
zly^=F_9@=&<*&`G&t6Z=E()`Y{LB(x_!sNO?H$O
zD6SQ1I#)0M>gq49^1~P4rNj?k5qd__LWQ0wp=K(i#SeH#xP&jP6f{-8$<7Czt#pTT
z!b;xU5Yv8Qcx;F}R!jKfgTlc#`JrLH;lc}oyhvJ-mi_n6-aZ>Gi(ZOa1xrh8mXgg<%pg_6v(lmAVShIhyp4Mg7l7cGRofT!~D6AXp@
zrXBE7wFmadu^rj-q?{kXZ3xKd7}n?HTr&uL2h{cTD8LffGx%!E$M1!g1CC#^0MIG_
z!VC)`Om@sM=k|-#1nZs&WYyQF4s|^`sDZqGDMmx18ts_&&UyTS>H
zW_^AAMocGQ974e(dQ(eAeZAx}GJSq8$HF6Ejty9b=VoR&7T#Erw@$kg2m_2;6%bK!
zqD69};1L3u_4OWay%UAE1BKmEFqXzu^lqE;&&>I)O3n(bTn$QUA+ajBOQ`%7O3=@U
z9100NXHaPnmC#Sia|qo3$KS4SLmQP3Q;oL*=3W=P<2QNWA(n$!+qA>uWdBR*c~c~>
zrT8PM{HL$HAdol3FNkt@hr$a2s?xVpj0X_7Ox`it1Bi{>#E}l1r#lgVz^NtWCLZvm
zTfaZkDAcC+i4ta}fQPs84z;MbLqMd70vwqhcyh;?LJz>8o9})I@~Z|pbT^75+?(x9
zEdk~6#v>4!p`c9)e5da%?gF$~Rieu6fOoE_#W%PeUhJX@n}(=!vtxeysF!0P=A-9a
z;}dethXM$jE~89*Iocu0!*Vk~`WgKBypRNmr^ThnD~Gcmsvqed=psw8uAi$v(|xLo
zffMQp>7MSP@73R~50^eHd}MxLjv8XaKQH{u{HZx^;8BPW-8SYGL-<^HE;1aRitdk^
zV#V?1SoSZ~zta6e7hihP@x=Y)8lN@th2|=D1_6V7%(~DeFOn4jstzDSpm3`>N`OEl
zYelj?pdaGAV=jNg8Hh5p1*r3H_P258UWqUySf@lS|q$;9z_*m6chmEYxe
zOGdo`>`-VOEkRO*SAJ<|)%UWlOj7
zMb*){ILn{5W5PR|2-cV?KF1G?VuCYG2xmnv#=H5xt&+mpXdQp-3g#Z!;MPasHh&wp
zUVf&_N$4zs&a$T48|;3Tq5Hwo_m|L50$Iyh>zbmk0YL4KRy?vku&%Zwx-JS`7x~e#
z#OSOrI=gP*g1x|yT^&`gWiTosuoPu(L;}p
zJ~$eieQ;bbwF2x&7F(mej|LtL#1#+T6pD}XR3U<+xhrP`9UbWsGOL1JUz+xWTO$_*
zb8W(WP%s}{GdFFDo+ZpD1oMeia?RYCF!u`P-Zk^7HB{<6p%?vJp7r7mG<6fBKv
zmga<|O|Z1Zp?EA|IVo69uI8*+x)PQ?!P2*8>0c{4%>!u9+s9WP7V-|m8gff`x=F}w
z3J$=u4B7AtB>Zf1artVn_bW;p>I}^zFdFiP$L>wto(f-$?u(8^-v;n2yWY&+b*e9^
zw+4H@1pZ4OTwT5zdNbU6Z{YSo_*hgK?TMAedSZuT#eD72cyoOIvm2k@h)?sGXV$5+
zJNM{~9*H%_=0Cad_(lvEyVt3nXSIz;t1k(q>KgfVCZWm>eJ@fLITOq0_Z{Nt!|T+M
zFR3g(yDHiky%Nvo>yGo*6YJE;XZx(tb3Zw{a_Cn3kJ@9spA0-6fF&1~MjC%?zPT^D
zFE{|*LVpo~cS+RoSQ+d6M;(}GLkC~lxe60J_l({bnf-}oMSV;EBYkx2(bR(}cn}p9
zhtK_JV%Znz3Z6p!=fln!%AcN!4o8m1a(Gj7?D9Hw0%>r&Ms`@=S0Lqkk@m=e
zb*c$Tyup_?@OuxYYKuJG$d@*)Q!U>{AfH_xaq!h`@fN=7Bwx|7PIW#jt%*85FKvKL
zUHsu9wM!9$yKbN*2(Oj?WABFFT{n!9J|V(nw#VHQMpO8LM48a6Ink>$~}~p4E5P
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