@@ -303,19 +303,34 @@
return side || "—" ;
}
function monitorOrderSourceLabel ( mo ) {
function isTrendContext ( monitorOrder , trendPlan ) {
const mo = monitorOrder || { } ;
const tp = trendPlan || { } ;
if ( tp . id != null && Number ( tp . id ) > 0 ) return true ;
const tid = Number ( mo . trend _plan _id ) ;
if ( Number . isFinite ( tid ) && tid > 0 ) return true ;
const mt = String ( mo . monitor _type || "" ) . trim ( ) ;
if ( mt === "趋势回调" ) return true ;
const kst = String ( mo . key _signal _type || "" ) . trim ( ) ;
return kst === "趋势回调" || kst === "趋势回调计划" ;
}
function trendAddZoneLabel ( direction ) {
return ( direction || "long" ) . toLowerCase ( ) === "short" ? "补仓下沿" : "补仓上沿" ;
}
function monitorOrderSourceLabel ( mo , trendPlan ) {
if ( isTrendContext ( mo , trendPlan ) ) return "趋势回调计划" ;
const o = mo || { } ;
const tid = Number ( o . trend _plan _id ) ;
if ( Number . isFinite ( tid ) && tid > 0 ) return "趋势回调" ;
const mt = String ( o . monitor _type || "" ) . trim ( ) ;
if ( mt === "趋势回调" ) return "趋势回调" ;
const kst = String ( o . key _signal _type || "" ) . trim ( ) ;
if ( kst === "趋势回调" || kst === "趋势回调计划" ) return "趋势回调" ;
return mt || "下单监控" ;
}
function monitorOrderSourceHtml ( mo ) {
const src = monitorOrderSourceLabel ( mo ) ;
function monitorOrderSourceHtml ( mo , trendPlan ) {
if ( isTrendContext ( mo , trendPlan ) ) {
return ` 来源: ${ esc ( monitorOrderSourceLabel ( mo , trendPlan ) ) } ` ;
}
const src = monitorOrderSourceLabel ( mo , trendPlan ) ;
const kst = String ( ( mo && mo . key _signal _type ) || "" ) . trim ( ) ;
let text = src ;
if ( kst && kst !== src && ! text . includes ( kst ) ) {
@@ -776,7 +791,23 @@
return reward / risk ;
}
function resolveSnapshotRr ( mo , side , entry , sl , tp , tpMonitored ) {
function resolveTrendPlanRr ( trendPlan , side , entry , sl , tp ) {
const t = trendPlan || { } ;
if ( t . planned _rr != null && t . planned _rr !== "" ) {
const n = Number ( t . planned _rr ) ;
if ( Number . isFinite ( n ) && n > 0 ) return n ;
}
const e = t . avg _entry _price != null && t . avg _entry _price !== "" ? t . avg _entry _price : entry ;
const s = t . stop _loss != null && t . stop _loss !== "" ? t . stop _loss : sl ;
const p = t . take _profit != null && t . take _profit !== "" ? t . take _profit : tp ;
return calcRrRatio ( side , e , s , p ) ;
}
function resolveSnapshotRr ( mo , side , entry , sl , tp , tpMonitored , trendPlan ) {
if ( tpMonitored && isTrendContext ( mo , trendPlan ) ) {
const rr = resolveTrendPlanRr ( trendPlan , side , entry , sl , tp ) ;
if ( rr != null ) return rr ;
}
if ( tpMonitored ) return null ;
const snap = mo && mo . rr _ratio ;
if ( snap != null && snap !== "" ) {
@@ -787,6 +818,17 @@
return calcRrRatio ( side , entry , initSl || sl , tp ) ;
}
function formatTpCellValue ( tp , tpMonitored , symbol , tickMap ) {
if ( tpMonitored ) {
if ( tp != null && tp !== "" ) {
return ` 程序监控 · ${ fmtSymbolPrice ( tp , symbol , tickMap ) } ` ;
}
return "程序监控" ;
}
if ( tp != null && tp !== "" ) return fmtSymbolPrice ( tp , symbol , tickMap ) ;
return "—" ;
}
function isBreakevenSecured ( side , entry , monitorOrder , cond , pos ) {
const mo = monitorOrder || { } ;
const p = pos || { } ;
@@ -1072,10 +1114,7 @@
? mo . trigger _price
: tp . avg _entry _price ;
const entryN = entryRaw != null && entryRaw !== "" ? Number ( entryRaw ) : null ;
const isTrend =
! ! ( trendPlan && trendPlan . id ) ||
String ( mo . monitor _type || "" ) . trim ( ) === "趋势回调" ||
( mo . trend _plan _id != null && Number ( mo . trend _plan _id ) > 0 ) ;
const isTrend = isTrendContext ( mo , trendPlan ) ;
let sl = mo . stop _loss != null && mo . stop _loss !== "" ? mo . stop _loss : "" ;
let takeProfit = mo . take _profit != null && mo . take _profit !== "" ? mo . take _profit : "" ;
@@ -1083,10 +1122,14 @@
if ( isTrend ) {
tpMonitored = true ;
takeProfit = "" ;
if ( trendPlan && trendPlan . stop _loss != null && trendPlan . stop _loss !== "" ) {
sl = trendPlan . stop _loss ;
}
if ( trendPlan && trendPlan . take _profit != null && trendPlan . take _profit !== "" ) {
takeProfit = trendPlan . take _profit ;
} else {
takeProfit = "" ;
}
}
const inferred = inferTpslFromCondOrders ( pos . side , cond , entryN ) ;
@@ -1401,20 +1444,79 @@
return html ;
}
function renderTrendPlanCard ( t , tickMap ) {
const sym = t . exchange _symbol || t . symbol || "" ;
const side = ( t . direction || "long" ) . toLowerCase ( ) ;
const sl = t . stop _loss _display || fmtSymbolPrice ( t . stop _loss , sym , tickMap ) ;
const tp = t . take _profit _display || fmtSymbolPrice ( t . take _profit , sym , tickMap ) ;
const avg = t . avg _entry _price _display || fmtSymbolPrice ( t . avg _entry _price , sym , tickMap ) ;
const addZone =
t . add _upper _display || fmtSymbolPrice ( t . add _upper , sym , tickMap ) || "—" ;
const rr = resolveTrendPlanRr ( t , side , t . avg _entry _price , t . stop _loss , t . take _profit ) ;
const rrTxt = rr != null ? ` ${ fmt ( rr , 2 ) } :1 ` : "—" ;
const mark =
t . floating _mark != null && t . floating _mark !== ""
? fmtSymbolPrice ( t . floating _mark , sym , tickMap )
: "—" ;
const legsDone = t . add _count != null ? t . add _count : t . legs _done ;
const legsTotal = t . add _count _total != null ? t . add _count _total : t . dca _legs ;
const legsTxt =
legsDone != null && legsTotal != null
? ` ${ esc ( legsDone ) } / ${ esc ( legsTotal ) } `
: legsDone != null
? esc ( legsDone )
: "—" ;
let pnlInner = "—" ;
if ( t . floating _pnl != null && t . floating _pnl !== "" ) {
let pnlText = ` ${ fmt ( t . floating _pnl , 2 ) } U ` ;
const margin = Number ( t . plan _margin _capital ) ;
if ( Number . isFinite ( margin ) && margin > 0 ) {
const pct = ( Number ( t . floating _pnl ) / margin ) * 100 ;
pnlText += ` ( ${ pct >= 0 ? "+" : "" } ${ pct . toFixed ( 2 ) } %) ` ;
}
pnlInner = ` <span class="pos-value ${ pnlCls ( t . floating _pnl ) } "> ${ esc ( pnlText ) } </span> ` ;
}
const riskTxt =
t . risk _percent != null && t . risk _percent !== "" ? ` ${ esc ( t . risk _percent ) } % ` : "—" ;
const foot = [ ] ;
if ( t . snapshot _available _usdt != null ) {
foot . push ( ` 快照可用 ${ fmt ( t . snapshot _available _usdt , 2 ) } U ` ) ;
}
if ( t . plan _margin _capital != null ) {
foot . push ( ` 计划保证金≈ ${ fmt ( t . plan _margin _capital , 2 ) } U ` ) ;
}
if ( t . leverage != null ) foot . push ( ` 杠杆 ${ esc ( t . leverage ) } x ` ) ;
const footHtml = foot . length
? ` <div class="hub-trend-plan-foot"> ${ foot . map ( ( x ) => esc ( x ) ) . join ( " | " ) } </div> `
: "" ;
return ` <div class="hub-trend-plan-card hub-pos-card">
<div class="hub-trend-plan-head">
<span class="hub-trend-plan-title"># ${ esc ( t . id ) } ${ esc ( sym ) } ${ renderDirectionHtml ( t . direction ) } </span>
<span class="hub-trend-plan-status"> ${ esc ( t . status || "active" ) } </span>
</div>
<div class="hub-trend-plan-meta">
<span>来源: 趋势回调计划</span>
<span>风险: ${ riskTxt } </span>
<span> ${ esc ( trendAddZoneLabel ( t . direction ) ) } ${ esc ( addZone ) } </span>
<span>已补仓 <strong> ${ legsTxt } </strong></span>
</div>
<div class="pos-grid hub-trend-plan-grid">
<div class="pos-cell"><span class="pos-label">均价</span><span class="pos-value"> ${ esc ( avg ) } </span></div>
<div class="pos-cell"><span class="pos-label">止损</span><span class="pos-value"> ${ esc ( sl ) } </span></div>
<div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value pos-tp-program">程序监控 · ${ esc ( tp ) } </span></div>
<div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value"> ${ esc ( rrTxt ) } </span></div>
<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value"> ${ esc ( mark ) } </span></div>
<div class="pos-cell"><span class="pos-label">浮盈亏</span> ${ pnlInner } </div>
</div>
${ footHtml }
</div> ` ;
}
function renderTrendSection ( trends , tickMap ) {
if ( ! trends || ! trends . length ) return "" ;
return trends
. map ( ( t ) => {
const sym = t . exchange _symbol || t . symbol || "" ;
const sl = t . stop _loss _display || fmtSymbolPrice ( t . stop _loss , sym , tickMap ) ;
const tp = t . take _profit _display || fmtSymbolPrice ( t . take _profit , sym , tickMap ) ;
const avg = t . avg _entry _price _display || fmtSymbolPrice ( t . avg _entry _price , sym , tickMap ) ;
return ` <div class="hub-mini-card">
<div class="hub-mini-title"># ${ esc ( t . id ) } · ${ esc ( t . symbol ) } · ${ renderDirectionHtml ( t . direction ) } </div>
<div class="hub-mini-line">均价 ${ esc ( avg ) } · SL ${ esc ( sl ) } · TP ${ esc ( tp ) } ${ trendAddSummaryHtml ( t , tickMap ) } · 状态 ${ esc ( t . status || "active" ) } </div>
</div> ` ;
} )
. join ( "" ) ;
return ` <div class="hub-trend-plan-list"> ${ trends
. map ( ( t ) => renderTrendPlanCard ( t , tickMap ) )
. join ( "" ) } </div> ` ;
}
function renderLivePositionCard ( exchangeId , exchangeKey , pos , monitorOrder , trendPlan , tickMap ) {
@@ -1436,7 +1538,8 @@
const sl = tpsl . sl ;
const tp = tpsl . tp ;
const tpMonitored = tpsl . tp _monitored ;
const rr = resolveSnapshotRr ( mo , side , entry , sl , tp , tpMonitored ) ;
const isTrend = isTrendContext ( mo , trendPlan ) ;
const rr = resolveSnapshotRr ( mo , side , entry , sl , tp , tpMonitored , trendPlan ) ;
const beSecured = isBreakevenSecured ( side , entry , mo , cond , pos ) ;
const upnl = pos . unrealized _pnl ;
let pnlText = fmt ( upnl , 2 ) + "U" ;
@@ -1445,22 +1548,42 @@
pnlText += ` ( ${ pct >= 0 ? "" : "" } ${ pct . toFixed ( 2 ) } %) ` ;
}
const meta = [ ] ;
if ( mo . monitor _type || mo . key _signal _type || mo . trend _plan _id ) {
meta . push ( monitorOrderSourceHtml ( mo ) ) ;
if ( isTrend ) {
meta . push ( monitorOrderSourceHtml ( mo , trendPlan ) ) ;
const riskPct =
trendPlan && trendPlan . risk _percent != null && trendPlan . risk _percent !== ""
? trendPlan . risk _percent
: mo . risk _percent ;
if ( riskPct != null && riskPct !== "" ) {
meta . push ( ` 风险: ${ esc ( riskPct ) } % ` ) ;
}
if ( trendPlan && trendPlan . id ) {
const zone =
trendPlan . add _upper _display ||
fmtSymbolPrice ( trendPlan . add _upper , symbol , tickMap ) ||
"—" ;
meta . push (
` <span class="pos-meta-accent"> ${ esc ( trendAddZoneLabel ( trendPlan . direction ) ) } ${ esc ( zone ) } </span> `
) ;
const addSum = trendAddSummaryHtml ( trendPlan , tickMap ) ;
if ( addSum ) meta . push ( addSum . replace ( /^ · / , "" ) ) ;
}
meta . push ( ` <span class="pos-meta-off">移动保本:关</span> ` ) ;
} else if ( mo . monitor _type || mo . key _signal _type || mo . trend _plan _id ) {
meta . push ( monitorOrderSourceHtml ( mo , trendPlan ) ) ;
if ( mo . trade _style ) meta . push ( ` 风格: ${ esc ( mo . trade _style ) } ` ) ;
else meta . push ( "风格: —" ) ;
if ( mo . risk _percent != null ) {
meta . push ( ` 风险: ${ esc ( mo . risk _percent ) } % ` ) ;
}
const beOn = mo . breakeven _enabled === 1 || mo . breakeven _enabled === true ;
meta . push (
` <span class=" ${ beOn ? "pos-meta-on" : "pos-meta-off" } ">移动保本: ${ beOn ? "开" : "关" } </span> `
) ;
} else {
meta . push ( "来源: 交易所持仓" ) ;
}
if ( mo . trade _style ) meta . push ( ` 风格: ${ esc ( mo . trade _style ) } ` ) ;
else meta . push ( "风格: —" ) ;
if ( mo . risk _percent != null ) {
meta . push ( ` 风险: ${ esc ( mo . risk _percent ) } % ` ) ;
}
const beOn = mo . breakeven _enabled === 1 || mo . breakeven _enabled === true ;
meta . push (
` <span class=" ${ beOn ? "pos-meta-on" : "pos-meta-off" } ">移动保本: ${ beOn ? "开" : "关" } </span> `
) ;
if ( trendPlan && trendPlan . id ) {
meta . push ( ` 趋势回调 ${ trendAddSummaryHtml ( trendPlan , tickMap ) } ` ) ;
meta . push ( "风格: —" ) ;
meta . push ( ` <span class="pos-meta-off">移动保本:关</span> ` ) ;
}
const symBeBadge = beSecured ? ` ${ breakevenBadgeHtml ( ) } ` : "" ;
const mktAttrs = marketOpenBtnAttrs ( exchangeId , exchangeKey , symbol , pos , monitorOrder , trendPlan ) ;
@@ -1480,8 +1603,8 @@
<div class="pos-cell"><span class="pos-label">开仓价</span><span class="pos-value"> ${ fmtEntryPrice ( pos , tickMap ) } </span></div>
<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value"> ${ fmtMarkPrice ( pos , tickMap ) } </span></div>
<div class="pos-cell"><span class="pos-label">止损</span><span class="pos-value"> ${ sl != null && sl !== "" ? fmtSymbolPrice ( sl , symbol , tickMap ) : "—" } </span></div>
<div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value"> ${ tpMonitored ? "程序监控" : tp != null && tp !== "" ? fmtSymbolPrice ( tp , symbol , tickMap ) : "—" } </span></div>
<div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value"> ${ tpMonitored ? "—" : rr != null ? fmt ( rr , 2 ) + ":1" : "-:1 " } </span></div>
<div class="pos-cell"><span class="pos-label">止盈</span><span class="pos-value ${ tpMonitored ? " pos-tp-program" : "" } "> ${ formatTpCellValue ( tp , tpMonitored , symbol , tickMap ) } </span></div>
<div class="pos-cell"><span class="pos-label">盈亏比</span><span class="pos-value"> ${ rr != null ? fmt ( rr , 2 ) + ":1" : "— " } </span></div>
<div class="pos-cell"><span class="pos-label">张数</span><span class="pos-value"> ${ fmt ( pos . contracts , 4 ) } </span></div>
<div class="pos-cell"><span class="pos-label">浮盈亏</span><span class="pos-value ${ pnlCls ( upnl ) } "> ${ pnlText } </span></div>
</div>