feat:首页增加自选等模块,存在bug

master
Lxy 4 months ago
parent b167d953db
commit d05b1ede63

@ -456,6 +456,36 @@ class DataFetcher:
'ask_price1': base_price + 1 'ask_price1': base_price + 1
} }
def get_all_symbols(self) -> List[str]:
"""获取所有品种列表
Returns:
List[str]: 所有品种的合约代码列表
"""
try:
if TQSDK_AVAILABLE and self.api:
# TQSDK 没有 get_instrument_info 方法,我们使用模拟数据
print("TQSDK 不支持获取所有品种列表,使用模拟数据")
return self._get_mock_all_symbols()
else:
# 返回模拟数据
print("使用模拟品种列表")
return self._get_mock_all_symbols()
except Exception as e:
print(f"获取所有品种列表失败:{e}")
return self._get_mock_all_symbols()
def _get_mock_all_symbols(self) -> List[str]:
"""获取模拟品种列表"""
# 返回常用的期货品种
return [
"CU2603", "AL2603", "ZN2603", "PB2603", "NI2603", "SN2603",
"AU2603", "AG2603", "RB2603", "HC2603", "BU2603", "RU2603",
"SC2603", "I2603", "J2603", "JM2603", "A2603", "M2603",
"Y2603", "P2603", "C2603", "CS2603", "L2603", "V2603",
"PP2603", "TA2603", "CF2603", "SR2603", "MA2603", "FG2603"
]
# 导入numpy # 导入numpy
import numpy as np import numpy as np

