复盘盈亏比自动计算与K线自动生成;居中页头导航
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from flask import (
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
from symbols import search_symbols, ths_to_codes
|
||||
from kline_chart import generate_review_kline_chart
|
||||
from market import get_price as market_get_price, set_ths_refresh_token, get_quote_source_label
|
||||
|
||||
load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env"))
|
||||
@@ -61,6 +62,23 @@ def calc_holding_duration(open_time: str, close_time: str) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def calc_rr_ratio(direction: str, entry: float, stop: float, target: float) -> Optional[float]:
|
||||
"""盈亏比 = 盈利空间 / 风险空间。"""
|
||||
if entry is None or stop is None or target is None:
|
||||
return None
|
||||
if direction == "long":
|
||||
risk = entry - stop
|
||||
if risk <= 0:
|
||||
return None
|
||||
return round((target - entry) / risk, 2)
|
||||
if direction == "short":
|
||||
risk = stop - entry
|
||||
if risk <= 0:
|
||||
return None
|
||||
return round((entry - target) / risk, 2)
|
||||
return None
|
||||
|
||||
|
||||
def calc_theoretical_pnl(direction: str, entry: float, target: float, lots: float) -> Optional[float]:
|
||||
if entry is None or target is None or lots is None:
|
||||
return None
|
||||
@@ -680,8 +698,29 @@ def add_review():
|
||||
lots = num("lots") or 1.0
|
||||
|
||||
holding = calc_holding_duration(open_time, close_time)
|
||||
initial_pnl = calc_theoretical_pnl(direction, entry_price, take_profit, lots)
|
||||
actual_pnl = calc_theoretical_pnl(direction, entry_price, close_price, lots)
|
||||
initial_pnl = calc_rr_ratio(direction, entry_price, stop_loss, take_profit)
|
||||
actual_pnl = calc_rr_ratio(direction, entry_price, stop_loss, close_price)
|
||||
|
||||
auto_kline = bool(d.get("auto_kline"))
|
||||
if auto_kline and not screenshot:
|
||||
try:
|
||||
generated = generate_review_kline_chart(
|
||||
symbol=d.get("symbol", "").strip(),
|
||||
periods=[d.get("kline_period1", "15m"), d.get("kline_period2", "1h")],
|
||||
count=int(d.get("kline_count") or 300),
|
||||
cutoff_label=d.get("kline_cutoff", "平仓时间"),
|
||||
open_time=open_time,
|
||||
close_time=close_time,
|
||||
entry_price=entry_price,
|
||||
stop_loss=stop_loss,
|
||||
take_profit=take_profit,
|
||||
close_price=close_price,
|
||||
upload_dir=UPLOAD_DIR,
|
||||
)
|
||||
if generated:
|
||||
screenshot = generated
|
||||
except Exception as exc:
|
||||
app.logger.warning("auto kline failed: %s", exc)
|
||||
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
@@ -702,14 +741,14 @@ def add_review():
|
||||
entry_price, stop_loss, take_profit, close_price, lots,
|
||||
holding, initial_pnl, actual_pnl, num("pnl"),
|
||||
open_type,
|
||||
num("expected_rr"),
|
||||
num("actual_rr"),
|
||||
None,
|
||||
None,
|
||||
exit_trigger,
|
||||
d.get("exit_supplement", "").strip(),
|
||||
d.get("watch_after_breakeven", "否"),
|
||||
d.get("new_position_while_occupied", "否"),
|
||||
screenshot,
|
||||
1 if d.get("auto_kline") else 0,
|
||||
1 if auto_kline else 0,
|
||||
d.get("kline_period1", "15m"),
|
||||
d.get("kline_period2", "1h"),
|
||||
int(d.get("kline_count") or 300),
|
||||
|
||||
Reference in New Issue
Block a user