From 5951b08811be371ead28efcfed3d442d9d2bd581 Mon Sep 17 00:00:00 2001 From: Ashu Gupta Date: Sat, 4 Jul 2026 12:30:37 +0530 Subject: [PATCH] feat: add configurable SMA window for trend confirmation --- __pycache__/strategy.cpython-314.pyc | Bin 9038 -> 9162 bytes __pycache__/trader.cpython-314.pyc | Bin 6392 -> 6436 bytes app.py | 13 ++++++++--- data/trades.json | 32 ++++++++++++++++++++------- logs/trades.csv | 12 ++++++++++ strategy.py | 9 ++++---- trader.py | 3 ++- 7 files changed, 53 insertions(+), 16 deletions(-) diff --git a/__pycache__/strategy.cpython-314.pyc b/__pycache__/strategy.cpython-314.pyc index cf0e366184132b95fcb9fdbb819aaafdad0df652..da2b95e6062ff6f001433d7eb31e24d62b7b2c90 100644 GIT binary patch delta 2005 zcma)7TTC2P7(O#QJF`2?URW**3oW~BxvWrVVTBcHL%Ed}0lUOAM%Hc!w1!Fr&qA9N zZ8yePW734=RMSV(Cm)1p*Cu`FgE8@?F%6X_wgj7)_+Wf6+xXDvlmCBahf8coPqN?n z=f9ovU(Ubx2k#yCHhDcdfi<~0bZ#K@t(QCa_rXTq8glac4quU;G?JcHSCquO{C7vX zNY9*~OkbRNZ+h+`?^QbV7BJlhzf78cqzreB%T&&jBhm#aPsoIPR(>izSQu2MGyr=Q z0pTD-`uj5{Az4Bto>lkpp!0x3qZ&WzY!FgrE9Plf=h_bW05d*V8H? z)ue_{AN8YTzBm=JPc^^TiZDQR8W8dlK|$p6rKu`glY01dSERMfT%l#nHWPMXQEsjR zS9F6VN}zR+7W4h?sVy+~v^HFzQ4NCmU;vJIJM1YX#3cN85qP1q} z1o$0yXVjugw)JUnD@x0z6m8s=UTIw8je6a9nR(NELW|9K+-==13ck1OpgXEb%Kjvj zB+LJ=pQ^J%Hf6|wH#e_8l|1sK0U{)<9-$E- zhS0>*o}Zi$1F7e&-gbQgDDgVJ&dqukB>$bu4_Zdnl+sPTbos4yy(#yX@3u5>3Qdy; zL4#!zO{l{t9L`mh zbx92`=!t(&{+)Un?b5s>*r;|P*~O=WKlD8BrWh@IQP*cH@(oj9Fv_e0fQ)HYQbj&X z&{^U7A6C|=C-M6vzgc;!RvfW+3ND25lJqZsr}|Cx6*TPUKUNk2;{iHyPJ zQ(D0VjM7x$fe_HJwYLU@BZVz27T9}idV8l1URBe2MG>#zk}*DVENqH--xX#vpvGPX zh`Cq~(sKwR*MmqML%=j`C<~J_v+OvK@RmeIF_|0AsoA;AnGMzY2gxYF!>U+C8G^XiY!(P-%Ij8CRFCE@hV< z`vRLD|1$rr=2DZWm=`_n*)yPUH}^?cm-^a(9AybWxUmSS2WSxu>qo9^#jyx<2CG(I zxOOk9A>8Hb5n9xNpPdL(JYGBa_PaW-}gXgJqxH(M5z1+T&hi5 delta 1809 zcma)7O>7%Q6rNezyY|{^Z~PPI$FA2lO=~4-0&yA_h?*Zrle7tmTa8h>ZPYeB1QiNdD&S z`_Iay-Is?yRMc}3FmI@4hwoq#XY zRNR}Vl3Q=Zecx5HhTm?kT62q()?)-7;LLE2QOEJaH7y?U)pLq&_&|#p!3X00O-oTP zXx`~#Ar?A83a=CL6y3Fb)U1b)1WA$7UF6T3cDfcrE!O!4=3@;)9u^e8*?h!qt!W1A z40K1Lu0BG^rPX_k4X3Nk{8#Agw1OS4!V8 zvy{*Ks*$ipnAVf&|M2adQiG6^!f$L;-qnAmZQY#dbRj&B@C1jPTQY=5a^bDO?L^$c z5_BFz2qUy1>_*7JYsRt(eK5YD4J&txK;COtux(~^H@OJ#`0T~v4<87Qq1pXw%dBh;$a?|FaQ`}(Ekq2-x zr^(ETq(#u#zfQpe%2-Nma>-kdq2?H@H-DB9hd(?yYb~3UUVwKJ&#MJA9ELv=-7S;o znKumgY+07{JA~pP*qgi#E7tyt!Z()R!?Vv7D*RV&;jl#R5fAOyLRhvpaeM!a_>yC5 zdtz`+LU4qMv9-a8CBjINbfAyzH3eVpNO<Qwm&DTj8s8WZbTeJ$t*V31PH4hiX$w zvHevq+}xa=QiY)Cs7v#Loe!e*(L(g(*Lq9w*I8z#%eCD~5Gk_w@#D;s} z9gm`XExOHb1%)FJ730`$}=hxdI0SZM1m$bu|#eK%;osyiC+R1 zbXs2XmKM0QcxiEI@pdSD$1tw~vo5rY%;TTL|7QM1k1HOcjBt?C4#Eu>%`ks2e(ps$1s`Wd3Uf#X5FCV21ksP!$c|_*(ITQE cMfr#;55k^olD-OuvM(FF1n!wCO^X8f2btO_Y5)KL diff --git a/__pycache__/trader.cpython-314.pyc b/__pycache__/trader.cpython-314.pyc index eb3309ea73bf943216657c626feafa5d87f56914..600d64c94ef206bb653b2c3dab17649d1ba7e788 100644 GIT binary patch delta 915 zcmZuv%}*0S6rX9??S6Dizi0~)nx=@UA5dD13JKBBZ3HyHYPMi%O!<(2LF#Oi24jSn zcr_+-^V@MFPp>&B!l2Z9Aal~6wtcB9G>fFug z#VVIQ50-UET_L+9PBU8wpR^X+4fE%4vn{}EizF1YJ;)>y2-`9;1(}M>OT<~ar2%D4 z;4~!wI0H9X-_=hS)+kbEp=Y1ONoN-+3lJiN$0UBD)BG4AWvK{>d&87^_7lspDf3%_ zFdvaZROVCCIlxtE$*q8%kdyG0ki)KtB|fGc2}m7~Uu-_Gkx@-araUS-+TXeGxR5j( zdOUkdSv3XzMQ+kmr*xb^3Ab`8`rqt{IrR3_E1D`_R+0mGs2z9w&3}A!I((TBnY58& z;$U5H>Qv#R*haxD+%Yx&O<9fl*m3xar4cd+S%j1Pk$SYP4`~jdCRSK!dd8{=R;5(2 zEFWkcd{b=?TmY7Yu+ngbE98fd$Gk5zn~<}vA=fq`!C@DF?_HyLeoq^s`uc{ZQ<~$U z#Ae8A-JO~1HF`MZW}~QGLcj)6E^v4oY*oflL73#ry!8 zYOl9@!b#b4fVV=Jfl&7<@LZ%Qq9eP(pGL9@zHWw4=9`g^bb-H)_S1g;J381tk4tz( v4DW$;BH(c`?4rI0rt{fYE4|C_$F4NP3*X7n7Ao%asx+~CC`d2T`jh_yVlA&H delta 874 zcmYjQOHWfl6h1@myS=of1zLGjQ>>{X(3V66!GyLXN))^%w?>=b4b%q$ZKfqe6CU!={FEy+?W)zU0|P>w>}z}Lz&hSdj`M=5d*zC$ld?g;o`?yI?I z{TK|_WLj$q1VS}cFb({{nl6|D$~Cs*Cd%6>e@-H34xLE7^e%d|?m=k~N-|Bw1|MYE zQ*y=EAu##qLGZH@2d-N*+-4Fp=!-$c<4b8_ie52iR>0}wzqqLjV+u3z| zsvq*JF-p&e8yX;T$QoBkOKIBvt+|J}=Aj& zTCZUn_}GZ1vs5k8RJ8xg%(AHq06O$S{=i?q)Kp$=0pQ!YZ0@G*LFRtaX7YZD;zI(G z0#f+OeYj^t&@@3+sc>h$P_D}5ic=|GMw!iP9{`=) None: index = 2, ) + st.markdown("### SMA Window") + sma_window = st.selectbox( + "SMA Window", + [5,10,20,50], + index = 0, + ) + # ── Trading Mode ────────────────────────────────── st.markdown("---") st.markdown('', unsafe_allow_html=True) @@ -127,7 +134,7 @@ def main() -> None: return with st.spinner("Analyzing data..."): - signals, charts, errors, trades, stats = _analyze_cached(tuple(symbols),target_percent,stop_percent,orb_window) + signals, charts, errors, trades, stats = _analyze_cached(tuple(symbols),target_percent,stop_percent,orb_window,sma_window) frame = signals_to_frame(signals) if frame.empty: @@ -367,8 +374,8 @@ def _render_backtest_tab() -> None: @st.cache_data(ttl=CACHE_TTL_SECONDS, show_spinner=False) -def _analyze_cached(symbols: tuple[str, ...],target_percent,stop_percent,orb_window): - return analyze_symbols(list(symbols),target_percent,stop_percent,orb_window) +def _analyze_cached(symbols: tuple[str, ...],target_percent,stop_percent,orb_window,sma_window): + return analyze_symbols(list(symbols),target_percent,stop_percent,orb_window,sma_window) @st.cache_data(ttl=300, show_spinner=False) diff --git a/data/trades.json b/data/trades.json index f666850..ce672b4 100644 --- a/data/trades.json +++ b/data/trades.json @@ -167,13 +167,13 @@ "target": 196.33, "stop_loss": 202.33, "entry_timestamp": "2026-06-10T19:55:00", - "current_price": 200.34, - "status": "OPEN", - "exit_price": null, - "exit_timestamp": null, - "exit_reason": null, - "pnl": 0.0, - "pnl_percent": 0.0 + "current_price": 194.62, + "status": "CLOSED", + "exit_price": 194.62, + "exit_timestamp": "2026-07-02T19:55:00", + "exit_reason": "TARGET", + "pnl": 5.72, + "pnl_percent": 2.86 }, { "id": "4c8c72c174cb", @@ -199,7 +199,23 @@ "target": 278.45, "stop_loss": 283.83, "entry_timestamp": "2026-06-29T19:55:00", - "current_price": 281.63, + "current_price": 308.22, + "status": "CLOSED", + "exit_price": 308.22, + "exit_timestamp": "2026-07-02T19:55:00", + "exit_reason": "STOP_LOSS", + "pnl": -26.59, + "pnl_percent": -9.44 + }, + { + "id": "11a98ecb47fb", + "symbol": "AAPL", + "side": "BUY", + "entry_price": 308.22, + "target": 311.3, + "stop_loss": 306.68, + "entry_timestamp": "2026-07-02T19:55:00", + "current_price": 308.22, "status": "OPEN", "exit_price": null, "exit_timestamp": null, diff --git a/logs/trades.csv b/logs/trades.csv index 2a21ad0..471cb02 100644 --- a/logs/trades.csv +++ b/logs/trades.csv @@ -202,3 +202,15 @@ timestamp,symbol,signal,entry_price,target,stop_loss,last_price,reason 2026-06-29T19:55:00,AAPL,SELL,281.63,278.45,283.83,281.63,Price broke below opening range low with volume confirmation 2026-06-29T19:55:00,AAPL,SELL,281.63,278.17,284.33,281.63,Price broke below opening range low with volume confirmation 2026-06-29T19:55:00,AAPL,SELL,281.63,278.17,284.33,281.63,Price broke below opening range low with volume confirmation +2026-06-29T19:55:00,AAPL,SELL,281.63,276.28,283.04,281.63,Price broke below opening range low with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation +2026-07-02T19:55:00,AAPL,BUY,308.22,311.3,306.68,308.22,Price broke above opening range high with volume confirmation diff --git a/strategy.py b/strategy.py index e055504..7ebe3e7 100644 --- a/strategy.py +++ b/strategy.py @@ -26,7 +26,7 @@ class TradeSignal: reason: str -def calculate_orb_signal(symbol: str, data: pd.DataFrame, timestamp: str, target_percent: float, stop_percent: float, orb_window: int) -> TradeSignal: +def calculate_orb_signal(symbol: str, data: pd.DataFrame, timestamp: str, target_percent: float, stop_percent: float, orb_window: int,sma_window: int) -> TradeSignal: if data.empty: return _hold(symbol, timestamp, "No data available") @@ -58,7 +58,7 @@ def calculate_orb_signal(symbol: str, data: pd.DataFrame, timestamp: str, target current_volume = float(signal_candle["Volume"]) average_volume = float(clean_data["Volume"].iloc[:-1].mean()) if len(clean_data) > 1 else 0.0 volume_confirmed = average_volume > 0 and current_volume > average_volume - sma = _trend_sma(clean_data) + sma = _trend_sma(clean_data,sma_window) buy_trend = last_price > sma sell_trend = last_price < sma breakout_buffer = max(range_high * BREAKOUT_BUFFER_PERCENT, 0.01) @@ -176,11 +176,12 @@ def _opening_range_window(data: pd.DataFrame, orb_window: int) -> pd.DataFrame: return opening_range -def _trend_sma(data: pd.DataFrame) -> float: +def _trend_sma(data: pd.DataFrame, sma_window: int) -> float: close = data["Close"].dropna() + print(f"SMA Window: {sma_window} minutes") if close.empty: return 0.0 - window = min(SMA_WINDOW, len(close)) + window = min(sma_window, len(close)) return float(close.tail(window).mean()) diff --git a/trader.py b/trader.py index 01a0fe3..e6db00d 100644 --- a/trader.py +++ b/trader.py @@ -37,6 +37,7 @@ def analyze_symbols( target_percent: float, stop_percent: float, orb_window: int, + sma_window: int, ) -> tuple[list[TradeSignal], dict[str, pd.DataFrame], dict[str, str], pd.DataFrame, dict[str, float]]: configure_logging() signals: list[TradeSignal] = [] @@ -52,7 +53,7 @@ def analyze_symbols( if result.error: errors[result.symbol or symbol] = result.error - signal = calculate_orb_signal(result.symbol or symbol, result.data, timestamp, target_percent, stop_percent,orb_window) + signal = calculate_orb_signal(result.symbol or symbol, result.data, timestamp, target_percent, stop_percent,orb_window,sma_window) signals.append(signal) if not result.data.empty: