benjamin5607 commited on
Commit
2cad49b
ยท
verified ยท
1 Parent(s): cd84d2b

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +83 -56
streamlit_app.py CHANGED
@@ -1,78 +1,105 @@
1
  import streamlit as st
2
  import yfinance as yf
3
  import FinanceDataReader as fdr
 
4
 
5
  # 1. ํŽ˜์ด์ง€ ์„ค์ •
6
  st.set_page_config(page_title="Pocket Quant", page_icon="๐Ÿ“ˆ", layout="centered")
 
 
7
 
8
- st.title("๐Ÿค– AI ์ฃผ์‹ ์ž๋ฌธ๊ฐ€")
9
- st.caption("์ด์ œ ํ•œ๊ธ€๋กœ ๊ฒ€์ƒ‰ํ•ด๋„ ๋‹ค ์•Œ์•„๋“ฃ์Šต๋‹ˆ๋‹ค!")
 
 
 
 
 
10
 
11
- # 2. [ํ•ต์‹ฌ] ํ•œ๊ตญ ์ฃผ์‹ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ (์บ์‹ฑ: ํ•œ ๋ฒˆ๋งŒ ๋กœ๋”ฉํ•ด์„œ ์†๋„ ๋น ๋ฆ„)
12
  @st.cache_data
13
  def get_stock_list():
14
- # KRX ์ „์ฒด ๋ฆฌ์ŠคํŠธ ๋‹ค์šด๋กœ๋“œ (์•ฝ 2~3์ดˆ ์†Œ์š”)
15
- df = fdr.StockListing('KRX')
16
- return df
17
 
18
- # ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์ค‘ ํ‘œ์‹œ
19
- with st.spinner('ํ•œ๊ตญ ์ฃผ์‹ "์ „ํ™”๋ฒˆํ˜ธ๋ถ€"๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...'):
20
  df_krx = get_stock_list()
21
 
22
- # 3. ๋˜‘๋˜‘ํ•œ ๊ฒ€์ƒ‰ ํ•จ์ˆ˜ (ํ•œ๊ธ€ -> ํ‹ฐ์ปค ๋ณ€ํ™˜)
23
  def search_ticker(input_text):
24
- # 3-1. ์ž…๋ ฅ๊ฐ’์ด ์˜์–ด/์ˆซ์ž๋ฉด ๊ทธ๋ƒฅ ํ‹ฐ์ปค๋กœ ๊ฐ„์ฃผ (๋ฏธ๊ตญ ์ฃผ์‹ ๋“ฑ)
25
- if input_text.isascii():
26
- return input_text.upper()
27
 
28
- # 3-2. ํ•œ๊ธ€์ด๋ฉด KRX ๋ฆฌ์ŠคํŠธ์—์„œ ๊ฒ€์ƒ‰
29
- else:
30
- # ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ์ข…๋ชฉ ์ฐพ๊ธฐ
31
- res = df_krx[df_krx['Name'] == input_text]
32
-
33
- # ์—†์œผ๋ฉด ํฌํ•จํ•˜๋Š” ์ข…๋ชฉ ์ฐพ๊ธฐ (์˜ˆ: "์‚ผ์„ฑ" -> ์‚ผ์„ฑ์ „์ž, ์‚ผ์„ฑ์ƒ๋ช…...)
34
- if res.empty:
35
- return None
36
-
37
- # ์ฐพ์•˜์œผ๋ฉด ํ‹ฐ์ปค ์ฝ”๋“œ ๋ณ€ํ™˜
38
- code = res.iloc[0]['Code']
39
- market = res.iloc[0]['Market']
40
-
41
- # ์•ผํ›„ ํŒŒ์ด๋‚ธ์Šค์šฉ ์ ‘๋ฏธ์‚ฌ ๋ถ™์ด๊ธฐ (.KS ์ฝ”์Šคํ”ผ, .KQ ์ฝ”์Šค๋‹ฅ)
42
- if market == 'KOSPI':
43
- return f"{code}.KS"
44
- elif market == 'KOSDAQ':
45
- return f"{code}.KQ"
46
- else:
47
- return code # ๊ธฐํƒ€
48
 
49
- # 4. ์‚ฌ์šฉ์ž ์ž…๋ ฅ
50
- user_input = st.text_input("์ข…๋ชฉ๋ช… ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”", "์‚ผ์„ฑ์ „์ž")
51
 
52
- # 5. ๋ถ„์„ ์‹œ์ž‘
53
- if st.button("๐Ÿ” ๋ถ„์„ ์‹คํ–‰", use_container_width=True):
54
-
55
- # ํ•œ๊ธ€ ์ด๋ฆ„์„ ํ‹ฐ์ปค๋กœ ๋ณ€ํ™˜!
56
  ticker = search_ticker(user_input)
