diff --git a/web/app.py b/web/app.py index 4d8918a..1d1bc9d 100644 --- a/web/app.py +++ b/web/app.py @@ -33,8 +33,11 @@ class TRADE_POINT_TYPE: SELL3 = -3 # 三类卖点 app = Flask(__name__) -macd_factor = 2 *6 +macd_factor = 2 smooth_factor = 1 +macd_fast_period = 12 * macd_factor +macd_slow_period = 26 * macd_factor +macd_signal_period = 9 * smooth_factor # 初始化交易所 exchange = ccxt.binance({ 'enableRateLimit': True, @@ -422,10 +425,8 @@ def get_a_stock_kl_data(symbol, timeframe, limit=100000, start_time=None, end_ti return None def add_indicators(df): - fast = 12 * macd_factor - slow = 26 * macd_factor - period = 9 * smooth_factor - macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) + global macd_fast_period, macd_slow_period, macd_signal_period + macd = ta.MACD(df, fastperiod=macd_fast_period, slowperiod=macd_slow_period, signalperiod=macd_signal_period) df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] @@ -510,10 +511,11 @@ def add_indicators(df): def calculate_macd(df): """计算MACD指标""" - exp1 = df['close'].ewm(span=12*macd_factor, adjust=False).mean() - exp2 = df['close'].ewm(span=26*macd_factor, adjust=False).mean() + global macd_fast_period, macd_slow_period, macd_signal_period + exp1 = df['close'].ewm(span=macd_fast_period, adjust=False).mean() + exp2 = df['close'].ewm(span=macd_slow_period, adjust=False).mean() macd = exp1 - exp2 - signal = macd.ewm(span=9 * smooth_factor, adjust=False).mean() + signal = macd.ewm(span=macd_signal_period, adjust=False).mean() histogram = macd - signal return { @@ -1845,6 +1847,34 @@ def search_stock(): return jsonify({'error': str(e)}) +@app.route('/api/macd_config', methods=['GET', 'POST']) +def macd_config(): + """获取或设置MACD参数""" + global macd_fast_period, macd_slow_period, macd_signal_period + if request.method == 'GET': + return jsonify({ + 'fast': macd_fast_period, + 'slow': macd_slow_period, + 'signal': macd_signal_period + }) + else: + data = request.get_json(silent=True) or {} + fast = data.get('fast') + slow = data.get('slow') + signal = data.get('signal') + if fast is not None: + macd_fast_period = int(fast) + if slow is not None: + macd_slow_period = int(slow) + if signal is not None: + macd_signal_period = int(signal) + return jsonify({ + 'fast': macd_fast_period, + 'slow': macd_slow_period, + 'signal': macd_signal_period + }) + + def get_uncompleted_seg_list(seg_list, client_tz): """获取未完成线段列表,正确处理倒数第二个和最后一个未完成线段""" uncompleted_segs = [seg for seg in seg_list if not seg.is_sure] diff --git a/web/templates/index.html b/web/templates/index.html index 060a485..77a27e7 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -764,6 +764,91 @@ width: 100%; height: 100%; } + + .macd-config-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 10000; + display: none; + justify-content: center; + align-items: center; + } + .macd-config-content { + background: white; + border-radius: 8px; + padding: 20px; + width: 360px; + max-width: 90vw; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + } + .macd-config-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 20px; + color: #333; + } + .macd-config-form { + display: grid; + gap: 14px; + } + .macd-config-group { + display: flex; + flex-direction: column; + gap: 6px; + } + .macd-config-label { + font-size: 14px; + font-weight: 500; + color: #555; + } + .macd-config-input { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; + } + .macd-config-input:focus { + outline: none; + border-color: #0d6efd; + box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25); + } + .macd-config-actions { + display: flex; + gap: 10px; + justify-content: flex-end; + margin-top: 20px; + } + .macd-config-btn { + padding: 8px 18px; + border: none; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; + } + .macd-config-btn-primary { + background: #0d6efd; + color: white; + } + .macd-config-btn-primary:hover { + background: #0b5ed7; + } + .macd-config-btn-secondary { + background: #f8f9fa; + color: #6c757d; + } + .macd-config-btn-secondary:hover { + background: #e9ecef; + } + .macd-config-hint { + font-size: 12px; + color: #999; + margin-top: 2px; + } @@ -854,6 +939,7 @@
+
@@ -1783,6 +1869,56 @@ $('#showMacd').change(function() { updateChartDisplay(); }); + + function showMacdConfig() { + $.get('/api/macd_config', function(data) { + $('#macdFastPeriod').val(data.fast); + $('#macdSlowPeriod').val(data.slow); + $('#macdSignalPeriod').val(data.signal); + $('#macdConfigModal').css('display', 'flex'); + }); + } + + function hideMacdConfig() { + $('#macdConfigModal').css('display', 'none'); + } + + function resetMacdConfig() { + $('#macdFastPeriod').val(24); + $('#macdSlowPeriod').val(52); + $('#macdSignalPeriod').val(9); + } + + function saveMacdConfig() { + const fast = parseInt($('#macdFastPeriod').val()); + const slow = parseInt($('#macdSlowPeriod').val()); + const signal = parseInt($('#macdSignalPeriod').val()); + if (fast >= slow) { + alert('快线周期必须小于慢线周期'); + return; + } + if (fast < 2 || slow < 2 || signal < 2) { + alert('周期值必须大于等于2'); + return; + } + $.ajax({ + url: '/api/macd_config', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify({ fast: fast, slow: slow, signal: signal }), + success: function() { + hideMacdConfig(); + updateChart(); + }, + error: function() { + alert('保存MACD参数失败'); + } + }); + } + + $(document).on('click', '#macdConfigModal', function(e) { + if (e.target === this) hideMacdConfig(); + }); // 添加原始K线复选框变更事件 $('#showOriginalKline').change(function() { @@ -6585,21 +6721,29 @@ // 首先同步时间轴设置 syncTimeScaleSettings(); - // 然后让主图表适应内容 - mainChart.timeScale().fitContent(); + // 显示最近 200 根K线而非全部挤压(避免K线过多时重叠) + const totalBars = candles ? candles.length : 0; + const visibleBarsCount = 200; + if (totalBars > visibleBarsCount) { + const rangeFrom = totalBars - visibleBarsCount; + const rangeTo = totalBars + 12; + mainChart.timeScale().setVisibleLogicalRange({ from: rangeFrom, to: rangeTo }); + } else { + mainChart.timeScale().fitContent(); + } // 立即同步其他图表到主图表的范围 setTimeout(() => { - const visibleRange = mainChart.timeScale().getVisibleRange(); - if (visibleRange) { - console.log('🔧 同步可见范围:', visibleRange); - volumeChart.timeScale().setVisibleRange(visibleRange); - atrChart.timeScale().setVisibleRange(visibleRange); + const logRange = mainChart.timeScale().getVisibleLogicalRange(); + if (logRange) { + console.log('🔧 同步可见范围:', logRange); + volumeChart.timeScale().setVisibleLogicalRange(logRange); + atrChart.timeScale().setVisibleLogicalRange(logRange); if (showMacd && macdChart) { - macdChart.timeScale().setVisibleRange(visibleRange); + macdChart.timeScale().setVisibleLogicalRange(logRange); } if (showMacd && chanMacdChart) { - chanMacdChart.timeScale().setVisibleRange(visibleRange); + chanMacdChart.timeScale().setVisibleLogicalRange(logRange); } console.log('🔧 时间轴同步完成'); } @@ -10461,5 +10605,34 @@
+ + +
+
+
ChanMACD 参数设置
+
+
+ + +
默认: 24 (12×2)
+
+
+ + +
默认: 52 (26×2)
+
+
+ + +
默认: 9
+
+
+
+ + + +
+
+
\ No newline at end of file