@ -41,14 +41,57 @@ deepseek_agent = DeepseekAgent()
# 连接API # 连接API
data_fetcher.connect() data_fetcher.connect()
# 测试品种列表 - 使用当前有效的合约代码 # 从SDK获取所有品种列表
test_symbols = ["CU2603", "AL2603", "ZN2603", "PB2603", "NI2603", "SN2603"] test_symbols = data_fetcher.get_all_symbols()
# 自选品种列表(使用内存存储,实际项目中可以使用数据库或文件存储)
selected_symbols = []
# 热门品种获取功能
def get_hot_symbols(all_symbols_data):
"""获取热门交易品种
Args:
all_symbols_data: 所有品种的分析数据
Returns:
dict: 包含成交量振幅涨速三个模块的热门品种数据
"""
hot_symbols = {
'volume': [], # 成交量热门
'amplitude': [], # 振幅热门
'speed': [] # 涨速热门
}
# 计算每个品种的成交量、振幅、涨速
for symbol_data in all_symbols_data:
try:
# 这里简化处理,实际项目中应该使用真实的成交量、振幅、涨速数据
# 假设我们从kline_data中获取这些数据
# 由于我们没有这些数据,这里使用随机值模拟
import random
symbol_data['volume'] = random.randint(10000, 1000000)
symbol_data['amplitude'] = random.uniform(0.1, 5.0)
symbol_data['speed'] = random.uniform(-2.0, 2.0)
except Exception:
pass
# 按成交量排序取前5个
hot_symbols['volume'] = sorted(all_symbols_data, key=lambda x: x.get('volume', 0), reverse=True)[:5]
# 按振幅排序取前5个
hot_symbols['amplitude'] = sorted(all_symbols_data, key=lambda x: x.get('amplitude', 0), reverse=True)[:5]
# 按涨速排序取前5个
hot_symbols['speed'] = sorted(all_symbols_data, key=lambda x: x.get('speed', 0), reverse=True)[:5]
return hot_symbols
@app.route('/') @app.route('/')
def index(): def index():
"""首页 - 多品种分析面板""" """首页 - 多品种分析面板"""
# 获取所有品种的分析数据 # 获取所有品种的分析数据
symbols_data = [] all_symbols_data = []
data_available = False data_available = False
for symbol in test_symbols: for symbol in test_symbols:
@ -130,22 +173,116 @@ def index():
'name': f"{product_name_cn}({symbol})".upper(), 'name': f"{product_name_cn}({symbol})".upper(),
'current_price': safe_current_price, 'current_price': safe_current_price,
'direction': trend_analysis.get('overall_trend', 'sideways'), 'direction': trend_analysis.get('overall_trend', 'sideways'),
'win_rate': safe_win_rate, 'trend_strength': _get_trend_strength_display(safe_adx)
'trend_strength': _get_trend_strength_display(safe_adx),
'cycle': cycle_to_cn(cycle),
'rollover_warning': _get_rollover_warning(rollover_analysis),
'fund_flow': fund_flow_to_cn(fund_flow_analysis.get('fund_signal', 'neutral'))
} }
symbols_data.append(symbol_data) all_symbols_data.append(symbol_data)
except Exception as e: except Exception as e:
print(f"分析 {symbol} 失败: {e}") print(f"分析 {symbol} 失败: {e}")
continue continue
# 获取自选品种的分析数据
selected_symbols_data = []
for symbol in selected_symbols:
try:
# 获取K线数据
kline_data = data_fetcher.get_kline_data(symbol, "1d", 200)
if kline_data is None or kline_data.empty:
continue
# 趋势分析
trend_analysis = trend_filter.analyze_trend(kline_data)
win_rate = trend_filter.calculate_win_rate(kline_data)
cycle = trend_filter.judge_cycle(kline_data)
# 资金流向分析
fund_flow_analysis = fund_flow_monitor.analyze_fund_flow(kline_data)
# 换月分析
rollover_analysis = rollover_detector.analyze_rollover(symbol, kline_data)
# 价格数据
current_price = kline_data['close'].iloc[-1]
# 获取中文名称
product_name_cn = data_fetcher.get_product_name_cn(symbol)
# 转换周期为中文
def cycle_to_cn(cycle):
cycle_map = {
'short': '短期',
'medium': '中期',
'long': '长期',
'bullish': '多头',
'bearish': '空头',
'sideways': '震荡'
}
return cycle_map.get(cycle, cycle)
# 转换资金流向为中文
def fund_flow_to_cn(fund_flow):
fund_flow_map = {
'bullish': '多头',
'bearish': '空头',
'neutral': '中性',
'Strong_bullish': '强多头',
'Strong_bearish': '强空头'
}
return fund_flow_map.get(fund_flow, fund_flow)
# 构建数据
# 安全处理可能的NaN值
safe_current_price = 0
try:
if current_price is not None and (not isinstance(current_price, float) or current_price == current_price): # 不是NaN
safe_current_price = round(current_price, 2)
except (ValueError, TypeError):
safe_current_price = 0
safe_win_rate = 0
try:
if win_rate is not None and (not isinstance(win_rate, float) or win_rate == win_rate): # 不是NaN
safe_win_rate = round(win_rate, 1)
except (ValueError, TypeError):
safe_win_rate = 0
# 安全获取ADX值
safe_adx = 0
try:
adx = trend_analysis.get('adx', 0)
if adx is not None and (not isinstance(adx, float) or adx == adx): # 不是NaN
safe_adx = adx
except (ValueError, TypeError):
safe_adx = 0
symbol_data = {
'symbol': symbol,
'name': f"{product_name_cn}({symbol})".upper(),
'current_price': safe_current_price,
'direction': trend_analysis.get('overall_trend', 'sideways'),
'trend_strength': _get_trend_strength_display(safe_adx)
}
selected_symbols_data.append(symbol_data)
except Exception as e:
print(f"分析自选品种 {symbol} 失败: {e}")
continue
# 获取热门品种数据
hot_symbols = get_hot_symbols(all_symbols_data)
# 如果没有任何数据可用,显示友好提示 # 如果没有任何数据可用,显示友好提示
if not data_available: if not data_available:
return render_template('index.html', symbols_data=[], data_unavailable=True, message="无法获取真实市场数据请检查网络连接和TQSDK账号状态") return render_template('index.html',
all_symbols_data=[],
return render_template('index.html', symbols_data=symbols_data, data_unavailable=False) selected_symbols_data=[],
hot_symbols={},
data_unavailable=True,
message="无法获取真实市场数据请检查网络连接和TQSDK账号状态")
return render_template('index.html',
all_symbols_data=all_symbols_data,
selected_symbols_data=selected_symbols_data,
hot_symbols=hot_symbols,
data_unavailable=False)
@app.route('/symbol/<symbol>') @app.route('/symbol/<symbol>')
def symbol_detail(symbol): def symbol_detail(symbol):
@ -374,7 +511,8 @@ def symbol_detail(symbol):
'rollover_analysis': rollover_analysis, 'rollover_analysis': rollover_analysis,
'ai_analysis': ai_analysis, 'ai_analysis': ai_analysis,
'recommendation': recommendation, 'recommendation': recommendation,
'kline_data': _get_kline_data_for_chart(kline_data) 'kline_data': _get_kline_data_for_chart(kline_data),
'is_favorite': symbol in selected_symbols
} }
print("[DEBUG] context built successfully") print("[DEBUG] context built successfully")
@ -641,5 +779,26 @@ def _get_kline_data_for_chart(kline_data):
except (ValueError, TypeError): except (ValueError, TypeError):
return [] return []
@app.route('/api/selected/add/<symbol>', methods=['POST'])
def add_selected_symbol(symbol):
"""添加自选品种"""
global selected_symbols
if symbol not in selected_symbols:
selected_symbols.append(symbol)
return jsonify({'status': 'success', 'selected_symbols': selected_symbols})
@app.route('/api/selected/remove/<symbol>', methods=['POST'])
def remove_selected_symbol(symbol):
"""删除自选品种"""
global selected_symbols
if symbol in selected_symbols:
selected_symbols.remove(symbol)
return jsonify({'status': 'success', 'selected_symbols': selected_symbols})
@app.route('/api/selected/list')
def get_selected_symbols():
"""获取自选品种列表"""
return jsonify({'selected_symbols': selected_symbols})
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000) app.run(debug=True, host='0.0.0.0', port=5000)

