From f15c14e4d71913e23b4e42dd3deb7288b9eec6ef Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 1 Mar 2026 10:38:53 +0000 Subject: [PATCH] Final version 1 --- core/__pycache__/market_utils.cpython-311.pyc | Bin 0 -> 10084 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 365 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 1231 bytes core/market_utils.py | 204 ++++++++++++ core/templates/base.html | 70 +++-- core/templates/core/index.html | 294 +++++++++--------- core/urls.py | 7 +- core/views.py | 37 ++- static/css/custom.css | 117 ++++++- 9 files changed, 543 insertions(+), 186 deletions(-) create mode 100644 core/__pycache__/market_utils.cpython-311.pyc create mode 100644 core/market_utils.py diff --git a/core/__pycache__/market_utils.cpython-311.pyc b/core/__pycache__/market_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3c9c56920fd2b0d9498b2a48dee2029070653a1 GIT binary patch literal 10084 zcmb_CTWlNGl{3TPn-ocr6h*zP*p6-5vgC*2$nhgpEGKast4-|0t>Tm&@s1?geEH5$ zme^s<6e++4!dC7oFt<^#?5;Oe5P#^SSomW<9AH1Pi~Sf1Q+P0d00X=J@E@~jfFM7+ z=g#o$B%2m{b$RCAbI*OBd+s^s>JM(WgM#pn{yXtE`YGx^Nv0m`wZs=!Xo|W`Aqvq6 zYKGRo%#2C@vNLA*G6_r4I%A~`8rzIbFSF0s;cH4blFk`t(lz5sx@X+Uu9>bHooB|w zP`u}=x85OQdEZ_0M+`-M0zV_r6hpm4Anod3xt~W@{F~|$}UgT@X>vS$Y#E1FNoT*9QOwF;V8*26; zKOX{WH_&#?F>U1mDDOw~MJm$snDjUup*7njeo1^Gk)DmP8vANo5H<6qY$m~L>{L7^ zYNpB5vSx|Tr_z`w)G})(G#7y;8;7j(M7ve6tqrHM;Pn| z1nvS5F=?#8C+3vK^L;6l?WzX`am?{zY#~bUxt`X2Y~s6tPRIc$P*uie?|tMCuAL}}>p$NdzrS04 z?M>N#x#GX9_%9cyRF8ipSEXq8fEwyw71g1gF`bNkyEh(>gi724S88<>eOj==_St}N4d5L$i_Ruk4a!f2KR zH_Imk&4EB1#fjX3y%R+E!l7d_NMo{0?Uil$>R?eyZUNtmWwVT6^&nWs4qCI=xIQVWt?^-(~tXXVakRTx$WNXf$y^XvB z`g6=NaCq!+DNRq~12E{vgi9{L`C+-GxS-jt@q7lwlL96@0%%h@3s?M&1s?N{{|7?E zjrTwa9tEITI6+*_@R|ware*=TnT12gq%xW%n~Go0LP3I0Y33Y{({LEEE*DKgHVMKp zz8FWm=Fp3xiTE`>;?$+1E=l12B?ni~+(tXKgQ*{0%_l-bLYzp{Xp$3gd`Y9@nmd-o zm`{lYspd$5no>*KaQa3RdKEMm=3@;Sf$Rg^y!Bv+VkZdx*;>1~P^%zlDIhB#9EX*I zYt`fb&CU02Ry@OsXSm3!PEYZq>K|Nv?xDy3z%#t%87>DZo;`|Z4;1=>tMhAv4}4E= z`JVo#SjD$rA@Ok0@(84fy}YN=8&P^AMfbL+d-YVs^OWLws>p5+46VOhMjKP*E0uvG z%D@rX7JlUGd0!|UyY=(6pO;^|b+h8ztN8X-DUf7wYIsMPE>Hg6 zygpcbMRg69l8S3&V@7sOKvY~4_a-YY5dS1s(cwe??g##TTmF3;+`Zkhe_zG_yyAbp zXnO?1I0p@=uE4gpd(ABmja9sd6z?ImyRXFk_8>^UK_dAEiR2q}z#zZuqI&yF$I3g` zpSyK)?Pk%X!eWfbo>AF0s>?#qCoy69NEgv#Qe2XcMm0w?noOf?f}~y1==Cg@sFj!m z5#wip9+S%uj{wl@Q*dX#h@oneEiq9N@NrUXO(70rZW*70+{cvOtG>^eY`zYNZYJCT zP-y&B8O9v0QUFSV{@K{2e>#E9Ztg9;u>NY5fy%Yb__Nc7zf`TEUw%|}r6lSXxD*`#05-=_!Wuk3*}dH^|Qp2h>vHy9e0 z8}SsVWSZ^bg~_PC6ncs*2M94MPA3xa)V#(fc`g+()D?UYs^Uuk>N>a~0sI_7z=DfF zALSN#%+2!xehbLX5Yo2+z^M=QmtI=SKM0L%g+?}~-zkbVmx6R1ppGXh}QrtF7Yimf5cFt%+?%t)icW=;J?#RZovinGdJ*qzYN?RGT<=(&XlI%WOVaMUlsk03Xsu3D& z^WT6XY=fA2CQr>%pbk=?7+uUW-)h)(9R0jWGPRg`aL9kv!a&g0K%NETj4Z#!~zZVQe-l#8Y77jbT9;T}mc8=`o)Lb5MXP$+23xQD+(&cuWQ! zu@Uo)M;Q(aJl4$mSF-{);y7q97A}+FQ%Ez5aWTQ;T~I?~fknYpBJg6wteN$(Y4!!4 zLm;D|iYbf1^FYwR*3itdK(Q`*c$G6#&I=ymuBL=YFKhqejTHc>U3+?K-Gs*Upb z3+Gf%ph{WHK^+uLRmN-&Zu5hcyN8794q5;ObPEN#^b0CR?Vt6 z{J=ZBdGWF>1IB4J0|){z>CR06*GlLe2IgU3x9@nr)h&=fpUqtkb}1m`k)xpU6;R`+PyxtMk@| z2AOF?F#ZUN;R>XeTIAQ)wv!1iIg7Z{xm_HH(B&K-6@Z!INdTiKLGp14;WQ)+q69P{ z5N<9|8@`RhcLN1OS0aXi=^DRG+#euJ1wLZNWKNpB(HOW&(g~2T;xdkt5{r<=B9DNO z>?f%xWD}yG(F-_F5N5+p?_g=)6NALR1Zoa`!c71mYP-CJmsEF8m9m)+>Yzw(`}<3i zw@$2`03KHShl@7V>0jmEI|(XyV5oSucvf`>OZ0nJidSm+hk-#r+C0DMdatWQmnO<@ z$e{xj+lXQtk@dLk^sVkK{i5`XO;L89tT<09&XY2G($KqzBx>6sg3uZCmoS}nCx_Hw zi4q(9``olwVAu0Z-h?P%Mdlp|Y}FR`ZQd-Bg{y^D%daV9J8j4I!;!bN+M-|)w%pqt zlvc?inQBMIjOe@#j*UI#(Xsjnuo;ex4GxVx#UeALEO|Q|5{qO-)(=?823D&L*^vV| z*GkHZn(n=cEEZ$ zCCAlNDiq7>R9YPKFwUsGGkyToROl3b|2nMDKb4*+<$ zZLQmATU!5Ci#-+i_WB*Oad(Vr@Glru3+-Pus+Rg4qw4Ay)zDussutS6YE&)tJ4WS^ zx+G5vUwd`FW`UQzgqJ0U% zT}lc}b@2iDnq2)vubUH(L9rv;y(QwpJxbrx>KgVUO|W&vncAsdSk2fnpt0D#kp zIG`|IgeJ)a7E2TFz;ezsdgN){-pF-b6mdE=KU}Mkb1ueb!Pz7p#Bia37dejbJEXEX zmQJLxe*5MeB6yk(CP7m~kI9W)LnPw!3*uvPIpiF%_BQQZ)0yCun-bv7&cmttd^7BBX2Q0z|X92WLhUldFWcEO!Hkqy~u+Mp#T-)#-XaXlJ z^#P0MbrVd=LliaqJ`jW3;VOJ|4$@U2tSfFTUMJWB0FBOQbV4BOMYMgxQS~9@zV|%; z6^I4mnuU3WGe-Qk8=qa5U%x27@n(5)lfM7dz1+Q=-1iEJbrM6p(Oq$@=H?dXo4TTA zB~~<-NEmzU7Sow%!&Hf|#`BkjY(aWaV?{o;fFfGGgLsDE%~>$1aHQAhn@Pqr=%4t3 zF|o`wr!g4)HLd0cCazln{|#y8Hw3H_K#q^$JF}Bv8}#ssM7UkE>+BxYp8^T18!jc1 z(miZ{N9z6#fY#My`e;&^HpEV!WuANfqG;QtiD;X4=DD5!V7Bu;NPGA-RVmI0j2*yh3zf8SbSCO?kSvCt!~*G zR)hVeSCwF-=-BQX{BU+XzTq%j5|zGjrEk3G+3x8ty;8oZ3_M%unNWHriq415zS5N~ zXGC^JRQ~|ji$swo`t?9b+HxO|-3Myu0fK&LknAX)ty-v|{pyZUbtoeDk3od@XEoUO zFw`sWI(jcDdtZg9hI-4#HznCS1@SvGq*6lmo`+a2g-b`)28)(2E!4B8@6Fx6{4eRx7AwczRF1tV2k`r6OMA+p^)aPybmO|xceoNb zq6Ch}fg^vq!pXA>ay+BVUawri$`xF+Z%;gb-*P{v{NQ!@(oALIEoI^@*`Is=nNm;L zp@a`8;X|7~C498vA6NY2vVZ(f+?+h0lv7K}{BniMDO|2-`~PU;As9sqTh1ZbIizn} zZSM?QC;%JwFc>cFF5Or^xN&5w@1PPKEjrXwFDa)k$l--j-};V?ZY4ajaajpJBSXBn zQ0#(j9XwRF%L8LZRD1T7_Q^f9*pUnN!H%na`>Jkh5H<@y(f(zC^7Y?xt+@*4wtFKR zkxK8l(mO7*LG|2>9EiPt_QRd)dux*U@C%Tr>^!3Y?3+^hrYeE6O5m&v@n^BZg@@L_ z>h&#auWapAy9bI^a99-0MYC$}DfzbSec)uPp?eAXVRt_v=`Whe9QMoMF^HmMR6HFkSUh{_yRQ-;UfGT^m)q2cb(fxCq zG_6uWxgJ%@FV~|=b<6drQiF1PR4Jcak1FLV=zl5|k^ir#Ql5hT2OCQF7HkR?D%mzp z$<*-*bzGs2L!!!#)9|SBB>0@*{@g4`&?edoPiVBaG`pVA6Pxh%1Nr;L=QI2tlAiAb z4jb()Gk09znV`L!-G794A4qh9fSVp(ohtd)&VkUPhdaTLlRi}rlw)_`^$C(2;^x_V vyY6N0$L@_OLHD8$Z6=W#qpb>O1vkXVxOk`cmTEz3+TFTTZai#0PZCA9)1otK!K>Zi$ei#sJNF)uw| zue2zqxCmrk5gUkL1rh8(;ubeh2545v#NEE3K0tvF%#4hTHyC6tprRWL!WU4{2Q~&) l&IXqbp^ng==o#e~Sky1Fs9#}G|G><|&(y#Tf<-()0|C_GK6U^A delta 190 zcmaFMbeoBHIWI340}x2uotBw3kynz@W}>>PSPJVJwq?u=46A_{0@4|ySSIF38?j{M z=ce9bC}IT)Yck$q$xF;l_0wd%#hsFsn3tZfS6Y-)Tm;f}i?u8>wY<29b>jZWU@xG+ z2WCb_#v2SW7f{gy2Js81=mQ%AD`$gChfqhz47Lj_@)ud;udv8}U}oZHYTyRJA|9Y| E0F*W|`2YX_ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8d204fa83f3150a3294bfb86b56b16ef24ab87c4..f57bd5e55d1283ec3edd5fbc41db8a0c367466af 100644 GIT binary patch literal 1231 zcmZ`&O=ufe5T3U`TFLgJG*V)va?=eCY1KLsh@e6+1)13Kp@c$PXxxKY@4i?{q+Rvx zlO>x~(V+)Z3N^jB&=!0su7g`Bh0OfX3e>STowFa=Yr33YKrL|OV2RwPq? z4|1_5;f5UXV~koRWlzY7np&rPwXU;!xagK%+jOdq5!!|%qafluY9P7`F5DejVDDMK z5o!`s*vAA9he^WTNE$hzu#fX_1aycA9Ra845>`(DS4Jjq$v&QRI6AE(xWWt07EgOR zGcWX%p7T^p*(G1Yw?an@GqK*B1s)&ga2eRej%3F8LY+}r4@`GK1MQ@!?wdabXP z25PCh>RSE@%g%KHf!{4mPIxE*_;Jrfk+7PV77O9cyP6MdIVCM NycNLz8G9MS{0kDwEzAG_ literal 1364 zcmZ`(&1)M+6ra_{dS%J>I!c! z|D2kdL_iDu-_09gg#HmqK%t><`ZWm8kbw-zLiw{@%1fY!tdJefM7AQl zBhn19Sa6Ea_&F0f52+#t(zs7E+?3c%aE4Gt31lN`$VZUXaWDY%0l$fN{S4($*^jWf zAQFOZ8L?*M2p%0Y9=dd7Ae9|G&s?)vuje~ z*w|s3fmzYCyi6leblsZSXw)-0vAJ@~b$=8ZkKMM4%pxUIZ^X86P1|903-h@L+xPEo zRqQc33p$+=>?7eip%aJUJ?4psnxMCzWy{3I+3KUJ!>U=lhxIBWG+QI|iRTt| zob~hDHF3?$xOE;8-gF7B3n+Sj0BfPw>ijEJJ5jZcy3$owUa6}m>S{+#chz)jv!|tA z{CuKaZ)?{N(g*36i>>ealjojZ{Vnq})5;BG^zo;?`IUZhwUbPDlj(kPrJMYslf2eV zUh6F^_AXrPFJC&Ww^QpcL!ISZcRANzT0Z=#edVjazv(R9>@M9L%*JNq0Rq;#^X5EC zoIAMEUcCCE)QPWk<7@56+9@On0Hh1B(RpB8KAS2P06puedS=OZWkMOW!1G(l6GL;A z0R;Uy__hCq=tXh$6JT$Z8#Xbj7G9^9K^1=i?=i4JSdye3inaW&hh|#-_ZofP9$!6l Xxjnvm=u&%p4P;gNa)91vejfh;ew pd.DataFrame: + try: + # Using period="1y" and auto_adjust=True + data = yf.download(self.symbol, period="1y", interval="1d", progress=False) + if data.empty: + return None + data["returns"] = data["Close"].pct_change() + return data.dropna() + except Exception: + return None + + def build_states(self, data: pd.DataFrame) -> pd.DataFrame: + data["state"] = pd.qcut( + data["returns"], + self.bins, + labels=False, + duplicates="drop" + ) + return data + + def prob_matrix(self) -> Tuple[np.ndarray, int]: + data = self.fetch_data() + if data is None: + return None, None + + data = self.build_states(data) + states = data["state"].astype(int).values + + unique_states = np.unique(states) + bins = len(unique_states) + transition_matrix = np.zeros((bins, bins)) + + for i in range(len(states) - 1): + current_state = states[i] + next_state = states[i + 1] + transition_matrix[current_state][next_state] += 1 + + row_sums = transition_matrix.sum(axis=1, keepdims=True) + # Fix the np.divide warning by providing 'out' and making it single-line/cleaner + transition_matrix = np.divide( + transition_matrix, + row_sums, + out=np.zeros_like(transition_matrix), + where=row_sums != 0 + ) + + recent_state = int(states[-1]) + return transition_matrix, recent_state + +def add_moving_averages(data: pd.DataFrame, fast: int = 20, slow: int = 50) -> pd.DataFrame: + data["SMA_fast"] = data["Close"].rolling(window=fast).mean() + data["SMA_slow"] = data["Close"].rolling(window=slow).mean() + return data + +def add_crossover_signals(data: pd.DataFrame) -> pd.DataFrame: + data["signal"] = 0 + data.loc[data["SMA_fast"] > data["SMA_slow"], "signal"] = 1 + data["position_change"] = data["signal"].diff() + + data["event"] = "" + data.loc[data["position_change"] == 1, "event"] = "Bullish Crossover" + data.loc[data["position_change"] == -1, "event"] = "Bearish Crossover" + return data + +def fetch_news() -> List[str]: + feeds = [ + "https://feeds.bbci.co.uk/news/business/rss.xml", + "https://feeds.reuters.com/reuters/businessNews", + ] + headlines = [] + for url in feeds: + try: + feed = feedparser.parse(url) + for entry in feed.entries[:10]: # reduced to 10 per feed for speed + headlines.append(entry.title) + except Exception: + continue + return list(set(headlines)) + +def analyze_sentiment(headlines: List[str]) -> List[Tuple[str, float, str]]: + results = [] + for h in headlines: + polarity = TextBlob(h).sentiment.polarity + if polarity > 0: + label = "Positive" + elif polarity < 0: + label = "Negative" + else: + label = "Neutral" + results.append((h, polarity, label)) + return sorted(results, key=lambda x: x[1], reverse=True) + +def get_market_analysis(symbol: str) -> Dict[str, Any]: + # Markov Analysis + mc = MarkovChain(symbol) + matrix, recent_state = mc.prob_matrix() + markov_data = None + if matrix is not None: + next_probs = matrix[recent_state] + predicted_state = int(np.argmax(next_probs)) + probability = float(next_probs[predicted_state]) + + if predicted_state > recent_state: + bias = "Bullish" + color = "success" + elif predicted_state < recent_state: + bias = "Bearish" + color = "danger" + else: + bias = "Neutral" + color = "secondary" + + markov_data = { + "current_state": recent_state, + "predicted_state": predicted_state, + "probability": f"{probability:.2%}", + "bias": bias, + "color": color, + "state_labels": [ + {"id": 0, "label": "Strong Bearish", "color": "vibrant-red"}, + {"id": 1, "label": "Bearish", "color": "text-muted"}, + {"id": 2, "label": "Neutral", "color": "text-light"}, + {"id": 3, "label": "Bullish", "color": "cyber-blue"}, + {"id": 4, "label": "Strong Bullish", "color": "neon-green"} + ] + } + + # Technical Analysis + data = yf.download(symbol, period="6mo", interval="1d", progress=False) + tech_data = None + if not data.empty: + data = add_moving_averages(data) + data = add_crossover_signals(data) + latest = data.iloc[-1] + + # In newer yfinance versions, 'Close' can be a series if it's a MultiIndex (even with 1 symbol) + close_val = float(latest['Close'].iloc[0]) if hasattr(latest['Close'], 'iloc') else float(latest['Close']) + sma_fast = float(latest['SMA_fast'].iloc[0]) if hasattr(latest['SMA_fast'], 'iloc') else float(latest['SMA_fast']) + sma_slow = float(latest['SMA_slow'].iloc[0]) if hasattr(latest['SMA_slow'], 'iloc') else float(latest['SMA_slow']) + + if sma_fast > sma_slow: + trend = "Bullish" + color = "success" + else: + trend = "Bearish" + color = "danger" + + last_event = data[data["event"] != ""] + last_event_msg = last_event.iloc[-1]["event"] if not last_event.empty else "No recent crossover" + + tech_data = { + "latest_close": f"${close_val:.2f}", + "sma_20": f"${sma_fast:.2f}", + "sma_50": f"${sma_slow:.2f}", + "trend": trend, + "color": color, + "last_event": last_event_msg + } + + # News Sentiment + headlines = fetch_news() + sentiment_results = analyze_sentiment(headlines) + avg_sentiment = float(np.mean([x[1] for x in sentiment_results])) if sentiment_results else 0.0 + + if avg_sentiment > 0: + overall = "Positive" + color = "success" + elif avg_sentiment < 0: + overall = "Negative" + color = "danger" + else: + overall = "Neutral" + color = "secondary" + + sentiment_data = { + "avg_sentiment": f"{avg_sentiment:.2f}", + "overall": overall, + "color": color, + "top_headlines": [{"title": h, "label": l, "polarity": f"{p:.2f}"} for h, p, l in sentiment_results[:5]] + } + + return { + "symbol": symbol.upper(), + "markov": markov_data, + "tech": tech_data, + "sentiment": sentiment_data + } \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..4e7b035 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,57 @@ - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}Market Intelligence Dashboard{% endblock %} + + + + + + + + + + {% load static %} + + + {% block extra_head %}{% endblock %} - - {% block content %}{% endblock %} - + - +
+ {% block content %}{% endblock %} +
+ +
+
+

© {% now 'Y' %} Market Insight Dashboard. All rights reserved.

+
+
+ + + + {% block extra_js %}{% endblock %} + + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..e897703 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,157 @@ -{% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% extends 'base.html' %} +{% load static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+
+

📊 Market Intelligence

+

Markov Chains • Moving Averages • News Sentiment

+
+ +
+
+
+ + +
+
+
+ + {% if error %} + + {% endif %} + + {% if analysis %} +
+ +
+
+

Markov Forecast

+ {% if analysis.markov %} +
+
Current State
+
{{ analysis.markov.current_state }}
+
+
+
Predicted State
+
{{ analysis.markov.predicted_state }}
+
+
+
Probability
+
{{ analysis.markov.probability }}
+
+
+
+ Market Bias: + {{ analysis.markov.bias }} +
+ + +
+
State Key:
+
+ {% for state in analysis.markov.state_labels %} +
+ {{ state.id }}: + {{ state.label }} +
+ {% endfor %} +
+
+
+ {% else %} +

Insufficient data for Markov analysis.

+ {% endif %} +
+
+ + +
+
+

Technical Trend

+ {% if analysis.tech %} +
+
Latest Close
+
{{ analysis.tech.latest_close }}
+
+
+
+
SMA 20
+
{{ analysis.tech.sma_20 }}
+
+
+
SMA 50
+
{{ analysis.tech.sma_50 }}
+
+
+
+
Last Crossover
+
{{ analysis.tech.last_event }}
+
+
+
+ Trend: + {{ analysis.tech.trend }} +
+
+ {% else %} +

Unable to fetch price data.

+ {% endif %} +
+
+ + +
+
+

News Sentiment

+ {% if analysis.sentiment %} +
+
Average Sentiment
+
{{ analysis.sentiment.avg_sentiment }}
+
+ Overall Bias: + {{ analysis.sentiment.overall }} +
+
+
+
Top Headlines
+ {% for headline in analysis.sentiment.top_headlines %} +
+
+ {{ headline.title }} + {{ headline.polarity }} +
+
+ {% endfor %} +
+ {% else %} +

Unable to fetch news sentiment.

+ {% endif %} +
+
+
+ {% endif %}
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
+ + + {% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 6299e3d..c486297 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,6 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), -] + path('', views.index, name='index'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..ce9eb1b 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,24 @@ -import os -import platform - -from django import get_version as django_version from django.shortcuts import render -from django.utils import timezone +from .market_utils import get_market_analysis +def index(request): + symbol = request.GET.get("symbol", "AAPL").strip() + analysis = None + error = None -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() + if symbol: + try: + analysis = get_market_analysis(symbol) + if not analysis.get("markov") and not analysis.get("tech"): + error = f"Unable to fetch data for symbol: {symbol}" + analysis = None + except Exception as e: + error = f"An error occurred: {str(e)}" + analysis = None context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + "analysis": analysis, + "error": error, + "symbol": symbol } - return render(request, "core/index.html", context) + return render(request, "core/index.html", context) \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..f8b22cb 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,115 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +:root { + --bg-dark: #121212; + --glass-bg: rgba(255, 255, 255, 0.05); + --glass-border: rgba(255, 255, 255, 0.1); + --cyber-blue: #00d2ff; + --neon-green: #39ff14; + --vibrant-red: #ff3131; + --text-muted: #888; + --text-light: #e0e0e0; } + +body { + background-color: var(--bg-dark); + color: var(--text-light); + font-family: 'Inter', sans-serif; + margin: 0; + padding: 0; +} + +h1, h2, h3, h4, .brand-font { + font-family: 'Montserrat', sans-serif; + font-weight: 700; +} + +.glass-card { + background: var(--glass-bg); + backdrop-filter: blur(10px); + border: 1px solid var(--glass-border); + border-radius: 16px; + padding: 24px; + height: 100%; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.glass-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); + border-color: var(--cyber-blue); +} + +.metric-value { + font-size: 2.5rem; + font-weight: 800; + margin: 10px 0; + background: linear-gradient(45deg, var(--cyber-blue), #fff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.metric-label { + color: var(--text-muted); + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +.ticker-input { + background: rgba(255, 255, 255, 0.1); + border: 2px solid var(--glass-border); + border-radius: 12px; + color: #fff; + padding: 12px 20px; + font-size: 1.2rem; + width: 300px; + transition: border-color 0.3s ease; +} + +.ticker-input:focus { + outline: none; + border-color: var(--cyber-blue); + background: rgba(255, 255, 255, 0.15); +} + +.btn-cyber { + background: linear-gradient(45deg, #00d2ff, #3a7bd5); + border: none; + border-radius: 12px; + color: #fff; + padding: 12px 30px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + transition: all 0.3s ease; +} + +.btn-cyber:hover { + box-shadow: 0 0 20px rgba(0, 210, 255, 0.4); + transform: scale(1.05); +} + +.headline-item { + padding: 12px; + border-bottom: 1px solid var(--glass-border); + font-size: 0.95rem; +} + +.headline-item:last-child { + border-bottom: none; +} + +.sentiment-badge { + padding: 4px 8px; + border-radius: 6px; + font-size: 0.75rem; + font-weight: 700; +} + +.badge-success { background: rgba(57, 255, 20, 0.2); color: var(--neon-green); border: 1px solid var(--neon-green); } +.badge-danger { background: rgba(255, 49, 49, 0.2); color: var(--vibrant-red); border: 1px solid var(--vibrant-red); } +.badge-secondary { background: rgba(136, 136, 136, 0.2); color: var(--text-muted); border: 1px solid var(--text-muted); } + +.hero-gradient { + background: radial-gradient(circle at top right, rgba(0, 210, 255, 0.1), transparent); + min-height: 100vh; +} \ No newline at end of file