添加了次次周期的显示
This commit is contained in:
+112
-1
@@ -1180,6 +1180,16 @@ def index():
|
|||||||
else:
|
else:
|
||||||
default_element = default_main
|
default_element = default_main
|
||||||
|
|
||||||
|
# 次次周期默认比次周期小一档
|
||||||
|
if timeframe_keys:
|
||||||
|
try:
|
||||||
|
idx_el = timeframe_keys.index(default_element)
|
||||||
|
default_sub_sub = timeframe_keys[idx_el - 1] if idx_el > 0 else timeframe_keys[0]
|
||||||
|
except ValueError:
|
||||||
|
default_sub_sub = timeframe_keys[0]
|
||||||
|
else:
|
||||||
|
default_sub_sub = default_element
|
||||||
|
|
||||||
default_symbol = 'BTC/USDT:USDT' if 'BTC/USDT:USDT' in symbols else (symbols[0] if symbols else '')
|
default_symbol = 'BTC/USDT:USDT' if 'BTC/USDT:USDT' in symbols else (symbols[0] if symbols else '')
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
@@ -1189,6 +1199,7 @@ def index():
|
|||||||
a_stock_symbols=A_STOCK_SYMBOLS,
|
a_stock_symbols=A_STOCK_SYMBOLS,
|
||||||
default_main_timeframe=default_main,
|
default_main_timeframe=default_main,
|
||||||
default_element_timeframe=default_element,
|
default_element_timeframe=default_element,
|
||||||
|
default_sub_sub_timeframe=default_sub_sub,
|
||||||
default_symbol=default_symbol,
|
default_symbol=default_symbol,
|
||||||
timeframe_keys_json=json.dumps(timeframe_keys),
|
timeframe_keys_json=json.dumps(timeframe_keys),
|
||||||
data_service_available=DATA_SERVICE_AVAILABLE,
|
data_service_available=DATA_SERVICE_AVAILABLE,
|
||||||
@@ -1211,8 +1222,9 @@ def analyze():
|
|||||||
# 获取客户端请求的时区
|
# 获取客户端请求的时区
|
||||||
client_timezone = request.args.get('timezone', 'Asia/Shanghai')
|
client_timezone = request.args.get('timezone', 'Asia/Shanghai')
|
||||||
|
|
||||||
# 获取分形元素时间周期
|
# 获取分形元素时间周期与次次周期
|
||||||
element_timeframe = request.args.get('element_timeframe')
|
element_timeframe = request.args.get('element_timeframe')
|
||||||
|
sub_sub_timeframe = request.args.get('sub_sub_timeframe')
|
||||||
|
|
||||||
# 获取是否只需要分形元素数据的参数
|
# 获取是否只需要分形元素数据的参数
|
||||||
elements_only_param = request.args.get('elements_only')
|
elements_only_param = request.args.get('elements_only')
|
||||||
@@ -1221,6 +1233,9 @@ def analyze():
|
|||||||
# 验证小周期是否小于主周期
|
# 验证小周期是否小于主周期
|
||||||
if element_timeframe and not is_smaller_or_equal_timeframe(element_timeframe, timeframe):
|
if element_timeframe and not is_smaller_or_equal_timeframe(element_timeframe, timeframe):
|
||||||
return jsonify({'error': '分形元素时间周期必须小于或等于主图表时间周期'})
|
return jsonify({'error': '分形元素时间周期必须小于或等于主图表时间周期'})
|
||||||
|
# 验证次次周期是否小于等于次周期
|
||||||
|
if sub_sub_timeframe and element_timeframe and not is_smaller_or_equal_timeframe(sub_sub_timeframe, element_timeframe):
|
||||||
|
return jsonify({'error': '次次周期必须小于或等于次周期'})
|
||||||
|
|
||||||
# 获取数据
|
# 获取数据
|
||||||
df = get_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time)
|
df = get_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time)
|
||||||
@@ -1597,6 +1612,102 @@ def analyze():
|
|||||||
'zs_count': int(bsp.zs_count) if hasattr(bsp, 'zs_count') else 0
|
'zs_count': int(bsp.zs_count) if hasattr(bsp, 'zs_count') else 0
|
||||||
} for bsp in element_analysis.get('bsp_list', [])]
|
} for bsp in element_analysis.get('bsp_list', [])]
|
||||||
|
|
||||||
|
# 次次周期:仅当已指定次周期且次次周期有效时获取
|
||||||
|
if sub_sub_timeframe and is_smaller_or_equal_timeframe(sub_sub_timeframe, element_timeframe):
|
||||||
|
sub_sub_df = get_kl_data(symbol, sub_sub_timeframe, start_time=start_time, end_time=end_time)
|
||||||
|
if sub_sub_df is not None and len(sub_sub_df) > 0:
|
||||||
|
sub_sub_df = add_indicators(sub_sub_df)
|
||||||
|
sub_sub_analysis = analyze_chan(sub_sub_df, symbol, sub_sub_timeframe)
|
||||||
|
result['sub_sub_timeframe'] = sub_sub_timeframe
|
||||||
|
result['sub_sub_bi_list'] = [{
|
||||||
|
'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||||
|
'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None,
|
||||||
|
'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
|
||||||
|
'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
|
||||||
|
'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None,
|
||||||
|
'direction': convert_direction(bi.dir),
|
||||||
|
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
|
||||||
|
} for bi in sub_sub_analysis['bi_list'] if bi.end_klc]
|
||||||
|
result['sub_sub_uncompleted_bi_list'] = [{
|
||||||
|
'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||||
|
'end_time': None,
|
||||||
|
'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
|
||||||
|
'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
|
||||||
|
'end_price': None,
|
||||||
|
'direction': convert_direction(bi.dir),
|
||||||
|
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
|
||||||
|
} for bi in sub_sub_analysis['bi_list'] if not bi.end_klc]
|
||||||
|
result['sub_sub_seg_list'] = [{
|
||||||
|
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||||
|
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
|
||||||
|
'sure_time': format_time_safely(seg.sure_time, client_tz) if seg.sure_time else None,
|
||||||
|
'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
|
||||||
|
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
|
||||||
|
'direction': convert_direction(seg.dir)
|
||||||
|
} for seg in sub_sub_analysis['seg_list'] if seg.is_sure]
|
||||||
|
result['sub_sub_uncompleted_seg_list'] = get_uncompleted_seg_list(sub_sub_analysis['seg_list'], client_tz)
|
||||||
|
result['sub_sub_zs_list'] = [{
|
||||||
|
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||||
|
'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None,
|
||||||
|
'zg': zs.zg, 'zd': zs.zd, 'gg': zs.gg, 'dd': zs.dd, 'is_sure': zs.is_sure
|
||||||
|
} for zs in sub_sub_analysis['zs_list'] if zs.end_klc]
|
||||||
|
result['sub_sub_uncompleted_zs_list'] = [{
|
||||||
|
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||||
|
'end_time': None, 'zg': zs.zg, 'zd': zs.zd, 'gg': zs.gg, 'dd': zs.dd, 'is_sure': zs.is_sure
|
||||||
|
} for zs in sub_sub_analysis['zs_list'] if not zs.is_sure]
|
||||||
|
result['sub_sub_bi_zs_list'] = [{
|
||||||
|
'start_time': (
|
||||||
|
(zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat())
|
||||||
|
if getattr(zs.start_klc, 'end_time', None) else
|
||||||
|
(zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat())
|
||||||
|
),
|
||||||
|
'end_time': (zs.end_time if isinstance(zs.end_time, str) else zs.end_time.astimezone(client_tz).isoformat()) if getattr(zs, 'end_time', None) else None,
|
||||||
|
'zg': zs.zg, 'zd': zs.zd, 'gg': zs.gg, 'dd': zs.dd, 'is_sure': bool(getattr(zs, 'is_sure', False))
|
||||||
|
} for zs in sub_sub_analysis.get('bi_zs_list', []) if getattr(zs, 'is_sure', False)]
|
||||||
|
result['sub_sub_uncompleted_bi_zs_list'] = [{
|
||||||
|
'start_time': (
|
||||||
|
(zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat())
|
||||||
|
if getattr(zs.start_klc, 'end_time', None) else
|
||||||
|
(zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat())
|
||||||
|
),
|
||||||
|
'end_time': None, 'zg': zs.zg, 'zd': zs.zd, 'gg': zs.gg, 'dd': zs.dd, 'is_sure': bool(getattr(zs, 'is_sure', False))
|
||||||
|
} for zs in sub_sub_analysis.get('bi_zs_list', []) if not getattr(zs, 'is_sure', False)]
|
||||||
|
result['sub_sub_klc_fx_info'] = [{
|
||||||
|
'time': format_time_safely(point['time'], client_tz),
|
||||||
|
'price': float(point['price']),
|
||||||
|
'fx_type': point['fx_type'],
|
||||||
|
'is_bottom': bool(point['is_bottom']),
|
||||||
|
'fx_strength': float(point['fx_strength']),
|
||||||
|
'fx_strength_level': str(point['fx_strength_level']),
|
||||||
|
'is_strong_fx': bool(point['is_strong_fx'])
|
||||||
|
} for point in sub_sub_analysis['klc_fx_info']]
|
||||||
|
result['sub_sub_bsp_list'] = [{
|
||||||
|
'time': format_time_safely(bsp.end_time, client_tz),
|
||||||
|
'price': float(bsp.klc.low if str(bsp.dir) == 'Chan_BSP_DIR.BUY' else bsp.klc.high),
|
||||||
|
'type': str(bsp.type).replace('Chan_BSP_TYPE.', ''),
|
||||||
|
'dir': str(bsp.dir).replace('Chan_BSP_DIR.', ''),
|
||||||
|
'is_sure': bool(bsp.is_sure),
|
||||||
|
'sure_time': format_time_safely(bsp.sure_time, client_tz) if bsp.sure_time else None,
|
||||||
|
'zs_count': int(bsp.zs_count) if hasattr(bsp, 'zs_count') else 0
|
||||||
|
} for bsp in sub_sub_analysis.get('bsp_list', [])]
|
||||||
|
result['sub_sub_chan_macd'] = serialize_chan_macd_data(sub_sub_analysis.get('chan_macd', {}), client_tz)
|
||||||
|
try:
|
||||||
|
sub_sub_klc_trend = []
|
||||||
|
for klc in sub_sub_analysis.get('klc_list', []):
|
||||||
|
trend_val = getattr(klc, 'trend', None)
|
||||||
|
t_obj = getattr(klc, 'end_time', None) or getattr(klc, 'start_time', None)
|
||||||
|
if trend_val is None or t_obj is None:
|
||||||
|
continue
|
||||||
|
trend_name = str(trend_val)
|
||||||
|
if '.' in trend_name:
|
||||||
|
trend_name = trend_name.split('.')[-1]
|
||||||
|
time_str = format_time_safely(t_obj, client_tz)
|
||||||
|
if time_str:
|
||||||
|
sub_sub_klc_trend.append({'time': time_str, 'trend': trend_name})
|
||||||
|
result['sub_sub_klc_trend'] = sub_sub_klc_trend
|
||||||
|
except Exception:
|
||||||
|
result['sub_sub_klc_trend'] = []
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|||||||
+412
-15
@@ -20,6 +20,7 @@
|
|||||||
window.AVAILABLE_TIMEFRAMES = JSON.parse('{{ timeframe_keys_json | safe }}');
|
window.AVAILABLE_TIMEFRAMES = JSON.parse('{{ timeframe_keys_json | safe }}');
|
||||||
window.DEFAULT_MAIN_TIMEFRAME = "{{ default_main_timeframe }}";
|
window.DEFAULT_MAIN_TIMEFRAME = "{{ default_main_timeframe }}";
|
||||||
window.DEFAULT_ELEMENT_TIMEFRAME = "{{ default_element_timeframe }}";
|
window.DEFAULT_ELEMENT_TIMEFRAME = "{{ default_element_timeframe }}";
|
||||||
|
window.DEFAULT_SUB_SUB_TIMEFRAME = "{{ default_sub_sub_timeframe }}";
|
||||||
window.timeframeToMs = function(tf) {
|
window.timeframeToMs = function(tf) {
|
||||||
if (!tf) return null;
|
if (!tf) return null;
|
||||||
var unit = tf.slice(-1);
|
var unit = tf.slice(-1);
|
||||||
@@ -959,6 +960,48 @@
|
|||||||
<label class="form-check-label" for="showElementBsp">买卖点</label>
|
<label class="form-check-label" for="showElementBsp">买卖点</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex align-items-center mt-2">
|
||||||
|
<label class="form-label me-0 mb-0">次次周期:</label>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<select id="subSubTimeframe" class="form-select form-select-sm me-2" style="width: 100px;">
|
||||||
|
{% for value, label in timeframes.items() %}
|
||||||
|
<option value="{{ value }}" {% if value == default_sub_sub_timeframe %}selected{% endif %}>{{ label }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubBi">
|
||||||
|
<label class="form-check-label" for="showSubSubBi">笔</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubSeg">
|
||||||
|
<label class="form-check-label" for="showSubSubSeg">线段</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubZs">
|
||||||
|
<label class="form-check-label" for="showSubSubZs">SEG中枢</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubBiZs">
|
||||||
|
<label class="form-check-label" for="showSubSubBiZs">BI中枢</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubKlcFxType">
|
||||||
|
<label class="form-check-label" for="showSubSubKlcFxType">KLC分型</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubTrend">
|
||||||
|
<label class="form-check-label" for="showSubSubTrend">Trend</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="toggleUOnSubSub">
|
||||||
|
<label class="form-check-label" for="toggleUOnSubSub">显示U</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showSubSubBsp">
|
||||||
|
<label class="form-check-label" for="showSubSubBsp">买卖点</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1509,6 +1552,10 @@
|
|||||||
elementSegSeries: [],
|
elementSegSeries: [],
|
||||||
elementZsSeries: [],
|
elementZsSeries: [],
|
||||||
elementUncompletedZsSeries: [],
|
elementUncompletedZsSeries: [],
|
||||||
|
subSubBiSeries: [],
|
||||||
|
subSubSegSeries: [],
|
||||||
|
subSubZsSeries: [],
|
||||||
|
subSubUncompletedZsSeries: [],
|
||||||
mainBollingerSeries: [],
|
mainBollingerSeries: [],
|
||||||
elementBollingerSeries: [],
|
elementBollingerSeries: [],
|
||||||
maSeries: [], // 添加均线系列
|
maSeries: [], // 添加均线系列
|
||||||
@@ -1763,6 +1810,14 @@
|
|||||||
console.log('次BI中枢切换为:', $('#showElementBiZs').is(':checked'));
|
console.log('次BI中枢切换为:', $('#showElementBiZs').is(':checked'));
|
||||||
updateChartDisplay();
|
updateChartDisplay();
|
||||||
});
|
});
|
||||||
|
// 次次周期显示开关变更事件
|
||||||
|
$('#showSubSubBi, #showSubSubSeg, #showSubSubZs, #showSubSubBiZs, #showSubSubKlcFxType, #showSubSubTrend, #showSubSubBsp').change(function() {
|
||||||
|
updateChartDisplay();
|
||||||
|
});
|
||||||
|
$(document).on('change', '#toggleUOnSubSub', function() {
|
||||||
|
window.showUOnSubSub = $('#toggleUOnSubSub').is(':checked');
|
||||||
|
updateChartDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
// 买卖点复选框已移除
|
// 买卖点复选框已移除
|
||||||
|
|
||||||
@@ -1790,9 +1845,30 @@
|
|||||||
setSmallerOrEqualTimeframe(); // 重置为最大的小于等于时间周期
|
setSmallerOrEqualTimeframe(); // 重置为最大的小于等于时间周期
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 次次周期必须小于等于次周期
|
||||||
|
ensureSubSubLteElement();
|
||||||
console.log(`当前选择的元素时间周期: ${elementTimeframe},需要点击分析按钮来应用更改`);
|
console.log(`当前选择的元素时间周期: ${elementTimeframe},需要点击分析按钮来应用更改`);
|
||||||
});
|
});
|
||||||
|
// 次次周期变更时校验 <= 次周期
|
||||||
|
$('#subSubTimeframe').change(function() {
|
||||||
|
const subSub = $(this).val();
|
||||||
|
const elementTf = $('#elementTimeframe').val();
|
||||||
|
if (compareTimeframes(subSub, elementTf) > 0) {
|
||||||
|
alert('次次周期必须小于或等于次周期。');
|
||||||
|
ensureSubSubLteElement();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function ensureSubSubLteElement() {
|
||||||
|
const timeframes = window.AVAILABLE_TIMEFRAMES || [];
|
||||||
|
const elementTf = $('#elementTimeframe').val();
|
||||||
|
const subSubTf = $('#subSubTimeframe').val();
|
||||||
|
if (compareTimeframes(subSubTf, elementTf) > 0) {
|
||||||
|
const idxEl = timeframes.indexOf(elementTf);
|
||||||
|
const validSubSub = idxEl > 0 ? timeframes[idxEl - 1] : timeframes[0];
|
||||||
|
$('#subSubTimeframe').val(validSubSub || elementTf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 比较两个时间周期的大小
|
// 比较两个时间周期的大小
|
||||||
function compareTimeframes(tf1, tf2) {
|
function compareTimeframes(tf1, tf2) {
|
||||||
@@ -1825,7 +1901,7 @@
|
|||||||
$('#elementTimeframe').val(mainTimeframe);
|
$('#elementTimeframe').val(mainTimeframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当主时间周期变更时,确保分形元素时间周期正确
|
// 当主时间周期变更时,确保分形元素时间周期、次次周期正确
|
||||||
$('#timeframe').change(function() {
|
$('#timeframe').change(function() {
|
||||||
const mainTimeframe = $(this).val();
|
const mainTimeframe = $(this).val();
|
||||||
const elementTimeframe = $('#elementTimeframe').val();
|
const elementTimeframe = $('#elementTimeframe').val();
|
||||||
@@ -1833,6 +1909,7 @@
|
|||||||
if (compareTimeframes(elementTimeframe, mainTimeframe) > 0) {
|
if (compareTimeframes(elementTimeframe, mainTimeframe) > 0) {
|
||||||
setSmallerOrEqualTimeframe(mainTimeframe);
|
setSmallerOrEqualTimeframe(mainTimeframe);
|
||||||
}
|
}
|
||||||
|
ensureSubSubLteElement();
|
||||||
});
|
});
|
||||||
function updateChartDisplay() {
|
function updateChartDisplay() {
|
||||||
if (currentData) {
|
if (currentData) {
|
||||||
@@ -1982,6 +2059,7 @@
|
|||||||
const timeframe = $('#timeframe').val() || window.DEFAULT_MAIN_TIMEFRAME || '5m';
|
const timeframe = $('#timeframe').val() || window.DEFAULT_MAIN_TIMEFRAME || '5m';
|
||||||
const timezone = $('#timezone').val() || 'Asia/Shanghai';
|
const timezone = $('#timezone').val() || 'Asia/Shanghai';
|
||||||
const elementTimeframe = $('#elementTimeframe').val() || window.DEFAULT_ELEMENT_TIMEFRAME || '1m';
|
const elementTimeframe = $('#elementTimeframe').val() || window.DEFAULT_ELEMENT_TIMEFRAME || '1m';
|
||||||
|
const subSubTimeframe = $('#subSubTimeframe').val() || '';
|
||||||
|
|
||||||
// 确保时区参数有效
|
// 确保时区参数有效
|
||||||
console.log('更新图表使用时区:', timezone);
|
console.log('更新图表使用时区:', timezone);
|
||||||
@@ -2017,6 +2095,7 @@
|
|||||||
timeframe: timeframe,
|
timeframe: timeframe,
|
||||||
timezone: timezone,
|
timezone: timezone,
|
||||||
element_timeframe: elementTimeframe,
|
element_timeframe: elementTimeframe,
|
||||||
|
sub_sub_timeframe: subSubTimeframe || undefined,
|
||||||
start_time: startTimeMs,
|
start_time: startTimeMs,
|
||||||
end_time: endTimeMs,
|
end_time: endTimeMs,
|
||||||
elements_only: false
|
elements_only: false
|
||||||
@@ -2189,6 +2268,12 @@
|
|||||||
elementUncompletedSegSeries: [],
|
elementUncompletedSegSeries: [],
|
||||||
elementZsSeries: [],
|
elementZsSeries: [],
|
||||||
elementUncompletedZsSeries: [],
|
elementUncompletedZsSeries: [],
|
||||||
|
subSubBiSeries: [],
|
||||||
|
subSubUncompletedBiSeries: [],
|
||||||
|
subSubSegSeries: [],
|
||||||
|
subSubUncompletedSegSeries: [],
|
||||||
|
subSubZsSeries: [],
|
||||||
|
subSubUncompletedZsSeries: [],
|
||||||
tradePointSeries: [],
|
tradePointSeries: [],
|
||||||
mainBollingerSeries: [],
|
mainBollingerSeries: [],
|
||||||
elementBollingerSeries: [],
|
elementBollingerSeries: [],
|
||||||
@@ -2751,6 +2836,9 @@
|
|||||||
if (typeof window.showUOnElement === 'undefined') {
|
if (typeof window.showUOnElement === 'undefined') {
|
||||||
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
||||||
}
|
}
|
||||||
|
if (typeof window.showUOnSubSub === 'undefined') {
|
||||||
|
window.showUOnSubSub = $('#toggleUOnSubSub').is(':checked');
|
||||||
|
}
|
||||||
if (showMacd && chanMacdChart && ((useElementPeriod && currentData.element_macd) || currentData.macd) && (useElementPeriod ? currentData.element_kline_data : currentData.kline_data)) {
|
if (showMacd && chanMacdChart && ((useElementPeriod && currentData.element_macd) || currentData.macd) && (useElementPeriod ? currentData.element_kline_data : currentData.kline_data)) {
|
||||||
console.log('✅ 开始创建 ChanMACD 系列');
|
console.log('✅ 开始创建 ChanMACD 系列');
|
||||||
// 创建ChanMACD线系列
|
// 创建ChanMACD线系列
|
||||||
@@ -2973,6 +3061,31 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const subSubCm = currentData.sub_sub_chan_macd || {};
|
||||||
|
const subSubMarkers = [];
|
||||||
|
const subSubMacdMap = buildMacdTimeMap(currentData.macd, currentData.kline_data);
|
||||||
|
if (window.showUOnSubSub && Array.isArray(subSubCm.klu_list)) {
|
||||||
|
subSubCm.klu_list.forEach((item) => {
|
||||||
|
if (!item || !item.time) return;
|
||||||
|
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||||
|
if (isNaN(ts)) return;
|
||||||
|
if (Number(item.separate_div) > 0) {
|
||||||
|
const macdVal = subSubMacdMap.get(ts);
|
||||||
|
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
|
||||||
|
subSubMarkers.push({ time: ts, position: posSd, color: '#00897b', shape: 'arrowUp', text: `SD${Number(item.separate_div)}`, size: 0.6 });
|
||||||
|
}
|
||||||
|
if (item.continue_div === true) {
|
||||||
|
const macdVal = subSubMacdMap.get(ts);
|
||||||
|
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
|
||||||
|
subSubMarkers.push({ time: ts, position: posCd, color: '#26a69a', shape: 'arrowDown', text: 'CD', size: 0.6 });
|
||||||
|
}
|
||||||
|
if (item.near0_return && Number(item.near0_return) > 0) {
|
||||||
|
subSubMarkers.push({ time: ts, position: 'belowBar', color: '#00695c', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.kluDivMarkersSubSub = subSubMarkers;
|
||||||
|
|
||||||
// 保存到全局,供主图合并标记使用
|
// 保存到全局,供主图合并标记使用
|
||||||
window.kluDivMarkersMain = mainMarkers;
|
window.kluDivMarkersMain = mainMarkers;
|
||||||
window.kluDivMarkersElement = elementMarkers;
|
window.kluDivMarkersElement = elementMarkers;
|
||||||
@@ -2980,12 +3093,14 @@
|
|||||||
console.warn('处理 KLU 背驰标记出错:', e);
|
console.warn('处理 KLU 背驰标记出错:', e);
|
||||||
window.kluDivMarkersMain = [];
|
window.kluDivMarkersMain = [];
|
||||||
window.kluDivMarkersElement = [];
|
window.kluDivMarkersElement = [];
|
||||||
|
window.kluDivMarkersSubSub = [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ 没有ChanMACD分析数据');
|
console.log('⚠️ 没有ChanMACD分析数据');
|
||||||
// 无数据时清空本次的 KLU 背驰标记
|
// 无数据时清空本次的 KLU 背驰标记
|
||||||
window.kluDivMarkersMain = [];
|
window.kluDivMarkersMain = [];
|
||||||
window.kluDivMarkersElement = [];
|
window.kluDivMarkersElement = [];
|
||||||
|
window.kluDivMarkersSubSub = [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('⚠️ ChanMACD图表创建条件不满足');
|
console.log('⚠️ ChanMACD图表创建条件不满足');
|
||||||
@@ -3057,10 +3172,30 @@
|
|||||||
}
|
}
|
||||||
window.kluDivMarkersMain = mainMarkersAll;
|
window.kluDivMarkersMain = mainMarkersAll;
|
||||||
window.kluDivMarkersElement = elementMarkersAll;
|
window.kluDivMarkersElement = elementMarkersAll;
|
||||||
|
const subSubCmAll = currentData.sub_sub_chan_macd || {};
|
||||||
|
const subSubMarkersAll = [];
|
||||||
|
if (window.showUOnSubSub && Array.isArray(subSubCmAll.klu_list)) {
|
||||||
|
subSubCmAll.klu_list.forEach((item) => {
|
||||||
|
if (!item || !item.time) return;
|
||||||
|
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||||
|
if (isNaN(ts)) return;
|
||||||
|
if (Number(item.separate_div) > 0) {
|
||||||
|
subSubMarkersAll.push({ time: ts, position: 'aboveBar', color: '#00897b', shape: 'arrowUp', text: `SD${Number(item.separate_div)}`, size: 0.6 });
|
||||||
|
}
|
||||||
|
if (item.continue_div === true) {
|
||||||
|
subSubMarkersAll.push({ time: ts, position: 'belowBar', color: '#26a69a', shape: 'arrowDown', text: 'CD', size: 0.6 });
|
||||||
|
}
|
||||||
|
if (item.near0_return && Number(item.near0_return) > 0) {
|
||||||
|
subSubMarkersAll.push({ time: ts, position: 'belowBar', color: '#00695c', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.kluDivMarkersSubSub = subSubMarkersAll;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('独立计算 KLU 背驰标记出错:', e);
|
console.warn('独立计算 KLU 背驰标记出错:', e);
|
||||||
window.kluDivMarkersMain = [];
|
window.kluDivMarkersMain = [];
|
||||||
window.kluDivMarkersElement = [];
|
window.kluDivMarkersElement = [];
|
||||||
|
window.kluDivMarkersSubSub = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实现三图联动滚动
|
// 实现三图联动滚动
|
||||||
@@ -3300,8 +3435,8 @@
|
|||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
// 显示笔的绘制 - 分别处理主周期和次周期
|
// 显示笔的绘制 - 分别处理主周期、次周期和次次周期
|
||||||
if ($('#showMainBi').is(':checked') || $('#showElementBi').is(':checked')) {
|
if ($('#showMainBi').is(':checked') || $('#showElementBi').is(':checked') || $('#showSubSubBi').is(':checked')) {
|
||||||
console.log('绘制笔 - 已启用');
|
console.log('绘制笔 - 已启用');
|
||||||
let biLines = [];
|
let biLines = [];
|
||||||
|
|
||||||
@@ -3588,6 +3723,49 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 次次周期笔
|
||||||
|
if ($('#showSubSubBi').is(':checked') && currentData.sub_sub_bi_list && currentData.sub_sub_bi_list.length > 0) {
|
||||||
|
tvWidget.series.subSubBiSeries = [];
|
||||||
|
tvWidget.series.subSubUncompletedBiSeries = [];
|
||||||
|
currentData.sub_sub_bi_list.forEach(function(bi) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(bi.start_time).getTime() / 1000);
|
||||||
|
const endTime = bi.end_time ? Math.floor(new Date(bi.end_time).getTime() / 1000) : 0;
|
||||||
|
if (isNaN(startTime) || !endTime) return;
|
||||||
|
const startPrice = parseFloat(bi.start_price);
|
||||||
|
const endPrice = parseFloat(bi.end_price);
|
||||||
|
if (isNaN(startPrice) || isNaN(endPrice)) return;
|
||||||
|
biLines.push({
|
||||||
|
startTime: startTime, endTime: endTime, startPrice: startPrice, endPrice: endPrice,
|
||||||
|
color: bi.direction === 1 ? '#00897b' : '#26a69a', lineWidth: 1, lineStyle: 0
|
||||||
|
});
|
||||||
|
tvWidget.series.subSubBiSeries.push({ time: startTime, value: startPrice, color: '#00897b', lineWidth: 1 });
|
||||||
|
} catch (e) { console.error('次次周期笔处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 次次周期未完成笔
|
||||||
|
if ($('#showSubSubBi').is(':checked') && currentData.sub_sub_uncompleted_bi_list && currentData.sub_sub_uncompleted_bi_list.length > 0) {
|
||||||
|
if (!tvWidget.series.subSubBiSeries) tvWidget.series.subSubBiSeries = [];
|
||||||
|
if (!tvWidget.series.subSubUncompletedBiSeries) tvWidget.series.subSubUncompletedBiSeries = [];
|
||||||
|
const klineData = currentData.kline_data || [];
|
||||||
|
const lastTime = klineData.length ? Math.floor(new Date(klineData[klineData.length-1].date).getTime() / 1000) : 0;
|
||||||
|
currentData.sub_sub_uncompleted_bi_list.forEach(function(bi) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(bi.start_time).getTime() / 1000);
|
||||||
|
if (isNaN(startTime) || !lastTime) return;
|
||||||
|
const startPrice = parseFloat(bi.start_price);
|
||||||
|
if (isNaN(startPrice)) return;
|
||||||
|
const lastK = klineData[klineData.length-1];
|
||||||
|
const endPrice = bi.direction === 1 ? parseFloat(lastK.high) : parseFloat(lastK.low);
|
||||||
|
biLines.push({
|
||||||
|
startTime: startTime, endTime: lastTime, startPrice: startPrice, endPrice: endPrice,
|
||||||
|
color: '#00695c', lineWidth: 1, lineStyle: 2
|
||||||
|
});
|
||||||
|
tvWidget.series.subSubUncompletedBiSeries.push({ time: startTime, value: startPrice, color: '#00695c', lineWidth: 1, lineStyle: 2 });
|
||||||
|
} catch (e) { console.error('次次周期未完成笔处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 添加所有笔到图表
|
// 添加所有笔到图表
|
||||||
biLines.forEach(line => {
|
biLines.forEach(line => {
|
||||||
const lineSeries = mainChart.addLineSeries({
|
const lineSeries = mainChart.addLineSeries({
|
||||||
@@ -3606,8 +3784,8 @@
|
|||||||
} else {
|
} else {
|
||||||
console.log('绘制笔 - 已禁用');
|
console.log('绘制笔 - 已禁用');
|
||||||
}
|
}
|
||||||
// 显示线段的绘制 - 分别处理主周期和次周期
|
// 显示线段的绘制 - 分别处理主周期、次周期和次次周期
|
||||||
if ($('#showMainSeg').is(':checked') || $('#showElementSeg').is(':checked')) {
|
if ($('#showMainSeg').is(':checked') || $('#showElementSeg').is(':checked') || $('#showSubSubSeg').is(':checked')) {
|
||||||
console.log('绘制线段 - 已启用');
|
console.log('绘制线段 - 已启用');
|
||||||
let segLines = [];
|
let segLines = [];
|
||||||
|
|
||||||
@@ -3865,6 +4043,54 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 次次周期线段
|
||||||
|
if ($('#showSubSubSeg').is(':checked') && currentData.sub_sub_seg_list && currentData.sub_sub_seg_list.length > 0) {
|
||||||
|
tvWidget.series.subSubSegSeries = [];
|
||||||
|
tvWidget.series.subSubUncompletedSegSeries = [];
|
||||||
|
currentData.sub_sub_seg_list.forEach(function(seg) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(seg.start_time).getTime() / 1000);
|
||||||
|
const endTime = seg.end_time ? Math.floor(new Date(seg.end_time).getTime() / 1000) : 0;
|
||||||
|
if (isNaN(startTime) || !endTime) return;
|
||||||
|
const startPrice = parseFloat(seg.start_price);
|
||||||
|
const endPrice = parseFloat(seg.end_price);
|
||||||
|
if (isNaN(startPrice) || isNaN(endPrice)) return;
|
||||||
|
segLines.push({
|
||||||
|
startTime: startTime, endTime: endTime, startPrice: startPrice, endPrice: endPrice,
|
||||||
|
color: seg.direction === 1 ? '#00897b' : '#26a69a', lineWidth: 2, lineStyle: 0
|
||||||
|
});
|
||||||
|
tvWidget.series.subSubSegSeries.push({ time: startTime, value: startPrice, color: '#00897b', lineWidth: 2 });
|
||||||
|
} catch (e) { console.error('次次周期线段处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 次次周期未完成线段
|
||||||
|
if ($('#showSubSubSeg').is(':checked') && currentData.sub_sub_uncompleted_seg_list && currentData.sub_sub_uncompleted_seg_list.length > 0) {
|
||||||
|
if (!tvWidget.series.subSubUncompletedSegSeries) tvWidget.series.subSubUncompletedSegSeries = [];
|
||||||
|
const klineDataSeg = currentData.kline_data || [];
|
||||||
|
const lastTimeSeg = klineDataSeg.length ? Math.floor(new Date(klineDataSeg[klineDataSeg.length-1].date).getTime() / 1000) : 0;
|
||||||
|
currentData.sub_sub_uncompleted_seg_list.forEach(function(seg) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(seg.start_time).getTime() / 1000);
|
||||||
|
if (isNaN(startTime) || !lastTimeSeg) return;
|
||||||
|
const startPrice = parseFloat(seg.start_price);
|
||||||
|
if (isNaN(startPrice)) return;
|
||||||
|
let endTime = lastTimeSeg, endPrice;
|
||||||
|
if (seg.end_time && seg.end_price) {
|
||||||
|
endTime = Math.floor(new Date(seg.end_time).getTime() / 1000);
|
||||||
|
endPrice = parseFloat(seg.end_price);
|
||||||
|
} else {
|
||||||
|
const lastK = klineDataSeg[klineDataSeg.length-1];
|
||||||
|
endPrice = seg.direction === 1 ? parseFloat(lastK.high) : parseFloat(lastK.low);
|
||||||
|
}
|
||||||
|
segLines.push({
|
||||||
|
startTime: startTime, endTime: endTime, startPrice: startPrice, endPrice: endPrice,
|
||||||
|
color: '#00695c', lineWidth: 2, lineStyle: 2
|
||||||
|
});
|
||||||
|
tvWidget.series.subSubUncompletedSegSeries.push({ time: startTime, value: startPrice, color: '#00695c', lineWidth: 2 });
|
||||||
|
} catch (e) { console.error('次次周期未完成线段处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 添加所有线段到图表
|
// 添加所有线段到图表
|
||||||
segLines.forEach(line => {
|
segLines.forEach(line => {
|
||||||
const lineSeries = mainChart.addLineSeries({
|
const lineSeries = mainChart.addLineSeries({
|
||||||
@@ -3883,8 +4109,8 @@
|
|||||||
} else {
|
} else {
|
||||||
console.log('绘制线段 - 已禁用');
|
console.log('绘制线段 - 已禁用');
|
||||||
}
|
}
|
||||||
// 显示中枢的绘制 - 分别处理主周期和次周期(包含BI中枢,沿用同样样式与开关)
|
// 显示中枢的绘制 - 分别处理主周期、次周期和次次周期(包含BI中枢,沿用同样样式与开关)
|
||||||
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked')) {
|
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || $('#showSubSubZs').is(':checked')) {
|
||||||
console.log('绘制中枢 - 已启用');
|
console.log('绘制中枢 - 已启用');
|
||||||
|
|
||||||
// 主周期中枢
|
// 主周期中枢
|
||||||
@@ -4162,6 +4388,25 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 次次周期SEG中枢
|
||||||
|
if ($('#showSubSubZs').is(':checked') && currentData.sub_sub_zs_list && currentData.sub_sub_zs_list.length > 0) {
|
||||||
|
const subSubZsColor = '#00897b';
|
||||||
|
currentData.sub_sub_zs_list.forEach(function(zs) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000);
|
||||||
|
const endTime = zs.end_time ? Math.floor(new Date(zs.end_time).getTime() / 1000) : 0;
|
||||||
|
if (isNaN(startTime) || !endTime) return;
|
||||||
|
const zg = parseFloat(zs.zg); const zd = parseFloat(zs.zd); const gg = parseFloat(zs.gg); const dd = parseFloat(zs.dd);
|
||||||
|
if (isNaN(zg) || isNaN(zd)) return;
|
||||||
|
mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zg }, { time: endTime, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: endTime, value: zd }]);
|
||||||
|
mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: startTime, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: endTime, value: zd }, { time: endTime, value: zg }]);
|
||||||
|
if (!isNaN(gg) && gg > 0) mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: gg }, { time: endTime, value: gg }]);
|
||||||
|
if (!isNaN(dd) && dd > 0) mainChart.addLineSeries({ color: subSubZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: dd }, { time: endTime, value: dd }]);
|
||||||
|
} catch (e) { console.error('次次周期中枢处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('绘制中枢 - 已禁用');
|
console.log('绘制中枢 - 已禁用');
|
||||||
}
|
}
|
||||||
@@ -4197,6 +4442,25 @@
|
|||||||
} catch (e) { console.error('主周期BI中枢处理出错:', e); }
|
} catch (e) { console.error('主周期BI中枢处理出错:', e); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if ($('#showSubSubBiZs').is(':checked') && currentData.sub_sub_bi_zs_list && currentData.sub_sub_bi_zs_list.length > 0) {
|
||||||
|
currentData.sub_sub_bi_zs_list.forEach(function(zs) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000);
|
||||||
|
const kd = currentData.kline_data || [];
|
||||||
|
const endTime = zs.end_time ? Math.floor(new Date(zs.end_time).getTime() / 1000) : (kd.length ? Math.floor(new Date(kd[kd.length-1].date).getTime() / 1000) : 0);
|
||||||
|
if (isNaN(startTime) || !endTime) return;
|
||||||
|
const zg = parseFloat(zs.zg); const zd = parseFloat(zs.zd); const gg = parseFloat(zs.gg); const dd = parseFloat(zs.dd);
|
||||||
|
if (isNaN(zg) || isNaN(zd)) return;
|
||||||
|
const color = '#00897b';
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zg }, { time: endTime, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: endTime, value: zd }]);
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: startTime, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: endTime, value: zd }, { time: endTime, value: zg }]);
|
||||||
|
if (!isNaN(gg) && gg > 0) mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: gg }, { time: endTime, value: gg }]);
|
||||||
|
if (!isNaN(dd) && dd > 0) mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: dd }, { time: endTime, value: dd }]);
|
||||||
|
} catch (e) { console.error('次次周期BI中枢处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
if ($('#showElementBiZs').is(':checked') && currentData.element_bi_zs_list && currentData.element_bi_zs_list.length > 0) {
|
if ($('#showElementBiZs').is(':checked') && currentData.element_bi_zs_list && currentData.element_bi_zs_list.length > 0) {
|
||||||
try {
|
try {
|
||||||
console.log(`绘制次周期BI中枢数据,共${currentData.element_bi_zs_list.length}条`);
|
console.log(`绘制次周期BI中枢数据,共${currentData.element_bi_zs_list.length}条`);
|
||||||
@@ -4228,11 +4492,11 @@
|
|||||||
} catch (e) { console.error('次周期BI中枢处理出错:', e); }
|
} catch (e) { console.error('次周期BI中枢处理出错:', e); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 显示未完成中枢 - 分别处理主周期和次周期
|
// 显示未完成中枢 - 分别处理主周期、次周期和次次周期
|
||||||
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked')) {
|
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || $('#showSubSubZs').is(':checked')) {
|
||||||
console.log('绘制未完成中枢 - 已启用');
|
console.log('绘制未完成中枢 - 已启用');
|
||||||
// 显示BI中枢绘制(沿用中枢样式)
|
// 显示BI中枢绘制(沿用中枢样式)
|
||||||
if ($('#showMainBiZs').is(':checked') || $('#showElementBiZs').is(':checked')) {
|
if ($('#showMainBiZs').is(':checked') || $('#showElementBiZs').is(':checked') || $('#showSubSubBiZs').is(':checked')) {
|
||||||
console.log('绘制BI中枢 - 已启用');
|
console.log('绘制BI中枢 - 已启用');
|
||||||
// 主周期 BI 中枢
|
// 主周期 BI 中枢
|
||||||
console.log('主BI开关:', $('#showMainBiZs').is(':checked'), '数据长度:', currentData.bi_zs_list ? currentData.bi_zs_list.length : 0);
|
console.log('主BI开关:', $('#showMainBiZs').is(':checked'), '数据长度:', currentData.bi_zs_list ? currentData.bi_zs_list.length : 0);
|
||||||
@@ -4354,6 +4618,25 @@
|
|||||||
} catch (e) { console.error('次周期未完成BI中枢处理出错:', e); }
|
} catch (e) { console.error('次周期未完成BI中枢处理出错:', e); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 次次周期 未完成 BI 中枢
|
||||||
|
if ($('#showSubSubBiZs').is(':checked') && currentData.sub_sub_uncompleted_bi_zs_list && currentData.sub_sub_uncompleted_bi_zs_list.length > 0) {
|
||||||
|
const kdBi = currentData.kline_data || [];
|
||||||
|
const endTimeBi = kdBi.length ? Math.floor(new Date(kdBi[kdBi.length-1].date).getTime() / 1000) : 0;
|
||||||
|
currentData.sub_sub_uncompleted_bi_zs_list.forEach(function(zs) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000);
|
||||||
|
if (isNaN(startTime) || !endTimeBi) return;
|
||||||
|
const zg = parseFloat(zs.zg), zd = parseFloat(zs.zd), gg = parseFloat(zs.gg), dd = parseFloat(zs.dd);
|
||||||
|
if (isNaN(zg) || isNaN(zd)) return;
|
||||||
|
const color = '#00897b';
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zg }, { time: endTimeBi, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: endTimeBi, value: zd }]);
|
||||||
|
mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: startTime, value: zg }]);
|
||||||
|
if (!isNaN(gg) && gg > 0) mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: gg }, { time: endTimeBi, value: gg }]);
|
||||||
|
if (!isNaN(dd) && dd > 0) mainChart.addLineSeries({ color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: dd }, { time: endTimeBi, value: dd }]);
|
||||||
|
} catch (e) { console.error('次次周期未完成BI中枢处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 主周期未完成中枢
|
// 主周期未完成中枢
|
||||||
if ($('#showMainZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) {
|
if ($('#showMainZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) {
|
||||||
@@ -4640,6 +4923,25 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 次次周期未完成SEG中枢
|
||||||
|
if ($('#showSubSubZs').is(':checked') && currentData.sub_sub_uncompleted_zs_list && currentData.sub_sub_uncompleted_zs_list.length > 0) {
|
||||||
|
const kdZs = currentData.kline_data || [];
|
||||||
|
const endTimeZs = kdZs.length ? Math.floor(new Date(kdZs[kdZs.length-1].date).getTime() / 1000) : 0;
|
||||||
|
const subSubUZsColor = '#00897b';
|
||||||
|
currentData.sub_sub_uncompleted_zs_list.forEach(function(zs) {
|
||||||
|
try {
|
||||||
|
const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000);
|
||||||
|
if (isNaN(startTime) || !endTimeZs) return;
|
||||||
|
const zg = parseFloat(zs.zg); const zd = parseFloat(zs.zd); const gg = parseFloat(zs.gg); const dd = parseFloat(zs.dd);
|
||||||
|
if (isNaN(zg) || isNaN(zd)) return;
|
||||||
|
mainChart.addLineSeries({ color: subSubUZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zg }, { time: endTimeZs, value: zg }]);
|
||||||
|
mainChart.addLineSeries({ color: subSubUZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: endTimeZs, value: zd }]);
|
||||||
|
mainChart.addLineSeries({ color: subSubUZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: zd }, { time: startTime, value: zg }]);
|
||||||
|
if (!isNaN(gg) && gg > 0) mainChart.addLineSeries({ color: subSubUZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: gg }, { time: endTimeZs, value: gg }]);
|
||||||
|
if (!isNaN(dd) && dd > 0) mainChart.addLineSeries({ color: subSubUZsColor, lineWidth: 1, lastValueVisible: false, priceLineVisible: false }).setData([{ time: startTime, value: dd }, { time: endTimeZs, value: dd }]);
|
||||||
|
} catch (e) { console.error('次次周期未完成中枢处理出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('绘制未完成中枢 - 已禁用');
|
console.log('绘制未完成中枢 - 已禁用');
|
||||||
}
|
}
|
||||||
@@ -4699,8 +5001,8 @@
|
|||||||
} catch (e) { console.error('次周期未完成BI中枢处理出错:', e); }
|
} catch (e) { console.error('次周期未完成BI中枢处理出错:', e); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 添加买卖点标记(新版:基于 bsp_list / element_bsp_list,按与 KLC 分型相同方式合并到主图标记)
|
// 添加买卖点标记(新版:基于 bsp_list / element_bsp_list / sub_sub_bsp_list,按与 KLC 分型相同方式合并到主图标记)
|
||||||
if ($('#showMainBsp').is(':checked') || $('#showElementBsp').is(':checked')) {
|
if ($('#showMainBsp').is(':checked') || $('#showElementBsp').is(':checked') || $('#showSubSubBsp').is(':checked')) {
|
||||||
console.log('绘制买卖点(BSP) - 已启用');
|
console.log('绘制买卖点(BSP) - 已启用');
|
||||||
|
|
||||||
// BSP 样式定义
|
// BSP 样式定义
|
||||||
@@ -4809,6 +5111,30 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 次次周期买卖点
|
||||||
|
const subSubBspList = currentData.sub_sub_bsp_list || [];
|
||||||
|
if ($('#showSubSubBsp').is(':checked') && subSubBspList.length > 0) {
|
||||||
|
subSubBspList.forEach(function(bsp) {
|
||||||
|
try {
|
||||||
|
const ts = Math.floor(new Date(bsp.time).getTime() / 1000);
|
||||||
|
if (isNaN(ts)) return;
|
||||||
|
const key = getBspStyleKey(bsp);
|
||||||
|
const style = BSP_STYLE[key] || { color: '#999', shape: 'circle', text: '?', position: 'inBar' };
|
||||||
|
const sureText = bsp.is_sure ? '' : '?';
|
||||||
|
allBspMarkers.push({
|
||||||
|
time: ts,
|
||||||
|
position: style.position,
|
||||||
|
color: '#00897b',
|
||||||
|
shape: style.shape,
|
||||||
|
text: 's' + (style.text || '?') + sureText,
|
||||||
|
size: 1
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('次次周期BSP处理出错:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 将 BSP 标记挂到全局,后面与 KLC 分型等标记一起合并到主系列上
|
// 将 BSP 标记挂到全局,后面与 KLC 分型等标记一起合并到主系列上
|
||||||
if (allBspMarkers.length > 0) {
|
if (allBspMarkers.length > 0) {
|
||||||
// 按时间排序(lightweight-charts 要求标记按时间升序)
|
// 按时间排序(lightweight-charts 要求标记按时间升序)
|
||||||
@@ -5595,9 +5921,10 @@
|
|||||||
window.mainFxMarkers = [];
|
window.mainFxMarkers = [];
|
||||||
window.fxMarkers = [];
|
window.fxMarkers = [];
|
||||||
}
|
}
|
||||||
// 绘制小周期分型标记
|
// 绘制小周期分型标记(含次次周期)
|
||||||
if (($('#showElementKlcFxType').is(':checked') && currentData.element_klc_fx_info && currentData.element_klc_fx_info.length > 0) ||
|
if (($('#showElementKlcFxType').is(':checked') && currentData.element_klc_fx_info && currentData.element_klc_fx_info.length > 0) ||
|
||||||
($('#showElementKluFxType').is(':checked') && currentData.element_klu_fx_info && currentData.element_klu_fx_info.length > 0)) {
|
($('#showElementKluFxType').is(':checked') && currentData.element_klu_fx_info && currentData.element_klu_fx_info.length > 0) ||
|
||||||
|
($('#showSubSubKlcFxType').is(':checked') && currentData.sub_sub_klc_fx_info && currentData.sub_sub_klc_fx_info.length > 0)) {
|
||||||
|
|
||||||
// 收集所有小周期分型标记
|
// 收集所有小周期分型标记
|
||||||
const allElementFxMarkers = [];
|
const allElementFxMarkers = [];
|
||||||
@@ -5715,6 +6042,28 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 次次周期KLC分型
|
||||||
|
if ($('#showSubSubKlcFxType').is(':checked') && currentData.sub_sub_klc_fx_info && currentData.sub_sub_klc_fx_info.length > 0) {
|
||||||
|
currentData.sub_sub_klc_fx_info.forEach(function(fx) {
|
||||||
|
try {
|
||||||
|
const timestamp = Math.floor(new Date(fx.time).getTime() / 1000);
|
||||||
|
const price = parseFloat(fx.price);
|
||||||
|
if (isNaN(timestamp) || isNaN(price)) return;
|
||||||
|
const strengthColor = '#00897b';
|
||||||
|
let displayText = (fx.fx_type || '').replace("TOP", "").replace("BOTTOM", "").replace("11", "").replace("21", "").replace("3", "").replace("41", "").replace("51", "").replace("0", "");
|
||||||
|
const markerConfig = {
|
||||||
|
time: timestamp,
|
||||||
|
position: fx.is_bottom ? 'belowBar' : 'aboveBar',
|
||||||
|
color: strengthColor,
|
||||||
|
shape: 'triangle',
|
||||||
|
text: displayText || 's',
|
||||||
|
size: (fx.is_strong_fx ? 0.6 : 0.5)
|
||||||
|
};
|
||||||
|
allElementFxMarkers.push(markerConfig);
|
||||||
|
} catch (e) { console.error('绘制次次周期KLC分型标记出错:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 将小周期分型标记添加到全局markers中以支持tooltip功能
|
// 将小周期分型标记添加到全局markers中以支持tooltip功能
|
||||||
if (window.fxMarkers) {
|
if (window.fxMarkers) {
|
||||||
window.fxMarkers = [...window.fxMarkers, ...elementFxMarkers];
|
window.fxMarkers = [...window.fxMarkers, ...elementFxMarkers];
|
||||||
@@ -5794,6 +6143,29 @@
|
|||||||
});
|
});
|
||||||
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||||
}
|
}
|
||||||
|
if ($('#showSubSubTrend').is(':checked') && currentData.sub_sub_klc_trend && currentData.sub_sub_klc_trend.length > 0) {
|
||||||
|
const candlesTimesSs = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTimeSs = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time, d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
const subSubColor = '#00897b';
|
||||||
|
const subSubMarkers = currentData.sub_sub_klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
const timeAligned = candlesTimesSs.has(ts) ? ts : nearestTimeSs(ts);
|
||||||
|
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: subSubColor, shape: 'arrowUp', size: 0.4 };
|
||||||
|
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: subSubColor, shape: 'arrowDown', size: 0.4 };
|
||||||
|
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: subSubColor, shape: 'circle', size: 0.5 };
|
||||||
|
return { time: timeAligned, position: 'inBar', color: subSubColor, shape: 'square', size: 0.5 };
|
||||||
|
});
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(subSubMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
// 合并标记并设置
|
// 合并标记并设置
|
||||||
const combinedMarkers = [
|
const combinedMarkers = [
|
||||||
@@ -5801,6 +6173,7 @@
|
|||||||
...allElementFxMarkers,
|
...allElementFxMarkers,
|
||||||
...(window.kluDivMarkersMain || []),
|
...(window.kluDivMarkersMain || []),
|
||||||
...(window.kluDivMarkersElement || []),
|
...(window.kluDivMarkersElement || []),
|
||||||
|
...(window.kluDivMarkersSubSub || []),
|
||||||
...trendMarkersToUse,
|
...trendMarkersToUse,
|
||||||
...(window.bspMarkers || [])
|
...(window.bspMarkers || [])
|
||||||
];
|
];
|
||||||
@@ -5900,6 +6273,29 @@
|
|||||||
});
|
});
|
||||||
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||||
}
|
}
|
||||||
|
if ($('#showSubSubTrend').is(':checked') && currentData.sub_sub_klc_trend && currentData.sub_sub_klc_trend.length > 0) {
|
||||||
|
const candlesTimesSs2 = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTimeSs2 = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time, d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
const subSubColor2 = '#00897b';
|
||||||
|
const subSubMarkers2 = currentData.sub_sub_klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
const timeAligned = candlesTimesSs2.has(ts) ? ts : nearestTimeSs2(ts);
|
||||||
|
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: subSubColor2, shape: 'arrowUp', size: 0.4 };
|
||||||
|
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: subSubColor2, shape: 'arrowDown', size: 0.4 };
|
||||||
|
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: subSubColor2, shape: 'circle', size: 0.5 };
|
||||||
|
return { time: timeAligned, position: 'inBar', color: subSubColor2, shape: 'square', size: 0.5 };
|
||||||
|
});
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(subSubMarkers2);
|
||||||
|
}
|
||||||
// 这里的 onlyMainAndU 实际上是「最终要挂到主K线上」的一组标记
|
// 这里的 onlyMainAndU 实际上是「最终要挂到主K线上」的一组标记
|
||||||
// 之前没有把 window.bspMarkers 合进去,导致上面已经合并了 BSP 标记,
|
// 之前没有把 window.bspMarkers 合进去,导致上面已经合并了 BSP 标记,
|
||||||
// 但在这里再次调用 setMarkers 时把 BSP 覆盖掉了,从而前端看不到买卖点。
|
// 但在这里再次调用 setMarkers 时把 BSP 覆盖掉了,从而前端看不到买卖点。
|
||||||
@@ -5908,6 +6304,7 @@
|
|||||||
...(window.mainFxMarkers || []),
|
...(window.mainFxMarkers || []),
|
||||||
...(window.kluDivMarkersMain || []),
|
...(window.kluDivMarkersMain || []),
|
||||||
...(window.kluDivMarkersElement || []),
|
...(window.kluDivMarkersElement || []),
|
||||||
|
...(window.kluDivMarkersSubSub || []),
|
||||||
...trendMarkersToUse,
|
...trendMarkersToUse,
|
||||||
...(window.bspMarkers || [])
|
...(window.bspMarkers || [])
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user