fix: 修复复盘计划小数点问题

master^2
Lxy 4 weeks ago
parent a1a334b23f
commit 90cccbf1df

@ -26,6 +26,6 @@ RUN mkdir -p /app/data /app/logs
EXPOSE 8600
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8600/api/v1/health || exit 1
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8600/api/v1/health')" || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8600", "--log-level", "info"]

@ -129,14 +129,40 @@ def _calc_trend_score(t60: float, t15: float, t5: float) -> Tuple[float, str, st
return round(raw, 1), direction, tag
def _get_price_precision(price: float) -> int:
"""根据价格大小确定合适的小数位数"""
if price >= 10000:
return 0 # 如白银8250、股指5000+
elif price >= 1000:
return 1 # 如原油528、黄金685(实际2位)
elif price >= 100:
return 2 # 如大部分化工品
else:
return 2 # 小数值的品种
def _round_by_price(value: float, price: float) -> float:
"""根据价格精度四舍五入"""
precision = _get_price_precision(price)
return round(value, precision)
def _calc_pivot_points(high: float, low: float, close: float) -> dict:
"""计算枢轴点和支撑/阻力位"""
"""计算枢轴点和支撑/阻力位(根据价格精度自动调整小数位)"""
pivot = (high + low + close) / 3
r1 = 2 * pivot - low
s1 = 2 * pivot - high
r2 = pivot + (high - low)
s2 = pivot - (high - low)
return {"pivot": pivot, "r1": r1, "r2": r2, "s1": s1, "s2": s2}
# 根据价格精度四舍五入
p = _get_price_precision(close)
return {
"pivot": round(pivot, p),
"r1": round(r1, p),
"r2": round(r2, p),
"s1": round(s1, p),
"s2": round(s2, p),
}
def _normalize_scores(values: List[float], reverse: bool = False) -> List[float]:
@ -163,6 +189,63 @@ def _normalize_abs_scores(values: List[float]) -> List[float]:
return _normalize_scores(abs_values)
def _generate_trigger(score_data: dict, direction: str) -> str:
"""
根据分析结果动态生成触发条件
Args:
score_data: 品种评分数据
direction: 方向 (long/short)
Returns:
触发条件文本
"""
conditions = []
direction_tag = score_data.get("direction_tag", "震荡")
volume_ratio = score_data.get("volume_ratio", 1.0)
amplitude_pct = score_data.get("amplitude_pct", 0)
change_pct = score_data.get("change_pct", 0)
# 根据方向标签生成基础条件
if direction == "long":
if direction_tag == "强多":
conditions.append("价格站稳支撑位上方")
elif direction_tag == "偏多":
conditions.append("回踩支撑企稳")
else:
conditions.append("突破枢轴点P位")
else:
if direction_tag == "强空":
conditions.append("价格跌破阻力位下方")
elif direction_tag == "偏空":
conditions.append("反弹阻力受阻")
else:
conditions.append("跌破枢轴点P位")
# 根据量比添加量能条件
if volume_ratio > 1.5:
conditions.append("量能持续放大")
elif volume_ratio > 1.2:
conditions.append("5分钟K线放量确认")
else:
conditions.append("等待量能配合")
# 根据振幅添加波动条件
if amplitude_pct > 2:
conditions.append("注意波动风险")
elif amplitude_pct > 1:
conditions.append("关注区间突破")
# 根据涨跌幅添加趋势条件
if abs(change_pct) > 2:
if change_pct > 0:
conditions.append("强势延续确认")
else:
conditions.append("弱势延续确认")
return " + ".join(conditions[:3]) # 最多返回3个条件
def generate_plan(db: Session, review_date_str: str, week_day: str) -> dict:
"""
生成V2复盘与交易计划
@ -350,21 +433,34 @@ def generate_plan(db: Session, review_date_str: str, week_day: str) -> dict:
green_items = [s for s in raw_scores if s["category"] == "green"]
for s in green_items:
direction = "long" if s["trend_score"] >= 50 else "short"
# 根据方向标签判断实际方向(强多/偏多→做多,强空/偏空→做空)
direction_tag = s.get("direction_tag", "震荡")
if direction_tag in ["强空", "偏空"]:
direction = "short"
elif direction_tag in ["强多", "偏多"]:
direction = "long"
else:
# 震荡情况下根据涨跌幅判断
direction = "long" if s["change_pct"] > 0 else "short"
pivots = s["pivots"]
price = s["close_price"]
if direction == "long":
entry_low = round(pivots["s1"], 2)
entry_high = round(pivots["pivot"], 2)
stop_loss = round(pivots["s2"], 2)
target1 = round(pivots["r1"], 2)
target2 = round(pivots["r2"], 2)
entry_low = _round_by_price(pivots["s1"], price)
entry_high = _round_by_price(pivots["pivot"], price)
stop_loss = _round_by_price(pivots["s2"], price)
target1 = _round_by_price(pivots["r1"], price)
target2 = _round_by_price(pivots["r2"], price)
else:
entry_low = round(pivots["pivot"], 2)
entry_high = round(pivots["r1"], 2)
stop_loss = round(pivots["r2"], 2)
target1 = round(pivots["s1"], 2)
target2 = round(pivots["s2"], 2)
entry_low = _round_by_price(pivots["pivot"], price)
entry_high = _round_by_price(pivots["r1"], price)
stop_loss = _round_by_price(pivots["r2"], price)
target1 = _round_by_price(pivots["s1"], price)
target2 = _round_by_price(pivots["s2"], price)
# 根据分析结果动态生成触发条件
trigger = _generate_trigger(s, direction)
plans.append({
"symbol": s["symbol"],
@ -376,7 +472,7 @@ def generate_plan(db: Session, review_date_str: str, week_day: str) -> dict:
"stop_loss": stop_loss,
"target1": target1,
"target2": target2,
"trigger": "回踩支撑企稳 + 5m放量突破",
"trigger": trigger,
"amplitude_score": s["amplitude_score"],
"volume_score": s["volume_score"],
"trend_score": s["trend_score"],

@ -2411,6 +2411,15 @@ function rvRenderRisk(meta) {
banner.style.display = '';
}
function rvFormatPrice(value, refPrice) {
if (!value && value !== 0) return '-';
// 根据参考价格确定小数位数
let precision = 2;
if (refPrice >= 10000) precision = 0;
else if (refPrice >= 1000) precision = 1;
return Number(value).toFixed(precision);
}
function rvScoreBarColor(score) {
if (score > 70) return '#34C759'; // --color-down
if (score > 40) return '#007AFF'; // --color-brand
@ -2473,10 +2482,10 @@ function rvRenderGreenSection(plans, scores) {
${rvScoreBar(p.activity_score || 0, '活跃')}
</div>
<div class="rv-plan-box">
<div class="rv-plan-row"><span>入场区间</span><span>${p.entry_low || '-'} ~ ${p.entry_high || '-'}</span></div>
<div class="rv-plan-row"><span>止损位</span><span style="color: var(--color-up);">${p.stop_loss || '-'}</span></div>
<div class="rv-plan-row"><span>目标一</span><span style="color: var(--color-down);">${p.target1 || '-'}</span></div>
<div class="rv-plan-row"><span>目标二</span><span style="color: var(--color-down);">${p.target2 || '-'}</span></div>
<div class="rv-plan-row"><span>入场区间</span><span>${rvFormatPrice(p.entry_low, score.close_price)} ~ ${rvFormatPrice(p.entry_high, score.close_price)}</span></div>
<div class="rv-plan-row"><span>止损位</span><span style="color: var(--color-up);">${rvFormatPrice(p.stop_loss, score.close_price)}</span></div>
<div class="rv-plan-row"><span>目标一</span><span style="color: var(--color-down);">${rvFormatPrice(p.target1, score.close_price)}</span></div>
<div class="rv-plan-row"><span>目标二</span><span style="color: var(--color-down);">${rvFormatPrice(p.target2, score.close_price)}</span></div>
<div class="rv-plan-trigger">触发条件: ${p.trigger || '-'}</div>
</div>
</div>
@ -2614,11 +2623,11 @@ function rvRenderDetails(scores) {
<div class="rv-detail-metric"><span>综合评分</span><span style="color: ${rvScoreBarColor(s.composite_score)};">${s.composite_score}</span></div>
</div>
<div class="rv-pivot-tags">
<span class="rv-pivot-tag resist">R2: ${s.r2 || '-'}</span>
<span class="rv-pivot-tag resist">R1: ${s.r1 || '-'}</span>
<span class="rv-pivot-tag pivot">P: ${s.pivot || '-'}</span>
<span class="rv-pivot-tag support">S1: ${s.s1 || '-'}</span>
<span class="rv-pivot-tag support">S2: ${s.s2 || '-'}</span>
<span class="rv-pivot-tag resist">R2: ${rvFormatPrice(s.r2, s.close_price)}</span>
<span class="rv-pivot-tag resist">R1: ${rvFormatPrice(s.r1, s.close_price)}</span>
<span class="rv-pivot-tag pivot">P: ${rvFormatPrice(s.pivot, s.close_price)}</span>
<span class="rv-pivot-tag support">S1: ${rvFormatPrice(s.s1, s.close_price)}</span>
<span class="rv-pivot-tag support">S2: ${rvFormatPrice(s.s2, s.close_price)}</span>
</div>
</div>
`).join('');

Binary file not shown.

Binary file not shown.

@ -1,5 +1,3 @@
version: '3.8'
services:
buffer-platform:
build:
@ -25,7 +23,7 @@ services:
# 日志持久化(可选)
- ./logs:/app/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8600/api/v1/health"]
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8600/api/v1/health')"]
interval: 30s
timeout: 10s
retries: 3

Loading…
Cancel
Save