添加本地数据源,以后就可以直接用本地数据了
This commit is contained in:
+232
-79
@@ -1,6 +1,10 @@
|
||||
from flask import Flask, render_template, jsonify, request
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
import logging
|
||||
import ccxt
|
||||
import pandas as pd
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
import sys
|
||||
import os
|
||||
@@ -41,31 +45,170 @@ exchange = ccxt.binance({
|
||||
# 初始化A股数据获取器
|
||||
china_stock = ChinaStockData()
|
||||
|
||||
# 时间周期映射
|
||||
TIMEFRAMES = {
|
||||
'1m': '1分钟',
|
||||
'3m': '3分钟',
|
||||
'5m': '5分钟',
|
||||
'15m': '15分钟',
|
||||
'30m': '30分钟',
|
||||
'1h': '1小时',
|
||||
'2h': '2小时',
|
||||
'4h': '4小时',
|
||||
'6h': '6小时',
|
||||
'8h': '8小时',
|
||||
'12h': '12小时',
|
||||
'1d': '日线',
|
||||
'3d': '3日线',
|
||||
'1w': '周线',
|
||||
'1M': '月线',
|
||||
}
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 常见交易对
|
||||
SYMBOLS = [
|
||||
DATA_SERVICE_URL = os.environ.get("DATA_SERVICE_URL", os.environ.get("DATASVC_URL", "http://127.0.0.1:9000"))
|
||||
|
||||
DEFAULT_TIMEFRAME_LABELS = OrderedDict([
|
||||
("1m", "1分钟"),
|
||||
("3m", "3分钟"),
|
||||
("5m", "5分钟"),
|
||||
("15m", "15分钟"),
|
||||
("30m", "30分钟"),
|
||||
("1h", "1小时"),
|
||||
("2h", "2小时"),
|
||||
("4h", "4小时"),
|
||||
("6h", "6小时"),
|
||||
("8h", "8小时"),
|
||||
("12h", "12小时"),
|
||||
("1d", "日线"),
|
||||
("3d", "3日线"),
|
||||
("1w", "周线"),
|
||||
("1M", "月线"),
|
||||
])
|
||||
|
||||
DEFAULT_SYMBOLS = [
|
||||
'SOL/USDT:USDT', 'BTC/USDT:USDT', 'ETH/USDT:USDT', 'BNB/USDT:USDT', 'XRP/USDT:USDT',
|
||||
'ADA/USDT:USDT', 'DOGE/USDT:USDT', 'AVAX/USDT:USDT', 'DOT/USDT:USDT', 'MATIC/USDT:USDT'
|
||||
]
|
||||
|
||||
TIMEFRAMES = DEFAULT_TIMEFRAME_LABELS.copy()
|
||||
SYMBOLS = DEFAULT_SYMBOLS.copy()
|
||||
DATA_SERVICE_AVAILABLE = False
|
||||
SERVICE_METADATA_LAST_REFRESH = 0
|
||||
|
||||
|
||||
def timeframe_to_minutes(tf: str):
|
||||
"""将时间周期转换为分钟数,用于排序。"""
|
||||
if not tf:
|
||||
return None
|
||||
unit = tf[-1]
|
||||
try:
|
||||
value = int(tf[:-1])
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
multiplier = {
|
||||
'm': 1,
|
||||
'h': 60,
|
||||
'd': 1440,
|
||||
'w': 10080,
|
||||
'M': 43200, # 30天近似
|
||||
}.get(unit)
|
||||
if multiplier is None:
|
||||
return None
|
||||
return value * multiplier
|
||||
|
||||
|
||||
def format_timeframe_label(tf: str) -> str:
|
||||
"""将时间周期转换为可读标签。"""
|
||||
if not tf:
|
||||
return tf
|
||||
unit = tf[-1]
|
||||
try:
|
||||
value = int(tf[:-1])
|
||||
except (ValueError, TypeError):
|
||||
return tf
|
||||
if unit == 'm':
|
||||
return f"{value}分钟"
|
||||
if unit == 'h':
|
||||
return f"{value}小时"
|
||||
if unit == 'd':
|
||||
return "日线" if value == 1 else f"{value}日线"
|
||||
if unit == 'w':
|
||||
return "周线" if value == 1 else f"{value}周线"
|
||||
if unit == 'M':
|
||||
return "月线" if value == 1 else f"{value}月线"
|
||||
return tf
|
||||
|
||||
|
||||
def build_timeframe_labels(timeframes):
|
||||
ordered = sorted(
|
||||
timeframes,
|
||||
key=lambda tf: timeframe_to_minutes(tf) if timeframe_to_minutes(tf) is not None else float('inf'),
|
||||
)
|
||||
labels = OrderedDict()
|
||||
for tf in ordered:
|
||||
labels[tf] = format_timeframe_label(tf)
|
||||
return labels
|
||||
|
||||
|
||||
def _parse_time_input(value):
|
||||
if value in (None, '', 0):
|
||||
return None
|
||||
try:
|
||||
return int(float(value))
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def refresh_data_service_metadata(force=False):
|
||||
"""刷新数据服务提供的交易对与周期元信息。"""
|
||||
global DATA_SERVICE_AVAILABLE, TIMEFRAMES, SYMBOLS, SERVICE_METADATA_LAST_REFRESH
|
||||
now = time.time()
|
||||
if not force and DATA_SERVICE_AVAILABLE and now - SERVICE_METADATA_LAST_REFRESH < 60:
|
||||
return True
|
||||
try:
|
||||
resp = requests.get(f"{DATA_SERVICE_URL}/health", timeout=5)
|
||||
resp.raise_for_status()
|
||||
payload = resp.json()
|
||||
service_symbols = payload.get("symbols") or payload.get("symbol_list") or []
|
||||
base_timeframes = payload.get("timeframes") or payload.get("base_timeframes") or []
|
||||
derived = payload.get("derived_timeframes") or []
|
||||
service_timeframes = list(base_timeframes)
|
||||
for tf in derived:
|
||||
if tf not in service_timeframes:
|
||||
service_timeframes.append(tf)
|
||||
if service_symbols:
|
||||
SYMBOLS[:] = service_symbols
|
||||
if service_timeframes:
|
||||
TIMEFRAMES.clear()
|
||||
TIMEFRAMES.update(build_timeframe_labels(service_timeframes))
|
||||
DATA_SERVICE_AVAILABLE = True
|
||||
SERVICE_METADATA_LAST_REFRESH = now
|
||||
return True
|
||||
except Exception as exc:
|
||||
logger.warning("无法加载数据服务元信息: %s", exc)
|
||||
if not DATA_SERVICE_AVAILABLE:
|
||||
TIMEFRAMES.clear()
|
||||
TIMEFRAMES.update(DEFAULT_TIMEFRAME_LABELS)
|
||||
SYMBOLS[:] = DEFAULT_SYMBOLS
|
||||
DATA_SERVICE_AVAILABLE = False
|
||||
return False
|
||||
|
||||
|
||||
def _fetch_kl_from_datasvc(symbol, timeframe, start_ms=None, end_ms=None, limit=None):
|
||||
params = {"symbol": symbol, "tf": timeframe}
|
||||
if start_ms is not None:
|
||||
params["start"] = int(start_ms)
|
||||
if end_ms is not None:
|
||||
params["end"] = int(end_ms)
|
||||
resp = requests.get(f"{DATA_SERVICE_URL}/api/candles", params=params, timeout=10)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if not data:
|
||||
return None
|
||||
df = pd.DataFrame(data)
|
||||
if df.empty or "timestamp" not in df.columns:
|
||||
return None
|
||||
numeric_cols = ["open", "high", "low", "close", "volume"]
|
||||
df["timestamp"] = pd.to_numeric(df["timestamp"], errors="coerce")
|
||||
df = df.dropna(subset=["timestamp"])
|
||||
df["timestamp"] = df["timestamp"].astype("int64")
|
||||
for col in numeric_cols:
|
||||
if col in df.columns:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
df = df.dropna(subset=numeric_cols)
|
||||
df = df.sort_values("timestamp")
|
||||
if limit and len(df) > limit:
|
||||
df = df.tail(limit)
|
||||
df = df.reset_index(drop=True)
|
||||
df["date"] = pd.to_datetime(df["timestamp"], unit='ms', utc=True).dt.tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
# 模块加载时尝试预取一次元信息,但失败不阻塞后续流程
|
||||
refresh_data_service_metadata(force=True)
|
||||
|
||||
# A股热门股票
|
||||
A_STOCK_SYMBOLS = china_stock.get_popular_stocks()
|
||||
|
||||
@@ -89,7 +232,7 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None):
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None):
|
||||
def _get_crypto_kl_data_via_ccxt(symbol, timeframe, limit=1000, start_time=None, end_time=None):
|
||||
"""获取加密货币K线数据,支持分页加载确保获取指定时间范围内的所有数据"""
|
||||
try:
|
||||
# 初始化参数
|
||||
@@ -202,6 +345,30 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
|
||||
def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None):
|
||||
"""优先通过本地数据服务获取加密货币K线,失败时回退至交易所API。"""
|
||||
start_ms = _parse_time_input(start_time)
|
||||
end_ms = _parse_time_input(end_time)
|
||||
|
||||
refresh_data_service_metadata()
|
||||
if DATA_SERVICE_AVAILABLE:
|
||||
try:
|
||||
df = _fetch_kl_from_datasvc(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
start_ms=start_ms,
|
||||
end_ms=end_ms,
|
||||
limit=limit,
|
||||
)
|
||||
if df is not None and not df.empty:
|
||||
return df
|
||||
except Exception as exc:
|
||||
logger.warning("数据服务请求失败,准备回退至交易所 API:%s", exc)
|
||||
|
||||
return _get_crypto_kl_data_via_ccxt(symbol, timeframe, limit, start_time, end_time)
|
||||
|
||||
|
||||
def get_a_stock_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None):
|
||||
"""获取A股K线数据"""
|
||||
try:
|
||||
@@ -733,66 +900,18 @@ def serialize_chan_macd_data(chan_macd_data, client_tz):
|
||||
|
||||
def is_smaller_timeframe(tf1, tf2):
|
||||
"""判断时间周期tf1是否小于tf2"""
|
||||
# 定义时间周期的分钟数映射
|
||||
tf_values = {
|
||||
'1m': 1,
|
||||
'3m': 3,
|
||||
'5m': 5,
|
||||
'15m': 15,
|
||||
'30m': 30,
|
||||
'1h': 60,
|
||||
'2h': 120,
|
||||
'4h': 240,
|
||||
'6h': 360,
|
||||
'8h': 480,
|
||||
'12h': 720,
|
||||
'1d': 1440,
|
||||
'3d': 4320,
|
||||
'1w': 10080,
|
||||
'1M': 43200
|
||||
}
|
||||
|
||||
# 获取时间周期对应的分钟数
|
||||
tf1_value = tf_values.get(tf1)
|
||||
tf2_value = tf_values.get(tf2)
|
||||
|
||||
# 如果某个时间周期不在映射中,返回False
|
||||
tf1_value = timeframe_to_minutes(tf1)
|
||||
tf2_value = timeframe_to_minutes(tf2)
|
||||
if tf1_value is None or tf2_value is None:
|
||||
return False
|
||||
|
||||
# 返回tf1是否小于tf2
|
||||
return tf1_value < tf2_value
|
||||
|
||||
def is_smaller_or_equal_timeframe(tf1, tf2):
|
||||
"""判断时间周期tf1是否小于等于tf2"""
|
||||
# 定义时间周期的分钟数映射
|
||||
tf_values = {
|
||||
'1m': 1,
|
||||
'3m': 3,
|
||||
'5m': 5,
|
||||
'15m': 15,
|
||||
'30m': 30,
|
||||
'1h': 60,
|
||||
'2h': 120,
|
||||
'4h': 240,
|
||||
'6h': 360,
|
||||
'8h': 480,
|
||||
'12h': 720,
|
||||
'1d': 1440,
|
||||
'3d': 4320,
|
||||
'1w': 10080,
|
||||
'1M': 43200
|
||||
}
|
||||
|
||||
# 获取时间周期对应的分钟数
|
||||
tf1_value = tf_values.get(tf1)
|
||||
tf2_value = tf_values.get(tf2)
|
||||
|
||||
# 如果某个时间周期不在映射中,返回False
|
||||
tf1_value = timeframe_to_minutes(tf1)
|
||||
tf2_value = timeframe_to_minutes(tf2)
|
||||
if tf1_value is None or tf2_value is None:
|
||||
return False
|
||||
|
||||
# 返回tf1是否小于等于tf2
|
||||
return tf1_value <= tf2_value
|
||||
|
||||
def clean_dataframe_for_json(df):
|
||||
@@ -900,12 +1019,15 @@ def classify_trend_stage(df):
|
||||
|
||||
def load_crypto_symbols(limit=200):
|
||||
"""加载常见USDT永续合约交易对,返回列表"""
|
||||
refresh_data_service_metadata()
|
||||
if SYMBOLS:
|
||||
return SYMBOLS[:limit]
|
||||
try:
|
||||
markets = exchange.load_markets()
|
||||
symbols = [s for s in markets.keys() if '/USDT' in s and ':USDT' in s]
|
||||
return symbols[:limit]
|
||||
except Exception:
|
||||
return SYMBOLS
|
||||
return DEFAULT_SYMBOLS[:limit]
|
||||
|
||||
|
||||
@app.route('/api/trend_filter', methods=['GET'])
|
||||
@@ -1030,10 +1152,38 @@ def trend_detail():
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""主页"""
|
||||
return render_template('index.html',
|
||||
timeframes=TIMEFRAMES,
|
||||
symbols=SYMBOLS,
|
||||
a_stock_symbols=A_STOCK_SYMBOLS)
|
||||
refresh_data_service_metadata()
|
||||
timeframe_items = list(TIMEFRAMES.items())
|
||||
timeframe_keys = [item[0] for item in timeframe_items]
|
||||
symbols = SYMBOLS if SYMBOLS else DEFAULT_SYMBOLS
|
||||
|
||||
preferred_main = next((tf for tf in ['5m', '15m', '1h'] if tf in TIMEFRAMES), None)
|
||||
default_main = preferred_main or (timeframe_keys[0] if timeframe_keys else '1m')
|
||||
if default_main not in TIMEFRAMES and timeframe_keys:
|
||||
default_main = timeframe_keys[0]
|
||||
|
||||
if timeframe_keys:
|
||||
try:
|
||||
idx = timeframe_keys.index(default_main)
|
||||
default_element = timeframe_keys[idx - 1] if idx > 0 else timeframe_keys[0]
|
||||
except ValueError:
|
||||
default_element = timeframe_keys[0]
|
||||
else:
|
||||
default_element = default_main
|
||||
|
||||
default_symbol = 'BTC/USDT:USDT' if 'BTC/USDT:USDT' in symbols else (symbols[0] if symbols else '')
|
||||
|
||||
return render_template(
|
||||
'index.html',
|
||||
timeframes=TIMEFRAMES,
|
||||
symbols=symbols,
|
||||
a_stock_symbols=A_STOCK_SYMBOLS,
|
||||
default_main_timeframe=default_main,
|
||||
default_element_timeframe=default_element,
|
||||
default_symbol=default_symbol,
|
||||
timeframe_keys_json=json.dumps(timeframe_keys),
|
||||
data_service_available=DATA_SERVICE_AVAILABLE,
|
||||
)
|
||||
|
||||
@app.route('/api/analyze')
|
||||
def analyze():
|
||||
@@ -1417,13 +1567,16 @@ def analyze():
|
||||
@app.route('/api/symbols')
|
||||
def get_symbols():
|
||||
"""获取可用交易对"""
|
||||
refresh_data_service_metadata()
|
||||
if SYMBOLS:
|
||||
return jsonify(SYMBOLS)
|
||||
try:
|
||||
markets = exchange.load_markets()
|
||||
# 合约交易对通常是以USDT结尾的永续合约
|
||||
symbols = [symbol for symbol in markets.keys() if '/USDT' in symbol and ':USDT' in symbol]
|
||||
return jsonify(symbols)
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)})
|
||||
return jsonify(DEFAULT_SYMBOLS)
|
||||
|
||||
@app.route('/api/a_stocks')
|
||||
def get_a_stocks():
|
||||
|
||||
@@ -2,6 +2,7 @@ flask>=2.0.1
|
||||
ccxt>=4.4.70
|
||||
pandas>=1.3.3
|
||||
numpy>=1.21.2
|
||||
requests>=2.31.0
|
||||
plotly>=5.3.1
|
||||
matplotlib>=3.4.3
|
||||
pytz>=2021.1
|
||||
|
||||
+42
-38
@@ -16,6 +16,25 @@
|
||||
<script defer src="{{ url_for('static', filename='js/indicators.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', filename='js/charts.js') }}"></script>
|
||||
<!-- TradingView Widget END -->
|
||||
<script>
|
||||
window.AVAILABLE_TIMEFRAMES = {{ timeframe_keys_json | safe }};
|
||||
window.DEFAULT_MAIN_TIMEFRAME = "{{ default_main_timeframe }}";
|
||||
window.DEFAULT_ELEMENT_TIMEFRAME = "{{ default_element_timeframe }}";
|
||||
window.timeframeToMs = function(tf) {
|
||||
if (!tf) return null;
|
||||
var unit = tf.slice(-1);
|
||||
var value = parseInt(tf.slice(0, -1), 10);
|
||||
if (isNaN(value)) return null;
|
||||
var unitMap = {
|
||||
m: 60 * 1000,
|
||||
h: 60 * 60 * 1000,
|
||||
d: 24 * 60 * 60 * 1000,
|
||||
w: 7 * 24 * 60 * 60 * 1000,
|
||||
M: 30 * 24 * 60 * 60 * 1000
|
||||
};
|
||||
return unitMap[unit] ? value * unitMap[unit] : null;
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
@@ -765,7 +784,7 @@
|
||||
<label for="symbol" class="form-label">交易对:</label>
|
||||
<select id="symbol" class="form-select">
|
||||
{% for symbol in symbols %}
|
||||
<option value="{{ symbol }}" {% if symbol == 'BTC/USDT:USDT' %}selected{% endif %}>{{ symbol }}</option>
|
||||
<option value="{{ symbol }}" {% if symbol == default_symbol %}selected{% endif %}>{{ symbol }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -861,7 +880,7 @@
|
||||
<div class="form-check form-check-inline">
|
||||
<select id="timeframe" class="form-select form-select-sm me-2" style="width: 100px;">
|
||||
{% for value, label in timeframes.items() %}
|
||||
<option value="{{ value }}" {% if value == '5m' %}selected{% endif %}>{{ label }}</option>
|
||||
<option value="{{ value }}" {% if value == default_main_timeframe %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -899,7 +918,7 @@
|
||||
<div class="form-check form-check-inline">
|
||||
<select id="elementTimeframe" class="form-select form-select-sm me-2" style="width: 100px;">
|
||||
{% for value, label in timeframes.items() %}
|
||||
<option value="{{ value }}" {% if value == '1m' %}selected{% endif %}>{{ label }}</option>
|
||||
<option value="{{ value }}" {% if value == default_element_timeframe %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -1144,11 +1163,7 @@
|
||||
// 周期变化时,自动填充当前时间回溯300根K线的时间范围
|
||||
$('#trendTimeframe').on('change', function(){
|
||||
const tf = $(this).val();
|
||||
const tfToMs = {
|
||||
'1m': 60*1000, '5m': 5*60*1000, '15m': 15*60*1000, '30m': 30*60*1000,
|
||||
'1h': 60*60*1000, '4h': 4*60*60*1000, '1d': 24*60*60*1000
|
||||
};
|
||||
const step = tfToMs[tf] || (60*60*1000);
|
||||
const step = window.timeframeToMs(tf) || (60*60*1000);
|
||||
const now = new Date();
|
||||
const endMs = now.getTime();
|
||||
const startMs = endMs - 300 * step;
|
||||
@@ -1177,11 +1192,7 @@
|
||||
|
||||
// 前端必须提供时间范围:若为空,自动以当前时间回溯300根
|
||||
if (!start || !end) {
|
||||
const tfToMs = {
|
||||
'1m': 60*1000, '5m': 5*60*1000, '15m': 15*60*1000, '30m': 30*60*1000,
|
||||
'1h': 60*60*1000, '4h': 4*60*60*1000, '1d': 24*60*60*1000
|
||||
};
|
||||
const step = tfToMs[timeframe] || (60*60*1000);
|
||||
const step = window.timeframeToMs(timeframe) || (60*60*1000);
|
||||
const now = Date.now();
|
||||
const startMsAuto = now - 300 * step;
|
||||
const toLocal = (ms) => new Date(ms - new Date(ms).getTimezoneOffset()*60000).toISOString().slice(0,16);
|
||||
@@ -1777,30 +1788,17 @@
|
||||
|
||||
// 比较两个时间周期的大小
|
||||
function compareTimeframes(tf1, tf2) {
|
||||
const tfValues = {
|
||||
'1m': 1,
|
||||
'3m': 3,
|
||||
'5m': 5,
|
||||
'15m': 15,
|
||||
'30m': 30,
|
||||
'1h': 60,
|
||||
'2h': 120,
|
||||
'4h': 240,
|
||||
'6h': 360,
|
||||
'8h': 480,
|
||||
'12h': 720,
|
||||
'1d': 1440,
|
||||
'3d': 4320,
|
||||
'1w': 10080,
|
||||
'1M': 43200
|
||||
};
|
||||
|
||||
return tfValues[tf1] - tfValues[tf2];
|
||||
const v1 = window.timeframeToMs(tf1);
|
||||
const v2 = window.timeframeToMs(tf2);
|
||||
if (v1 === null || v2 === null) {
|
||||
return 0;
|
||||
}
|
||||
return v1 - v2;
|
||||
}
|
||||
|
||||
// 设置比主周期小的最大周期
|
||||
function setSmallestLargerTimeframe(mainTimeframe) {
|
||||
const timeframes = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M'];
|
||||
const timeframes = window.AVAILABLE_TIMEFRAMES || [];
|
||||
const mainIndex = timeframes.indexOf(mainTimeframe);
|
||||
|
||||
if (mainIndex > 0) {
|
||||
@@ -1812,7 +1810,7 @@
|
||||
|
||||
// 设置小于或等于主周期的时间周期
|
||||
function setSmallerOrEqualTimeframe(mainTimeframe) {
|
||||
const timeframes = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M'];
|
||||
const timeframes = window.AVAILABLE_TIMEFRAMES || [];
|
||||
const mainIndex = timeframes.indexOf(mainTimeframe);
|
||||
|
||||
// 默认选择相同的时间周期
|
||||
@@ -1973,9 +1971,9 @@
|
||||
symbol = $('#astockSymbol').val() || '000001';
|
||||
}
|
||||
|
||||
const timeframe = $('#timeframe').val() || '5m';
|
||||
const timeframe = $('#timeframe').val() || window.DEFAULT_MAIN_TIMEFRAME || '5m';
|
||||
const timezone = $('#timezone').val() || 'Asia/Shanghai';
|
||||
const elementTimeframe = $('#elementTimeframe').val() || '1m';
|
||||
const elementTimeframe = $('#elementTimeframe').val() || window.DEFAULT_ELEMENT_TIMEFRAME || '1m';
|
||||
|
||||
// 确保时区参数有效
|
||||
console.log('更新图表使用时区:', timezone);
|
||||
@@ -7166,8 +7164,14 @@
|
||||
// 初始化交易对下拉菜单
|
||||
$('#symbol').val('BTC/USDT:USDT');
|
||||
$('#astockSymbol').val('000001');
|
||||
$('#timeframe').val('5m');
|
||||
$('#elementTimeframe').val('1m');
|
||||
const mainDefault = window.DEFAULT_MAIN_TIMEFRAME || $('#timeframe option:first').val();
|
||||
const elementDefault = window.DEFAULT_ELEMENT_TIMEFRAME || $('#elementTimeframe option:first').val();
|
||||
if (mainDefault) {
|
||||
$('#timeframe').val(mainDefault);
|
||||
}
|
||||
if (elementDefault) {
|
||||
$('#elementTimeframe').val(elementDefault);
|
||||
}
|
||||
|
||||
// 测试打印时区偏移量
|
||||
console.log('当前时区偏移量 (UTC+8):', getTimezoneOffset('Asia/Shanghai'));
|
||||
|
||||
Reference in New Issue
Block a user