57
 
58
  if ticker:
59
- st.success(f"'{user_input}'์„(๋ฅผ) ์ฐพ์•˜์Šต๋‹ˆ๋‹ค! ๐Ÿ‘‰ ์ฝ”๋“œ: {ticker}")
60
-
61
- try:
62
- stock = yf.Ticker(ticker)
63
- hist = stock.history(period="1mo")
64
-
65
- if not hist.empty:
66
- curr = hist['Close'].iloc[-1]
67
- prev = hist['Close'].iloc[-2]
68
- pct = ((curr - prev) / prev) * 100
69
 
70
- st.metric(label="ํ˜„์žฌ ์ฃผ๊ฐ€", value=f"{curr:,.0f}์›", delta=f"{pct:.2f}%")
71
- st.line_chart(hist['Close'])
72
- else:
73
- st.error("๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ƒ์žฅ ํ์ง€๋˜์—ˆ๊ฑฐ๋‚˜ ์ฝ”๋“œ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
74
-
75
- except Exception as e:
76
- st.error(f"์—๋Ÿฌ ๋ฐœ์ƒ: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  else:
78
- st.error(f"โŒ '{user_input}'๋ผ๋Š” ์ข…๋ชฉ์„ ํ•œ๊ตญ ์‹œ์žฅ์—์„œ ๋ชป ์ฐพ๊ฒ ์–ด์š”. ์ •ํ™•ํ•œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
 
1
  import streamlit as st
2
  import yfinance as yf
3
  import FinanceDataReader as fdr
4
+ from huggingface_hub import InferenceClient
5
 
6
  # 1. ํŽ˜์ด์ง€ ์„ค์ •
7
  st.set_page_config(page_title="Pocket Quant", page_icon="๐Ÿ“ˆ", layout="centered")
8
+ st.title("๐Ÿ‡จ๐Ÿ‡ณ Qwen ํˆฌ์ž ์ž๋ฌธ๊ฐ€")
9
+ st.caption("Alibaba Qwen2.5-72B ๋ชจ๋ธ์ด ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. (Zero Cost)")
10
 
11
+ # 2. ํ—ˆ๊น…ํŽ˜์ด์Šค ํ† ํฐ & ํด๋ผ์ด์–ธํŠธ ์„ค์ •
12
+ # Secrets์— HF_TOKEN์ด ์—†์œผ๋ฉด ์—๋Ÿฌ ๋ฐฉ์ง€
13
+ if "HF_TOKEN" in st.secrets:
14
+ client = InferenceClient(api_key=st.secrets["HF_TOKEN"])
15
+ else:
16
+ st.error("๐Ÿšจ ์„ค์ • ์˜ค๋ฅ˜: Settings > Secrets์— 'HF_TOKEN'์„ ๋„ฃ์–ด์ฃผ์„ธ์š”!")
17
+ st.stop()
18
 
19
+ # 3. ์ฃผ์‹ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ
20
  @st.cache_data
21
  def get_stock_list():
22
+ return fdr.StockListing('KRX')
 
 
23
 
24
+ with st.spinner('์ „ํ™”๋ฒˆํ˜ธ๋ถ€ ๋’ค์ง€๋Š” ์ค‘...'):
 
25
  df_krx = get_stock_list()
26
 
27
+ # 4. ์ข…๋ชฉ ๊ฒ€์ƒ‰ ํ•จ์ˆ˜
28
  def search_ticker(input_text):
29
+ if input_text.isascii(): return input_text.upper()
 
 
30
 
31
+ res = df_krx[df_krx['Name'] == input_text]
32
+ if res.empty: return None
33
+
34
+ code = res.iloc[0]['Code']
35
+ market = res.iloc[0]['Market']
36
+ return f"{code}.KS" if market == 'KOSPI' else f"{code}.KQ"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ # 5. UI ๋ฐ ์‹คํ–‰
39
+ user_input = st.text_input("์ข…๋ชฉ๋ช… (์˜ˆ: ์‚ผ์„ฑ์ „์ž, ์นด์นด์˜ค, TSLA)", "์‚ผ์„ฑ์ „์ž")
40
 
41
+ if st.button("๐Ÿš€ Qwen์—๊ฒŒ ๋ฌผ์–ด๋ณด๊ธฐ", use_container_width=True):
 
 
 
42
  ticker = search_ticker(user_input)
43
 
44
  if ticker:
45
+ with st.spinner(f"๐Ÿ“Š 1๋‹จ๊ณ„: '{ticker}' ์‹œ์„ธ ์กฐํšŒ ์ค‘..."):
46
+ try:
47
+ # ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
48
+ stock = yf.Ticker(ticker)
49
+ hist = stock.history(period="1mo")
50
+ info = stock.info
 
 
 
 
51
 
52
+ if not hist.empty:
53
+ # ๊ธฐ๋ณธ ์ฐจํŠธ ํ‘œ์‹œ
54
+ curr = hist['Close'].iloc[-1]
55
+ start = hist['Close'].iloc[0]
56
+ change = ((curr - start) / start) * 100
57
+
58
+ st.metric("ํ˜„์žฌ ์ฃผ๊ฐ€", f"{curr:,.0f}์›", f"{change:.2f}% (1๋‹ฌ)")
59
+ st.line_chart(hist['Close'])
60
+
61
+ # --- Qwen AI ํŒŒํŠธ ---
62
+ with st.spinner("๐Ÿ‰ 2๋‹จ๊ณ„: ํ์›ฌ(Qwen)์ด ์ฐจํŠธ๋ฅผ ํ•ด์„ํ•˜๋Š” ์ค‘..."):
63
+
64
+ # ํ”„๋กฌํ”„ํŠธ ์ž‘์„ฑ
65
+ prompt = f"""
66
+ ๋‹น์‹ ์€ ๋ƒ‰์ฒ ํ•œ ์ฃผ์‹ ํˆฌ์ž ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•ด์„œ ํ•œ๊ตญ์–ด๋กœ ๋ณด๊ณ ์„œ๋ฅผ ์จ์ฃผ์„ธ์š”.
67
+
68
+ [์ข…๋ชฉ ๋ฐ์ดํ„ฐ]
69
+ - ์ข…๋ชฉ๋ช…: {user_input} ({ticker})
70
+ - ํ˜„์žฌ๊ฐ€: {curr:,.0f}
71
+ - 1๋‹ฌ ๋ณ€๋™๋ฅ : {change:.2f}%
72
+ - 52์ฃผ ์ตœ๊ณ ๊ฐ€: {info.get('fiftyTwoWeekHigh', 'N/A')}
73
+ - 52์ฃผ ์ตœ์ €๊ฐ€: {info.get('fiftyTwoWeekLow', 'N/A')}
74
+
75
+ [๋ถ„์„ ์š”์ฒญ]
76
+ 1. ์ตœ๊ทผ 1๋‹ฌ๊ฐ„์˜ ์ถ”์„ธ๋ฅผ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์š”์•ฝํ•˜์„ธ์š”.
77
+ 2. ํ˜„์žฌ ๊ฐ€๊ฒฉ์ด ๊ณ ์  ๋Œ€๋น„ ์–ด๋–ค ์œ„์น˜์ธ์ง€ ์„ค๋ช…ํ•˜์„ธ์š”.
78
+ 3. ํˆฌ์ž ๊ด€์ (๋งค์ˆ˜/๋งค๋„/๊ด€๋ง)์„ ์†”์งํ•˜๊ฒŒ ์ œ์‹œํ•˜๊ณ  ์ด์œ ๋ฅผ ๋ง๋ถ™์ด์„ธ์š”.
79
+
80
+ ๋งํˆฌ๋Š” 'ํ•ด์š”์ฒด'๋ฅผ ์“ฐ๊ณ , ์ด๋ชจ์ง€๋ฅผ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜์„ธ์š”.
81
+ """
82
+
83
+ # Qwen ํ˜ธ์ถœ (์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ํ˜•์‹)
84
+ messages = [
85
+ {"role": "system", "content": "You are a helpful financial analyst."},
86
+ {"role": "user", "content": prompt}
87
+ ]
88
+
89
+ # ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ์ถœ๋ ฅ (ํƒ€์ž ์น˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋‚˜์˜ด)
90
+ stream = client.chat.completions.create(
91
+ model="Qwen/Qwen2.5-72B-Instruct",
92
+ messages=messages,
93
+ max_tokens=500,
94
+ stream=True
95
+ )
96
+
97
+ st.subheader("๐Ÿ‰ Qwen์˜ ๋ถ„์„ ๋ฆฌํฌํŠธ")
98
+ st.write_stream(stream)
99
+
100
+ else:
101
+ st.error("๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด์š”. ์ƒ์žฅํ์ง€ ๋๊ฑฐ๋‚˜ ํ‹ฐ์ปค๊ฐ€ ํ‹€๋ ธ์„ ์ˆ˜๋„?")
102
+ except Exception as e:
103
+ st.error(f"์—๋Ÿฌ๊ฐ€ ๋‚ฌ์Šต๋‹ˆ๋‹ค: {e}")
104
  else:
105
+ st.error(f"'{user_input}'? ๊ทธ๋Ÿฐ ์ฃผ์‹์€ ํ•œ๊ตญ์— ์—†๋Š”๋ฐ์š”.")