Upload folder using huggingface_hub
Browse files- static/README.md +29 -0
- static/app.py +257 -0
- static/css/style.css +545 -0
- static/gitattributes +35 -0
- static/index.html +1034 -0
- static/js/main.js +1174 -0
- static/requirements.txt +3 -0
static/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Trading Affirmations App
|
3 |
+
emoji: 📈
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
+
sdk: static
|
7 |
+
pinned: false
|
8 |
+
---
|
9 |
+
|
10 |
+
# Trading Affirmations App
|
11 |
+
|
12 |
+
A web application for traders that combines affirmations with real-time market data to help improve trading psychology and performance.
|
13 |
+
|
14 |
+
## Features
|
15 |
+
|
16 |
+
- Real-time market data from Alpha Vantage and Finnhub
|
17 |
+
- Trading session awareness (Asia, Europe, US)
|
18 |
+
- Trading affirmations based on your current trading status
|
19 |
+
- Intermarket analysis visualizations
|
20 |
+
- Trading opportunities powered by Gemini AI
|
21 |
+
- Trade tracking and journal
|
22 |
+
|
23 |
+
## Environment Variables
|
24 |
+
|
25 |
+
This app requires the following environment variables to be set in your Hugging Face Space:
|
26 |
+
|
27 |
+
- `ALPHA_VANTAGE_API_KEY` - Your Alpha Vantage API key
|
28 |
+
- `FINNHUB_API_KEY` - Your Finnhub API key
|
29 |
+
- `GEMINI_API_KEY` - Your Google Gemini API key (optional)
|
static/app.py
ADDED
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import requests
|
3 |
+
from flask import Flask, jsonify, request, send_from_directory
|
4 |
+
|
5 |
+
app = Flask(__name__, static_folder='static')
|
6 |
+
|
7 |
+
# Get API keys from environment variables
|
8 |
+
ALPHA_VANTAGE_API_KEY = os.environ.get('ALPHA_VANTAGE_API_KEY', '')
|
9 |
+
FINNHUB_API_KEY = os.environ.get('FINNHUB_API_KEY', '')
|
10 |
+
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY', '')
|
11 |
+
|
12 |
+
@app.route('/')
|
13 |
+
def index():
|
14 |
+
return send_from_directory('static', 'index.html')
|
15 |
+
|
16 |
+
@app.route('/api/ticker')
|
17 |
+
def get_ticker_data():
|
18 |
+
"""Get stock ticker data from Alpha Vantage"""
|
19 |
+
symbols = request.args.get('symbols', 'AAPL,NVDA,ASML,INTC').split(',')
|
20 |
+
results = []
|
21 |
+
|
22 |
+
for symbol in symbols:
|
23 |
+
try:
|
24 |
+
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={ALPHA_VANTAGE_API_KEY}"
|
25 |
+
response = requests.get(url)
|
26 |
+
data = response.json()
|
27 |
+
|
28 |
+
if "Global Quote" in data and data["Global Quote"]:
|
29 |
+
quote = data["Global Quote"]
|
30 |
+
price = float(quote.get("05. price", 0))
|
31 |
+
change_percent = quote.get("10. change percent", "0%").replace('%', '')
|
32 |
+
change_direction = "up" if float(change_percent) >= 0 else "down"
|
33 |
+
|
34 |
+
results.append({
|
35 |
+
"symbol": symbol,
|
36 |
+
"price": price,
|
37 |
+
"change_percent": abs(float(change_percent)),
|
38 |
+
"direction": change_direction
|
39 |
+
})
|
40 |
+
else:
|
41 |
+
# Fallback to default values if API fails
|
42 |
+
results.append({
|
43 |
+
"symbol": symbol,
|
44 |
+
"price": 100.00,
|
45 |
+
"change_percent": 1.0,
|
46 |
+
"direction": "up"
|
47 |
+
})
|
48 |
+
except Exception as e:
|
49 |
+
print(f"Error fetching data for {symbol}: {str(e)}")
|
50 |
+
# Return default values on error
|
51 |
+
results.append({
|
52 |
+
"symbol": symbol,
|
53 |
+
"price": 100.00,
|
54 |
+
"change_percent": 1.0,
|
55 |
+
"direction": "up"
|
56 |
+
})
|
57 |
+
|
58 |
+
return jsonify(results)
|
59 |
+
|
60 |
+
@app.route('/api/market_overview')
|
61 |
+
def get_market_overview():
|
62 |
+
"""Get market overview data from Finnhub"""
|
63 |
+
try:
|
64 |
+
url = f"https://finnhub.io/api/v1/quote?symbol=SPY&token={FINNHUB_API_KEY}"
|
65 |
+
response = requests.get(url)
|
66 |
+
data = response.json()
|
67 |
+
|
68 |
+
return jsonify({
|
69 |
+
"market_status": "open" if data.get("t", 0) > 0 else "closed",
|
70 |
+
"spy_price": data.get("c", 0),
|
71 |
+
"spy_change": data.get("dp", 0)
|
72 |
+
})
|
73 |
+
except Exception as e:
|
74 |
+
print(f"Error fetching market overview: {str(e)}")
|
75 |
+
return jsonify({
|
76 |
+
"market_status": "open",
|
77 |
+
"spy_price": 470.00,
|
78 |
+
"spy_change": 0.5
|
79 |
+
})
|
80 |
+
|
81 |
+
@app.route('/api/economic_calendar')
|
82 |
+
def get_economic_calendar():
|
83 |
+
"""Get economic calendar from Finnhub"""
|
84 |
+
try:
|
85 |
+
# Current date in YYYY-MM-DD format
|
86 |
+
from datetime import datetime, timedelta
|
87 |
+
today = datetime.now().strftime('%Y-%m-%d')
|
88 |
+
next_week = (datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d')
|
89 |
+
|
90 |
+
url = f"https://finnhub.io/api/v1/calendar/economic?from={today}&to={next_week}&token={FINNHUB_API_KEY}"
|
91 |
+
response = requests.get(url)
|
92 |
+
data = response.json()
|
93 |
+
|
94 |
+
# Process and simplify the data
|
95 |
+
events = []
|
96 |
+
if "economicCalendar" in data:
|
97 |
+
for event in data["economicCalendar"][:15]: # Limit to 15 events
|
98 |
+
events.append({
|
99 |
+
"country": event.get("country", ""),
|
100 |
+
"event": event.get("event", ""),
|
101 |
+
"time": event.get("time", ""),
|
102 |
+
"impact": event.get("impact", "low"),
|
103 |
+
"forecast": event.get("forecast", ""),
|
104 |
+
"previous": event.get("previous", "")
|
105 |
+
})
|
106 |
+
|
107 |
+
return jsonify(events)
|
108 |
+
except Exception as e:
|
109 |
+
print(f"Error fetching economic calendar: {str(e)}")
|
110 |
+
return jsonify([])
|
111 |
+
|
112 |
+
@app.route('/api/market_data/<market>')
|
113 |
+
def get_market_data(market):
|
114 |
+
"""Get market data for specific regions"""
|
115 |
+
try:
|
116 |
+
symbols = {
|
117 |
+
"asia": ["^N225", "000001.SS", "^HSI", "^AXJO"],
|
118 |
+
"europe": ["^GDAXI", "^FTSE", "^FCHI", "^STOXX50E"],
|
119 |
+
"us": ["^GSPC", "^IXIC", "^DJI", "^VIX"]
|
120 |
+
}
|
121 |
+
|
122 |
+
if market not in symbols:
|
123 |
+
return jsonify([])
|
124 |
+
|
125 |
+
results = []
|
126 |
+
for symbol in symbols[market]:
|
127 |
+
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={ALPHA_VANTAGE_API_KEY}"
|
128 |
+
response = requests.get(url)
|
129 |
+
data = response.json()
|
130 |
+
|
131 |
+
if "Global Quote" in data and data["Global Quote"]:
|
132 |
+
quote = data["Global Quote"]
|
133 |
+
price = float(quote.get("05. price", 0))
|
134 |
+
change_percent = quote.get("10. change percent", "0%").replace('%', '')
|
135 |
+
change_direction = "up" if float(change_percent) >= 0 else "down"
|
136 |
+
|
137 |
+
# Map symbol to readable name
|
138 |
+
name_map = {
|
139 |
+
"^N225": "Nikkei 225",
|
140 |
+
"000001.SS": "Shanghai Composite",
|
141 |
+
"^HSI": "Hang Seng",
|
142 |
+
"^AXJO": "ASX 200",
|
143 |
+
"^GDAXI": "DAX",
|
144 |
+
"^FTSE": "FTSE 100",
|
145 |
+
"^FCHI": "CAC 40",
|
146 |
+
"^STOXX50E": "EURO STOXX 50",
|
147 |
+
"^GSPC": "S&P 500",
|
148 |
+
"^IXIC": "NASDAQ",
|
149 |
+
"^DJI": "Dow Jones",
|
150 |
+
"^VIX": "VIX"
|
151 |
+
}
|
152 |
+
|
153 |
+
results.append({
|
154 |
+
"name": name_map.get(symbol, symbol),
|
155 |
+
"price": price,
|
156 |
+
"change_percent": abs(float(change_percent)),
|
157 |
+
"direction": change_direction
|
158 |
+
})
|
159 |
+
else:
|
160 |
+
# Fallback to default values
|
161 |
+
results.append({
|
162 |
+
"name": symbol,
|
163 |
+
"price": 100.00,
|
164 |
+
"change_percent": 1.0,
|
165 |
+
"direction": "up"
|
166 |
+
})
|
167 |
+
|
168 |
+
return jsonify(results)
|
169 |
+
except Exception as e:
|
170 |
+
print(f"Error fetching data for {market}: {str(e)}")
|
171 |
+
return jsonify([])
|
172 |
+
|
173 |
+
@app.route('/api/gemini_key')
|
174 |
+
def get_gemini_key():
|
175 |
+
"""Return a masked version of the Gemini API key for verification"""
|
176 |
+
if GEMINI_API_KEY:
|
177 |
+
# Return only first 4 chars and mask the rest for verification
|
178 |
+
masked_key = GEMINI_API_KEY[:4] + '*' * (len(GEMINI_API_KEY) - 4)
|
179 |
+
return jsonify({"status": "available", "key": masked_key})
|
180 |
+
else:
|
181 |
+
return jsonify({"status": "unavailable", "key": ""})
|
182 |
+
|
183 |
+
@app.route('/api/intermarket')
|
184 |
+
def get_intermarket_data():
|
185 |
+
"""Get intermarket analysis data"""
|
186 |
+
try:
|
187 |
+
# Get data for stocks, bonds, commodities, and forex
|
188 |
+
indices = ["SPY", "TLT", "GLD", "UUP"]
|
189 |
+
results = {}
|
190 |
+
|
191 |
+
for idx in indices:
|
192 |
+
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={idx}&apikey={ALPHA_VANTAGE_API_KEY}"
|
193 |
+
response = requests.get(url)
|
194 |
+
data = response.json()
|
195 |
+
|
196 |
+
if "Global Quote" in data and data["Global Quote"]:
|
197 |
+
quote = data["Global Quote"]
|
198 |
+
price = float(quote.get("05. price", 0))
|
199 |
+
change_percent = quote.get("10. change percent", "0%").replace('%', '')
|
200 |
+
|
201 |
+
results[idx] = {
|
202 |
+
"price": price,
|
203 |
+
"change_percent": float(change_percent.replace('%', ''))
|
204 |
+
}
|
205 |
+
|
206 |
+
# Calculate simplified correlations (simulated)
|
207 |
+
correlations = {
|
208 |
+
"stocks_bonds": 0.68,
|
209 |
+
"stocks_commodities": -0.42,
|
210 |
+
"stocks_dollar": 0.12,
|
211 |
+
"bonds_commodities": -0.71,
|
212 |
+
"bonds_dollar": -0.53,
|
213 |
+
"commodities_dollar": -0.65
|
214 |
+
}
|
215 |
+
|
216 |
+
return jsonify({
|
217 |
+
"assets": results,
|
218 |
+
"correlations": correlations
|
219 |
+
})
|
220 |
+
except Exception as e:
|
221 |
+
print(f"Error fetching intermarket data: {str(e)}")
|
222 |
+
return jsonify({"assets": {}, "correlations": {}})
|
223 |
+
|
224 |
+
@app.route('/api/affirmations/<category>/<status>')
|
225 |
+
def get_affirmation(category, status):
|
226 |
+
"""Get a specific affirmation based on category and status"""
|
227 |
+
affirmations = {
|
228 |
+
'discipline': {
|
229 |
+
'preparing': 'Ich bin ein disziplinierter und geduldiger Trader, der seinem Handelsplan mit unerschütterlichem Engagement folgt. Ich vertraue auf die Wirksamkeit meiner Strategien und warte geduldig auf Setups mit hoher Wahrscheinlichkeit.',
|
230 |
+
'active': 'Während mein Trade aktiv ist, bleibe ich diszipliniert und geduldig. Ich folge meinem Plan und erlaube keiner Emotion, meine Strategie zu untergraben.',
|
231 |
+
'developing': 'Beim Entwickeln meiner Strategie wende ich Disziplin und Geduld an. Ich nehme mir die Zeit, jedes Detail zu durchdenken und teste gründlich, bevor ich handele.',
|
232 |
+
'break': 'In dieser Pause pflege ich meine Disziplin und Geduld, indem ich reflektiere und lerne. Ich verstehe, dass Ruhezeiten wesentlich für nachhaltigen Trading-Erfolg sind.'
|
233 |
+
},
|
234 |
+
'abundance': {
|
235 |
+
'preparing': 'Ich ziehe reichlich Handelsmöglichkeiten an, die mit meiner Strategie übereinstimmen. Der Markt bietet einen endlosen Strom von Gelegenheiten, und ich bin bereit, sie zu nutzen.',
|
236 |
+
'active': 'Mein aktueller Trade ist eine von vielen Gelegenheiten für Wohlstand. Ich denke in Fülle und weiß, dass unabhängig vom Ausgang dieses Trades weitere profitable Chancen folgen werden.',
|
237 |
+
'developing': 'Ich entwickle meine Strategie mit einer Überfluss-Denkweise. Ich erkenne die unbegrenzten Möglichkeiten des Marktes an und erschaffe einen Ansatz, der diesen Reichtum anzieht.',
|
238 |
+
'break': 'Während dieser Pause ziehe ich neue Erkenntnisse und Möglichkeiten an. Ich nutze diese Zeit, um meine Überfluss-Denkweise zu stärken und mich auf neue Handelschancen vorzubereiten.'
|
239 |
+
}
|
240 |
+
# Additional categories could be added here
|
241 |
+
}
|
242 |
+
|
243 |
+
if category in affirmations and status in affirmations[category]:
|
244 |
+
return jsonify({
|
245 |
+
'text': affirmations[category][status],
|
246 |
+
'category': category,
|
247 |
+
'status': status
|
248 |
+
})
|
249 |
+
else:
|
250 |
+
return jsonify({
|
251 |
+
'text': 'Ich handle mit Klarheit, Disziplin und Vertrauen.',
|
252 |
+
'category': 'general',
|
253 |
+
'status': 'default'
|
254 |
+
})
|
255 |
+
|
256 |
+
if __name__ == '__main__':
|
257 |
+
app.run(host='0.0.0.0', port=7860, debug=True)
|
static/css/style.css
ADDED
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
--primary: #2c3e50;
|
3 |
+
--secondary: #3498db;
|
4 |
+
--accent: #e74c3c;
|
5 |
+
--light: #ecf0f1;
|
6 |
+
--dark: #2c3e50;
|
7 |
+
--success: #2ecc71;
|
8 |
+
--warning: #f39c12;
|
9 |
+
}
|
10 |
+
|
11 |
+
body {
|
12 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
13 |
+
background-color: #f5f7fa;
|
14 |
+
color: #333;
|
15 |
+
overflow-x: hidden;
|
16 |
+
}
|
17 |
+
|
18 |
+
.navbar {
|
19 |
+
background-color: var(--primary);
|
20 |
+
color: white;
|
21 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
22 |
+
position: sticky;
|
23 |
+
top: 0;
|
24 |
+
z-index: 100;
|
25 |
+
}
|
26 |
+
|
27 |
+
.ticker-container {
|
28 |
+
background-color: #1d2630;
|
29 |
+
color: white;
|
30 |
+
overflow: hidden;
|
31 |
+
white-space: nowrap;
|
32 |
+
padding: 8px 0;
|
33 |
+
position: relative;
|
34 |
+
}
|
35 |
+
|
36 |
+
.ticker-content {
|
37 |
+
display: inline-block;
|
38 |
+
animation: ticker 30s linear infinite;
|
39 |
+
}
|
40 |
+
|
41 |
+
@keyframes ticker {
|
42 |
+
0% { transform: translateX(100%); }
|
43 |
+
100% { transform: translateX(-100%); }
|
44 |
+
}
|
45 |
+
|
46 |
+
.ticker-item {
|
47 |
+
display: inline-block;
|
48 |
+
padding: 0 20px;
|
49 |
+
}
|
50 |
+
|
51 |
+
.ticker-item.up {
|
52 |
+
color: #2ecc71;
|
53 |
+
}
|
54 |
+
|
55 |
+
.ticker-item.down {
|
56 |
+
color: #e74c3c;
|
57 |
+
}
|
58 |
+
|
59 |
+
.section {
|
60 |
+
background: white;
|
61 |
+
border-radius: 8px;
|
62 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
63 |
+
margin-bottom: 20px;
|
64 |
+
padding: 20px;
|
65 |
+
}
|
66 |
+
|
67 |
+
.section-title {
|
68 |
+
color: var(--primary);
|
69 |
+
border-bottom: 2px solid var(--secondary);
|
70 |
+
padding-bottom: 8px;
|
71 |
+
margin-bottom: 16px;
|
72 |
+
font-weight: 600;
|
73 |
+
}
|
74 |
+
|
75 |
+
.btn-primary {
|
76 |
+
background-color: var(--secondary);
|
77 |
+
color: white;
|
78 |
+
border: none;
|
79 |
+
padding: 8px 16px;
|
80 |
+
border-radius: 4px;
|
81 |
+
cursor: pointer;
|
82 |
+
transition: all 0.3s;
|
83 |
+
}
|
84 |
+
|
85 |
+
.btn-primary:hover {
|
86 |
+
background-color: #2980b9;
|
87 |
+
transform: translateY(-2px);
|
88 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
89 |
+
}
|
90 |
+
|
91 |
+
.btn-danger {
|
92 |
+
background-color: var(--accent);
|
93 |
+
color: white;
|
94 |
+
}
|
95 |
+
|
96 |
+
.btn-danger:hover {
|
97 |
+
background-color: #c0392b;
|
98 |
+
}
|
99 |
+
|
100 |
+
.card {
|
101 |
+
background: white;
|
102 |
+
border-radius: 8px;
|
103 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
104 |
+
margin-bottom: 16px;
|
105 |
+
transition: all 0.3s;
|
106 |
+
}
|
107 |
+
|
108 |
+
.card:hover {
|
109 |
+
transform: translateY(-2px);
|
110 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
111 |
+
}
|
112 |
+
|
113 |
+
.tab-container {
|
114 |
+
display: flex;
|
115 |
+
border-bottom: 1px solid #ddd;
|
116 |
+
margin-bottom: 16px;
|
117 |
+
}
|
118 |
+
|
119 |
+
.tab {
|
120 |
+
padding: 10px 16px;
|
121 |
+
cursor: pointer;
|
122 |
+
border-bottom: 3px solid transparent;
|
123 |
+
transition: all 0.3s;
|
124 |
+
}
|
125 |
+
|
126 |
+
.tab.active {
|
127 |
+
border-bottom: 3px solid var(--secondary);
|
128 |
+
color: var(--secondary);
|
129 |
+
font-weight: bold;
|
130 |
+
}
|
131 |
+
|
132 |
+
.tab-content {
|
133 |
+
display: none;
|
134 |
+
}
|
135 |
+
|
136 |
+
.tab-content.active {
|
137 |
+
display: block;
|
138 |
+
}
|
139 |
+
|
140 |
+
.badge {
|
141 |
+
padding: 4px 8px;
|
142 |
+
border-radius: 4px;
|
143 |
+
font-size: 12px;
|
144 |
+
font-weight: 600;
|
145 |
+
}
|
146 |
+
|
147 |
+
.badge-success {
|
148 |
+
background-color: var(--success);
|
149 |
+
color: white;
|
150 |
+
}
|
151 |
+
|
152 |
+
.badge-warning {
|
153 |
+
background-color: var(--warning);
|
154 |
+
color: white;
|
155 |
+
}
|
156 |
+
|
157 |
+
.badge-danger {
|
158 |
+
background-color: var(--accent);
|
159 |
+
color: white;
|
160 |
+
}
|
161 |
+
|
162 |
+
.form-group {
|
163 |
+
margin-bottom: 16px;
|
164 |
+
}
|
165 |
+
|
166 |
+
.form-control {
|
167 |
+
width: 100%;
|
168 |
+
padding: 8px 12px;
|
169 |
+
border: 1px solid #ddd;
|
170 |
+
border-radius: 4px;
|
171 |
+
transition: border 0.3s;
|
172 |
+
}
|
173 |
+
|
174 |
+
.form-control:focus {
|
175 |
+
border-color: var(--secondary);
|
176 |
+
outline: none;
|
177 |
+
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
|
178 |
+
}
|
179 |
+
|
180 |
+
.select-control {
|
181 |
+
width: 100%;
|
182 |
+
padding: 8px 12px;
|
183 |
+
border: 1px solid #ddd;
|
184 |
+
border-radius: 4px;
|
185 |
+
background-color: white;
|
186 |
+
}
|
187 |
+
|
188 |
+
.session-card {
|
189 |
+
background: linear-gradient(145deg, #2c3e50, #34495e);
|
190 |
+
color: white;
|
191 |
+
border-radius: 8px;
|
192 |
+
padding: 16px;
|
193 |
+
margin-bottom: 16px;
|
194 |
+
}
|
195 |
+
|
196 |
+
.session-title {
|
197 |
+
font-size: 18px;
|
198 |
+
font-weight: 600;
|
199 |
+
margin-bottom: 8px;
|
200 |
+
}
|
201 |
+
|
202 |
+
.session-time {
|
203 |
+
font-size: 14px;
|
204 |
+
opacity: 0.8;
|
205 |
+
}
|
206 |
+
|
207 |
+
.session-status {
|
208 |
+
display: inline-block;
|
209 |
+
padding: 4px 8px;
|
210 |
+
border-radius: 4px;
|
211 |
+
font-size: 12px;
|
212 |
+
font-weight: 600;
|
213 |
+
margin-top: 8px;
|
214 |
+
}
|
215 |
+
|
216 |
+
.widget {
|
217 |
+
background: white;
|
218 |
+
border-radius: 8px;
|
219 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
220 |
+
margin-bottom: 16px;
|
221 |
+
padding: 16px;
|
222 |
+
}
|
223 |
+
|
224 |
+
.widget-title {
|
225 |
+
font-size: 16px;
|
226 |
+
font-weight: 600;
|
227 |
+
margin-bottom: 12px;
|
228 |
+
color: var(--primary);
|
229 |
+
}
|
230 |
+
|
231 |
+
.event-item {
|
232 |
+
padding: 8px 0;
|
233 |
+
border-bottom: 1px solid #eee;
|
234 |
+
}
|
235 |
+
|
236 |
+
.event-time {
|
237 |
+
font-size: 12px;
|
238 |
+
color: #777;
|
239 |
+
}
|
240 |
+
|
241 |
+
.event-name {
|
242 |
+
font-weight: 500;
|
243 |
+
}
|
244 |
+
|
245 |
+
.event-impact {
|
246 |
+
width: 10px;
|
247 |
+
height: 10px;
|
248 |
+
border-radius: 50%;
|
249 |
+
display: inline-block;
|
250 |
+
margin-right: 4px;
|
251 |
+
}
|
252 |
+
|
253 |
+
.event-impact.high {
|
254 |
+
background-color: var(--accent);
|
255 |
+
}
|
256 |
+
|
257 |
+
.event-impact.medium {
|
258 |
+
background-color: var(--warning);
|
259 |
+
}
|
260 |
+
|
261 |
+
.event-impact.low {
|
262 |
+
background-color: var(--success);
|
263 |
+
}
|
264 |
+
|
265 |
+
.market-item {
|
266 |
+
display: flex;
|
267 |
+
justify-content: space-between;
|
268 |
+
padding: 8px 0;
|
269 |
+
border-bottom: 1px solid #eee;
|
270 |
+
}
|
271 |
+
|
272 |
+
.market-name {
|
273 |
+
font-weight: 500;
|
274 |
+
}
|
275 |
+
|
276 |
+
.market-value.up {
|
277 |
+
color: var(--success);
|
278 |
+
}
|
279 |
+
|
280 |
+
.market-value.down {
|
281 |
+
color: var(--accent);
|
282 |
+
}
|
283 |
+
|
284 |
+
.intermarket-grid {
|
285 |
+
display: grid;
|
286 |
+
grid-template-columns: repeat(2, 1fr);
|
287 |
+
gap: 16px;
|
288 |
+
}
|
289 |
+
|
290 |
+
.correlation-matrix {
|
291 |
+
background: white;
|
292 |
+
border-radius: 8px;
|
293 |
+
padding: 16px;
|
294 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
295 |
+
}
|
296 |
+
|
297 |
+
.matrix-title {
|
298 |
+
font-size: 16px;
|
299 |
+
font-weight: 600;
|
300 |
+
margin-bottom: 12px;
|
301 |
+
color: var(--primary);
|
302 |
+
}
|
303 |
+
|
304 |
+
.matrix-table {
|
305 |
+
width: 100%;
|
306 |
+
border-collapse: collapse;
|
307 |
+
}
|
308 |
+
|
309 |
+
.matrix-table th, .matrix-table td {
|
310 |
+
padding: 8px;
|
311 |
+
text-align: center;
|
312 |
+
border: 1px solid #eee;
|
313 |
+
}
|
314 |
+
|
315 |
+
.matrix-table th {
|
316 |
+
background-color: #f5f7fa;
|
317 |
+
font-weight: 600;
|
318 |
+
}
|
319 |
+
|
320 |
+
.correlation-positive {
|
321 |
+
background-color: rgba(46, 204, 113, 0.2);
|
322 |
+
}
|
323 |
+
|
324 |
+
.correlation-negative {
|
325 |
+
background-color: rgba(231, 76, 60, 0.2);
|
326 |
+
}
|
327 |
+
|
328 |
+
.correlation-neutral {
|
329 |
+
background-color: rgba(241, 196, 15, 0.1);
|
330 |
+
}
|
331 |
+
|
332 |
+
.affirmation-card {
|
333 |
+
background: linear-gradient(145deg, #3498db, #2980b9);
|
334 |
+
color: white;
|
335 |
+
border-radius: 8px;
|
336 |
+
padding: 20px;
|
337 |
+
margin-bottom: 16px;
|
338 |
+
}
|
339 |
+
|
340 |
+
.affirmation-text {
|
341 |
+
font-size: 18px;
|
342 |
+
line-height: 1.6;
|
343 |
+
font-style: italic;
|
344 |
+
margin-bottom: 16px;
|
345 |
+
}
|
346 |
+
|
347 |
+
.affirmation-category {
|
348 |
+
font-size: 14px;
|
349 |
+
opacity: 0.8;
|
350 |
+
text-align: right;
|
351 |
+
}
|
352 |
+
|
353 |
+
.trade-item {
|
354 |
+
background: white;
|
355 |
+
border-radius: 8px;
|
356 |
+
padding: 16px;
|
357 |
+
margin-bottom: 16px;
|
358 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
359 |
+
position: relative;
|
360 |
+
}
|
361 |
+
|
362 |
+
.trade-symbol {
|
363 |
+
font-size: 18px;
|
364 |
+
font-weight: 600;
|
365 |
+
margin-bottom: 4px;
|
366 |
+
}
|
367 |
+
|
368 |
+
.trade-details {
|
369 |
+
margin-bottom: 8px;
|
370 |
+
font-size: 14px;
|
371 |
+
color: #666;
|
372 |
+
}
|
373 |
+
|
374 |
+
.trade-pnl {
|
375 |
+
position: absolute;
|
376 |
+
top: 16px;
|
377 |
+
right: 16px;
|
378 |
+
font-weight: 600;
|
379 |
+
}
|
380 |
+
|
381 |
+
.trade-pnl.profit {
|
382 |
+
color: var(--success);
|
383 |
+
}
|
384 |
+
|
385 |
+
.trade-pnl.loss {
|
386 |
+
color: var(--accent);
|
387 |
+
}
|
388 |
+
|
389 |
+
.trade-actions {
|
390 |
+
display: flex;
|
391 |
+
justify-content: flex-end;
|
392 |
+
gap: 8px;
|
393 |
+
margin-top: 8px;
|
394 |
+
}
|
395 |
+
|
396 |
+
.chat-popup {
|
397 |
+
position: fixed;
|
398 |
+
bottom: 20px;
|
399 |
+
right: 20px;
|
400 |
+
width: 350px;
|
401 |
+
background: white;
|
402 |
+
border-radius: 8px 8px 0 0;
|
403 |
+
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
404 |
+
z-index: 1000;
|
405 |
+
transition: all 0.3s;
|
406 |
+
}
|
407 |
+
|
408 |
+
.chat-header {
|
409 |
+
background: var(--primary);
|
410 |
+
color: white;
|
411 |
+
padding: 10px 16px;
|
412 |
+
border-radius: 8px 8px 0 0;
|
413 |
+
display: flex;
|
414 |
+
justify-content: space-between;
|
415 |
+
align-items: center;
|
416 |
+
cursor: pointer;
|
417 |
+
}
|
418 |
+
|
419 |
+
.chat-body {
|
420 |
+
padding: 16px;
|
421 |
+
max-height: 300px;
|
422 |
+
overflow-y: auto;
|
423 |
+
}
|
424 |
+
|
425 |
+
.chat-message {
|
426 |
+
margin-bottom: 16px;
|
427 |
+
}
|
428 |
+
|
429 |
+
.chat-message.user {
|
430 |
+
text-align: right;
|
431 |
+
}
|
432 |
+
|
433 |
+
.chat-message.user .message-content {
|
434 |
+
background-color: var(--secondary);
|
435 |
+
color: white;
|
436 |
+
border-radius: 18px 18px 0 18px;
|
437 |
+
padding: 8px 16px;
|
438 |
+
display: inline-block;
|
439 |
+
max-width: 80%;
|
440 |
+
}
|
441 |
+
|
442 |
+
.chat-message.ai .message-content {
|
443 |
+
background-color: #f1f1f1;
|
444 |
+
color: #333;
|
445 |
+
border-radius: 18px 18px 18px 0;
|
446 |
+
padding: 8px 16px;
|
447 |
+
display: inline-block;
|
448 |
+
max-width: 80%;
|
449 |
+
}
|
450 |
+
|
451 |
+
.chat-footer {
|
452 |
+
padding: 10px 16px;
|
453 |
+
border-top: 1px solid #eee;
|
454 |
+
display: flex;
|
455 |
+
gap: 8px;
|
456 |
+
}
|
457 |
+
|
458 |
+
.chat-input {
|
459 |
+
flex: 1;
|
460 |
+
padding: 8px 12px;
|
461 |
+
border: 1px solid #ddd;
|
462 |
+
border-radius: 20px;
|
463 |
+
outline: none;
|
464 |
+
}
|
465 |
+
|
466 |
+
.minimized {
|
467 |
+
height: 46px;
|
468 |
+
overflow: hidden;
|
469 |
+
}
|
470 |
+
|
471 |
+
.gemini-badge {
|
472 |
+
background: linear-gradient(135deg, #1a73e8, #8e24aa);
|
473 |
+
color: white;
|
474 |
+
padding: 4px 10px;
|
475 |
+
border-radius: 12px;
|
476 |
+
font-size: 12px;
|
477 |
+
margin-left: 8px;
|
478 |
+
}
|
479 |
+
|
480 |
+
.api-key-container {
|
481 |
+
display: flex;
|
482 |
+
align-items: center;
|
483 |
+
gap: 8px;
|
484 |
+
}
|
485 |
+
|
486 |
+
.api-key-input {
|
487 |
+
background-color: rgba(255, 255, 255, 0.1);
|
488 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
489 |
+
color: white;
|
490 |
+
padding: 4px 8px;
|
491 |
+
border-radius: 4px;
|
492 |
+
font-size: 14px;
|
493 |
+
width: 160px;
|
494 |
+
}
|
495 |
+
|
496 |
+
.api-key-input::placeholder {
|
497 |
+
color: rgba(255, 255, 255, 0.6);
|
498 |
+
}
|
499 |
+
|
500 |
+
.api-status {
|
501 |
+
width: 8px;
|
502 |
+
height: 8px;
|
503 |
+
border-radius: 50%;
|
504 |
+
background-color: #e74c3c;
|
505 |
+
margin-right: 4px;
|
506 |
+
}
|
507 |
+
|
508 |
+
.api-status.connected {
|
509 |
+
background-color: #2ecc71;
|
510 |
+
}
|
511 |
+
|
512 |
+
.loading-spinner {
|
513 |
+
border: 3px solid rgba(255, 255, 255, 0.3);
|
514 |
+
border-top: 3px solid #ffffff;
|
515 |
+
border-radius: 50%;
|
516 |
+
width: 16px;
|
517 |
+
height: 16px;
|
518 |
+
animation: spin 1s linear infinite;
|
519 |
+
margin-left: 5px;
|
520 |
+
display: none;
|
521 |
+
}
|
522 |
+
|
523 |
+
@keyframes spin {
|
524 |
+
0% { transform: rotate(0deg); }
|
525 |
+
100% { transform: rotate(360deg); }
|
526 |
+
}
|
527 |
+
|
528 |
+
/* Responsive adjustments */
|
529 |
+
@media (max-width: 768px) {
|
530 |
+
.intermarket-grid {
|
531 |
+
grid-template-columns: 1fr;
|
532 |
+
}
|
533 |
+
|
534 |
+
.chat-popup {
|
535 |
+
width: 100%;
|
536 |
+
left: 0;
|
537 |
+
right: 0;
|
538 |
+
bottom: 0;
|
539 |
+
border-radius: 0;
|
540 |
+
}
|
541 |
+
|
542 |
+
.chat-header {
|
543 |
+
border-radius: 0;
|
544 |
+
}
|
545 |
+
}
|
static/gitattributes
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
static/index.html
ADDED
@@ -0,0 +1,1034 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="de">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Trading Affirmations App</title>
|
7 |
+
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
8 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css">
|
9 |
+
<link rel="stylesheet" href="/static/css/styles.css">
|
10 |
+
</head>
|
11 |
+
<body>
|
12 |
+
<!-- Header and Navigation -->
|
13 |
+
<nav class="navbar p-4">
|
14 |
+
<div class="container mx-auto flex justify-between items-center">
|
15 |
+
<a href="#" class="text-xl font-bold">Trading Affirmations App</a>
|
16 |
+
<div class="api-key-container">
|
17 |
+
<div class="flex items-center">
|
18 |
+
<span class="api-status" id="apiStatus"></span>
|
19 |
+
<span class="text-sm mr-2">API Status</span>
|
20 |
+
</div>
|
21 |
+
<input type="password" id="geminiApiKey" class="api-key-input" placeholder="Gemini API Key" onchange="saveApiKey()">
|
22 |
+
<button onclick="connectApi()" class="btn-primary text-sm py-1">
|
23 |
+
<span id="connectText">Verbinden</span>
|
24 |
+
<div class="loading-spinner" id="apiSpinner"></div>
|
25 |
+
</button>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
</nav>
|
29 |
+
|
30 |
+
<!-- TradingView Ticker -->
|
31 |
+
<div class="ticker-container">
|
32 |
+
<div class="ticker-content">
|
33 |
+
<div class="ticker-item up">NASDAQ:AAPL <span>186.32</span> <i class="fas fa-caret-up"></i> 1.2%</div>
|
34 |
+
<div class="ticker-item down">NASDAQ:NVDA <span>432.65</span> <i class="fas fa-caret-down"></i> 0.8%</div>
|
35 |
+
<div class="ticker-item up">NASDAQ:ASML <span>689.43</span> <i class="fas fa-caret-up"></i> 2.1%</div>
|
36 |
+
<div class="ticker-item up">BINANCE:BNBUSDT <span>573.21</span> <i class="fas fa-caret-up"></i> 3.4%</div>
|
37 |
+
<div class="ticker-item down">NASDAQ:INTC <span>34.87</span> <i class="fas fa-caret-down"></i> 1.5%</div>
|
38 |
+
<div class="ticker-item up">CAPITALCOM:BTCUSD <span>43256.78</span> <i class="fas fa-caret-up"></i> 2.3%</div>
|
39 |
+
<div class="ticker-item up">PEPPERSTONE:XAUUSD <span>2316.42</span> <i class="fas fa-caret-up"></i> 0.7%</div>
|
40 |
+
<div class="ticker-item down">FX:USDJPY <span>143.85</span> <i class="fas fa-caret-down"></i> 0.3%</div>
|
41 |
+
<div class="ticker-item up">PEPPERSTONE:GER40 <span>18234.6</span> <i class="fas fa-caret-up"></i> 1.1%</div>
|
42 |
+
<div class="ticker-item down">CAPITALCOM:EURUSD <span>1.0845</span> <i class="fas fa-caret-down"></i> 0.2%</div>
|
43 |
+
</div>
|
44 |
+
</div>
|
45 |
+
|
46 |
+
<!-- Main Content -->
|
47 |
+
<div class="container mx-auto p-4">
|
48 |
+
<!-- Tabs -->
|
49 |
+
<div class="tab-container mb-6">
|
50 |
+
<div class="tab active" data-tab="dashboard">Dashboard</div>
|
51 |
+
<div class="tab" data-tab="sessions">Trading Sessions</div>
|
52 |
+
<div class="tab" data-tab="intermarket">Intermarket Analyse</div>
|
53 |
+
<div class="tab" data-tab="affirmations">Affirmationen</div>
|
54 |
+
<div class="tab" data-tab="opportunities">Trading Opportunities</div>
|
55 |
+
<div class="tab" data-tab="mytrades">Meine Trades</div>
|
56 |
+
</div>
|
57 |
+
|
58 |
+
<!-- Dashboard Tab Content -->
|
59 |
+
<div class="tab-content active" id="dashboard">
|
60 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
61 |
+
<div class="section">
|
62 |
+
<h2 class="section-title">Aktuelle Session</h2>
|
63 |
+
<div class="session-card">
|
64 |
+
<div class="session-title">Europa Session</div>
|
65 |
+
<div class="session-time">08:00 - 16:30 CET</div>
|
66 |
+
<div class="session-status bg-green-500">Aktiv</div>
|
67 |
+
<div class="mt-4">
|
68 |
+
<p class="text-sm opacity-80">Hauptmärkte: DAX, FTSE, CAC40</p>
|
69 |
+
<p class="text-sm opacity-80">13 Events heute</p>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
<div class="mt-4">
|
73 |
+
<p class="font-medium mb-2">Nächste Sessions:</p>
|
74 |
+
<div class="text-sm text-gray-600 mb-1">US Session - Beginnt in 3h 20min</div>
|
75 |
+
<div class="text-sm text-gray-600">Asien Session - Beginnt in 15h 30min</div>
|
76 |
+
</div>
|
77 |
+
</div>
|
78 |
+
|
79 |
+
<div class="section">
|
80 |
+
<h2 class="section-title">Trader Status</h2>
|
81 |
+
<div class="form-group">
|
82 |
+
<label class="block text-gray-700 mb-2">Dein aktueller Status:</label>
|
83 |
+
<select id="traderStatus" class="select-control" onchange="updateAffirmation()">
|
84 |
+
<option value="preparing">Vorbereitung auf Trade</option>
|
85 |
+
<option value="active">Aktiver Trade läuft</option>
|
86 |
+
<option value="developing">Entwickle Strategie</option>
|
87 |
+
<option value="break">Pause / Reflexion</option>
|
88 |
+
</select>
|
89 |
+
</div>
|
90 |
+
|
91 |
+
<div class="affirmation-card mt-4">
|
92 |
+
<div class="affirmation-text" id="currentAffirmation">
|
93 |
+
Ich bin ein disziplinierter und geduldiger Trader, der seinem Handelsplan mit unerschütterlichem Engagement folgt. Ich vertraue auf die Wirksamkeit meiner Strategien und warte geduldig auf Setups mit hoher Wahrscheinlichkeit.
|
94 |
+
</div>
|
95 |
+
<div class="affirmation-category" id="affirmationCategory">
|
96 |
+
Disziplin und Geduld entwickeln
|
97 |
+
</div>
|
98 |
+
</div>
|
99 |
+
</div>
|
100 |
+
|
101 |
+
<div class="section">
|
102 |
+
<h2 class="section-title">Aktive Trades</h2>
|
103 |
+
<div id="activeTrades">
|
104 |
+
<div class="trade-item">
|
105 |
+
<div class="trade-symbol">NASDAQ:AAPL</div>
|
106 |
+
<div class="trade-details">Long @ 180.45 • Stop: 175.60 • Target: 190.00</div>
|
107 |
+
<div class="trade-pnl profit">+3.25%</div>
|
108 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: Heute, 10:15 CET</div>
|
109 |
+
<div class="trade-actions">
|
110 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
111 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
112 |
+
</div>
|
113 |
+
</div>
|
114 |
+
|
115 |
+
<div class="trade-item">
|
116 |
+
<div class="trade-symbol">FX:EURUSD</div>
|
117 |
+
<div class="trade-details">Short @ 1.0890 • Stop: 1.0925 • Target: 1.0820</div>
|
118 |
+
<div class="trade-pnl loss">-0.12%</div>
|
119 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: Gestern, 16:45 CET</div>
|
120 |
+
<div class="trade-actions">
|
121 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
122 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
123 |
+
</div>
|
124 |
+
</div>
|
125 |
+
</div>
|
126 |
+
<div class="mt-4 text-center">
|
127 |
+
<button class="btn-primary" onclick="showAddTradeModal()">+ Neuen Trade hinzufügen</button>
|
128 |
+
</div>
|
129 |
+
</div>
|
130 |
+
</div>
|
131 |
+
|
132 |
+
<div class="section mt-6">
|
133 |
+
<h2 class="section-title">Intermarket-Übersicht (Murphy's Analyse)</h2>
|
134 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
135 |
+
<div class="correlation-matrix">
|
136 |
+
<div class="matrix-title">Aktuelle Korrelationen</div>
|
137 |
+
<table class="matrix-table">
|
138 |
+
<thead>
|
139 |
+
<tr>
|
140 |
+
<th></th>
|
141 |
+
<th>Aktien</th>
|
142 |
+
<th>Anleihen</th>
|
143 |
+
<th>Rohstoffe</th>
|
144 |
+
<th>Dollar</th>
|
145 |
+
</tr>
|
146 |
+
</thead>
|
147 |
+
<tbody>
|
148 |
+
<tr>
|
149 |
+
<th>Aktien</th>
|
150 |
+
<td>—</td>
|
151 |
+
<td class="correlation-positive">+0.68</td>
|
152 |
+
<td class="correlation-negative">-0.42</td>
|
153 |
+
<td class="correlation-neutral">+0.12</td>
|
154 |
+
</tr>
|
155 |
+
<tr>
|
156 |
+
<th>Anleihen</th>
|
157 |
+
<td class="correlation-positive">+0.68</td>
|
158 |
+
<td>—</td>
|
159 |
+
<td class="correlation-negative">-0.71</td>
|
160 |
+
<td class="correlation-negative">-0.53</td>
|
161 |
+
</tr>
|
162 |
+
<tr>
|
163 |
+
<th>Rohstoffe</th>
|
164 |
+
<td class="correlation-negative">-0.42</td>
|
165 |
+
<td class="correlation-negative">-0.71</td>
|
166 |
+
<td>—</td>
|
167 |
+
<td class="correlation-negative">-0.65</td>
|
168 |
+
</tr>
|
169 |
+
<tr>
|
170 |
+
<th>Dollar</th>
|
171 |
+
<td class="correlation-neutral">+0.12</td>
|
172 |
+
<td class="correlation-negative">-0.53</td>
|
173 |
+
<td class="correlation-negative">-0.65</td>
|
174 |
+
<td>—</td>
|
175 |
+
</tr>
|
176 |
+
</tbody>
|
177 |
+
</table>
|
178 |
+
</div>
|
179 |
+
|
180 |
+
<div>
|
181 |
+
<div class="widget">
|
182 |
+
<div class="widget-title">Marktrotation nach Murphy</div>
|
183 |
+
<div class="text-sm mb-4">
|
184 |
+
Aktuelle Zyklusposition: <span class="font-semibold">Späte Expansion / Frühe Inflation</span>
|
185 |
+
</div>
|
186 |
+
<ul class="text-sm">
|
187 |
+
<li class="mb-2"><span class="text-green-600 font-semibold">Führende Sektoren:</span> Energie, Materialien, Industrie</li>
|
188 |
+
<li class="mb-2"><span class="text-yellow-600 font-semibold">Neutrale Sektoren:</span> Finanzen, Gesundheit, Versorger</li>
|
189 |
+
<li class="mb-2"><span class="text-red-600 font-semibold">Schwächere Sektoren:</span> Technologie, Konsumgüter, Immobilien</li>
|
190 |
+
</ul>
|
191 |
+
<div class="mt-4 text-sm text-gray-600">
|
192 |
+
<p class="mb-2"><i class="fas fa-info-circle mr-1"></i> Die steigende Korrelation zwischen Aktien und Anleihen deutet auf einen möglichen Übergang in die Inflationsphase hin.</p>
|
193 |
+
<p><i class="fas fa-exclamation-triangle mr-1 text-yellow-600"></i> Die negative Korrelation zwischen Rohstoffen und Dollar hat sich verstärkt.</p>
|
194 |
+
</div>
|
195 |
+
</div>
|
196 |
+
</div>
|
197 |
+
</div>
|
198 |
+
</div>
|
199 |
+
</div>
|
200 |
+
|
201 |
+
<!-- Sessions Tab Content -->
|
202 |
+
<div class="tab-content" id="sessions">
|
203 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
204 |
+
<!-- Asia Session -->
|
205 |
+
<div class="section">
|
206 |
+
<h2 class="section-title">Asien Session</h2>
|
207 |
+
<div class="session-card" style="background: linear-gradient(145deg, #e74c3c, #c0392b);">
|
208 |
+
<div class="session-title">Asien Märkte</div>
|
209 |
+
<div class="session-time">00:00 - 09:00 CET</div>
|
210 |
+
<div class="session-status bg-gray-500">Geschlossen</div>
|
211 |
+
<div class="mt-4">
|
212 |
+
<p class="text-sm opacity-80">Nächste Session beginnt in 15h 30min</p>
|
213 |
+
</div>
|
214 |
+
</div>
|
215 |
+
|
216 |
+
<div class="widget mt-4">
|
217 |
+
<div class="widget-title">Session-Zeiten</div>
|
218 |
+
<table class="w-full text-sm">
|
219 |
+
<tr>
|
220 |
+
<td>Tokyo (JST):</td>
|
221 |
+
<td class="font-medium">09:00 - 18:00</td>
|
222 |
+
</tr>
|
223 |
+
<tr>
|
224 |
+
<td>Shanghai (CST):</td>
|
225 |
+
<td class="font-medium">09:30 - 15:00</td>
|
226 |
+
</tr>
|
227 |
+
<tr>
|
228 |
+
<td>Sydney (AEST):</td>
|
229 |
+
<td class="font-medium">10:00 - 16:00</td>
|
230 |
+
</tr>
|
231 |
+
<tr>
|
232 |
+
<td>Singapore (SGT):</td>
|
233 |
+
<td class="font-medium">09:00 - 17:00</td>
|
234 |
+
</tr>
|
235 |
+
</table>
|
236 |
+
</div>
|
237 |
+
|
238 |
+
<div class="widget mt-4">
|
239 |
+
<div class="widget-title">Wirtschaftskalender</div>
|
240 |
+
<div class="event-item">
|
241 |
+
<div class="flex justify-between">
|
242 |
+
<div>
|
243 |
+
<span class="event-impact high"></span>
|
244 |
+
<span class="event-name">BoJ Zinsentscheid</span>
|
245 |
+
</div>
|
246 |
+
<div class="event-time">00:30 CET</div>
|
247 |
+
</div>
|
248 |
+
<div class="text-sm text-gray-600 mt-1">
|
249 |
+
Prognose: Unverändert | Vorher: -0.10%
|
250 |
+
</div>
|
251 |
+
</div>
|
252 |
+
<div class="event-item">
|
253 |
+
<div class="flex justify-between">
|
254 |
+
<div>
|
255 |
+
<span class="event-impact medium"></span>
|
256 |
+
<span class="event-name">China PMI</span>
|
257 |
+
</div>
|
258 |
+
<div class="event-time">03:45 CET</div>
|
259 |
+
</div>
|
260 |
+
<div class="text-sm text-gray-600 mt-1">
|
261 |
+
Prognose: 50.5 | Vorher: 50.2
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
<div class="event-item">
|
265 |
+
<div class="flex justify-between">
|
266 |
+
<div>
|
267 |
+
<span class="event-impact low"></span>
|
268 |
+
<span class="event-name">Australien Einzelhandelsumsätze</span>
|
269 |
+
</div>
|
270 |
+
<div class="event-time">02:30 CET</div>
|
271 |
+
</div>
|
272 |
+
<div class="text-sm text-gray-600 mt-1">
|
273 |
+
Prognose: 0.3% | Vorher: 0.2%
|
274 |
+
</div>
|
275 |
+
</div>
|
276 |
+
</div>
|
277 |
+
|
278 |
+
<div class="widget mt-4">
|
279 |
+
<div class="widget-title">Marktbewegungen</div>
|
280 |
+
<div class="market-item">
|
281 |
+
<div class="market-name">Nikkei 225</div>
|
282 |
+
<div class="market-value up">+1.2%</div>
|
283 |
+
</div>
|
284 |
+
<div class="market-item">
|
285 |
+
<div class="market-name">Shanghai Composite</div>
|
286 |
+
<div class="market-value down">-0.5%</div>
|
287 |
+
</div>
|
288 |
+
<div class="market-item">
|
289 |
+
<div class="market-name">Hang Seng</div>
|
290 |
+
<div class="market-value up">+0.8%</div>
|
291 |
+
</div>
|
292 |
+
<div class="market-item">
|
293 |
+
<div class="market-name">ASX 200</div>
|
294 |
+
<div class="market-value up">+0.3%</div>
|
295 |
+
</div>
|
296 |
+
</div>
|
297 |
+
</div>
|
298 |
+
|
299 |
+
<!-- Europe Session -->
|
300 |
+
<div class="section">
|
301 |
+
<h2 class="section-title">Europa Session</h2>
|
302 |
+
<div class="session-card" style="background: linear-gradient(145deg, #3498db, #2980b9);">
|
303 |
+
<div class="session-title">Europa Märkte</div>
|
304 |
+
<div class="session-time">08:00 - 16:30 CET</div>
|
305 |
+
<div class="session-status bg-green-500">Aktiv</div>
|
306 |
+
<div class="mt-4">
|
307 |
+
<p class="text-sm opacity-80">Aktive Session läuft noch 3h 15min</p>
|
308 |
+
</div>
|
309 |
+
</div>
|
310 |
+
|
311 |
+
<div class="widget mt-4">
|
312 |
+
<div class="widget-title">Session-Zeiten</div>
|
313 |
+
<table class="w-full text-sm">
|
314 |
+
<tr>
|
315 |
+
<td>London (GMT):</td>
|
316 |
+
<td class="font-medium">08:00 - 16:30</td>
|
317 |
+
</tr>
|
318 |
+
<tr>
|
319 |
+
<td>Frankfurt (CET):</td>
|
320 |
+
<td class="font-medium">09:00 - 17:30</td>
|
321 |
+
</tr>
|
322 |
+
<tr>
|
323 |
+
<td>Paris (CET):</td>
|
324 |
+
<td class="font-medium">09:00 - 17:30</td>
|
325 |
+
</tr>
|
326 |
+
<tr>
|
327 |
+
<td>Zürich (CET):</td>
|
328 |
+
<td class="font-medium">09:00 - 17:30</td>
|
329 |
+
</tr>
|
330 |
+
</table>
|
331 |
+
</div>
|
332 |
+
|
333 |
+
<div class="widget mt-4">
|
334 |
+
<div class="widget-title">Wirtschaftskalender</div>
|
335 |
+
<div class="event-item">
|
336 |
+
<div class="flex justify-between">
|
337 |
+
<div>
|
338 |
+
<span class="event-impact high"></span>
|
339 |
+
<span class="event-name">DE Verbraucherpreisindex</span>
|
340 |
+
</div>
|
341 |
+
<div class="event-time">14:00 CET</div>
|
342 |
+
</div>
|
343 |
+
<div class="text-sm text-gray-600 mt-1">
|
344 |
+
Prognose: 2.3% | Vorher: 2.2%
|
345 |
+
</div>
|
346 |
+
</div>
|
347 |
+
<div class="event-item">
|
348 |
+
<div class="flex justify-between">
|
349 |
+
<div>
|
350 |
+
<span class="event-impact medium"></span>
|
351 |
+
<span class="event-name">UK BIP</span>
|
352 |
+
</div>
|
353 |
+
<div class="event-time">10:30 CET</div>
|
354 |
+
</div>
|
355 |
+
<div class="text-sm text-gray-600 mt-1">
|
356 |
+
Prognose: 0.4% | Vorher: 0.3%
|
357 |
+
</div>
|
358 |
+
</div>
|
359 |
+
<div class="event-item">
|
360 |
+
<div class="flex justify-between">
|
361 |
+
<div>
|
362 |
+
<span class="event-impact high"></span>
|
363 |
+
<span class="event-name">EZB Zinsentscheid</span>
|
364 |
+
</div>
|
365 |
+
<div class="event-time">13:45 CET</div>
|
366 |
+
</div>
|
367 |
+
<div class="text-sm text-gray-600 mt-1">
|
368 |
+
Prognose: Unverändert | Vorher: 4.50%
|
369 |
+
</div>
|
370 |
+
</div>
|
371 |
+
</div>
|
372 |
+
|
373 |
+
<div class="widget mt-4">
|
374 |
+
<div class="widget-title">Marktbewegungen</div>
|
375 |
+
<div class="market-item">
|
376 |
+
<div class="market-name">DAX</div>
|
377 |
+
<div class="market-value up">+0.9%</div>
|
378 |
+
</div>
|
379 |
+
<div class="market-item">
|
380 |
+
<div class="market-name">FTSE 100</div>
|
381 |
+
<div class="market-value up">+0.3%</div>
|
382 |
+
</div>
|
383 |
+
<div class="market-item">
|
384 |
+
<div class="market-name">CAC 40</div>
|
385 |
+
<div class="market-value down">-0.1%</div>
|
386 |
+
</div>
|
387 |
+
<div class="market-item">
|
388 |
+
<div class="market-name">EURO STOXX 50</div>
|
389 |
+
<div class="market-value up">+0.6%</div>
|
390 |
+
</div>
|
391 |
+
</div>
|
392 |
+
</div>
|
393 |
+
|
394 |
+
<!-- US Session -->
|
395 |
+
<div class="section">
|
396 |
+
<h2 class="section-title">USA Session</h2>
|
397 |
+
<div class="session-card" style="background: linear-gradient(145deg, #2ecc71, #27ae60);">
|
398 |
+
<div class="session-title">US Märkte</div>
|
399 |
+
<div class="session-time">14:30 - 21:00 CET</div>
|
400 |
+
<div class="session-status bg-yellow-500">Beginnt bald</div>
|
401 |
+
<div class="mt-4">
|
402 |
+
<p class="text-sm opacity-80">Session beginnt in 3h 20min</p>
|
403 |
+
</div>
|
404 |
+
</div>
|
405 |
+
|
406 |
+
<div class="widget mt-4">
|
407 |
+
<div class="widget-title">Session-Zeiten</div>
|
408 |
+
<table class="w-full text-sm">
|
409 |
+
<tr>
|
410 |
+
<td>New York (EDT):</td>
|
411 |
+
<td class="font-medium">09:30 - 16:00</td>
|
412 |
+
</tr>
|
413 |
+
<tr>
|
414 |
+
<td>Chicago (CDT):</td>
|
415 |
+
<td class="font-medium">08:30 - 15:00</td>
|
416 |
+
</tr>
|
417 |
+
<tr>
|
418 |
+
<td>Futures (Globex):</td>
|
419 |
+
<td class="font-medium">18:00 - 17:00</td>
|
420 |
+
</tr>
|
421 |
+
<tr>
|
422 |
+
<td>After-Hours:</td>
|
423 |
+
<td class="font-medium">16:00 - 20:00</td>
|
424 |
+
</tr>
|
425 |
+
</table>
|
426 |
+
</div>
|
427 |
+
|
428 |
+
<div class="widget mt-4">
|
429 |
+
<div class="widget-title">Wirtschaftskalender</div>
|
430 |
+
<div class="event-item">
|
431 |
+
<div class="flex justify-between">
|
432 |
+
<div>
|
433 |
+
<span class="event-impact high"></span>
|
434 |
+
<span class="event-name">US Non-Farm Payrolls</span>
|
435 |
+
</div>
|
436 |
+
<div class="event-time">14:30 CET</div>
|
437 |
+
</div>
|
438 |
+
<div class="text-sm text-gray-600 mt-1">
|
439 |
+
Prognose: 180K | Vorher: 175K
|
440 |
+
</div>
|
441 |
+
</div>
|
442 |
+
<div class="event-item">
|
443 |
+
<div class="flex justify-between">
|
444 |
+
<div>
|
445 |
+
<span class="event-impact high"></span>
|
446 |
+
<span class="event-name">US Arbeitslosenquote</span>
|
447 |
+
</div>
|
448 |
+
<div class="event-time">14:30 CET</div>
|
449 |
+
</div>
|
450 |
+
<div class="text-sm text-gray-600 mt-1">
|
451 |
+
Prognose: 3.8% | Vorher: 3.9%
|
452 |
+
</div>
|
453 |
+
</div>
|
454 |
+
<div class="event-item">
|
455 |
+
<div class="flex justify-between">
|
456 |
+
<div>
|
457 |
+
<span class="event-impact medium"></span>
|
458 |
+
<span class="event-name">US ISM Dienstleistungen</span>
|
459 |
+
</div>
|
460 |
+
<div class="event-time">16:00 CET</div>
|
461 |
+
</div>
|
462 |
+
<div class="text-sm text-gray-600 mt-1">
|
463 |
+
Prognose: 52.0 | Vorher: 51.4
|
464 |
+
</div>
|
465 |
+
</div>
|
466 |
+
</div>
|
467 |
+
|
468 |
+
<div class="widget mt-4">
|
469 |
+
<div class="widget-title">Marktbewegungen</div>
|
470 |
+
<div class="market-item">
|
471 |
+
<div class="market-name">S&P 500 (Futures)</div>
|
472 |
+
<div class="market-value up">+0.5%</div>
|
473 |
+
</div>
|
474 |
+
<div class="market-item">
|
475 |
+
<div class="market-name">NASDAQ (Futures)</div>
|
476 |
+
<div class="market-value up">+0.7%</div>
|
477 |
+
</div>
|
478 |
+
<div class="market-item">
|
479 |
+
<div class="market-name">Dow Jones (Futures)</div>
|
480 |
+
<div class="market-value up">+0.3%</div>
|
481 |
+
</div>
|
482 |
+
<div class="market-item">
|
483 |
+
<div class="market-name">VIX</div>
|
484 |
+
<div class="market-value down">-4.2%</div>
|
485 |
+
</div>
|
486 |
+
</div>
|
487 |
+
</div>
|
488 |
+
</div>
|
489 |
+
</div>
|
490 |
+
|
491 |
+
<!-- Intermarket Analysis Tab -->
|
492 |
+
<div class="tab-content" id="intermarket">
|
493 |
+
<div class="section">
|
494 |
+
<h2 class="section-title">Murphy's Intermarket-Analyse</h2>
|
495 |
+
<div class="mb-6">
|
496 |
+
<p class="text-gray-700 mb-4">Die Intermarket-Analyse nach Joseph Murphy basiert auf den Beziehungen zwischen den vier Hauptmärkten: Aktien, Anleihen, Rohstoffe und Währungen. Diese Beziehungen helfen Tradern, größere Markttrends zu verstehen und zu antizipieren.</p>
|
497 |
+
|
498 |
+
<div class="bg-blue-50 p-4 rounded-lg border border-blue-200">
|
499 |
+
<h3 class="font-semibold text-blue-800 mb-2">Aktueller Wirtschaftszyklus:</h3>
|
500 |
+
<div class="flex items-center">
|
501 |
+
<div class="w-full bg-gray-200 rounded-full h-4">
|
502 |
+
<div class="bg-blue-600 h-4 rounded-full" style="width: 65%"></div>
|
503 |
+
</div>
|
504 |
+
<span class="ml-2 text-sm font-medium">65%</span>
|
505 |
+
</div>
|
506 |
+
<div class="flex justify-between text-xs text-gray-600 mt-1">
|
507 |
+
<span>Erholung</span>
|
508 |
+
<span>Expansion</span>
|
509 |
+
<span>Inflation</span>
|
510 |
+
<span>Abschwächung</span>
|
511 |
+
</div>
|
512 |
+
<p class="text-sm text-blue-700 mt-3"><i class="fas fa-info-circle mr-1"></i> Wir befinden uns in der späten Expansion mit Anzeichen eines Übergangs zur Inflationsphase.</p>
|
513 |
+
</div>
|
514 |
+
</div>
|
515 |
+
|
516 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
517 |
+
<div class="correlation-matrix">
|
518 |
+
<div class="matrix-title">Intermarket-Beziehungen</div>
|
519 |
+
<table class="matrix-table">
|
520 |
+
<thead>
|
521 |
+
<tr>
|
522 |
+
<th></th>
|
523 |
+
<th>Aktien</th>
|
524 |
+
<th>Anleihen</th>
|
525 |
+
<th>Rohstoffe</th>
|
526 |
+
<th>Dollar</th>
|
527 |
+
</tr>
|
528 |
+
</thead>
|
529 |
+
<tbody>
|
530 |
+
<tr>
|
531 |
+
<th>Aktien</th>
|
532 |
+
<td>—</td>
|
533 |
+
<td class="correlation-positive">+0.68</td>
|
534 |
+
<td class="correlation-negative">-0.42</td>
|
535 |
+
<td class="correlation-neutral">+0.12</td>
|
536 |
+
</tr>
|
537 |
+
<tr>
|
538 |
+
<th>Anleihen</th>
|
539 |
+
<td class="correlation-positive">+0.68</td>
|
540 |
+
<td>—</td>
|
541 |
+
<td class="correlation-negative">-0.71</td>
|
542 |
+
<td class="correlation-negative">-0.53</td>
|
543 |
+
</tr>
|
544 |
+
<tr>
|
545 |
+
<th>Rohstoffe</th>
|
546 |
+
<td class="correlation-negative">-0.42</td>
|
547 |
+
<td class="correlation-negative">-0.71</td>
|
548 |
+
<td>—</td>
|
549 |
+
<td class="correlation-negative">-0.65</td>
|
550 |
+
</tr>
|
551 |
+
<tr>
|
552 |
+
<th>Dollar</th>
|
553 |
+
<td class="correlation-neutral">+0.12</td>
|
554 |
+
<td class="correlation-negative">-0.53</td>
|
555 |
+
<td class="correlation-negative">-0.65</td>
|
556 |
+
<td>—</td>
|
557 |
+
</tr>
|
558 |
+
</tbody>
|
559 |
+
</table>
|
560 |
+
<div class="text-sm text-gray-600 mt-3">
|
561 |
+
<p class="mb-1"><span class="text-green-600 font-medium">Positive Korrelation</span>: Märkte bewegen sich tendenziell in dieselbe Richtung</p>
|
562 |
+
<p class="mb-1"><span class="text-red-600 font-medium">Negative Korrelation</span>: Märkte bewegen sich tendenziell in entgegengesetzte Richtungen</p>
|
563 |
+
<p><span class="text-yellow-600 font-medium">Neutrale Korrelation</span>: Keine starke Beziehung zwischen den Märkten</p>
|
564 |
+
</div>
|
565 |
+
</div>
|
566 |
+
|
567 |
+
<div>
|
568 |
+
<div class="widget">
|
569 |
+
<div class="widget-title">Aktuelle Marktdynamik</div>
|
570 |
+
<ul class="text-sm space-y-3">
|
571 |
+
<li>
|
572 |
+
<span class="font-medium">Anleihen ↔ Aktien:</span>
|
573 |
+
<div class="text-gray-700">Anleiherenditen fallen, Aktienmarkt steigt. Diese positive Korrelation ist typisch für die Expansionsphase.</div>
|
574 |
+
</li>
|
575 |
+
<li>
|
576 |
+
<span class="font-medium">Anleihen ↔ Rohstoffe:</span>
|
577 |
+
<div class="text-gray-700">Anleihepreise fallen, Rohstoffpreise steigen. Diese negative Korrelation deutet auf steigende Inflationserwartungen hin.</div>
|
578 |
+
</li>
|
579 |
+
<li>
|
580 |
+
<span class="font-medium">Rohstoffe ↔ Dollar:</span>
|
581 |
+
<div class="text-gray-700">Der US-Dollar schwächt sich ab, während Rohstoffpreise steigen. Diese inverse Beziehung ist besonders für Goldtrader relevant.</div>
|
582 |
+
</li>
|
583 |
+
<li>
|
584 |
+
<span class="font-medium">Dollar ↔ Aktien:</span>
|
585 |
+
<div class="text-gray-700">Aktuell eine schwache Korrelation, was typisch für die Übergangsphase zwischen Expansion und Inflation ist.</div>
|
586 |
+
</li>
|
587 |
+
</ul>
|
588 |
+
</div>
|
589 |
+
|
590 |
+
<div class="widget mt-4">
|
591 |
+
<div class="widget-title">Wichtige Divergenzen</div>
|
592 |
+
<div class="p-3 bg-yellow-50 rounded-lg border border-yellow-200 mb-3">
|
593 |
+
<div class="font-medium text-yellow-800">Rohstoffe vs. Anleiherenditen</div>
|
594 |
+
<div class="text-sm text-gray-700 mt-1">Rohstoffpreise steigen stärker als Anleiherenditen, was auf verstärkte Inflationsrisiken hindeuten könnte.</div>
|
595 |
+
</div>
|
596 |
+
<div class="p-3 bg-red-50 rounded-lg border border-red-200">
|
597 |
+
<div class="font-medium text-red-800">Aktien vs. Wirtschaftsdaten</div>
|
598 |
+
<div class="text-sm text-gray-700 mt-1">Aktienindizes steigen trotz gemischter Wirtschaftsdaten, was auf eine mögliche Überbewertung hindeuten könnte.</div>
|
599 |
+
</div>
|
600 |
+
</div>
|
601 |
+
</div>
|
602 |
+
</div>
|
603 |
+
|
604 |
+
<div class="mt-6">
|
605 |
+
<h3 class="font-semibold text-lg mb-3">Symbole für Intermarket-Analyse</h3>
|
606 |
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
607 |
+
<div class="form-group">
|
608 |
+
<label class="block text-gray-700 mb-2 text-sm">Aktienindex auswählen:</label>
|
609 |
+
<select id="stockIndex" class="select-control">
|
610 |
+
<option value="PEPPERSTONE:US500">S&P 500</option>
|
611 |
+
<option value="PEPPERSTONE:NAS100">NASDAQ 100</option>
|
612 |
+
<option value="PEPPERSTONE:US30">Dow Jones</option>
|
613 |
+
<option value="PEPPERSTONE:GER40">DAX</option>
|
614 |
+
</select>
|
615 |
+
</div>
|
616 |
+
|
617 |
+
<div class="form-group">
|
618 |
+
<label class="block text-gray-700 mb-2 text-sm">Anleihe auswählen:</label>
|
619 |
+
<select id="bondIndex" class="select-control">
|
620 |
+
<option value="TVC:TNX">US 10Y Rendite</option>
|
621 |
+
<option value="CBOT:ZB1!">US T-Bond Futures</option>
|
622 |
+
<option value="TVC:DE10Y">DE 10Y Rendite</option>
|
623 |
+
</select>
|
624 |
+
</div>
|
625 |
+
|
626 |
+
<div class="form-group">
|
627 |
+
<label class="block text-gray-700 mb-2 text-sm">Rohstoff auswählen:</label>
|
628 |
+
<select id="commodityIndex" class="select-control">
|
629 |
+
<option value="PEPPERSTONE:XAUUSD">Gold</option>
|
630 |
+
<option value="PEPPERSTONE:SPOTCRUDE">Rohöl</option>
|
631 |
+
<option value="COMEX:SI1!">Silber</option>
|
632 |
+
<option value="NYMEX:NG1!">Erdgas</option>
|
633 |
+
</select>
|
634 |
+
</div>
|
635 |
+
|
636 |
+
<div class="form-group">
|
637 |
+
<label class="block text-gray-700 mb-2 text-sm">Währung auswählen:</label>
|
638 |
+
<select id="currencyIndex" class="select-control">
|
639 |
+
<option value="PEPPERSTONE:USDX">US Dollar Index</option>
|
640 |
+
<option value="PEPPERSTONE:EURUSD">EUR/USD</option>
|
641 |
+
<option value="FX:USDJPY">USD/JPY</option>
|
642 |
+
</select>
|
643 |
+
</div>
|
644 |
+
</div>
|
645 |
+
|
646 |
+
<div class="mt-4 text-center">
|
647 |
+
<button class="btn-primary" onclick="updateIntermarketAnalysis()">Intermarket-Analyse aktualisieren</button>
|
648 |
+
</div>
|
649 |
+
</div>
|
650 |
+
</div>
|
651 |
+
</div>
|
652 |
+
|
653 |
+
<!-- Affirmations Tab -->
|
654 |
+
<div class="tab-content" id="affirmations">
|
655 |
+
<div class="section">
|
656 |
+
<h2 class="section-title">Trading-Affirmationen</h2>
|
657 |
+
|
658 |
+
<div class="mb-6">
|
659 |
+
<div class="form-group">
|
660 |
+
<label class="block text-gray-700 mb-2">Dein aktueller Trader-Status:</label>
|
661 |
+
<select id="affirmationTraderStatus" class="select-control" onchange="updateAffirmationTab()">
|
662 |
+
<option value="preparing">Vorbereitung auf Trade</option>
|
663 |
+
<option value="active">Aktiver Trade läuft</option>
|
664 |
+
<option value="developing">Entwickle Strategie</option>
|
665 |
+
<option value="break">Pause / Reflexion</option>
|
666 |
+
</select>
|
667 |
+
</div>
|
668 |
+
|
669 |
+
<div class="form-group mt-4">
|
670 |
+
<label class="block text-gray-700 mb-2">Affirmations-Kategorie:</label>
|
671 |
+
<select id="affirmationCategory" class="select-control" onchange="updateAffirmationTab()">
|
672 |
+
<option value="discipline">Disziplin und Geduld entwickeln</option>
|
673 |
+
<option value="abundance">Überflussdenken fördern</option>
|
674 |
+
<option value="selection">Handelsauswahl verbessern</option>
|
675 |
+
<option value="burnout">Burnout und Erschöpfung überwinden</option>
|
676 |
+
<option value="bias">Bestätigungsfehler überwinden</option>
|
677 |
+
<option value="paralysis">Entscheidungslähmung überwinden</option>
|
678 |
+
<option value="fomo">FOMO überwinden</option>
|
679 |
+
<option value="losses">Verluste schnell akzeptieren</option>
|
680 |
+
<option value="emotional">Emotionale Bindung an Trades lösen</option>
|
681 |
+
<option value="overtrading">Überhandel widerstehen</option>
|
682 |
+
<option value="patience">Geduld bei langsamen Märkten bewahren</option>
|
683 |
+
</select>
|
684 |
+
</div>
|
685 |
+
</div>
|
686 |
+
|
687 |
+
<div class="affirmation-card" id="detailedAffirmation">
|
688 |
+
<div class="affirmation-text">
|
689 |
+
Ich bin ein disziplinierter und geduldiger Trader, der seinem Handelsplan mit unerschütterlichem Engagement folgt. Ich vertraue auf die Wirksamkeit meiner Strategien und warte geduldig auf Setups mit hoher Wahrscheinlichkeit. Ich habe die Selbstkontrolle, mich an meine etablierten Regeln zu halten und impulsive Handlungen zu vermeiden.
|
690 |
+
</div>
|
691 |
+
<div class="affirmation-category">
|
692 |
+
Disziplin und Geduld entwickeln
|
693 |
+
</div>
|
694 |
+
</div>
|
695 |
+
|
696 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
|
697 |
+
<div class="widget">
|
698 |
+
<div class="widget-title">Affirmations-Timer</div>
|
699 |
+
<div class="text-center py-4">
|
700 |
+
<div class="text-4xl font-bold" id="affirmationTimer">05:00</div>
|
701 |
+
<div class="flex justify-center gap-3 mt-4">
|
702 |
+
<button class="btn-primary" id="startTimerBtn" onclick="startAffirmationTimer()">Start</button>
|
703 |
+
<button class="btn-primary" id="pauseTimerBtn" onclick="pauseAffirmationTimer()" disabled>Pause</button>
|
704 |
+
<button class="btn-primary" id="resetTimerBtn" onclick="resetAffirmationTimer()">Reset</button>
|
705 |
+
</div>
|
706 |
+
<div class="mt-4 text-sm text-gray-600">
|
707 |
+
Für optimale Ergebnisse, wiederhole die Affirmation während des Timers laut oder in Gedanken.
|
708 |
+
</div>
|
709 |
+
</div>
|
710 |
+
</div>
|
711 |
+
|
712 |
+
<div class="widget">
|
713 |
+
<div class="widget-title">Meine gespeicherten Affirmationen</div>
|
714 |
+
<div id="savedAffirmations">
|
715 |
+
<div class="event-item">
|
716 |
+
<div class="font-medium">Disziplin beim Warten auf Setups</div>
|
717 |
+
<div class="text-sm text-gray-600">Kategorie: Disziplin und Geduld</div>
|
718 |
+
</div>
|
719 |
+
<div class="event-item">
|
720 |
+
<div class="font-medium">Überwindung von FOMO bei volatilen Märkten</div>
|
721 |
+
<div class="text-sm text-gray-600">Kategorie: FOMO überwinden</div>
|
722 |
+
</div>
|
723 |
+
<div class="event-item">
|
724 |
+
<div class="font-medium">Emotionsloses Trading nach Verlusten</div>
|
725 |
+
<div class="text-sm text-gray-600">Kategorie: Verluste akzeptieren</div>
|
726 |
+
</div>
|
727 |
+
</div>
|
728 |
+
<div class="mt-4 text-center">
|
729 |
+
<button class="btn-primary" onclick="saveCurrentAffirmation()">Aktuelle Affirmation speichern</button>
|
730 |
+
</div>
|
731 |
+
</div>
|
732 |
+
</div>
|
733 |
+
|
734 |
+
<div class="mt-6">
|
735 |
+
<h3 class="font-semibold text-lg mb-3">Affirmations-Journal</h3>
|
736 |
+
<div class="form-group">
|
737 |
+
<label class="block text-gray-700 mb-2">Deine Gedanken zur heutigen Affirmation:</label>
|
738 |
+
<textarea class="form-control h-32" placeholder="Notiere, wie die Affirmation dein Trading heute beeinflusst hat..."></textarea>
|
739 |
+
</div>
|
740 |
+
<div class="mt-3 text-center">
|
741 |
+
<button class="btn-primary">Im Journal speichern</button>
|
742 |
+
</div>
|
743 |
+
</div>
|
744 |
+
</div>
|
745 |
+
</div>
|
746 |
+
|
747 |
+
<!-- Trading Opportunities Tab -->
|
748 |
+
<div class="tab-content" id="opportunities">
|
749 |
+
<div class="section">
|
750 |
+
<h2 class="section-title">Trading Opportunities mit Gemini AI</h2>
|
751 |
+
|
752 |
+
<div class="mb-6 p-4 bg-gradient-to-r from-blue-50 to-purple-50 rounded-lg border border-blue-100">
|
753 |
+
<div class="flex items-center mb-3">
|
754 |
+
<h3 class="font-semibold text-blue-800">Gemini AI Trading Analysis</h3>
|
755 |
+
<span class="gemini-badge ml-2">Powered by Google</span>
|
756 |
+
</div>
|
757 |
+
<p class="text-gray-700 text-sm mb-4">Gemini analysiert aktuelle Marktdaten, Nachrichten und technische Indikatoren, um Trading-Opportunitäten zu identifizieren. Die Analyse kombiniert KI-Erkenntnisse mit Intermarket-Beziehungen.</p>
|
758 |
+
</div>
|
759 |
+
|
760 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
761 |
+
<div>
|
762 |
+
<div class="form-group">
|
763 |
+
<label class="block text-gray-700 mb-2">Symbol auswählen:</label>
|
764 |
+
<select id="opportunitySymbol" class="select-control">
|
765 |
+
<option value="">-- Symbol wählen --</option>
|
766 |
+
<option value="NASDAQ:AAPL">NASDAQ:AAPL - Apple</option>
|
767 |
+
<option value="NASDAQ:NVDA">NASDAQ:NVDA - NVIDIA</option>
|
768 |
+
<option value="NASDAQ:ASML">NASDAQ:ASML - ASML Holding</option>
|
769 |
+
<option value="BINANCE:BNBUSDT">BINANCE:BNBUSDT - Binance Coin</option>
|
770 |
+
<option value="NASDAQ:INTC">NASDAQ:INTC - Intel</option>
|
771 |
+
<option value="CAPITALCOM:BTCUSD">CAPITALCOM:BTCUSD - Bitcoin</option>
|
772 |
+
<option value="AMEX:GLD">AMEX:GLD - Gold ETF</option>
|
773 |
+
<option value="PEPPERSTONE:EURUSD">PEPPERSTONE:EURUSD - EUR/USD</option>
|
774 |
+
<option value="PEPPERSTONE:US500">PEPPERSTONE:US500 - S&P 500</option>
|
775 |
+
<option value="FX:USDJPY">FX:USDJPY - USD/JPY</option>
|
776 |
+
</select>
|
777 |
+
</div>
|
778 |
+
|
779 |
+
<div class="form-group">
|
780 |
+
<label class="block text-gray-700 mb-2">Zeitrahmen:</label>
|
781 |
+
<select id="opportunityTimeframe" class="select-control">
|
782 |
+
<option value="shortTerm">Kurzfristig (Intraday - 1-3 Tage)</option>
|
783 |
+
<option value="mediumTerm">Mittelfristig (Swing - 1-2 Wochen)</option>
|
784 |
+
<option value="longTerm">Langfristig (Position - 1+ Monate)</option>
|
785 |
+
</select>
|
786 |
+
</div>
|
787 |
+
|
788 |
+
<div class="form-group">
|
789 |
+
<label class="block text-gray-700 mb-2">Analysestil:</label>
|
790 |
+
<select id="opportunityStyle" class="select-control">
|
791 |
+
<option value="technical">Technische Analyse</option>
|
792 |
+
<option value="fundamental">Fundamentale Analyse</option>
|
793 |
+
<option value="sentiment">Sentiment-Analyse</option>
|
794 |
+
<option value="combined">Kombiniert (empfohlen)</option>
|
795 |
+
</select>
|
796 |
+
</div>
|
797 |
+
|
798 |
+
<div class="form-group">
|
799 |
+
<label class="block text-gray-700 mb-2">Zusätzliche Anweisungen (optional):</label>
|
800 |
+
<textarea id="opportunityInstructions" class="form-control h-24" placeholder="Z.B.: Fokus auf Unterstützungs-/Widerstandsniveaus, bestimmte Nachrichten berücksichtigen, etc."></textarea>
|
801 |
+
</div>
|
802 |
+
|
803 |
+
<div class="mt-4">
|
804 |
+
<button class="btn-primary w-full py-3" onclick="generateOpportunity()">
|
805 |
+
<span id="analyzeText">Trading Opportunity analysieren</span>
|
806 |
+
<div class="loading-spinner" id="opportunitySpinner"></div>
|
807 |
+
</button>
|
808 |
+
</div>
|
809 |
+
</div>
|
810 |
+
|
811 |
+
<div class="bg-white p-4 rounded-lg border border-gray-200" id="opportunityResults">
|
812 |
+
<div class="text-center py-10 text-gray-500">
|
813 |
+
<i class="fas fa-chart-line text-4xl mb-3 text-gray-300"></i>
|
814 |
+
<p>Wähle ein Symbol und klicke auf "Analysieren", um eine KI-gestützte Trading-Opportunity zu erhalten.</p>
|
815 |
+
</div>
|
816 |
+
</div>
|
817 |
+
</div>
|
818 |
+
|
819 |
+
<div class="mt-6 p-4 bg-gray-50 rounded-lg">
|
820 |
+
<h3 class="font-semibold mb-3">Gespeicherte Opportunitäten</h3>
|
821 |
+
<div id="savedOpportunities">
|
822 |
+
<div class="p-3 bg-white rounded-lg shadow-sm mb-3">
|
823 |
+
<div class="flex justify-between items-start">
|
824 |
+
<div>
|
825 |
+
<div class="font-medium">NASDAQ:AAPL - Unterstützungstest</div>
|
826 |
+
<div class="text-sm text-gray-600">Kurzfristig · Technische Analyse</div>
|
827 |
+
</div>
|
828 |
+
<div class="badge badge-success">Long</div>
|
829 |
+
</div>
|
830 |
+
<div class="text-sm mt-2">
|
831 |
+
Apple testet wichtige Unterstützung bei $180. Technische Indikatoren deuten auf überkaufte Bedingungen hin, mit potenziellem Aufschwung.
|
832 |
+
</div>
|
833 |
+
</div>
|
834 |
+
|
835 |
+
<div class="p-3 bg-white rounded-lg shadow-sm">
|
836 |
+
<div class="flex justify-between items-start">
|
837 |
+
<div>
|
838 |
+
<div class="font-medium">PEPPERSTONE:EURUSD - EZB vs Fed Divergenz</div>
|
839 |
+
<div class="text-sm text-gray-600">Mittelfristig · Fundamental</div>
|
840 |
+
</div>
|
841 |
+
<div class="badge badge-danger">Short</div>
|
842 |
+
</div>
|
843 |
+
<div class="text-sm mt-2">
|
844 |
+
EUR/USD könnte unter Druck geraten aufgrund zunehmender Diskrepanz zwischen EZB und Fed Politik. Nächstes Unterstützungsniveau bei 1.0780.
|
845 |
+
</div>
|
846 |
+
</div>
|
847 |
+
</div>
|
848 |
+
</div>
|
849 |
+
</div>
|
850 |
+
</div>
|
851 |
+
|
852 |
+
<!-- My Trades Tab -->
|
853 |
+
<div class="tab-content" id="mytrades">
|
854 |
+
<div class="section">
|
855 |
+
<h2 class="section-title">Meine Trades</h2>
|
856 |
+
|
857 |
+
<div class="mb-6">
|
858 |
+
<div class="bg-white p-4 rounded-lg border border-gray-200">
|
859 |
+
<h3 class="font-semibold mb-3">Neuen Trade hinzufügen</h3>
|
860 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
861 |
+
<div class="form-group">
|
862 |
+
<label class="block text-gray-700 mb-2 text-sm">Symbol:</label>
|
863 |
+
<select id="newTradeSymbol" class="select-control">
|
864 |
+
<option value="">-- Symbol wählen --</option>
|
865 |
+
<option value="NASDAQ:AAPL">NASDAQ:AAPL - Apple</option>
|
866 |
+
<option value="NASDAQ:NVDA">NASDAQ:NVDA - NVIDIA</option>
|
867 |
+
<option value="NASDAQ:ASML">NASDAQ:ASML - ASML Holding</option>
|
868 |
+
<option value="BINANCE:BNBUSDT">BINANCE:BNBUSDT - Binance Coin</option>
|
869 |
+
<option value="NASDAQ:INTC">NASDAQ:INTC - Intel</option>
|
870 |
+
<option value="CAPITALCOM:BTCUSD">CAPITALCOM:BTCUSD - Bitcoin</option>
|
871 |
+
<option value="AMEX:GLD">AMEX:GLD - Gold ETF</option>
|
872 |
+
<option value="PEPPERSTONE:EURUSD">PEPPERSTONE:EURUSD - EUR/USD</option>
|
873 |
+
<option value="PEPPERSTONE:US500">PEPPERSTONE:US500 - S&P 500</option>
|
874 |
+
<option value="FX:USDJPY">FX:USDJPY - USD/JPY</option>
|
875 |
+
</select>
|
876 |
+
</div>
|
877 |
+
|
878 |
+
<div class="form-group">
|
879 |
+
<label class="block text-gray-700 mb-2 text-sm">Richtung:</label>
|
880 |
+
<select id="newTradeDirection" class="select-control">
|
881 |
+
<option value="long">Long</option>
|
882 |
+
<option value="short">Short</option>
|
883 |
+
</select>
|
884 |
+
</div>
|
885 |
+
|
886 |
+
<div class="form-group">
|
887 |
+
<label class="block text-gray-700 mb-2 text-sm">Einstiegspreis:</label>
|
888 |
+
<input type="number" step="0.0001" id="newTradeEntry" class="form-control" placeholder="z.B. 180.45">
|
889 |
+
</div>
|
890 |
+
|
891 |
+
<div class="form-group">
|
892 |
+
<label class="block text-gray-700 mb-2 text-sm">Stop-Loss:</label>
|
893 |
+
<input type="number" step="0.0001" id="newTradeStop" class="form-control" placeholder="z.B. 175.60">
|
894 |
+
</div>
|
895 |
+
|
896 |
+
<div class="form-group">
|
897 |
+
<label class="block text-gray-700 mb-2 text-sm">Take-Profit:</label>
|
898 |
+
<input type="number" step="0.0001" id="newTradeTarget" class="form-control" placeholder="z.B. 190.00">
|
899 |
+
</div>
|
900 |
+
|
901 |
+
<div class="form-group">
|
902 |
+
<label class="block text-gray-700 mb-2 text-sm">Position:</label>
|
903 |
+
<input type="text" id="newTradePosition" class="form-control" placeholder="z.B. 10 Anteile oder 0.5 Lots">
|
904 |
+
</div>
|
905 |
+
</div>
|
906 |
+
|
907 |
+
<div class="form-group mt-2">
|
908 |
+
<label class="block text-gray-700 mb-2 text-sm">Notizen:</label>
|
909 |
+
<textarea id="newTradeNotes" class="form-control h-20" placeholder="Trade-Begründung, Strategie, Beobachtungen..."></textarea>
|
910 |
+
</div>
|
911 |
+
|
912 |
+
<div class="mt-4 text-center">
|
913 |
+
<button class="btn-primary" onclick="addNewTrade()">Trade hinzufügen</button>
|
914 |
+
</div>
|
915 |
+
</div>
|
916 |
+
</div>
|
917 |
+
|
918 |
+
<div class="mt-6">
|
919 |
+
<div class="flex justify-between items-center mb-4">
|
920 |
+
<h3 class="font-semibold">Aktive Trades</h3>
|
921 |
+
<div class="flex gap-2">
|
922 |
+
<select id="tradeSortOption" class="select-control text-sm py-1" onchange="sortTrades()">
|
923 |
+
<option value="newest">Neueste zuerst</option>
|
924 |
+
<option value="oldest">Älteste zuerst</option>
|
925 |
+
<option value="profit">Höchster Gewinn</option>
|
926 |
+
<option value="loss">Höchster Verlust</option>
|
927 |
+
</select>
|
928 |
+
<button class="btn-primary text-sm py-1" onclick="refreshTrades()">
|
929 |
+
<i class="fas fa-sync-alt mr-1"></i> Aktualisieren
|
930 |
+
</button>
|
931 |
+
</div>
|
932 |
+
</div>
|
933 |
+
|
934 |
+
<div id="tradesList">
|
935 |
+
<div class="trade-item">
|
936 |
+
<div class="trade-symbol">NASDAQ:AAPL</div>
|
937 |
+
<div class="trade-details">Long @ 180.45 • Stop: 175.60 • Target: 190.00</div>
|
938 |
+
<div class="trade-pnl profit">+3.25%</div>
|
939 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: Heute, 10:15 CET</div>
|
940 |
+
<div class="mt-3 text-sm">
|
941 |
+
<div class="font-medium">Notizen:</div>
|
942 |
+
<p class="text-gray-600">Unterstützungsbereich getestet, MACD zeigt bullisches Crossover. Steigende Volumenmuster bestätigen den Trend.</p>
|
943 |
+
</div>
|
944 |
+
<div class="trade-actions">
|
945 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
946 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
947 |
+
</div>
|
948 |
+
</div>
|
949 |
+
|
950 |
+
<div class="trade-item">
|
951 |
+
<div class="trade-symbol">FX:EURUSD</div>
|
952 |
+
<div class="trade-details">Short @ 1.0890 • Stop: 1.0925 • Target: 1.0820</div>
|
953 |
+
<div class="trade-pnl loss">-0.12%</div>
|
954 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: Gestern, 16:45 CET</div>
|
955 |
+
<div class="mt-3 text-sm">
|
956 |
+
<div class="font-medium">Notizen:</div>
|
957 |
+
<p class="text-gray-600">Widerstandszone erreicht, RSI im überkauften Bereich. Divergenz zum US Dollar Index, was auf Schwäche hindeutet.</p>
|
958 |
+
</div>
|
959 |
+
<div class="trade-actions">
|
960 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
961 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
962 |
+
</div>
|
963 |
+
</div>
|
964 |
+
|
965 |
+
<div class="trade-item">
|
966 |
+
<div class="trade-symbol">CAPITALCOM:BTCUSD</div>
|
967 |
+
<div class="trade-details">Long @ 40250.00 • Stop: 39000.00 • Target: 45000.00</div>
|
968 |
+
<div class="trade-pnl profit">+7.47%</div>
|
969 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: 12.05.2023, 09:30 CET</div>
|
970 |
+
<div class="mt-3 text-sm">
|
971 |
+
<div class="font-medium">Notizen:</div>
|
972 |
+
<p class="text-gray-600">Ausbruch über wichtiges Fibonacci-Retracement. On-Chain-Metriken deuten auf Akkumulation hin. Reduzierte Verkaufsaktivität der Miner.</p>
|
973 |
+
</div>
|
974 |
+
<div class="trade-actions">
|
975 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
976 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
977 |
+
</div>
|
978 |
+
</div>
|
979 |
+
</div>
|
980 |
+
</div>
|
981 |
+
|
982 |
+
<div class="mt-6">
|
983 |
+
<h3 class="font-semibold mb-3">Trade-Statistiken</h3>
|
984 |
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
985 |
+
<div class="p-4 bg-white rounded-lg shadow-sm text-center">
|
986 |
+
<div class="text-gray-700 text-sm mb-1">Offene Trades</div>
|
987 |
+
<div class="text-2xl font-bold">3</div>
|
988 |
+
</div>
|
989 |
+
<div class="p-4 bg-white rounded-lg shadow-sm text-center">
|
990 |
+
<div class="text-gray-700 text-sm mb-1">Win Rate</div>
|
991 |
+
<div class="text-2xl font-bold text-green-600">67%</div>
|
992 |
+
</div>
|
993 |
+
<div class="p-4 bg-white rounded-lg shadow-sm text-center">
|
994 |
+
<div class="text-gray-700 text-sm mb-1">Durchschn. Gewinn</div>
|
995 |
+
<div class="text-2xl font-bold text-green-600">5.36%</div>
|
996 |
+
</div>
|
997 |
+
<div class="p-4 bg-white rounded-lg shadow-sm text-center">
|
998 |
+
<div class="text-gray-700 text-sm mb-1">Durchschn. Verlust</div>
|
999 |
+
<div class="text-2xl font-bold text-red-600">-0.12%</div>
|
1000 |
+
</div>
|
1001 |
+
</div>
|
1002 |
+
</div>
|
1003 |
+
</div>
|
1004 |
+
</div>
|
1005 |
+
</div>
|
1006 |
+
|
1007 |
+
<!-- Gemini Chat Popup -->
|
1008 |
+
<div class="chat-popup minimized" id="chatPopup">
|
1009 |
+
<div class="chat-header" onclick="toggleChat()">
|
1010 |
+
<div class="flex items-center">
|
1011 |
+
<i class="fas fa-robot mr-2"></i>
|
1012 |
+
<span>Gemini Trading Assistant</span>
|
1013 |
+
<span class="gemini-badge">AI</span>
|
1014 |
+
</div>
|
1015 |
+
<i class="fas fa-chevron-up" id="chatChevron"></i>
|
1016 |
+
</div>
|
1017 |
+
<div class="chat-body" id="chatBody">
|
1018 |
+
<div class="chat-message ai">
|
1019 |
+
<div class="message-content">
|
1020 |
+
Hallo! Ich bin dein Gemini Trading Assistant. Wie kann ich dir heute bei deinen Trading-Aktivitäten helfen?
|
1021 |
+
</div>
|
1022 |
+
</div>
|
1023 |
+
</div>
|
1024 |
+
<div class="chat-footer">
|
1025 |
+
<input type="text" id="chatInput" class="chat-input" placeholder="Frage stellen..." onkeypress="handleChatKeyPress(event)">
|
1026 |
+
<button class="btn-primary" onclick="sendChatMessage()">
|
1027 |
+
<i class="fas fa-paper-plane"></i>
|
1028 |
+
</button>
|
1029 |
+
</div>
|
1030 |
+
</div>
|
1031 |
+
|
1032 |
+
<script src="/static/js/main.js"></script>
|
1033 |
+
</body>
|
1034 |
+
</html>
|
static/js/main.js
ADDED
@@ -0,0 +1,1174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Global variables
|
2 |
+
let apiConnected = false;
|
3 |
+
let affirmationTimerInterval;
|
4 |
+
let affirmationTimeLeft = 300; // 5 minutes in seconds
|
5 |
+
let affirmationTimerRunning = false;
|
6 |
+
|
7 |
+
// Initialize the app
|
8 |
+
document.addEventListener('DOMContentLoaded', function() {
|
9 |
+
initTabs();
|
10 |
+
updateDateTime();
|
11 |
+
fetchAndUpdateTicker();
|
12 |
+
checkStoredApiKey();
|
13 |
+
|
14 |
+
// Set interval updates
|
15 |
+
setInterval(updateDateTime, 1000);
|
16 |
+
setInterval(fetchAndUpdateTicker, 60000); // Update ticker every minute
|
17 |
+
setInterval(updateActiveTrades, 5000);
|
18 |
+
|
19 |
+
// Check if Gemini API key is pre-set in environment
|
20 |
+
checkPresetGeminiKey();
|
21 |
+
});
|
22 |
+
|
23 |
+
// Check if Gemini API key is preset in environment variables
|
24 |
+
function checkPresetGeminiKey() {
|
25 |
+
fetch('/api/gemini_key')
|
26 |
+
.then(response => response.json())
|
27 |
+
.then(data => {
|
28 |
+
if (data.status === 'available') {
|
29 |
+
// Auto-fill the field with masked key
|
30 |
+
document.getElementById('geminiApiKey').value = data.key;
|
31 |
+
// Auto-connect if key is available
|
32 |
+
connectApi(true);
|
33 |
+
}
|
34 |
+
})
|
35 |
+
.catch(error => console.error('Error checking preset Gemini key:', error));
|
36 |
+
}
|
37 |
+
|
38 |
+
// Tab navigation
|
39 |
+
function initTabs() {
|
40 |
+
const tabs = document.querySelectorAll('.tab');
|
41 |
+
|
42 |
+
tabs.forEach(tab => {
|
43 |
+
tab.addEventListener('click', function() {
|
44 |
+
// Remove active class from all tabs and contents
|
45 |
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
46 |
+
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
47 |
+
|
48 |
+
// Add active class to clicked tab
|
49 |
+
this.classList.add('active');
|
50 |
+
|
51 |
+
// Show corresponding content
|
52 |
+
const tabId = this.getAttribute('data-tab');
|
53 |
+
document.getElementById(tabId).classList.add('active');
|
54 |
+
|
55 |
+
// Load tab-specific data if needed
|
56 |
+
if (tabId === 'sessions') {
|
57 |
+
loadSessionsData();
|
58 |
+
} else if (tabId === 'intermarket') {
|
59 |
+
loadIntermarketData();
|
60 |
+
}
|
61 |
+
});
|
62 |
+
});
|
63 |
+
}
|
64 |
+
|
65 |
+
// Load sessions data
|
66 |
+
function loadSessionsData() {
|
67 |
+
// Load Asia session data
|
68 |
+
fetch('/api/market_data/asia')
|
69 |
+
.then(response => response.json())
|
70 |
+
.then(data => {
|
71 |
+
updateMarketMovements('asia', data);
|
72 |
+
})
|
73 |
+
.catch(error => console.error('Error loading Asia data:', error));
|
74 |
+
|
75 |
+
// Load Europe session data
|
76 |
+
fetch('/api/market_data/europe')
|
77 |
+
.then(response => response.json())
|
78 |
+
.then(data => {
|
79 |
+
updateMarketMovements('europe', data);
|
80 |
+
})
|
81 |
+
.catch(error => console.error('Error loading Europe data:', error));
|
82 |
+
|
83 |
+
// Load US session data
|
84 |
+
fetch('/api/market_data/us')
|
85 |
+
.then(response => response.json())
|
86 |
+
.then(data => {
|
87 |
+
updateMarketMovements('us', data);
|
88 |
+
})
|
89 |
+
.catch(error => console.error('Error loading US data:', error));
|
90 |
+
|
91 |
+
// Load economic calendar
|
92 |
+
fetch('/api/economic_calendar')
|
93 |
+
.then(response => response.json())
|
94 |
+
.then(data => {
|
95 |
+
updateEconomicCalendar(data);
|
96 |
+
})
|
97 |
+
.catch(error => console.error('Error loading economic calendar:', error));
|
98 |
+
}
|
99 |
+
|
100 |
+
// Load intermarket data
|
101 |
+
function loadIntermarketData() {
|
102 |
+
fetch('/api/intermarket')
|
103 |
+
.then(response => response.json())
|
104 |
+
.then(data => {
|
105 |
+
updateIntermarketDisplay(data);
|
106 |
+
})
|
107 |
+
.catch(error => console.error('Error loading intermarket data:', error));
|
108 |
+
}
|
109 |
+
|
110 |
+
// Update market movements display
|
111 |
+
function updateMarketMovements(region, data) {
|
112 |
+
// Find the container for this region (would need to add IDs to the HTML)
|
113 |
+
let container;
|
114 |
+
if (region === 'asia') {
|
115 |
+
container = document.querySelector('#sessions .section:nth-child(1) .widget:nth-child(4)');
|
116 |
+
} else if (region === 'europe') {
|
117 |
+
container = document.querySelector('#sessions .section:nth-child(2) .widget:nth-child(4)');
|
118 |
+
} else if (region === 'us') {
|
119 |
+
container = document.querySelector('#sessions .section:nth-child(3) .widget:nth-child(4)');
|
120 |
+
}
|
121 |
+
|
122 |
+
if (!container) return;
|
123 |
+
|
124 |
+
// Clear existing content
|
125 |
+
container.innerHTML = '<div class="widget-title">Marktbewegungen</div>';
|
126 |
+
|
127 |
+
// Add each market item
|
128 |
+
data.forEach(market => {
|
129 |
+
const marketItem = document.createElement('div');
|
130 |
+
marketItem.className = 'market-item';
|
131 |
+
marketItem.innerHTML = `
|
132 |
+
<div class="market-name">${market.name}</div>
|
133 |
+
<div class="market-value ${market.direction}">${market.direction === 'up' ? '+' : '-'}${market.change_percent.toFixed(1)}%</div>
|
134 |
+
`;
|
135 |
+
container.appendChild(marketItem);
|
136 |
+
});
|
137 |
+
}
|
138 |
+
|
139 |
+
// Update economic calendar
|
140 |
+
function updateEconomicCalendar(events) {
|
141 |
+
// Update all three economic calendar sections
|
142 |
+
const containers = [
|
143 |
+
document.querySelector('#sessions .section:nth-child(1) .widget:nth-child(3)'), // Asia
|
144 |
+
document.querySelector('#sessions .section:nth-child(2) .widget:nth-child(3)'), // Europe
|
145 |
+
document.querySelector('#sessions .section:nth-child(3) .widget:nth-child(3)') // US
|
146 |
+
];
|
147 |
+
|
148 |
+
containers.forEach(container => {
|
149 |
+
if (!container) return;
|
150 |
+
|
151 |
+
// Keep the title
|
152 |
+
const title = container.querySelector('.widget-title');
|
153 |
+
const titleHTML = title ? title.outerHTML : '<div class="widget-title">Wirtschaftskalender</div>';
|
154 |
+
|
155 |
+
// Clear existing content
|
156 |
+
container.innerHTML = titleHTML;
|
157 |
+
|
158 |
+
// Add first 3 events to each calendar
|
159 |
+
events.slice(0, 3).forEach(event => {
|
160 |
+
const eventItem = document.createElement('div');
|
161 |
+
eventItem.className = 'event-item';
|
162 |
+
|
163 |
+
// Determine impact level
|
164 |
+
let impactClass = 'low';
|
165 |
+
if (event.impact === 'high') impactClass = 'high';
|
166 |
+
else if (event.impact === 'medium') impactClass = 'medium';
|
167 |
+
|
168 |
+
eventItem.innerHTML = `
|
169 |
+
<div class="flex justify-between">
|
170 |
+
<div>
|
171 |
+
<span class="event-impact ${impactClass}"></span>
|
172 |
+
<span class="event-name">${event.country} ${event.event}</span>
|
173 |
+
</div>
|
174 |
+
<div class="event-time">${new Date(event.time).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</div>
|
175 |
+
</div>
|
176 |
+
<div class="text-sm text-gray-600 mt-1">
|
177 |
+
Prognose: ${event.forecast || 'N/A'} | Vorher: ${event.previous || 'N/A'}
|
178 |
+
</div>
|
179 |
+
`;
|
180 |
+
container.appendChild(eventItem);
|
181 |
+
});
|
182 |
+
});
|
183 |
+
}
|
184 |
+
|
185 |
+
// Update intermarket display
|
186 |
+
function updateIntermarketDisplay(data) {
|
187 |
+
// Update correlation matrix
|
188 |
+
const correlationCells = document.querySelectorAll('.correlation-matrix .matrix-table tbody td:not(:first-child)');
|
189 |
+
|
190 |
+
if (correlationCells.length > 0 && data.correlations) {
|
191 |
+
// Map cell indexes to correlation keys
|
192 |
+
const correlationMap = [
|
193 |
+
null, 'stocks_bonds', 'stocks_commodities', 'stocks_dollar', // Row 1 (Stocks)
|
194 |
+
'stocks_bonds', null, 'bonds_commodities', 'bonds_dollar', // Row 2 (Bonds)
|
195 |
+
'stocks_commodities', 'bonds_commodities', null, 'commodities_dollar', // Row 3 (Commodities)
|
196 |
+
'stocks_dollar', 'bonds_dollar', 'commodities_dollar', null // Row 4 (Dollar)
|
197 |
+
];
|
198 |
+
|
199 |
+
correlationCells.forEach((cell, index) => {
|
200 |
+
const key = correlationMap[index];
|
201 |
+
if (key && data.correlations[key] !== undefined) {
|
202 |
+
const value = data.correlations[key];
|
203 |
+
cell.textContent = value.toFixed(2);
|
204 |
+
|
205 |
+
// Update correlation class
|
206 |
+
cell.className = '';
|
207 |
+
if (value > 0.3) cell.classList.add('correlation-positive');
|
208 |
+
else if (value < -0.3) cell.classList.add('correlation-negative');
|
209 |
+
else cell.classList.add('correlation-neutral');
|
210 |
+
}
|
211 |
+
});
|
212 |
+
}
|
213 |
+
|
214 |
+
// Update asset data if available
|
215 |
+
if (data.assets) {
|
216 |
+
// Could update specific asset information on the page
|
217 |
+
console.log('Asset data available for further display:', data.assets);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
// API key functions
|
222 |
+
function saveApiKey() {
|
223 |
+
const keyInput = document.getElementById('geminiApiKey');
|
224 |
+
if (keyInput.value.trim()) {
|
225 |
+
const apiKey = keyInput.value.trim();
|
226 |
+
localStorage.setItem('geminiApiKey', apiKey);
|
227 |
+
// Visual confirmation that key was saved
|
228 |
+
keyInput.style.borderColor = '#2ecc71';
|
229 |
+
setTimeout(() => {
|
230 |
+
keyInput.style.borderColor = '';
|
231 |
+
}, 2000);
|
232 |
+
|
233 |
+
return apiKey;
|
234 |
+
}
|
235 |
+
return null;
|
236 |
+
}
|
237 |
+
|
238 |
+
function checkStoredApiKey() {
|
239 |
+
const storedKey = localStorage.getItem('geminiApiKey');
|
240 |
+
if (storedKey) {
|
241 |
+
document.getElementById('geminiApiKey').value = storedKey;
|
242 |
+
// Optional: Auto-connect with stored key
|
243 |
+
// connectApi();
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
function connectApi(isPreset = false) {
|
248 |
+
let apiKey;
|
249 |
+
|
250 |
+
if (isPreset) {
|
251 |
+
apiKey = document.getElementById('geminiApiKey').value;
|
252 |
+
} else {
|
253 |
+
apiKey = saveApiKey();
|
254 |
+
if (!apiKey) {
|
255 |
+
alert('Bitte gib einen API-Schlüssel ein.');
|
256 |
+
return;
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
const connectText = document.getElementById('connectText');
|
261 |
+
const apiSpinner = document.getElementById('apiSpinner');
|
262 |
+
const apiStatus = document.getElementById('apiStatus');
|
263 |
+
|
264 |
+
// Show spinner, change button text
|
265 |
+
connectText.textContent = 'Verbinde...';
|
266 |
+
apiSpinner.style.display = 'inline-block';
|
267 |
+
|
268 |
+
// Simulate API connection (in a real app, we'd validate the key)
|
269 |
+
setTimeout(() => {
|
270 |
+
// Connection successful
|
271 |
+
apiConnected = true;
|
272 |
+
connectText.textContent = 'Verbunden';
|
273 |
+
apiSpinner.style.display = 'none';
|
274 |
+
apiStatus.classList.add('connected');
|
275 |
+
|
276 |
+
// Add success class to button
|
277 |
+
const connectButton = document.querySelector('button[onclick="connectApi()"]');
|
278 |
+
connectButton.style.backgroundColor = '#2ecc71';
|
279 |
+
connectButton.disabled = true;
|
280 |
+
|
281 |
+
// Show a welcome message in chat
|
282 |
+
addChatMessage('ai', 'API-Verbindung hergestellt! Ich bin jetzt bereit, dir mit KI-gestützten Trading-Analysen zu helfen. Frag mich etwas zu Märkten, Trading-Strategien oder deinen aktuellen Trades.');
|
283 |
+
}, 2000);
|
284 |
+
}
|
285 |
+
|
286 |
+
// Update date and time
|
287 |
+
function updateDateTime() {
|
288 |
+
const now = new Date();
|
289 |
+
|
290 |
+
// Update trading session status
|
291 |
+
updateSessionStatus(now);
|
292 |
+
|
293 |
+
// Update any countdown timers
|
294 |
+
updateCountdowns(now);
|
295 |
+
}
|
296 |
+
|
297 |
+
function updateSessionStatus(now) {
|
298 |
+
const hour = now.getUTCHours();
|
299 |
+
const isWeekend = now.getUTCDay() === 0 || now.getUTCDay() === 6;
|
300 |
+
|
301 |
+
// Get market status from the backend to determine if markets are open
|
302 |
+
fetch('/api/market_overview')
|
303 |
+
.then(response => response.json())
|
304 |
+
.then(data => {
|
305 |
+
const marketStatus = data.market_status;
|
306 |
+
|
307 |
+
// This is a simplified version - a real implementation would be more nuanced
|
308 |
+
if (!isWeekend && marketStatus === 'open') {
|
309 |
+
// Asia session (roughly 00:00-09:00 CET / 23:00-08:00 UTC)
|
310 |
+
if (hour >= 23 || hour < 8) {
|
311 |
+
updateSessionElement('asia', 'active');
|
312 |
+
updateSessionElement('europe', 'upcoming');
|
313 |
+
updateSessionElement('us', 'closed');
|
314 |
+
}
|
315 |
+
// Europe session (roughly 08:00-16:30 CET / 07:00-15:30 UTC)
|
316 |
+
else if (hour >= 8 && hour < 16) {
|
317 |
+
updateSessionElement('asia', 'closed');
|
318 |
+
updateSessionElement('europe', 'active');
|
319 |
+
updateSessionElement('us', 'upcoming');
|
320 |
+
}
|
321 |
+
// US session (roughly 14:30-21:00 CET / 13:30-20:00 UTC)
|
322 |
+
else if (hour >= 16 && hour < 20) {
|
323 |
+
updateSessionElement('asia', 'upcoming');
|
324 |
+
updateSessionElement('europe', 'closed');
|
325 |
+
updateSessionElement('us', 'active');
|
326 |
+
}
|
327 |
+
// Overlap or non-active period
|
328 |
+
else {
|
329 |
+
updateSessionElement('asia', 'upcoming');
|
330 |
+
updateSessionElement('europe', 'closed');
|
331 |
+
updateSessionElement('us', 'closed');
|
332 |
+
}
|
333 |
+
} else {
|
334 |
+
// Weekend or markets closed - all markets closed
|
335 |
+
updateSessionElement('asia', 'closed');
|
336 |
+
updateSessionElement('europe', 'closed');
|
337 |
+
updateSessionElement('us', 'closed');
|
338 |
+
}
|
339 |
+
})
|
340 |
+
.catch(error => {
|
341 |
+
console.error('Error fetching market status:', error);
|
342 |
+
// Fallback to time-based logic on error
|
343 |
+
if (!isWeekend) {
|
344 |
+
if (hour >= 23 || hour < 8) {
|
345 |
+
updateSessionElement('asia', 'active');
|
346 |
+
updateSessionElement('europe', 'upcoming');
|
347 |
+
updateSessionElement('us', 'closed');
|
348 |
+
} else if (hour >= 8 && hour < 16) {
|
349 |
+
updateSessionElement('asia', 'closed');
|
350 |
+
updateSessionElement('europe', 'active');
|
351 |
+
updateSessionElement('us', 'upcoming');
|
352 |
+
} else if (hour >= 16 && hour < 20) {
|
353 |
+
updateSessionElement('asia', 'upcoming');
|
354 |
+
updateSessionElement('europe', 'closed');
|
355 |
+
updateSessionElement('us', 'active');
|
356 |
+
} else {
|
357 |
+
updateSessionElement('asia', 'upcoming');
|
358 |
+
updateSessionElement('europe', 'closed');
|
359 |
+
updateSessionElement('us', 'closed');
|
360 |
+
}
|
361 |
+
} else {
|
362 |
+
updateSessionElement('asia', 'closed');
|
363 |
+
updateSessionElement('europe', 'closed');
|
364 |
+
updateSessionElement('us', 'closed');
|
365 |
+
}
|
366 |
+
});
|
367 |
+
}
|
368 |
+
|
369 |
+
function updateSessionElement(session, status) {
|
370 |
+
// Find session elements by region
|
371 |
+
let sessionCard;
|
372 |
+
let statusElement;
|
373 |
+
|
374 |
+
if (session === 'asia') {
|
375 |
+
sessionCard = document.querySelector('#sessions .section:nth-child(1) .session-card');
|
376 |
+
statusElement = sessionCard ? sessionCard.querySelector('.session-status') : null;
|
377 |
+
} else if (session === 'europe') {
|
378 |
+
sessionCard = document.querySelector('#sessions .section:nth-child(2) .session-card');
|
379 |
+
statusElement = sessionCard ? sessionCard.querySelector('.session-status') : null;
|
380 |
+
} else if (session === 'us') {
|
381 |
+
sessionCard = document.querySelector('#sessions .section:nth-child(3) .session-card');
|
382 |
+
statusElement = sessionCard ? sessionCard.querySelector('.session-status') : null;
|
383 |
+
}
|
384 |
+
|
385 |
+
if (statusElement) {
|
386 |
+
// Remove existing classes
|
387 |
+
statusElement.classList.remove('bg-green-500', 'bg-yellow-500', 'bg-gray-500');
|
388 |
+
|
389 |
+
// Add class based on status
|
390 |
+
if (status === 'active') {
|
391 |
+
statusElement.classList.add('bg-green-500');
|
392 |
+
statusElement.textContent = 'Aktiv';
|
393 |
+
} else if (status === 'upcoming') {
|
394 |
+
statusElement.classList.add('bg-yellow-500');
|
395 |
+
statusElement.textContent = 'Beginnt bald';
|
396 |
+
} else if (status === 'closed') {
|
397 |
+
statusElement.classList.add('bg-gray-500');
|
398 |
+
statusElement.textContent = 'Geschlossen';
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
// Update session times and countdown info
|
403 |
+
if (sessionCard) {
|
404 |
+
const timeInfo = sessionCard.querySelector('.mt-4 p.text-sm');
|
405 |
+
if (timeInfo) {
|
406 |
+
const now = new Date();
|
407 |
+
const hour = now.getHours();
|
408 |
+
const minute = now.getMinutes();
|
409 |
+
|
410 |
+
if (status === 'active') {
|
411 |
+
// Calculate time until session ends
|
412 |
+
let hoursLeft = 0;
|
413 |
+
let minutesLeft = 0;
|
414 |
+
|
415 |
+
if (session === 'asia') {
|
416 |
+
hoursLeft = 9 - hour;
|
417 |
+
if (hoursLeft < 0) hoursLeft += 24;
|
418 |
+
} else if (session === 'europe') {
|
419 |
+
hoursLeft = 16 - hour;
|
420 |
+
} else if (session === 'us') {
|
421 |
+
hoursLeft = 21 - hour;
|
422 |
+
}
|
423 |
+
|
424 |
+
minutesLeft = 60 - minute;
|
425 |
+
if (minutesLeft === 60) {
|
426 |
+
minutesLeft = 0;
|
427 |
+
} else {
|
428 |
+
hoursLeft -= 1;
|
429 |
+
}
|
430 |
+
|
431 |
+
timeInfo.textContent = `Aktive Session läuft noch ${hoursLeft}h ${minutesLeft}min`;
|
432 |
+
} else if (status === 'upcoming') {
|
433 |
+
// Calculate time until session starts
|
434 |
+
let hoursLeft = 0;
|
435 |
+
let minutesLeft = 0;
|
436 |
+
|
437 |
+
if (session === 'asia') {
|
438 |
+
hoursLeft = 24 - hour;
|
439 |
+
} else if (session === 'europe') {
|
440 |
+
hoursLeft = 8 - hour;
|
441 |
+
if (hoursLeft < 0) hoursLeft += 24;
|
442 |
+
} else if (session === 'us') {
|
443 |
+
hoursLeft = 14 - hour;
|
444 |
+
if (hoursLeft < 0) hoursLeft += 24;
|
445 |
+
}
|
446 |
+
|
447 |
+
if (minute > 0) {
|
448 |
+
hoursLeft -= 1;
|
449 |
+
minutesLeft = 60 - minute;
|
450 |
+
}
|
451 |
+
|
452 |
+
timeInfo.textContent = `Session beginnt in ${hoursLeft}h ${minutesLeft}min`;
|
453 |
+
} else {
|
454 |
+
// Calculate time until next session
|
455 |
+
let hoursLeft = 0;
|
456 |
+
let minutesLeft = 0;
|
457 |
+
|
458 |
+
if (session === 'asia') {
|
459 |
+
hoursLeft = 24 - hour;
|
460 |
+
} else if (session === 'europe') {
|
461 |
+
hoursLeft = 8 - hour;
|
462 |
+
if (hoursLeft < 0) hoursLeft += 24;
|
463 |
+
} else if (session === 'us') {
|
464 |
+
hoursLeft = 14 - hour;
|
465 |
+
if (hoursLeft < 0) hoursLeft += 24;
|
466 |
+
}
|
467 |
+
|
468 |
+
if (minute > 0) {
|
469 |
+
hoursLeft -= 1;
|
470 |
+
minutesLeft = 60 - minute;
|
471 |
+
}
|
472 |
+
|
473 |
+
timeInfo.textContent = `Nächste Session beginnt in ${hoursLeft}h ${minutesLeft}min`;
|
474 |
+
}
|
475 |
+
}
|
476 |
+
}
|
477 |
+
}
|
478 |
+
|
479 |
+
function updateCountdowns(now) {
|
480 |
+
// Update any countdown timers in the UI
|
481 |
+
// This is a placeholder for actual countdown logic
|
482 |
+
}
|
483 |
+
|
484 |
+
// Fetch and update ticker from API
|
485 |
+
function fetchAndUpdateTicker() {
|
486 |
+
fetch('/api/ticker?symbols=AAPL,NVDA,ASML,BNBUSDT,INTC,BTCUSD,XAUUSD,USDJPY,GER40,EURUSD')
|
487 |
+
.then(response => response.json())
|
488 |
+
.then(data => {
|
489 |
+
updateTickerUI(data);
|
490 |
+
})
|
491 |
+
.catch(error => {
|
492 |
+
console.error('Error fetching ticker data:', error);
|
493 |
+
// Fallback to randomized data on error
|
494 |
+
updateTickerWithRandomData();
|
495 |
+
});
|
496 |
+
}
|
497 |
+
|
498 |
+
// Update ticker UI with real data
|
499 |
+
function updateTickerUI(data) {
|
500 |
+
const tickerContainer = document.querySelector('.ticker-content');
|
501 |
+
if (!tickerContainer) return;
|
502 |
+
|
503 |
+
// Clear current ticker items
|
504 |
+
tickerContainer.innerHTML = '';
|
505 |
+
|
506 |
+
// Add ticker items from API data
|
507 |
+
data.forEach(item => {
|
508 |
+
const tickerItem = document.createElement('div');
|
509 |
+
tickerItem.className = `ticker-item ${item.direction}`;
|
510 |
+
|
511 |
+
const symbolPrefix = getSymbolPrefix(item.symbol);
|
512 |
+
|
513 |
+
tickerItem.innerHTML = `${symbolPrefix}:${item.symbol} <span>${item.price.toFixed(2)}</span>
|
514 |
+
<i class="fas fa-caret-${item.direction}"></i> ${item.change_percent.toFixed(1)}%`;
|
515 |
+
|
516 |
+
tickerContainer.appendChild(tickerItem);
|
517 |
+
});
|
518 |
+
}
|
519 |
+
|
520 |
+
// Get symbol prefix for ticker display
|
521 |
+
function getSymbolPrefix(symbol) {
|
522 |
+
const prefixMap = {
|
523 |
+
'AAPL': 'NASDAQ',
|
524 |
+
'NVDA': 'NASDAQ',
|
525 |
+
'ASML': 'NASDAQ',
|
526 |
+
'INTC': 'NASDAQ',
|
527 |
+
'BNBUSDT': 'BINANCE',
|
528 |
+
'BTCUSD': 'CAPITALCOM',
|
529 |
+
'XAUUSD': 'PEPPERSTONE',
|
530 |
+
'USDJPY': 'FX',
|
531 |
+
'GER40': 'PEPPERSTONE',
|
532 |
+
'EURUSD': 'PEPPERSTONE'
|
533 |
+
};
|
534 |
+
|
535 |
+
return prefixMap[symbol] || 'SYMBOL';
|
536 |
+
}
|
537 |
+
|
538 |
+
// Fallback to randomized data if API fails
|
539 |
+
function updateTickerWithRandomData() {
|
540 |
+
const tickerItems = document.querySelectorAll('.ticker-item');
|
541 |
+
|
542 |
+
tickerItems.forEach(item => {
|
543 |
+
// Simulate price change
|
544 |
+
const randomChange = (Math.random() * 0.2 - 0.1).toFixed(2);
|
545 |
+
const currentValue = parseFloat(item.querySelector('span').textContent);
|
546 |
+
const newValue = (currentValue * (1 + randomChange / 100)).toFixed(2);
|
547 |
+
|
548 |
+
item.querySelector('span').textContent = newValue;
|
549 |
+
|
550 |
+
// Update direction indicator
|
551 |
+
if (randomChange > 0) {
|
552 |
+
item.classList.remove('down');
|
553 |
+
item.classList.add('up');
|
554 |
+
item.querySelector('i').className = 'fas fa-caret-up';
|
555 |
+
} else {
|
556 |
+
item.classList.remove('up');
|
557 |
+
item.classList.add('down');
|
558 |
+
item.querySelector('i').className = 'fas fa-caret-down';
|
559 |
+
}
|
560 |
+
|
561 |
+
// Update percentage
|
562 |
+
const percentEl = item.querySelector('i').nextSibling;
|
563 |
+
percentEl.textContent = ` ${Math.abs(randomChange)}%`;
|
564 |
+
});
|
565 |
+
}
|
566 |
+
|
567 |
+
// Affirmation functions
|
568 |
+
function updateAffirmation() {
|
569 |
+
const status = document.getElementById('traderStatus').value;
|
570 |
+
let affirmationText = '';
|
571 |
+
let categoryText = '';
|
572 |
+
|
573 |
+
switch (status) {
|
574 |
+
case 'preparing':
|
575 |
+
affirmationText = 'Ich bereite mich gründlich vor und analysiere den Markt mit Klarheit und Fokus. Ich warte geduldig auf hochwertige Setups und halte mich an meine Handelsstrategie.';
|
576 |
+
categoryText = 'Geduld und Vorbereitung';
|
577 |
+
break;
|
578 |
+
case 'active':
|
579 |
+
affirmationText = 'Ich bleibe ruhig und diszipliniert während mein Trade läuft. Ich halte mich an meinen Plan und lasse keine Emotionen meine Entscheidungen beeinflussen.';
|
580 |
+
categoryText = 'Emotionale Kontrolle';
|
581 |
+
break;
|
582 |
+
case 'developing':
|
583 |
+
affirmationText = 'Ich entwickle meine Strategie mit Klarheit und Weisheit. Ich ziehe Lehren aus vergangenen Trades und verbessere kontinuierlich meine Fähigkeiten.';
|
584 |
+
categoryText = 'Strategische Entwicklung';
|
585 |
+
break;
|
586 |
+
case 'break':
|
587 |
+
affirmationText = 'Ich nehme mir bewusst Zeit für Erholung und Reflexion. Diese Pause stärkt meine Handelsleistung und schärft meinen Fokus.';
|
588 |
+
categoryText = 'Erholung und Reflexion';
|
589 |
+
break;
|
590 |
+
}
|
591 |
+
|
592 |
+
document.getElementById('currentAffirmation').textContent = affirmationText;
|
593 |
+
document.getElementById('affirmationCategory').textContent = categoryText;
|
594 |
+
}
|
595 |
+
|
596 |
+
// Add the rest of your JavaScript functions here
|
597 |
+
// Remaining functions are from the original code: updateAffirmationTab, startAffirmationTimer, pauseAffirmationTimer,
|
598 |
+
// resetAffirmationTimer, saveCurrentAffirmation, generateOpportunity, etc.
|
599 |
+
|
600 |
+
// Chat functions
|
601 |
+
function toggleChat() {
|
602 |
+
const chatPopup = document.getElementById('chatPopup');
|
603 |
+
const chevron = document.getElementById('chatChevron');
|
604 |
+
|
605 |
+
if (chatPopup.classList.contains('minimized')) {
|
606 |
+
chatPopup.classList.remove('minimized');
|
607 |
+
chevron.className = 'fas fa-chevron-down';
|
608 |
+
} else {
|
609 |
+
chatPopup.classList.add('minimized');
|
610 |
+
chevron.className = 'fas fa-chevron-up';
|
611 |
+
}
|
612 |
+
}
|
613 |
+
|
614 |
+
function sendChatMessage() {
|
615 |
+
const chatInput = document.getElementById('chatInput');
|
616 |
+
const message = chatInput.value.trim();
|
617 |
+
|
618 |
+
if (!message) return;
|
619 |
+
|
620 |
+
// Add user message
|
621 |
+
addChatMessage('user', message);
|
622 |
+
|
623 |
+
// Clear input
|
624 |
+
chatInput.value = '';
|
625 |
+
|
626 |
+
// Check API connection
|
627 |
+
if (!apiConnected) {
|
628 |
+
addChatMessage('ai', 'Bitte verbinde zuerst die Gemini API, um mit dem Chat zu interagieren.');
|
629 |
+
return;
|
630 |
+
}
|
631 |
+
|
632 |
+
// Show typing indicator
|
633 |
+
showTypingIndicator();
|
634 |
+
|
635 |
+
// In a real implementation, you would make an API call to Gemini here
|
636 |
+
// For this demo, we'll simulate a response
|
637 |
+
setTimeout(() => {
|
638 |
+
// Remove typing indicator
|
639 |
+
removeTypingIndicator();
|
640 |
+
|
641 |
+
// Generate response
|
642 |
+
const response = generateChatResponse(message);
|
643 |
+
|
644 |
+
// Add AI message
|
645 |
+
addChatMessage('ai', response);
|
646 |
+
|
647 |
+
// Scroll to bottom
|
648 |
+
scrollChatToBottom();
|
649 |
+
}, 1500);
|
650 |
+
}
|
651 |
+
|
652 |
+
// Affirmation Timer functions
|
653 |
+
function startAffirmationTimer() {
|
654 |
+
if (affirmationTimerRunning) return;
|
655 |
+
|
656 |
+
document.getElementById('startTimerBtn').disabled = true;
|
657 |
+
document.getElementById('pauseTimerBtn').disabled = false;
|
658 |
+
|
659 |
+
affirmationTimerRunning = true;
|
660 |
+
|
661 |
+
affirmationTimerInterval = setInterval(() => {
|
662 |
+
affirmationTimeLeft--;
|
663 |
+
|
664 |
+
const minutes = Math.floor(affirmationTimeLeft / 60);
|
665 |
+
const seconds = affirmationTimeLeft % 60;
|
666 |
+
|
667 |
+
document.getElementById('affirmationTimer').textContent =
|
668 |
+
`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
669 |
+
|
670 |
+
if (affirmationTimeLeft <= 0) {
|
671 |
+
clearInterval(affirmationTimerInterval);
|
672 |
+
affirmationTimerRunning = false;
|
673 |
+
document.getElementById('startTimerBtn').disabled = false;
|
674 |
+
document.getElementById('pauseTimerBtn').disabled = true;
|
675 |
+
document.getElementById('affirmationTimer').textContent = "00:00";
|
676 |
+
// Play sound or notification
|
677 |
+
// playTimerEndSound();
|
678 |
+
}
|
679 |
+
}, 1000);
|
680 |
+
}
|
681 |
+
|
682 |
+
function pauseAffirmationTimer() {
|
683 |
+
clearInterval(affirmationTimerInterval);
|
684 |
+
affirmationTimerRunning = false;
|
685 |
+
document.getElementById('startTimerBtn').disabled = false;
|
686 |
+
document.getElementById('pauseTimerBtn').disabled = true;
|
687 |
+
}
|
688 |
+
|
689 |
+
function resetAffirmationTimer() {
|
690 |
+
clearInterval(affirmationTimerInterval);
|
691 |
+
affirmationTimerRunning = false;
|
692 |
+
affirmationTimeLeft = 300; // Reset to 5 minutes
|
693 |
+
document.getElementById('affirmationTimer').textContent = "05:00";
|
694 |
+
document.getElementById('startTimerBtn').disabled = false;
|
695 |
+
document.getElementById('pauseTimerBtn').disabled = true;
|
696 |
+
}
|
697 |
+
|
698 |
+
function saveCurrentAffirmation() {
|
699 |
+
const affirmationText = document.getElementById('detailedAffirmation').querySelector('.affirmation-text').textContent;
|
700 |
+
const categoryText = document.getElementById('detailedAffirmation').querySelector('.affirmation-category').textContent;
|
701 |
+
|
702 |
+
// Create a shortened version
|
703 |
+
const shortAffirmation = affirmationText.substring(0, 60) + (affirmationText.length > 60 ? '...' : '');
|
704 |
+
|
705 |
+
// Create new element
|
706 |
+
const newSavedAffirmation = document.createElement('div');
|
707 |
+
newSavedAffirmation.className = 'event-item';
|
708 |
+
newSavedAffirmation.innerHTML = `
|
709 |
+
<div class="font-medium">${shortAffirmation}</div>
|
710 |
+
<div class="text-sm text-gray-600">Kategorie: ${categoryText}</div>
|
711 |
+
`;
|
712 |
+
|
713 |
+
// Add to saved affirmations
|
714 |
+
const savedAffirmationsContainer = document.getElementById('savedAffirmations');
|
715 |
+
savedAffirmationsContainer.prepend(newSavedAffirmation);
|
716 |
+
|
717 |
+
// Show success message
|
718 |
+
alert('Affirmation gespeichert!');
|
719 |
+
}
|
720 |
+
|
721 |
+
// Update Affirmation Tab
|
722 |
+
function updateAffirmationTab() {
|
723 |
+
const status = document.getElementById('affirmationTraderStatus').value;
|
724 |
+
const category = document.getElementById('affirmationCategory').value;
|
725 |
+
|
726 |
+
// Map of affirmations based on status and category
|
727 |
+
const affirmations = {
|
728 |
+
discipline: {
|
729 |
+
preparing: 'Ich bin ein disziplinierter und geduldiger Trader, der seinem Handelsplan mit unerschütterlichem Engagement folgt. Ich vertraue auf die Wirksamkeit meiner Strategien und warte geduldig auf Setups mit hoher Wahrscheinlichkeit. Ich habe die Selbstkontrolle, mich an meine etablierten Regeln zu halten und impulsive Handlungen zu vermeiden.',
|
730 |
+
active: 'Während mein Trade aktiv ist, bleibe ich diszipliniert und geduldig. Ich folge meinem Plan und erlaube keiner Emotion, meine Strategie zu untergraben. Ich bin ruhig und kontrolliert, selbst wenn der Markt volatil wird.',
|
731 |
+
developing: 'Beim Entwickeln meiner Strategie wende ich Disziplin und Geduld an. Ich nehme mir die Zeit, jedes Detail zu durchdenken und teste gründlich, bevor ich handele. Qualität geht vor Schnelligkeit.',
|
732 |
+
break: 'In dieser Pause pflege ich meine Disziplin und Geduld, indem ich reflektiere und lerne. Ich verstehe, dass Ruhezeiten wesentlich für nachhaltigen Trading-Erfolg sind.'
|
733 |
+
},
|
734 |
+
abundance: {
|
735 |
+
preparing: 'Ich ziehe reichlich Handelsmöglichkeiten an, die mit meiner Strategie übereinstimmen. Der Markt bietet einen endlosen Strom von Gelegenheiten, und ich bin bereit, sie zu nutzen.',
|
736 |
+
active: 'Mein aktueller Trade ist eine von vielen Gelegenheiten für Wohlstand. Ich denke in Fülle und weiß, dass unabhängig vom Ausgang dieses Trades weitere profitable Chancen folgen werden.',
|
737 |
+
developing: 'Ich entwickle meine Strategie mit einer Überfluss-Denkweise. Ich erkenne die unbegrenzten Möglichkeiten des Marktes an und erschaffe einen Ansatz, der diesen Reichtum anzieht.',
|
738 |
+
break: 'Während dieser Pause ziehe ich neue Erkenntnisse und Möglichkeiten an. Ich nutze diese Zeit, um meine Überfluss-Denkweise zu stärken und mich auf neue Handelschancen vorzubereiten.'
|
739 |
+
}
|
740 |
+
// Additional categories would be added here
|
741 |
+
};
|
742 |
+
|
743 |
+
// Get the appropriate affirmation or default
|
744 |
+
let affirmationText = 'Ich handle mit Klarheit, Disziplin und Vertrauen.';
|
745 |
+
if (affirmations[category] && affirmations[category][status]) {
|
746 |
+
affirmationText = affirmations[category][status];
|
747 |
+
}
|
748 |
+
|
749 |
+
// Update the display
|
750 |
+
document.getElementById('detailedAffirmation').querySelector('.affirmation-text').textContent = affirmationText;
|
751 |
+
|
752 |
+
// Update the category display
|
753 |
+
let categoryDisplayText = '';
|
754 |
+
switch (category) {
|
755 |
+
case 'discipline': categoryDisplayText = 'Disziplin und Geduld entwickeln'; break;
|
756 |
+
case 'abundance': categoryDisplayText = 'Überflussdenken fördern'; break;
|
757 |
+
case 'selection': categoryDisplayText = 'Handelsauswahl verbessern'; break;
|
758 |
+
case 'burnout': categoryDisplayText = 'Burnout und Erschöpfung überwinden'; break;
|
759 |
+
case 'bias': categoryDisplayText = 'Bestätigungsfehler überwinden'; break;
|
760 |
+
case 'paralysis': categoryDisplayText = 'Entscheidungslähmung überwinden'; break;
|
761 |
+
case 'fomo': categoryDisplayText = 'FOMO überwinden'; break;
|
762 |
+
case 'losses': categoryDisplayText = 'Verluste schnell akzeptieren'; break;
|
763 |
+
case 'emotional': categoryDisplayText = 'Emotionale Bindung an Trades lösen'; break;
|
764 |
+
case 'overtrading': categoryDisplayText = 'Überhandel widerstehen'; break;
|
765 |
+
case 'patience': categoryDisplayText = 'Geduld bei langsamen Märkten bewahren'; break;
|
766 |
+
default: categoryDisplayText = 'Allgemeine Trading-Affirmation';
|
767 |
+
}
|
768 |
+
|
769 |
+
document.getElementById('detailedAffirmation').querySelector('.affirmation-category').textContent = categoryDisplayText;
|
770 |
+
}
|
771 |
+
|
772 |
+
// Trading opportunity functions
|
773 |
+
function generateOpportunity() {
|
774 |
+
const symbol = document.getElementById('opportunitySymbol').value;
|
775 |
+
|
776 |
+
if (!symbol) {
|
777 |
+
alert('Bitte wähle ein Symbol aus.');
|
778 |
+
return;
|
779 |
+
}
|
780 |
+
|
781 |
+
if (!apiConnected) {
|
782 |
+
alert('Bitte verbinde zuerst die Gemini API.');
|
783 |
+
return;
|
784 |
+
}
|
785 |
+
|
786 |
+
// Get other parameters
|
787 |
+
const timeframe = document.getElementById('opportunityTimeframe').value;
|
788 |
+
const style = document.getElementById('opportunityStyle').value;
|
789 |
+
const instructions = document.getElementById('opportunityInstructions').value;
|
790 |
+
|
791 |
+
// Show loading state
|
792 |
+
document.getElementById('analyzeText').textContent = 'Analysiere...';
|
793 |
+
document.getElementById('opportunitySpinner').style.display = 'inline-block';
|
794 |
+
|
795 |
+
// Simulate API call (in a real implementation, you'd call the Gemini API)
|
796 |
+
setTimeout(() => {
|
797 |
+
// Reset button
|
798 |
+
document.getElementById('analyzeText').textContent = 'Trading Opportunity analysieren';
|
799 |
+
document.getElementById('opportunitySpinner').style.display = 'none';
|
800 |
+
|
801 |
+
// Generate result
|
802 |
+
const result = generateOpportunityResult(symbol, timeframe, style, instructions);
|
803 |
+
|
804 |
+
// Display result
|
805 |
+
document.getElementById('opportunityResults').innerHTML = result;
|
806 |
+
}, 3000);
|
807 |
+
}
|
808 |
+
|
809 |
+
function generateOpportunityResult(symbol, timeframe, style, instructions) {
|
810 |
+
// This is a simulation - in production this would come from the Gemini API
|
811 |
+
|
812 |
+
// Extract symbol name
|
813 |
+
const symbolName = symbol.split(':')[1];
|
814 |
+
|
815 |
+
// Determine direction (random for demo)
|
816 |
+
const isLong = Math.random() > 0.5;
|
817 |
+
|
818 |
+
// Generate fictional analysis
|
819 |
+
let analysis = '';
|
820 |
+
let technicalInsights = '';
|
821 |
+
let fundamentalInsights = '';
|
822 |
+
let tradeSetup = '';
|
823 |
+
|
824 |
+
// Technical insights
|
825 |
+
if (style === 'technical' || style === 'combined') {
|
826 |
+
const patterns = ['Double Bottom', 'Bull Flag', 'Cup and Handle', 'Descending Triangle', 'Head and Shoulders'];
|
827 |
+
const indicators = ['RSI', 'MACD', 'Moving Average', 'Bollinger Bands', 'Fibonacci Retracement'];
|
828 |
+
|
829 |
+
const pattern = patterns[Math.floor(Math.random() * patterns.length)];
|
830 |
+
const indicator1 = indicators[Math.floor(Math.random() * indicators.length)];
|
831 |
+
const indicator2 = indicators[Math.floor(Math.random() * indicators.length)];
|
832 |
+
|
833 |
+
technicalInsights = `<p class="mb-3"><strong>Technische Analyse:</strong> ${symbolName} zeigt ein ${pattern}-Muster, das auf eine potenzielle ${isLong ? 'Aufwärts' : 'Abwärts'}bewegung hindeutet. Der ${indicator1} ist ${isLong ? 'überverkauft' : 'überkauft'}, während der ${indicator2} ein ${isLong ? 'bullisches' : 'bärisches'} Signal gibt.</p>`;
|
834 |
+
}
|
835 |
+
|
836 |
+
// Fundamental insights
|
837 |
+
if (style === 'fundamental' || style === 'combined') {
|
838 |
+
fundamentalInsights = `<p class="mb-3"><strong>Fundamentale Analyse:</strong> ${symbolName} ${isLong ? 'profitiert von positiven Branchentrends und zeigt starkes Gewinnwachstum' : 'steht vor Herausforderungen durch Branchendruck und Margenschwäche'}. Bevorstehende Ereignisse könnten ${isLong ? 'positive' : 'negative'} Auswirkungen auf den Kurs haben.</p>`;
|
839 |
+
}
|
840 |
+
|
841 |
+
// Trade setup
|
842 |
+
const currentPrice = Math.random() * 1000;
|
843 |
+
const stopLoss = isLong ? currentPrice * 0.95 : currentPrice * 1.05;
|
844 |
+
const takeProfit = isLong ? currentPrice * 1.1 : currentPrice * 0.9;
|
845 |
+
|
846 |
+
tradeSetup = `
|
847 |
+
<div class="p-3 bg-gray-50 rounded-lg mt-4">
|
848 |
+
<h4 class="font-semibold mb-2">Trade Setup</h4>
|
849 |
+
<div class="grid grid-cols-2 gap-3 text-sm">
|
850 |
+
<div>
|
851 |
+
<div class="text-gray-600">Richtung:</div>
|
852 |
+
<div class="font-medium">${isLong ? 'Long (Kaufen)' : 'Short (Verkaufen)'}</div>
|
853 |
+
</div>
|
854 |
+
<div>
|
855 |
+
<div class="text-gray-600">Einstieg:</div>
|
856 |
+
<div class="font-medium">${currentPrice.toFixed(2)}</div>
|
857 |
+
</div>
|
858 |
+
<div>
|
859 |
+
<div class="text-gray-600">Stop-Loss:</div>
|
860 |
+
<div class="font-medium">${stopLoss.toFixed(2)}</div>
|
861 |
+
</div>
|
862 |
+
<div>
|
863 |
+
<div class="text-gray-600">Take-Profit:</div>
|
864 |
+
<div class="font-medium">${takeProfit.toFixed(2)}</div>
|
865 |
+
</div>
|
866 |
+
<div>
|
867 |
+
<div class="text-gray-600">Risk/Reward:</div>
|
868 |
+
<div class="font-medium">1:${isLong ? '2' : '3'}</div>
|
869 |
+
</div>
|
870 |
+
<div>
|
871 |
+
<div class="text-gray-600">Zeitrahmen:</div>
|
872 |
+
<div class="font-medium">${timeframe === 'shortTerm' ? 'Kurzfristig (1-3 Tage)' : timeframe === 'mediumTerm' ? 'Mittelfristig (1-2 Wochen)' : 'Langfristig (1+ Monate)'}</div>
|
873 |
+
</div>
|
874 |
+
</div>
|
875 |
+
</div>
|
876 |
+
`;
|
877 |
+
|
878 |
+
// Build the full analysis
|
879 |
+
analysis = `
|
880 |
+
<div>
|
881 |
+
<div class="mb-4 flex justify-between items-start">
|
882 |
+
<div>
|
883 |
+
<h3 class="font-semibold text-lg">${symbol}</h3>
|
884 |
+
<div class="text-sm text-gray-600">${new Date().toLocaleString()}</div>
|
885 |
+
</div>
|
886 |
+
<div class="badge ${isLong ? 'badge-success' : 'badge-danger'}">${isLong ? 'Long' : 'Short'}</div>
|
887 |
+
</div>
|
888 |
+
|
889 |
+
${technicalInsights}
|
890 |
+
${fundamentalInsights}
|
891 |
+
|
892 |
+
<p class="mb-4">
|
893 |
+
<strong>Analyse:</strong> Basierend auf ${style === 'technical' ? 'technischer Analyse' : style === 'fundamental' ? 'fundamentaler Analyse' : 'technischer und fundamentaler Analyse'}
|
894 |
+
erscheint ${symbolName} als ${isLong ? 'attraktive Long-Opportunity' : 'potentielle Short-Position'} für den ${timeframe === 'shortTerm' ? 'kurzfristigen' : timeframe === 'mediumTerm' ? 'mittelfristigen' : 'langfristigen'} Handel.
|
895 |
+
</p>
|
896 |
+
|
897 |
+
${tradeSetup}
|
898 |
+
|
899 |
+
<div class="mt-6 flex justify-end gap-3">
|
900 |
+
<button class="btn-primary text-sm" onclick="saveOpportunity()">Speichern</button>
|
901 |
+
<button class="btn-primary text-sm" onclick="createTradeFromOpportunity()">Trade erstellen</button>
|
902 |
+
</div>
|
903 |
+
</div>
|
904 |
+
`;
|
905 |
+
|
906 |
+
return analysis;
|
907 |
+
}
|
908 |
+
|
909 |
+
function saveOpportunity() {
|
910 |
+
alert('Trading Opportunity gespeichert!');
|
911 |
+
}
|
912 |
+
|
913 |
+
function createTradeFromOpportunity() {
|
914 |
+
alert('Trade aus dieser Opportunity wird erstellt. Bitte bestätige die Details im "Meine Trades" Bereich.');
|
915 |
+
|
916 |
+
// Switch to the My Trades tab
|
917 |
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
918 |
+
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
919 |
+
|
920 |
+
document.querySelector('[data-tab="mytrades"]').classList.add('active');
|
921 |
+
document.getElementById('mytrades').classList.add('active');
|
922 |
+
}
|
923 |
+
|
924 |
+
// Chat helper functions
|
925 |
+
function handleChatKeyPress(event) {
|
926 |
+
if (event.key === 'Enter') {
|
927 |
+
sendChatMessage();
|
928 |
+
}
|
929 |
+
}
|
930 |
+
|
931 |
+
function addChatMessage(sender, text) {
|
932 |
+
const chatBody = document.getElementById('chatBody');
|
933 |
+
const messageDiv = document.createElement('div');
|
934 |
+
messageDiv.className = `chat-message ${sender}`;
|
935 |
+
|
936 |
+
messageDiv.innerHTML = `
|
937 |
+
<div class="message-content">${text}</div>
|
938 |
+
`;
|
939 |
+
|
940 |
+
chatBody.appendChild(messageDiv);
|
941 |
+
|
942 |
+
// Expand chat if minimized
|
943 |
+
if (document.getElementById('chatPopup').classList.contains('minimized')) {
|
944 |
+
toggleChat();
|
945 |
+
}
|
946 |
+
|
947 |
+
// Scroll to bottom
|
948 |
+
scrollChatToBottom();
|
949 |
+
}
|
950 |
+
|
951 |
+
function scrollChatToBottom() {
|
952 |
+
const chatBody = document.getElementById('chatBody');
|
953 |
+
chatBody.scrollTop = chatBody.scrollHeight;
|
954 |
+
}
|
955 |
+
|
956 |
+
function showTypingIndicator() {
|
957 |
+
const chatBody = document.getElementById('chatBody');
|
958 |
+
const typingDiv = document.createElement('div');
|
959 |
+
typingDiv.className = 'chat-message ai';
|
960 |
+
typingDiv.id = 'typingIndicator';
|
961 |
+
|
962 |
+
typingDiv.innerHTML = `
|
963 |
+
<div class="message-content">
|
964 |
+
<div class="typing-indicator">
|
965 |
+
<span></span>
|
966 |
+
<span></span>
|
967 |
+
<span></span>
|
968 |
+
</div>
|
969 |
+
</div>
|
970 |
+
`;
|
971 |
+
|
972 |
+
chatBody.appendChild(typingDiv);
|
973 |
+
scrollChatToBottom();
|
974 |
+
}
|
975 |
+
|
976 |
+
function removeTypingIndicator() {
|
977 |
+
const typingIndicator = document.getElementById('typingIndicator');
|
978 |
+
if (typingIndicator) {
|
979 |
+
typingIndicator.remove();
|
980 |
+
}
|
981 |
+
}
|
982 |
+
|
983 |
+
function generateChatResponse(message) {
|
984 |
+
// This is a simulation - in production this would come from the Gemini API
|
985 |
+
|
986 |
+
// Simple pattern matching for demo
|
987 |
+
if (message.toLowerCase().includes('hallo') || message.toLowerCase().includes('hi')) {
|
988 |
+
return 'Hallo! Wie kann ich dir bei deinem Trading heute helfen?';
|
989 |
+
} else if (message.toLowerCase().includes('market') || message.toLowerCase().includes('markt')) {
|
990 |
+
return 'Die Märkte zeigen heute gemischte Signale. US-Indizes sind leicht im Plus, während europäische Märkte sich seitwärts bewegen. Gold steigt aufgrund geopolitischer Spannungen.';
|
991 |
+
} else if (message.toLowerCase().includes('affirmation')) {
|
992 |
+
return 'Affirmationen können deine Trading-Psychologie stark verbessern. Ich empfehle dir, täglich 5 Minuten für Affirmationen einzuplanen, besonders vor Handelssessions oder nach Verlusten.';
|
993 |
+
} else if (message.toLowerCase().includes('verlust') || message.toLowerCase().includes('loss')) {
|
994 |
+
return 'Verluste sind ein normaler Teil des Tradings. Das Wichtigste ist, wie du darauf reagierst. Halte dich an dein Risikomanagement und betrachte jeden Verlust als Lernchance.';
|
995 |
+
} else if (message.toLowerCase().includes('strategie') || message.toLowerCase().includes('strategy')) {
|
996 |
+
return 'Eine erfolgreiche Trading-Strategie sollte klare Ein- und Ausstiegskriterien, Risikomanagement und ein positives Erwartungswert haben. Möchtest du Hilfe bei einem bestimmten Aspekt deiner Strategie?';
|
997 |
+
} else {
|
998 |
+
return 'Danke für deine Nachricht. Ich kann dir bei Trading-Analysen, Affirmationen, Marktinformationen und Handelsstrategien helfen. Was interessiert dich am meisten?';
|
999 |
+
}
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
// Trade management functions
|
1003 |
+
function addNewTrade() {
|
1004 |
+
const symbol = document.getElementById('newTradeSymbol').value;
|
1005 |
+
if (!symbol) {
|
1006 |
+
alert('Bitte wähle ein Symbol aus.');
|
1007 |
+
return;
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
const entry = document.getElementById('newTradeEntry').value;
|
1011 |
+
if (!entry) {
|
1012 |
+
alert('Bitte gib einen Einstiegspreis ein.');
|
1013 |
+
return;
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
const direction = document.getElementById('newTradeDirection').value;
|
1017 |
+
const stop = document.getElementById('newTradeStop').value;
|
1018 |
+
const target = document.getElementById('newTradeTarget').value;
|
1019 |
+
const position = document.getElementById('newTradePosition').value;
|
1020 |
+
const notes = document.getElementById('newTradeNotes').value;
|
1021 |
+
|
1022 |
+
// Create trade element
|
1023 |
+
const tradeElement = document.createElement('div');
|
1024 |
+
tradeElement.className = 'trade-item';
|
1025 |
+
|
1026 |
+
// Random P&L for demo
|
1027 |
+
const pnl = ((Math.random() * 2) - 0.5).toFixed(2);
|
1028 |
+
const isProfitable = parseFloat(pnl) > 0;
|
1029 |
+
|
1030 |
+
tradeElement.innerHTML = `
|
1031 |
+
<div class="trade-symbol">${symbol}</div>
|
1032 |
+
<div class="trade-details">${direction === 'long' ? 'Long' : 'Short'} @ ${entry} • Stop: ${stop} • Target: ${target}</div>
|
1033 |
+
<div class="trade-pnl ${isProfitable ? 'profit' : 'loss'}">${pnl > 0 ? '+' : ''}${pnl}%</div>
|
1034 |
+
<div class="text-sm text-gray-500 mt-2">Eröffnet: ${new Date().toLocaleString()}</div>
|
1035 |
+
${notes ? `
|
1036 |
+
<div class="mt-3 text-sm">
|
1037 |
+
<div class="font-medium">Notizen:</div>
|
1038 |
+
<p class="text-gray-600">${notes}</p>
|
1039 |
+
</div>
|
1040 |
+
` : ''}
|
1041 |
+
<div class="trade-actions">
|
1042 |
+
<button class="btn-primary text-xs py-1">Bearbeiten</button>
|
1043 |
+
<button class="btn-danger text-xs py-1" onclick="removeTrade(this)">Entfernen</button>
|
1044 |
+
</div>
|
1045 |
+
`;
|
1046 |
+
|
1047 |
+
// Add to trades list
|
1048 |
+
document.getElementById('tradesList').prepend(tradeElement);
|
1049 |
+
|
1050 |
+
// Clear form
|
1051 |
+
document.getElementById('newTradeSymbol').value = '';
|
1052 |
+
document.getElementById('newTradeEntry').value = '';
|
1053 |
+
document.getElementById('newTradeStop').value = '';
|
1054 |
+
document.getElementById('newTradeTarget').value = '';
|
1055 |
+
document.getElementById('newTradePosition').value = '';
|
1056 |
+
document.getElementById('newTradeNotes').value = '';
|
1057 |
+
|
1058 |
+
// Update trade count and statistics
|
1059 |
+
updateTradeStatistics();
|
1060 |
+
|
1061 |
+
// Show notification
|
1062 |
+
alert('Trade erfolgreich hinzugefügt!');
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
function removeTrade(button) {
|
1066 |
+
// Find the parent trade item and remove it
|
1067 |
+
const tradeItem = button.closest('.trade-item');
|
1068 |
+
tradeItem.style.opacity = '0.5';
|
1069 |
+
|
1070 |
+
// Simulate a delay before removal
|
1071 |
+
setTimeout(() => {
|
1072 |
+
tradeItem.remove();
|
1073 |
+
// Update statistics
|
1074 |
+
updateTradeStatistics();
|
1075 |
+
// Show notification
|
1076 |
+
alert('Trade entfernt!');
|
1077 |
+
}, 500);
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
function updateTradeStatistics() {
|
1081 |
+
// This would calculate real statistics in production
|
1082 |
+
// For demo, we'll just update the count
|
1083 |
+
const tradeCount = document.querySelectorAll('.trade-item').length;
|
1084 |
+
// Update the trade count in the statistics
|
1085 |
+
// This assumes there's an element to update
|
1086 |
+
}
|
1087 |
+
|
1088 |
+
function updateActiveTrades() {
|
1089 |
+
// In a real implementation, this would fetch actual trade data
|
1090 |
+
// For demo, we'll randomly update P&L
|
1091 |
+
|
1092 |
+
const tradeItems = document.querySelectorAll('.trade-item');
|
1093 |
+
|
1094 |
+
tradeItems.forEach(item => {
|
1095 |
+
const pnlElement = item.querySelector('.trade-pnl');
|
1096 |
+
if (pnlElement) {
|
1097 |
+
// Get current value
|
1098 |
+
const currentValue = parseFloat(pnlElement.textContent.replace('%', '').replace('+', ''));
|
1099 |
+
|
1100 |
+
// Small random change
|
1101 |
+
const change = (Math.random() * 0.2) - 0.1;
|
1102 |
+
const newValue = (currentValue + change).toFixed(2);
|
1103 |
+
|
1104 |
+
// Update display
|
1105 |
+
const isProfit = parseFloat(newValue) > 0;
|
1106 |
+
pnlElement.className = `trade-pnl ${isProfit ? 'profit' : 'loss'}`;
|
1107 |
+
pnlElement.textContent = `${isProfit ? '+' : ''}${newValue}%`;
|
1108 |
+
}
|
1109 |
+
});
|
1110 |
+
}
|
1111 |
+
|
1112 |
+
// Sort trades based on user selection
|
1113 |
+
function sortTrades() {
|
1114 |
+
const sortOption = document.getElementById('tradeSortOption').value;
|
1115 |
+
const tradesList = document.getElementById('tradesList');
|
1116 |
+
const trades = Array.from(tradesList.querySelectorAll('.trade-item'));
|
1117 |
+
|
1118 |
+
// Clear the container
|
1119 |
+
tradesList.innerHTML = '';
|
1120 |
+
|
1121 |
+
// Sort trades based on option
|
1122 |
+
switch (sortOption) {
|
1123 |
+
case 'newest':
|
1124 |
+
// Just reverse the current order for demo
|
1125 |
+
trades.reverse();
|
1126 |
+
break;
|
1127 |
+
case 'oldest':
|
1128 |
+
// Keep current order for demo
|
1129 |
+
break;
|
1130 |
+
case 'profit':
|
1131 |
+
trades.sort((a, b) => {
|
1132 |
+
const aPnl = parseFloat(a.querySelector('.trade-pnl').textContent.replace('%', '').replace('+', ''));
|
1133 |
+
const bPnl = parseFloat(b.querySelector('.trade-pnl').textContent.replace('%', '').replace('+', ''));
|
1134 |
+
return bPnl - aPnl; // Descending
|
1135 |
+
});
|
1136 |
+
break;
|
1137 |
+
case 'loss':
|
1138 |
+
trades.sort((a, b) => {
|
1139 |
+
const aPnl = parseFloat(a.querySelector('.trade-pnl').textContent.replace('%', '').replace('+', ''));
|
1140 |
+
const bPnl = parseFloat(b.querySelector('.trade-pnl').textContent.replace('%', '').replace('+', ''));
|
1141 |
+
return aPnl - bPnl; // Ascending
|
1142 |
+
});
|
1143 |
+
break;
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
// Add sorted trades back to container
|
1147 |
+
trades.forEach(trade => tradesList.appendChild(trade));
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
function refreshTrades() {
|
1151 |
+
updateActiveTrades();
|
1152 |
+
alert('Trades aktualisiert!');
|
1153 |
+
}
|
1154 |
+
|
1155 |
+
// Update intermarket analysis
|
1156 |
+
function updateIntermarketAnalysis() {
|
1157 |
+
const stockIndex = document.getElementById('stockIndex').value;
|
1158 |
+
const bondIndex = document.getElementById('bondIndex').value;
|
1159 |
+
const commodityIndex = document.getElementById('commodityIndex').value;
|
1160 |
+
const currencyIndex = document.getElementById('currencyIndex').value;
|
1161 |
+
|
1162 |
+
// In a real implementation, you would fetch data for these symbols
|
1163 |
+
alert(`Intermarket-Analyse wird aktualisiert mit: ${stockIndex}, ${bondIndex}, ${commodityIndex}, ${currencyIndex}`);
|
1164 |
+
|
1165 |
+
// Simulate loading data
|
1166 |
+
fetch('/api/intermarket')
|
1167 |
+
.then(response => response.json())
|
1168 |
+
.then(data => {
|
1169 |
+
updateIntermarketDisplay(data);
|
1170 |
+
})
|
1171 |
+
.catch(error => {
|
1172 |
+
console.error('Error updating intermarket analysis:', error);
|
1173 |
+
});
|
1174 |
+
}
|
static/requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
flask==2.2.3
|
2 |
+
requests==2.28.2
|
3 |
+
gunicorn==20.1.0
|