Smart-Trader-EA commited on
Commit
19848e6
ยท
1 Parent(s): 8b49703

Complete trading analysis system with preprocessing

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
app.py CHANGED
@@ -1,360 +1,541 @@
1
  import gradio as gr
2
  import pandas as pd
 
3
  import plotly.graph_objects as go
4
  from prophet import Prophet
5
  import os
6
- import numpy as np
7
- from datetime import datetime
 
 
8
 
9
- # ่ฎพ็ฝฎ็Žฏๅขƒๅ˜้‡๏ผˆไผ˜ๅŒ–M1่Šฏ็‰‡ๆ€ง่ƒฝ๏ผ‰
10
  os.environ["OMP_NUM_THREADS"] = "1"
11
  os.environ["OPENBLAS_NUM_THREADS"] = "1"
12
  os.environ["MKL_NUM_THREADS"] = "1"
13
 
14
- # ้ข„ๅŠ ่ฝฝๆ‰€ๆœ‰ๆ•ฐๆฎๆ–‡ไปถ
15
- DATA_DIR = "data"
16
- available_tickers = {}
 
17
 
18
- # ๆฃ€ๆŸฅdata็›ฎๅฝ•ๆ˜ฏๅฆๅญ˜ๅœจ
19
- if os.path.exists(DATA_DIR):
20
- for filename in os.listdir(DATA_DIR):
21
- if filename.endswith(".csv"):
22
- ticker_name = filename.replace(".csv", "").upper()
23
- file_path = os.path.join(DATA_DIR, filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  try:
25
- # ๅฐ่ฏ•ไธๅŒ็ผ–็ ่ฏปๅ–
26
- encodings = ['utf-8', 'gbk', 'latin1', 'ISO-8859-1']
27
- df = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- for encoding in encodings:
 
30
  try:
31
- df = pd.read_csv(file_path, encoding=encoding)
32
- print(f"ๆˆๅŠŸไฝฟ็”จ {encoding} ็ผ–็ ๅŠ ่ฝฝ {filename}")
33
- break
34
- except UnicodeDecodeError:
35
  continue
36
-
37
- if df is None:
38
- raise Exception("ๆ— ๆณ•่งฃ็ ๆ–‡ไปถ")
39
-
40
- # ่‡ชๅŠจๆฃ€ๆต‹ๆ—ฅๆœŸๅˆ—
41
- date_col = None
42
- for col in df.columns:
43
- if 'date' in col.lower() or 'time' in col.lower():
44
- date_col = col
45
- break
46
-
47
- # ๅฆ‚ๆžœๆฒกๆœ‰ๆ‰พๅˆฐๆ—ฅๆœŸๅˆ—๏ผŒๅฐ่ฏ•็ฌฌไธ€ๅˆ—
48
- if date_col is None and len(df.columns) > 0:
49
- date_col = df.columns[0]
50
-
51
- # ่ฝฌๆขๆ—ฅๆœŸๅˆ—
52
- if date_col:
53
- df[date_col] = pd.to_datetime(df[date_col], errors='coerce')
54
- df = df.dropna(subset=[date_col])
55
- df.set_index(date_col, inplace=True)
56
-
57
- # ๆฃ€ๆŸฅๅฟ…้œ€็š„ๅˆ—
58
- required_columns = ['Open', 'High', 'Low', 'Close']
59
- found_columns = [col for col in required_columns if col in df.columns]
60
-
61
- if len(found_columns) < 3: # ่‡ณๅฐ‘้œ€่ฆ3ๅˆ—
62
- print(f"่ญฆๅ‘Š: {filename} ็ผบๅฐ‘ๅฟ…่ฆๅˆ—๏ผŒๅฐ†่ทณ่ฟ‡")
63
- continue
64
-
65
- available_tickers[ticker_name] = {
66
- "file": file_path,
67
- "data": df
68
- }
69
- print(f"โœ… ๆˆๅŠŸๅŠ ่ฝฝ: {ticker_name} ({len(df)} ๆก่ฎฐๅฝ•)")
70
- except Exception as e:
71
- print(f"โŒ ๅŠ ่ฝฝๅคฑ่ดฅ {filename}: {str(e)}")
72
- else:
73
- print(f"โš ๏ธ ่ญฆๅ‘Š: ๆ•ฐๆฎ็›ฎๅฝ•ไธๅญ˜ๅœจ - {DATA_DIR}")
74
 
75
- def analyze_stock(ticker):
76
- """ๅˆ†ๆž่‚ก็ฅจ/ๅค–ๆฑ‡ๆ•ฐๆฎ"""
77
  try:
78
- # ๆฃ€ๆŸฅๆ˜ฏๅฆๆœ‰ๅฏ็”จๆ•ฐๆฎ
79
- if not available_tickers:
80
- return "โŒ ้”™่ฏฏ: ๆฒกๆœ‰ๆ‰พๅˆฐไปปไฝ•ๆ•ฐๆฎๆ–‡ไปถใ€‚่ฏทๆฃ€ๆŸฅdata/็›ฎๅฝ•", None, None
81
-
82
- # ๆจก็ณŠๅŒน้…่‚ก็ฅจไปฃ็ 
83
- ticker_upper = ticker.upper()
84
- matched_ticker = None
85
 
86
- # ๅฐ่ฏ•็ฒพ็กฎๅŒน้…
87
- if ticker_upper in available_tickers:
88
- matched_ticker = ticker_upper
89
- else:
90
- # ๅฐ่ฏ•้ƒจๅˆ†ๅŒน้…
91
- for name in available_tickers.keys():
92
- if ticker_upper in name or name in ticker_upper:
93
- matched_ticker = name
94
  break
 
 
 
 
 
 
 
 
 
 
95
 
96
- if not matched_ticker:
97
- return (f"โŒ ๆœชๆ‰พๅˆฐๅŒน้…็š„ๆ•ฐๆฎ: {ticker}\n"
98
- f"ๅฏ็”จๆ•ฐๆฎ: {', '.join(available_tickers.keys())}"), None, None
99
-
100
- # ่Žทๅ–ๆ•ฐๆฎ
101
- hist = available_tickers[matched_ticker]["data"].copy()
102
 
103
- # ็กฎไฟๅฟ…่ฆ็š„ๅˆ—ๅญ˜ๅœจ
104
  required_cols = ['Open', 'High', 'Low', 'Close']
105
  missing_cols = [col for col in required_cols if col not in hist.columns]
106
 
107
  if missing_cols:
108
- # ๅฐ่ฏ•ๆŸฅๆ‰พ็›ธไผผๅˆ—ๅ
109
- col_mapping = {}
110
- for col in missing_cols:
111
- for existing_col in hist.columns:
112
- if col.lower() in existing_col.lower() or existing_col.lower() in col.lower():
113
- col_mapping[existing_col] = col
114
-
115
- if col_mapping:
116
- hist.rename(columns=col_mapping, inplace=True)
117
- missing_cols = [col for col in required_cols if col not in hist.columns]
118
-
119
- if missing_cols:
120
- return (f"โŒ ๆ•ฐๆฎๆ ผๅผ้”™่ฏฏ: ็ผบๅฐ‘ๅฟ…่ฆๅˆ—: {', '.join(missing_cols)}\n"
121
- f"ๅฏ็”จๅˆ—: {', '.join(hist.columns)}"), None, None
122
 
123
- # ๅˆ›ๅปบK็บฟๅ›พ
124
- fig = go.Figure(data=[go.Candlestick(
125
  x=hist.index,
126
  open=hist['Open'],
127
  high=hist['High'],
128
  low=hist['Low'],
129
  close=hist['Close'],
130
- name='ไปทๆ ผ'
131
- )])
132
 
133
- # ๆทปๅŠ ็งปๅŠจๅนณๅ‡็บฟ
134
  if len(hist) >= 20:
135
  hist['MA20'] = hist['Close'].rolling(window=20, min_periods=1).mean()
136
  fig.add_trace(go.Scatter(
137
- x=hist.index,
138
- y=hist['MA20'],
139
- mode='lines',
140
- name='20ๆ—ฅๅ‡็บฟ',
141
  line=dict(color='blue', width=1.5)
142
  ))
143
 
144
  if len(hist) >= 50:
145
  hist['MA50'] = hist['Close'].rolling(window=50, min_periods=1).mean()
146
  fig.add_trace(go.Scatter(
147
- x=hist.index,
148
- y=hist['MA50'],
149
- mode='lines',
150
- name='50ๆ—ฅๅ‡็บฟ',
151
  line=dict(color='orange', width=1.5)
152
  ))
153
 
 
154
  fig.update_layout(
155
- title=f"{matched_ticker} ไปทๆ ผ่ตฐๅŠฟ (ๆœฌๅœฐๆ•ฐๆฎ)",
156
- xaxis_title="ๆ—ฅๆœŸ",
157
- yaxis_title="ไปทๆ ผ",
158
  template="plotly_white",
159
  hovermode="x unified",
160
- height=500
 
161
  )
162
 
163
- # ้ข„ๆต‹ - ไฝฟ็”จProphet (ไฟฎๅค็‰ˆๆœฌ)
 
 
 
164
  try:
165
- # ๅ‡†ๅค‡ๆ•ฐๆฎ
166
- df = hist[['Close']].reset_index()
167
- df.columns = ['ds', 'y']
168
-
169
- # ็งป้™คNaNๅ€ผ
170
- df = df.dropna()
171
-
172
- # ็กฎไฟๆœ‰่ถณๅคŸๆ•ฐๆฎ
173
- if len(df) < 30:
174
- raise ValueError("ๆ•ฐๆฎ็‚นไธ่ถณ๏ผŒๆ— ๆณ•่ฟ›่กŒ้ข„ๆต‹")
175
-
176
- # ๅˆ›ๅปบๅนถๆ‹Ÿๅˆๆจกๅž‹ (ไฟฎๅค: ็งป้™คstan_backend)
177
- model = Prophet(
178
- daily_seasonality=True,
179
- yearly_seasonality=True,
180
- interval_width=0.95,
181
- uncertainty_samples=1000
182
- )
183
-
184
- model.fit(df)
185
-
186
- # ๅˆ›ๅปบๆœชๆฅๆ•ฐๆฎๆก† (30ๅคฉ้ข„ๆต‹)
187
- future = model.make_future_dataframe(periods=30, freq='D')
188
- forecast = model.predict(future)
189
 
190
- # ้ข„ๆต‹ๅ›พ่กจ
191
- fig2 = go.Figure()
192
-
193
- # ๅކๅฒๆ•ฐๆฎ
194
- fig2.add_trace(go.Scatter(
195
- x=df['ds'],
196
- y=df['y'],
197
- mode='lines',
198
- name='ๅކๅฒไปทๆ ผ',
199
- line=dict(color='blue', width=2)
200
- ))
201
-
202
- # ้ข„ๆต‹ๆ•ฐๆฎ
203
- fig2.add_trace(go.Scatter(
204
- x=forecast['ds'],
205
- y=forecast['yhat'],
206
- mode='lines',
207
- name='้ข„ๆต‹ไปทๆ ผ',
208
- line=dict(color='red', width=2, dash='dash')
209
- ))
210
-
211
- # ้ข„ๆต‹ๅŒบ้—ด
212
- fig2.add_trace(go.Scatter(
213
- x=forecast['ds'].tolist() + forecast['ds'][::-1].tolist(),
214
- y=forecast['yhat_upper'].tolist() + forecast['yhat_lower'][::-1].tolist(),
215
- fill='toself',
216
- fillcolor='rgba(255,0,0,0.1)',
217
- line=dict(color='rgba(255,255,255,0)'),
218
- name='็ฝฎไฟกๅŒบ้—ด'
219
- ))
220
-
221
- fig2.update_layout(
222
- title=f"{matched_ticker} 30ๅคฉไปทๆ ผ้ข„ๆต‹",
223
- xaxis_title="ๆ—ฅๆœŸ",
224
- yaxis_title="ไปทๆ ผ",
225
- template="plotly_white",
226
- hovermode="x unified",
227
- height=500
228
- )
229
-
230
- except Exception as e:
231
- print(f"้ข„ๆต‹้”™่ฏฏ: {str(e)}")
232
- fig2 = None
233
- forecast_error = str(e)
234
-
235
- # ๆŠ€ๆœฏๆŒ‡ๆ ‡่ฎก็ฎ—
236
- if 'MA20' not in hist.columns and len(hist) >= 20:
237
- hist['MA20'] = hist['Close'].rolling(20).mean()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- if 'MA50' not in hist.columns and len(hist) >= 50:
240
- hist['MA50'] = hist['Close'].rolling(50).mean()
 
241
 
 
242
  current_price = hist['Close'].iloc[-1]
243
- ma20 = hist['MA20'].iloc[-1] if 'MA20' in hist.columns else None
244
- ma50 = hist['MA50'].iloc[-1] if 'MA50' in hist.columns else None
245
 
246
- # ไฟกๅทๅˆคๆ–ญ
247
- signal = "๐Ÿ” ๆ•ฐๆฎไธ่ถณ๏ผŒๆ— ๆณ•ๅˆคๆ–ญไฟกๅท"
248
- if ma20 is not None:
249
  if current_price > ma20:
250
- signal = "๐Ÿ“ˆ ็œ‹ๆถจ (ไปทๆ ผ > 20ๆ—ฅๅ‡็บฟ)"
251
  else:
252
- signal = "๐Ÿ“‰ ็œ‹่ทŒ (ไปทๆ ผ < 20ๆ—ฅๅ‡็บฟ)"
253
 
254
- if ma20 is not None and ma50 is not None:
255
- if current_price > ma20 > ma50:
256
- signal = "๐Ÿš€ ๅผบ็ƒˆ็œ‹ๆถจ (้ป„้‡‘ไบคๅ‰)"
257
- elif current_price < ma20 < ma50:
258
- signal = "๐Ÿ’ฃ ๅผบ็ƒˆ็œ‹่ทŒ (ๆญปไบกไบคๅ‰)"
 
259
 
260
- # ่ฎก็ฎ—ๅ›žๆŠฅ็އ
261
- period_return = (current_price / hist['Close'].iloc[0] - 1) * 100
 
 
262
 
 
263
  result_text = (
264
- f"๐Ÿ“Š {matched_ticker} ๅˆ†ๆžๆŠฅๅ‘Š\n"
265
- f"๐Ÿ’ฐ ๅฝ“ๅ‰ไปทๆ ผ: {current_price:.5f}\n"
266
- f"๐Ÿ“ˆ 20ๆ—ฅๅ‡็บฟ: {ma20:.5f}\n" if ma20 is not None else ""
267
- f"๐Ÿ“‰ 50ๆ—ฅๅ‡็บฟ: {ma50:.5f}\n" if ma50 is not None else ""
268
- f"๐ŸŽฏ ไบคๆ˜“ไฟกๅท: {signal}\n"
269
- f"๐Ÿ“Š ๆ€ปๅ›žๆŠฅ็އ: {period_return:.2f}%\n"
270
- f"๐Ÿ’พ ๆ•ฐๆฎ่ฎฐๅฝ•: {len(hist)} ๆก\n"
271
- f"๐Ÿ•’ ๆ›ดๆ–ฐๆ—ถ้—ด: {datetime.now().strftime('%Y-%m-%d %H:%M')}"
272
  )
273
 
274
- return result_text, fig, fig2
275
-
 
276
  except Exception as e:
277
- error_msg = f"โŒ ๅˆ†ๆž้”™่ฏฏ: {str(e)}"
278
  print(error_msg)
 
 
279
  return error_msg, None, None
280
 
281
- def list_available_data():
282
- """ๅˆ—ๅ‡บๅฏ็”จๆ•ฐๆฎ"""
283
- if not available_tickers:
284
- return "โš ๏ธ ๆœชๆ‰พๅˆฐๆ•ฐๆฎๆ–‡ไปถใ€‚่ฏทๅฐ†CSVๆ–‡ไปถๆ”พๅ…ฅdata/็›ฎๅฝ•"
285
- return "โœ… ๅฏ็”จๆ•ฐๆฎ: " + ", ".join(available_tickers.keys())
286
-
287
- def get_app_version():
288
- """่Žทๅ–ๅบ”็”จ็‰ˆๆœฌไฟกๆฏ"""
289
- return f"๐Ÿ“ˆ ๅค–ๆฑ‡/่‚ก็ฅจAIๅˆ†ๆž็ณป็ปŸ v2.1\n" \
290
- f"๐Ÿ•’ ๆœ€ๅŽๆ›ดๆ–ฐ: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n" \
291
- f"๐Ÿ“Š ๅฏ็”จๆ•ฐๆฎ้›†: {len(available_tickers)}"
292
-
293
- # ๅˆ›ๅปบGradio็•Œ้ข
294
- with gr.Blocks(title="ๅค–ๆฑ‡AIๅˆ†ๆž็ณป็ปŸ", theme=gr.themes.Soft()) as demo:
295
- gr.Markdown("# ๐Ÿ“ˆ ๅค–ๆฑ‡/่‚ก็ฅจAIๅˆ†ๆž็ณป็ปŸ (ๆœฌๅœฐๆ•ฐๆฎ็‰ˆ)")
296
- gr.Markdown("โœ… ไผ˜ๅŠฟ: ๆ— ้œ€็ฝ‘็ปœ๏ผŒๆ•ฐๆฎ็จณๅฎš๏ผŒ้š็งๅฎ‰ๅ…จ๏ผŒ้€‚ๅˆไธญ้•ฟๆœŸๅˆ†ๆž")
297
 
298
- # ็Šถๆ€ไฟกๆฏ
299
  with gr.Row():
300
- version_info = gr.Textbox(label="็ณป็ปŸไฟกๆฏ", value=get_app_version(), interactive=False)
301
- data_status = gr.Textbox(label="ๆ•ฐๆฎ็Šถๆ€", value=list_available_data(), interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
- # ๅˆ†ๆžๅŒบๅŸŸ
304
  with gr.Row():
305
- ticker_input = gr.Textbox(label="ๆ•ฐๆฎๅ็งฐ", value="EURUSD", placeholder="่พ“ๅ…ฅๆ•ฐๆฎๆ–‡ไปถๅ๏ผŒๅฆ‚: EURUSD, AAPL")
306
- analyze_btn = gr.Button("ๅˆ†ๆž", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
- # ็ป“ๆžœๅŒบๅŸŸ
309
- signal_output = gr.Textbox(label="ๅˆ†ๆž็ป“ๆžœ", lines=6)
310
 
311
  with gr.Row():
312
- kchart = gr.Plot(label="ไปทๆ ผ่ตฐๅŠฟไธŽๆŠ€ๆœฏๆŒ‡ๆ ‡")
313
- pred_chart = gr.Plot(label="30ๅคฉไปทๆ ผ้ข„ๆต‹")
314
 
315
- # ไฝฟ็”จๆŒ‡ๅ—
316
- gr.Markdown("""
317
- ### ๐Ÿ“‹ ไฝฟ็”จๆŒ‡ๅ—
318
- 1. **่พ“ๅ…ฅๆ•ฐๆฎๅ็งฐ**๏ผšไฝฟ็”จๆ•ฐๆฎๆ–‡ไปถๅ๏ผˆไธๅซ.csvๅŽ็ผ€๏ผ‰๏ผŒไพ‹ๅฆ‚ `EURUSD`
319
- 2. **็‚นๅ‡ป"ๅˆ†ๆž"**๏ผš็ณป็ปŸๅฐ†ๅŠ ่ฝฝๆœฌๅœฐๆ•ฐๆฎๅนถ็”Ÿๆˆๅˆ†ๆžๆŠฅๅ‘Š
320
- 3. **ๆŸฅ็œ‹็ป“ๆžœ**๏ผšๅŒ…ๆ‹ฌๆŠ€ๆœฏๆŒ‡ๆ ‡ใ€ไบคๆ˜“ไฟกๅทๅ’Œไปทๆ ผ้ข„ๆต‹
321
- 4. **ๆ•ฐๆฎๆ›ดๆ–ฐ**๏ผšไธŠไผ ๆ–ฐ็š„CSVๆ–‡ไปถๅˆฐdata/็›ฎๅฝ•๏ผŒ้‡ๆ–ฐ้ƒจ็ฝฒๅบ”็”จ
322
-
323
- ### ๐Ÿ” ๆ•ฐๆฎๆ ผๅผ่ฆๆฑ‚
324
- CSVๆ–‡ไปถๅบ”ๅŒ…ๅซไปฅไธ‹ๅˆ—๏ผˆไธๅŒบๅˆ†ๅคงๅฐๅ†™๏ผ‰๏ผš
325
- - ๆ—ฅๆœŸๅˆ— (Date/Time)
326
- - ๅผ€็›˜ไปท (Open)
327
- - ๆœ€้ซ˜ไปท (High)
328
- - ๆœ€ไฝŽไปท (Low)
329
- - ๆ”ถ็›˜ไปท (Close)
330
-
331
- ### ๐Ÿ’ก ๆ็คบ
332
- - ้ฆ–ๆฌกๅŠ ่ฝฝๅฏ่ƒฝ้œ€่ฆ1-2ๅˆ†้’Ÿ
333
- - ๆ•ฐๆฎ่ถŠๅคš๏ผŒ้ข„ๆต‹่ถŠๅ‡†็กฎ๏ผˆๅปบ่ฎฎ่‡ณๅฐ‘6ไธชๆœˆๆ•ฐๆฎ๏ผ‰
334
- - ๆ”ฏ๏ฟฝ๏ฟฝๅคš็ง้‡‘่žไบงๅ“๏ผšๅค–ๆฑ‡ใ€่‚ก็ฅจใ€ๅŠ ๅฏ†่ดงๅธ
335
- """)
336
 
337
- # ็คบไพ‹ๆŒ‰้’ฎ
338
- with gr.Row():
339
- gr.Examples(
340
- examples=[
341
- ["EURUSD"],
342
- ["AAPL"],
343
- ["BTCUSD"]
344
- ],
345
- inputs=ticker_input,
346
- label="ๅธธ็”จๆ•ฐๆฎ็คบไพ‹",
347
- examples_per_page=3
348
- )
349
 
350
- # ๅˆ†ๆžๆŒ‰้’ฎ็‚นๅ‡ปไบ‹ไปถ
351
  analyze_btn.click(
352
- fn=analyze_stock,
353
- inputs=ticker_input,
354
- outputs=[signal_output, kchart, pred_chart]
355
  )
356
 
357
- # ๅฏๅŠจๅบ”็”จ
358
  if __name__ == "__main__":
359
  demo.launch(
360
  server_name="0.0.0.0",
 
1
  import gradio as gr
2
  import pandas as pd
3
+ import numpy as np
4
  import plotly.graph_objects as go
5
  from prophet import Prophet
6
  import os
7
+ import re
8
+ from datetime import datetime, timedelta
9
+ import warnings
10
+ warnings.filterwarnings('ignore')
11
 
12
+ # Performance optimization for Apple Silicon
13
  os.environ["OMP_NUM_THREADS"] = "1"
14
  os.environ["OPENBLAS_NUM_THREADS"] = "1"
15
  os.environ["MKL_NUM_THREADS"] = "1"
16
 
17
+ # Define data directories
18
+ RAW_DATA_DIR = "data/raw"
19
+ PROCESSED_DATA_DIR = "data/processed"
20
+ os.makedirs(PROCESSED_DATA_DIR, exist_ok=True)
21
 
22
+ # Predefined trading pairs with expected formats
23
+ TRADING_PAIRS = {
24
+ "EURUSD": {
25
+ "description": "Euro to US Dollar Forex Pair",
26
+ "date_format": "%d.%m.%Y %H:%M:%S.%f %z",
27
+ "has_timezone": True,
28
+ "decimal_separator": ".",
29
+ "required_columns": ["Open", "High", "Low", "Close"]
30
+ },
31
+ "BTCUSD": {
32
+ "description": "Bitcoin to US Dollar",
33
+ "date_format": "%Y-%m-%d %H:%M:%S",
34
+ "has_timezone": False,
35
+ "decimal_separator": ".",
36
+ "required_columns": ["Open", "High", "Low", "Close"]
37
+ },
38
+ "AAPL": {
39
+ "description": "Apple Inc. Stock",
40
+ "date_format": "%Y-%m-%d",
41
+ "has_timezone": False,
42
+ "decimal_separator": ".",
43
+ "required_columns": ["Open", "High", "Low", "Close", "Volume"]
44
+ }
45
+ }
46
+
47
+ def preprocess_data_file(raw_file_path, pair_name):
48
+ """Preprocess raw data file to standardized format"""
49
+ print(f"๐Ÿ”„ Preprocessing data for {pair_name}...")
50
+
51
+ try:
52
+ # Get pair configuration
53
+ config = TRADING_PAIRS.get(pair_name, TRADING_PAIRS["EURUSD"])
54
+
55
+ # Read raw data with proper encoding
56
+ encodings = ['utf-8', 'latin1', 'ISO-8859-1', 'cp1252']
57
+ df = None
58
+
59
+ for encoding in encodings:
60
  try:
61
+ df = pd.read_csv(raw_file_path, encoding=encoding)
62
+ print(f"โœ… Successfully read {pair_name} data with {encoding} encoding")
63
+ break
64
+ except (UnicodeDecodeError, pd.errors.ParserError):
65
+ continue
66
+
67
+ if df is None:
68
+ raise Exception(f"โŒ Failed to read {pair_name} data with any encoding")
69
+
70
+ # Standardize column names (case-insensitive)
71
+ column_mapping = {}
72
+ for col in df.columns:
73
+ col_lower = col.lower()
74
+
75
+ if any(keyword in col_lower for keyword in ['date', 'time', 'timestamp']):
76
+ column_mapping[col] = 'datetime'
77
+ elif 'open' in col_lower:
78
+ column_mapping[col] = 'Open'
79
+ elif 'high' in col_lower:
80
+ column_mapping[col] = 'High'
81
+ elif 'low' in col_lower:
82
+ column_mapping[col] = 'Low'
83
+ elif 'close' in col_lower:
84
+ column_mapping[col] = 'Close'
85
+ elif 'volume' in col_lower:
86
+ column_mapping[col] = 'Volume'
87
+
88
+ if column_mapping:
89
+ df.rename(columns=column_mapping, inplace=True)
90
+ print(f"๐Ÿท๏ธ Standardized columns: {list(column_mapping.keys())} โ†’ {list(column_mapping.values())}")
91
+
92
+ # Process datetime column
93
+ datetime_col = None
94
+ for col in ['datetime', 'date', 'time', 'timestamp']:
95
+ if col in df.columns:
96
+ datetime_col = col
97
+ break
98
+
99
+ if datetime_col is None:
100
+ raise Exception("โŒ No datetime column found in data")
101
+
102
+ # Handle special EURUSD format with GMT
103
+ if pair_name == "EURUSD" and df[datetime_col].astype(str).str.contains('GMT').any():
104
+ print("๐Ÿ•— Handling EURUSD special datetime format...")
105
+ # Clean GMT format
106
+ df[datetime_col] = df[datetime_col].str.replace(' GMT', '', regex=False)
107
+
108
+ # Parse with specified format
109
+ df[datetime_col] = pd.to_datetime(
110
+ df[datetime_col],
111
+ format=config['date_format'],
112
+ errors='coerce',
113
+ utc=True
114
+ )
115
+ else:
116
+ # Standard datetime parsing
117
+ df[datetime_col] = pd.to_datetime(
118
+ df[datetime_col],
119
+ errors='coerce',
120
+ utc=config['has_timezone']
121
+ )
122
+
123
+ # Remove rows with invalid dates
124
+ before_count = len(df)
125
+ df = df.dropna(subset=[datetime_col])
126
+ print(f"๐Ÿงน Removed {before_count - len(df)} rows with invalid dates")
127
+
128
+ # Set datetime as index
129
+ df.set_index(datetime_col, inplace=True)
130
+ df.sort_index(inplace=True)
131
+
132
+ # Handle decimal separators if needed
133
+ if config['decimal_separator'] != '.':
134
+ for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
135
+ if col in df.columns:
136
+ df[col] = df[col].astype(str).str.replace(',', '.').astype(float)
137
+
138
+ # Fill missing values
139
+ for col in ['Open', 'High', 'Low', 'Close']:
140
+ if col in df.columns:
141
+ missing_before = df[col].isna().sum()
142
+ if missing_before > 0:
143
+ df[col] = df[col].fillna(method='ffill').fillna(method='bfill')
144
+ print(f" ๐Ÿ”„ Filled {missing_before} missing values in {col}")
145
+
146
+ # Remove duplicates
147
+ before_count = len(df)
148
+ df = df[~df.index.duplicated(keep='first')]
149
+ print(f"๐Ÿงน Removed {before_count - len(df)} duplicate entries")
150
+
151
+ # Validate required columns
152
+ missing_cols = [col for col in config['required_columns'] if col not in df.columns]
153
+ if missing_cols:
154
+ print(f"โŒ Missing required columns: {missing_cols}")
155
+ print(f"Available columns: {df.columns.tolist()}")
156
+ return None
157
+
158
+ # Save preprocessed data
159
+ processed_file = os.path.join(PROCESSED_DATA_DIR, f"{pair_name}_processed.csv")
160
+ df.to_csv(processed_file)
161
+ print(f"โœ… Saved preprocessed data to {processed_file}")
162
+
163
+ return df
164
+
165
+ except Exception as e:
166
+ print(f"โŒ Preprocessing error for {pair_name}: {str(e)}")
167
+ return None
168
+
169
+ def load_available_data():
170
+ """Load and preprocess all available data files"""
171
+ available_data = {}
172
+
173
+ if not os.path.exists(RAW_DATA_DIR):
174
+ print(f"โš ๏ธ Raw data directory not found: {RAW_DATA_DIR}")
175
+ return available_data
176
+
177
+ print(f"๐Ÿ” Scanning for data files in {RAW_DATA_DIR}...")
178
+
179
+ # Scan for CSV files in raw data directory
180
+ for filename in os.listdir(RAW_DATA_DIR):
181
+ if filename.endswith('.csv'):
182
+ # Extract pair name from filename
183
+ pair_name = filename.split('.')[0].upper()
184
+
185
+ # Check if we have config for this pair, or use default
186
+ if pair_name not in TRADING_PAIRS:
187
+ TRADING_PAIRS[pair_name] = {
188
+ "description": f"{pair_name} Trading Pair",
189
+ "date_format": "%Y-%m-%d %H:%M:%S",
190
+ "has_timezone": False,
191
+ "decimal_separator": ".",
192
+ "required_columns": ["Open", "High", "Low", "Close"]
193
+ }
194
+
195
+ raw_file_path = os.path.join(RAW_DATA_DIR, filename)
196
+ processed_file_path = os.path.join(PROCESSED_DATA_DIR, f"{pair_name}_processed.csv")
197
+
198
+ # Check if preprocessed file exists and is newer than raw file
199
+ if os.path.exists(processed_file_path):
200
+ raw_mod_time = os.path.getmtime(raw_file_path)
201
+ processed_mod_time = os.path.getmtime(processed_file_path)
202
 
203
+ if processed_mod_time > raw_mod_time:
204
+ print(f"โœ… Using existing preprocessed data for {pair_name}")
205
  try:
206
+ df = pd.read_csv(processed_file_path, index_col=0, parse_dates=True)
207
+ available_data[pair_name] = df
 
 
208
  continue
209
+ except Exception as e:
210
+ print(f"โš ๏ธ Error loading preprocessed file: {str(e)}")
211
+
212
+ # Preprocess the file
213
+ print(f"๐Ÿ”„ Processing {pair_name} data...")
214
+ df = preprocess_data_file(raw_file_path, pair_name)
215
+ if df is not None:
216
+ available_data[pair_name] = df
217
+ print(f"โœ… Successfully loaded {pair_name} with {len(df)} records")
218
+ else:
219
+ print(f"โŒ Failed to load {pair_name} data")
220
+
221
+ return available_data
222
+
223
+ # Load available data at startup
224
+ print("๐Ÿš€ Initializing data processing system...")
225
+ available_data = load_available_data()
226
+ print(f"๐Ÿ“Š Available trading pairs: {list(available_data.keys())}")
227
+
228
+ def get_available_pairs():
229
+ """Get list of available trading pairs with status"""
230
+ if not available_data:
231
+ return "โš ๏ธ No data files found. Please upload CSV files to the 'data/raw' directory."
232
+
233
+ status = "โœ… Available trading pairs:\n"
234
+ for pair in sorted(available_data.keys()):
235
+ df = available_data[pair]
236
+ records = len(df)
237
+ date_range = f"{df.index.min().strftime('%Y-%m-%d')} to {df.index.max().strftime('%Y-%m-%d')}"
238
+ status += f"โ€ข {pair}: {records} records ({date_range})\n"
239
+ return status
 
 
 
 
 
 
 
240
 
241
+ def analyze_trading_pair(pair_name):
242
+ """Analyze a specific trading pair"""
243
  try:
244
+ print(f"\n๐Ÿ” Starting analysis for {pair_name}")
 
 
 
 
 
 
245
 
246
+ # Check if data is available
247
+ if pair_name not in available_data:
248
+ # Try case-insensitive match
249
+ matched_pair = None
250
+ for available_pair in available_data.keys():
251
+ if pair_name.upper() == available_pair.upper():
252
+ matched_pair = available_pair
 
253
  break
254
+
255
+ if matched_pair is None:
256
+ available_pairs = ", ".join(available_data.keys())
257
+ return (
258
+ f"โŒ Data not available for '{pair_name}'\n"
259
+ f"Available pairs: {available_pairs}\n"
260
+ f"Upload your data to 'data/raw' directory and restart the app",
261
+ None, None
262
+ )
263
+ pair_name = matched_pair
264
 
265
+ # Get the data
266
+ hist = available_data[pair_name].copy()
267
+ print(f"๐Ÿ“ˆ Loaded {len(hist)} records for {pair_name}")
 
 
 
268
 
269
+ # Basic data validation
270
  required_cols = ['Open', 'High', 'Low', 'Close']
271
  missing_cols = [col for col in required_cols if col not in hist.columns]
272
 
273
  if missing_cols:
274
+ return (
275
+ f"โŒ Missing required columns: {', '.join(missing_cols)}\n"
276
+ f"Available columns: {', '.join(hist.columns)}",
277
+ None, None
278
+ )
279
+
280
+ # Create candlestick chart
281
+ fig = go.Figure()
 
 
 
 
 
 
282
 
283
+ # Add candlestick
284
+ fig.add_trace(go.Candlestick(
285
  x=hist.index,
286
  open=hist['Open'],
287
  high=hist['High'],
288
  low=hist['Low'],
289
  close=hist['Close'],
290
+ name='Price'
291
+ ))
292
 
293
+ # Add moving averages if enough data
294
  if len(hist) >= 20:
295
  hist['MA20'] = hist['Close'].rolling(window=20, min_periods=1).mean()
296
  fig.add_trace(go.Scatter(
297
+ x=hist.index,
298
+ y=hist['MA20'],
299
+ mode='lines',
300
+ name='20-period MA',
301
  line=dict(color='blue', width=1.5)
302
  ))
303
 
304
  if len(hist) >= 50:
305
  hist['MA50'] = hist['Close'].rolling(window=50, min_periods=1).mean()
306
  fig.add_trace(go.Scatter(
307
+ x=hist.index,
308
+ y=hist['MA50'],
309
+ mode='lines',
310
+ name='50-period MA',
311
  line=dict(color='orange', width=1.5)
312
  ))
313
 
314
+ # Update layout
315
  fig.update_layout(
316
+ title=f"{pair_name} Price Analysis",
317
+ xaxis_title="Date",
318
+ yaxis_title="Price",
319
  template="plotly_white",
320
  hovermode="x unified",
321
+ height=500,
322
+ margin=dict(l=50, r=50, t=50, b=50)
323
  )
324
 
325
+ # Create forecast using Prophet
326
+ forecast_fig = None
327
+ forecast_result = ""
328
+
329
  try:
330
+ # Prepare data for Prophet
331
+ prophet_df = hist[['Close']].reset_index()
332
+ prophet_df.columns = ['ds', 'y']
333
+ prophet_df = prophet_df.dropna()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
+ if len(prophet_df) < 30:
336
+ forecast_result = "โš ๏ธ Not enough data points for reliable forecasting (need at least 30)"
337
+ else:
338
+ # Create and fit model
339
+ model = Prophet(
340
+ daily_seasonality=True,
341
+ yearly_seasonality=True,
342
+ interval_width=0.95,
343
+ changepoint_prior_scale=0.05
344
+ )
345
+
346
+ with warnings.catch_warnings():
347
+ warnings.simplefilter("ignore")
348
+ model.fit(prophet_df)
349
+
350
+ # Create future dataframe (30 days forecast)
351
+ future = model.make_future_dataframe(periods=30, freq='D')
352
+ forecast = model.predict(future)
353
+
354
+ # Create forecast chart
355
+ forecast_fig = go.Figure()
356
+
357
+ # Historical data
358
+ forecast_fig.add_trace(go.Scatter(
359
+ x=prophet_df['ds'],
360
+ y=prophet_df['y'],
361
+ mode='lines',
362
+ name='Historical',
363
+ line=dict(color='blue', width=2)
364
+ ))
365
+
366
+ # Forecast data
367
+ forecast_fig.add_trace(go.Scatter(
368
+ x=forecast['ds'],
369
+ y=forecast['yhat'],
370
+ mode='lines',
371
+ name='Forecast',
372
+ line=dict(color='red', width=2, dash='dash')
373
+ ))
374
+
375
+ # Confidence interval
376
+ forecast_fig.add_trace(go.Scatter(
377
+ x=forecast['ds'].tolist() + forecast['ds'][::-1].tolist(),
378
+ y=forecast['yhat_upper'].tolist() + forecast['yhat_lower'][::-1].tolist(),
379
+ fill='toself',
380
+ fillcolor='rgba(255,0,0,0.1)',
381
+ line=dict(color='rgba(255,255,255,0)'),
382
+ name='95% CI'
383
+ ))
384
+
385
+ forecast_fig.update_layout(
386
+ title=f"{pair_name} 30-Day Price Forecast",
387
+ xaxis_title="Date",
388
+ yaxis_title="Price",
389
+ template="plotly_white",
390
+ height=500,
391
+ hovermode="x unified"
392
+ )
393
+
394
+ # Get last forecast values
395
+ last_forecast = forecast.iloc[-1]
396
+ forecast_result = (
397
+ f"๐Ÿ”ฎ 30-Day Forecast:\n"
398
+ f"Predicted price: {last_forecast['yhat']:.5f}\n"
399
+ f"Range: {last_forecast['yhat_lower']:.5f} to {last_forecast['yhat_upper']:.5f}"
400
+ )
401
 
402
+ except Exception as e:
403
+ forecast_result = f"โš ๏ธ Forecasting error: {str(e)}"
404
+ print(forecast_result)
405
 
406
+ # Technical analysis
407
  current_price = hist['Close'].iloc[-1]
408
+ signal = "๐Ÿ“Š Analyzing market conditions..."
 
409
 
410
+ if 'MA20' in hist.columns:
411
+ ma20 = hist['MA20'].iloc[-1]
 
412
  if current_price > ma20:
413
+ signal = "๐Ÿ“ˆ BULLISH: Price above 20-period MA"
414
  else:
415
+ signal = "๐Ÿ“‰ BEARISH: Price below 20-period MA"
416
 
417
+ if 'MA50' in hist.columns:
418
+ ma50 = hist['MA50'].iloc[-1]
419
+ if current_price > ma20 and ma20 > ma50:
420
+ signal = "๐Ÿš€ STRONG BULLISH: Golden Cross pattern"
421
+ elif current_price < ma20 and ma20 < ma50:
422
+ signal = "๐Ÿ’ฃ STRONG BEARISH: Death Cross pattern"
423
 
424
+ # Calculate performance metrics
425
+ start_price = hist['Close'].iloc[0]
426
+ total_return = (current_price / start_price - 1) * 100
427
+ volatility = hist['Close'].pct_change().std() * np.sqrt(252) * 100 # Annualized volatility
428
 
429
+ # Create result text
430
  result_text = (
431
+ f"๐Ÿ“Š {pair_name} Analysis Report\n"
432
+ f"{'=' * 40}\n"
433
+ f"๐Ÿ’ฐ Current Price: {current_price:.5f}\n"
434
+ f"๐Ÿ“ˆ Total Return: {total_return:.2f}%\n"
435
+ f"โšก Volatility: {volatility:.2f}%\n"
436
+ f"๐ŸŽฏ Signal: {signal}\n"
437
+ f"{'=' * 40}\n"
438
+ f"{forecast_result}"
439
  )
440
 
441
+ print(f"โœ… Analysis completed for {pair_name}")
442
+ return result_text, fig, forecast_fig
443
+
444
  except Exception as e:
445
+ error_msg = f"โŒ Analysis error: {str(e)}"
446
  print(error_msg)
447
+ import traceback
448
+ traceback.print_exc()
449
  return error_msg, None, None
450
 
451
+ # Create Gradio interface
452
+ with gr.Blocks(title="Trading Pair AI Analyzer", theme=gr.themes.Soft()) as demo:
453
+ gr.Markdown("# ๐Ÿ“ˆ Trading Pair AI Analysis System")
454
+ gr.Markdown("### Analyze forex, stocks, and crypto with AI-powered insights")
 
 
 
 
 
 
 
 
 
 
 
 
455
 
 
456
  with gr.Row():
457
+ with gr.Column(scale=2):
458
+ data_status = gr.Textbox(
459
+ label="๐Ÿ“Š Available Data",
460
+ value=get_available_pairs(),
461
+ interactive=False,
462
+ lines=5
463
+ )
464
+
465
+ with gr.Column(scale=1):
466
+ gr.Markdown("### โ„น๏ธ System Information")
467
+ system_info = gr.Textbox(
468
+ value=f"๐Ÿ“ˆ Trading Analysis System v2.0\n"
469
+ f"๐Ÿ•’ Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n"
470
+ f"๐Ÿงฎ Loaded pairs: {len(available_data)}",
471
+ interactive=False
472
+ )
473
 
 
474
  with gr.Row():
475
+ with gr.Column():
476
+ pair_input = gr.Textbox(
477
+ label="๐Ÿ” Trading Pair",
478
+ value="EURUSD",
479
+ placeholder="Enter pair name (e.g., EURUSD, BTCUSD, AAPL)"
480
+ )
481
+ analyze_btn = gr.Button("๐Ÿš€ Analyze", variant="primary")
482
+
483
+ with gr.Column():
484
+ gr.Markdown("### ๐Ÿ’ก Quick Tips")
485
+ gr.Markdown("""
486
+ - Use pair names from the available data list
487
+ - System automatically preprocesses your data
488
+ - First analysis may take 30-60 seconds
489
+ - Ensure your CSV has Open, High, Low, Close columns
490
+ """)
491
 
492
+ result_output = gr.Textbox(label="๐Ÿ“ Analysis Results", lines=8)
 
493
 
494
  with gr.Row():
495
+ price_chart = gr.Plot(label="๐Ÿ“Š Price Chart & Technical Indicators")
496
+ forecast_chart = gr.Plot(label="๐Ÿ”ฎ 30-Day Price Forecast")
497
 
498
+ with gr.Accordion("๐Ÿ“ Data Upload Instructions", open=False):
499
+ gr.Markdown("""
500
+ ### How to Add Your Own Data
501
+
502
+ 1. **Prepare your CSV file** with these columns:
503
+ - Date/Time column (any format)
504
+ - Open, High, Low, Close prices
505
+ - Volume (optional)
506
+
507
+ 2. **Upload to Hugging Face Space**:
508
+ - Go to your Space Files tab
509
+ - Create directories: `data/raw/`
510
+ - Upload your CSV files to `data/raw/`
511
+ - Example filenames: `EURUSD.csv`, `BTCUSD.csv`
512
+
513
+ 3. **Restart the application**:
514
+ - Go to Settings โ†’ Restart Space
515
+ - Wait 2-3 minutes for rebuild
516
+
517
+ 4. **Your data will be automatically preprocessed** and ready for analysis!
518
+ """)
519
 
520
+ # Examples for quick testing
521
+ examples = gr.Examples(
522
+ examples=[
523
+ ["EURUSD"],
524
+ ["BTCUSD"],
525
+ ["AAPL"]
526
+ ],
527
+ inputs=pair_input,
528
+ label="Try these examples:"
529
+ )
 
 
530
 
531
+ # Analysis function
532
  analyze_btn.click(
533
+ fn=analyze_trading_pair,
534
+ inputs=pair_input,
535
+ outputs=[result_output, price_chart, forecast_chart]
536
  )
537
 
538
+ # Launch the app
539
  if __name__ == "__main__":
540
  demo.launch(
541
  server_name="0.0.0.0",
data/{EURUSD2022_2025.csv โ†’ raw/EURUSD2022_2025.csv} RENAMED
File without changes
pre_treat_data.rtf ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {\rtf1\ansi\ansicpg1252\cocoartf2822
2
+ \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3
+ {\colortbl;\red255\green255\blue255;}
4
+ {\*\expandedcolortbl;;}
5
+ \margl1440\margr1440\vieww11520\viewh8400\viewkind0
6
+ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
7
+
8
+ \f0\fs24 \cf0 # \uc0\u22312 \u26412 \u22320 Mac \u19978 \u36816 \u34892 \
9
+ import pandas as pd\
10
+ \
11
+ # \uc0\u35835 \u21462 \u21407 \u22987 \u25991 \u20214 \
12
+ df = pd.read_csv('data/EURUSD2022_2025.csv')\
13
+ \
14
+ # \uc0\u20551 \u35774 \u26085 \u26399 \u21015 \u21517 \u20026 'Date'\
15
+ if 'Date' in df.columns:\
16
+ # \uc0\u31227 \u38500 GMT \u37096 \u20998 \u24182 \u26631 \u20934 \u21270 \u26684 \u24335 \
17
+ df['Date'] = df['Date'].str.replace(' GMT', '', regex=False)\
18
+ df['Date'] = pd.to_datetime(df['Date'], format='%d.%m.%Y %H:%M:%S.%f %z', utc=True)\
19
+ \
20
+ # \uc0\u37325 \u32622 \u32034 \u24341 \
21
+ df.set_index('Date', inplace=True)\
22
+ \
23
+ # \uc0\u20445 \u23384 \u26631 \u20934 \u21270 \u25991 \u20214 \
24
+ df.to_csv('data/EURUSD_standard.csv')\
25
+ print("\uc0\u9989 \u24050 \u21019 \u24314 \u26631 \u20934 \u21270 \u25991 \u20214 : data/EURUSD_standard.csv")}