@ -45,11 +45,48 @@
opacity: 0.9; opacity: 0.9;
} }
.section {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
}
.section-title {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.search-container {
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 12px 16px;
border: 1px solid #d9d9d9;
border-radius: 8px;
font-size: 14px;
outline: none;
transition: all 0.3s ease;
}
.search-input:focus {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.panel-grid { .panel-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 20px; gap: 20px;
margin-bottom: 30px; margin-bottom: 20px;
} }
.symbol-card { .symbol-card {
@ -59,6 +96,7 @@
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease; transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer; cursor: pointer;
position: relative;
} }
.symbol-card:hover { .symbol-card:hover {
@ -159,6 +197,53 @@
color: #52c41a; color: #52c41a;
} }
.hot-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px;
margin-top: 20px;
}
.hot-subsection {
background: #f9fafb;
border-radius: 8px;
padding: 16px;
}
.hot-subsection-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 12px;
}
.favorite-btn {
position: absolute;
top: 8px;
right: 8px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
font-size: 12px;
color: #999;
}
.favorite-btn:hover {
background-color: rgba(255, 77, 79, 0.1);
color: #ff4d4f;
}
.favorite-btn.favorite {
background-color: rgba(255, 77, 79, 0.1);
color: #ff4d4f;
}
.footer { .footer {
text-align: center; text-align: center;
padding: 20px; padding: 20px;
@ -175,6 +260,10 @@
.analysis-grid { .analysis-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.hot-section {
grid-template-columns: 1fr;
}
} }
</style> </style>
</head> </head>
@ -186,7 +275,6 @@
<p>基于DeepSeek大模型和量化分析算法的智能决策平台</p> <p>基于DeepSeek大模型和量化分析算法的智能决策平台</p>
</div> </div>
<!-- 多品种分析面板 -->
{% if data_unavailable %} {% if data_unavailable %}
<div style="text-align: center; padding: 60px; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);"> <div style="text-align: center; padding: 60px; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);">
<div style="font-size: 48px; margin-bottom: 20px;">⚠️</div> <div style="font-size: 48px; margin-bottom: 20px;">⚠️</div>
@ -198,9 +286,19 @@
</div> </div>
</div> </div>
{% else %} {% else %}
<!-- 自选关注模块 -->
<div class="section">
<div class="section-title">
自选关注
<span style="font-size: 14px; font-weight: 400; color: #666;">{{ selected_symbols_data | length }} 个品种</span>
</div>
{% if selected_symbols_data %}
<div class="panel-grid"> <div class="panel-grid">
{% for symbol_data in symbols_data %} {% for symbol_data in selected_symbols_data %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'"> <div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<div class="favorite-btn favorite" onclick="event.stopPropagation(); removeFromFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header"> <div class="symbol-header">
<div> <div>
<div class="symbol-name">{{ symbol_data.name }}</div> <div class="symbol-name">{{ symbol_data.name }}</div>
@ -224,30 +322,171 @@
</div> </div>
<div class="analysis-grid"> <div class="analysis-grid">
<div class="analysis-item">
<div class="analysis-label">胜率</div>
<div class="analysis-value win-rate">{{ symbol_data.win_rate }}%</div>
</div>
<div class="analysis-item"> <div class="analysis-item">
<div class="analysis-label">趋势强度</div> <div class="analysis-label">趋势强度</div>
<div class="analysis-value">{{ symbol_data.trend_strength }}</div> <div class="analysis-value">{{ symbol_data.trend_strength }}</div>
</div> </div>
<div class="analysis-item">
<div class="analysis-label">周期</div>
<div class="analysis-value">{{ symbol_data.cycle }}</div>
</div> </div>
<div class="analysis-item">
<div class="analysis-label">换月预警</div>
<div class="analysis-value rollover-warning">{{ symbol_data.rollover_warning }}</div>
</div> </div>
{% endfor %}
</div>
{% else %}
<div style="text-align: center; padding: 40px; color: #666;">
<p>暂无自选品种,可从下方品种列表中添加</p>
</div>
{% endif %}
</div>
<!-- 热门交易品种展示模块 -->
<div class="section">
<div class="section-title">热门交易品种</div>
<div class="hot-section">
<!-- 成交量热门 -->
<div class="hot-subsection">
<div class="hot-subsection-title">成交量热门</div>
<div class="panel-grid">
{% for symbol_data in hot_symbols.volume %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<div class="favorite-btn" onclick="event.stopPropagation(); addToFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
<div class="symbol-name">{{ symbol_data.name }}</div>
<div class="symbol-code">{{ symbol_data.symbol }}</div>
</div>
<div class="price-info">
<div class="current-price">{{ symbol_data.current_price }}</div>
<div class="direction-indicator
{% if symbol_data.direction == 'bullish' or symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' %}direction-up{% elif symbol_data.direction == 'bearish' or symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' %}direction-down{% else %}direction-sideways{% endif %}">
{% if symbol_data.direction == 'strong_bullish' %}强多头
{% elif symbol_data.direction == 'strong_bearish' %}强空头
{% elif symbol_data.direction == 'weak_bullish' %}弱多头
{% elif symbol_data.direction == 'weak_bearish' %}弱空头
{% elif symbol_data.direction == 'bullish' %}多头
{% elif symbol_data.direction == 'bearish' %}空头
{% elif symbol_data.direction == 'neutral' %}中性
{% else %}{{ symbol_data.direction | capitalize }}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- 振幅热门 -->
<div class="hot-subsection">
<div class="hot-subsection-title">振幅热门</div>
<div class="panel-grid">
{% for symbol_data in hot_symbols.amplitude %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<div class="favorite-btn" onclick="event.stopPropagation(); addToFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
<div class="symbol-name">{{ symbol_data.name }}</div>
<div class="symbol-code">{{ symbol_data.symbol }}</div>
</div>
<div class="price-info">
<div class="current-price">{{ symbol_data.current_price }}</div>
<div class="direction-indicator
{% if symbol_data.direction == 'bullish' or symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' %}direction-up{% elif symbol_data.direction == 'bearish' or symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' %}direction-down{% else %}direction-sideways{% endif %}">
{% if symbol_data.direction == 'strong_bullish' %}强多头
{% elif symbol_data.direction == 'strong_bearish' %}强空头
{% elif symbol_data.direction == 'weak_bullish' %}弱多头
{% elif symbol_data.direction == 'weak_bearish' %}弱空头
{% elif symbol_data.direction == 'bullish' %}多头
{% elif symbol_data.direction == 'bearish' %}空头
{% elif symbol_data.direction == 'neutral' %}中性
{% else %}{{ symbol_data.direction | capitalize }}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- 涨速热门 -->
<div class="hot-subsection">
<div class="hot-subsection-title">涨速热门</div>
<div class="panel-grid">
{% for symbol_data in hot_symbols.speed %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<div class="favorite-btn" onclick="event.stopPropagation(); addToFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
<div class="symbol-name">{{ symbol_data.name }}</div>
<div class="symbol-code">{{ symbol_data.symbol }}</div>
</div>
<div class="price-info">
<div class="current-price">{{ symbol_data.current_price }}</div>
<div class="direction-indicator
{% if symbol_data.direction == 'bullish' or symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' %}direction-up{% elif symbol_data.direction == 'bearish' or symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' %}direction-down{% else %}direction-sideways{% endif %}">
{% if symbol_data.direction == 'strong_bullish' %}强多头
{% elif symbol_data.direction == 'strong_bearish' %}强空头
{% elif symbol_data.direction == 'weak_bullish' %}弱多头
{% elif symbol_data.direction == 'weak_bearish' %}弱空头
{% elif symbol_data.direction == 'bullish' %}多头
{% elif symbol_data.direction == 'bearish' %}空头
{% elif symbol_data.direction == 'neutral' %}中性
{% else %}{{ symbol_data.direction | capitalize }}
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- 所有品种的列表 -->
<div class="section">
<div class="section-title">所有品种</div>
<div class="search-container">
<input type="text" class="search-input" placeholder="搜索品种代码或名称" id="symbolSearch">
</div>
<div class="panel-grid" id="allSymbolsGrid">
{% for symbol_data in all_symbols_data %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<div class="favorite-btn" onclick="event.stopPropagation(); addToFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
<div class="symbol-name">{{ symbol_data.name }}</div>
<div class="symbol-code">{{ symbol_data.symbol }}</div>
</div>
<div class="price-info">
<div class="current-price">{{ symbol_data.current_price }}</div>
<div class="direction-indicator
{% if symbol_data.direction == 'bullish' or symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' %}direction-up{% elif symbol_data.direction == 'bearish' or symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' %}direction-down{% else %}direction-sideways{% endif %}">
{% if symbol_data.direction == 'strong_bullish' %}强多头
{% elif symbol_data.direction == 'strong_bearish' %}强空头
{% elif symbol_data.direction == 'weak_bullish' %}弱多头
{% elif symbol_data.direction == 'weak_bearish' %}弱空头
{% elif symbol_data.direction == 'bullish' %}多头
{% elif symbol_data.direction == 'bearish' %}空头
{% elif symbol_data.direction == 'neutral' %}中性
{% else %}{{ symbol_data.direction | capitalize }}
{% endif %}
</div>
</div>
</div>
<div class="analysis-grid">
<div class="analysis-item"> <div class="analysis-item">
<div class="analysis-label">资金流向</div> <div class="analysis-label">趋势强度</div>
<div class="analysis-value fund-flow">{{ symbol_data.fund_flow }}</div> <div class="analysis-value">{{ symbol_data.trend_strength }}</div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
{% endif %} {% endif %}
<!-- 底部 --> <!-- 底部 -->
@ -263,6 +502,70 @@
return date.toLocaleString('zh-CN'); return date.toLocaleString('zh-CN');
} }
// 添加到自选
function addToFavorite(symbol) {
fetch(`/api/selected/add/${symbol}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
alert('已添加到自选');
// 刷新页面以更新自选列表
window.location.reload();
})
.catch(error => {
console.error('添加自选失败:', error);
alert('添加自选失败');
});
}
// 从自选中删除
function removeFromFavorite(symbol) {
fetch(`/api/selected/remove/${symbol}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
alert('已从自选中删除');
// 刷新页面以更新自选列表
window.location.reload();
})
.catch(error => {
console.error('删除自选失败:', error);
alert('删除自选失败');
});
}
// 搜索功能
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('symbolSearch');
const symbolsGrid = document.getElementById('allSymbolsGrid');
if (searchInput && symbolsGrid) {
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const symbolCards = symbolsGrid.querySelectorAll('.symbol-card');
symbolCards.forEach(card => {
const symbolName = card.querySelector('.symbol-name').textContent.toLowerCase();
const symbolCode = card.querySelector('.symbol-code').textContent.toLowerCase();
if (symbolName.includes(searchTerm) || symbolCode.includes(searchTerm)) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
});
}
});
// 定时刷新数据 // 定时刷新数据
setInterval(() => { setInterval(() => {
window.location.reload(); window.location.reload();

@ -336,6 +336,14 @@
<div class="stat-label">周期</div> <div class="stat-label">周期</div>
<div class="stat-value">{{ cycle }}</div> <div class="stat-value">{{ cycle }}</div>
</div> </div>
<div class="stat-item">
<div class="stat-label">自选</div>
<div class="stat-value" style="cursor: pointer;" onclick="toggleFavorite('{{ symbol }}')">
<span id="favoriteBtn" style="font-size: 18px;">
{% if is_favorite %}★{% else %}☆{% endif %}
</span>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -567,6 +575,54 @@
return date.toLocaleString('zh-CN'); return date.toLocaleString('zh-CN');
} }
// 添加到自选
function addToFavorite(symbol) {
fetch(`/api/selected/add/${symbol}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
alert('已添加到自选');
document.getElementById('favoriteBtn').textContent = '★';
})
.catch(error => {
console.error('添加自选失败:', error);
alert('添加自选失败');
});
}
// 从自选中删除
function removeFromFavorite(symbol) {
fetch(`/api/selected/remove/${symbol}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
alert('已从自选中删除');
document.getElementById('favoriteBtn').textContent = '☆';
})
.catch(error => {
console.error('删除自选失败:', error);
alert('删除自选失败');
});
}
// 切换自选状态
function toggleFavorite(symbol) {
const favoriteBtn = document.getElementById('favoriteBtn');
if (favoriteBtn.textContent === '☆') {
addToFavorite(symbol);
} else {
removeFromFavorite(symbol);
}
}
// 初始化K线图表 // 初始化K线图表
window.onload = function() { window.onload = function() {
const chart = echarts.init(document.getElementById('kline-chart')); const chart = echarts.init(document.getElementById('kline-chart'));

Loading…
Cancel
Save