下单预览实时更新:现货买卖、永续开平仓、互换
This commit is contained in:
1 parent
465e46c6bb
commit
df57c417d9
10 files changed
+121
-20
No files matched your search
@@ -110,7 +110,7 @@ namespace Decentralized
|
||||
{
|
||||
var b = CustomUIManager.Window(Id, L.T("term.title"));
|
||||
if (b == null) return;
|
||||
_picked.Clear(); _clear.Clear(); _rows.Clear();
|
||||
_picked.Clear(); _clear.Clear(); _rows.Clear(); _live.Clear();
|
||||
b.SetSize(WinW, WinH).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
|
||||
// 黑色外边框包裹墨水屏
|
||||
b.BeginPanel(ScreenW + Bezel * 2, ScreenH + Bezel * 2, TextAnchor.MiddleCenter, Bezel + 3f, 0f);
|
||||
@@ -148,7 +148,7 @@ namespace Decentralized
|
||||
private static void Rebuild()
|
||||
{
|
||||
var m = CustomUIManager.Instance;
|
||||
_picked.Clear(); _clear.Clear(); _rows.Clear();
|
||||
_picked.Clear(); _clear.Clear(); _rows.Clear(); _live.Clear();
|
||||
CustomUIBuilder.ClearContainer(_topRoot);
|
||||
CustomUIBuilder.ClearContainer(_bodyRoot);
|
||||
CustomUIBuilder.ClearContainer(_noteRoot);
|
||||
@@ -285,12 +285,36 @@ namespace Decentralized
|
||||
b.End();
|
||||
}
|
||||
|
||||
// 输入时实时更新的预览文字:输入框每次输入只改这些文字,不重建页面(重建会让输入框失焦)。
|
||||
private static readonly List<KeyValuePair<CustomUIElement, Func<string>>> _live = new List<KeyValuePair<CustomUIElement, Func<string>>>();
|
||||
|
||||
/// <summary>加一行实时预览:文字由 text() 算,输入框内容变化时刷新;没有内容时是一个空行占位。</summary>
|
||||
private static void LiveLabel(CustomUIBuilder b, Func<string> text, string style)
|
||||
{
|
||||
string t = text();
|
||||
b.AddLabel(string.IsNullOrEmpty(t) ? " " : t, style);
|
||||
if (b.Last != null && b.Last.text != null) _live.Add(new KeyValuePair<CustomUIElement, Func<string>>(b.Last, text));
|
||||
}
|
||||
|
||||
private static void RefreshLive()
|
||||
{
|
||||
foreach (var kv in _live)
|
||||
{
|
||||
if (kv.Key == null || kv.Key.text == null) continue;
|
||||
string t = kv.Value();
|
||||
kv.Key.text.text = string.IsNullOrEmpty(t) ? " " : t;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ParseAmount(out double v) =>
|
||||
double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out v) && v > 0;
|
||||
|
||||
private static void AmountRow(CustomUIBuilder b, bool label = true)
|
||||
{
|
||||
if (label) b.AddLabel(L.T("trade.amount"));
|
||||
b.BeginPanel(RightW - 30f, -1f, TextAnchor.MiddleCenter, 0f, 0f);
|
||||
_clear.Add(b.Last.image);
|
||||
b.AddInput("0", _amount, s => _amount = s, "amount", s => _amount = s);
|
||||
b.AddInput("0", _amount, s => { _amount = s; RefreshLive(); }, "amount", s => { _amount = s; RefreshLive(); });
|
||||
b.End();
|
||||
}
|
||||
|
||||
@@ -361,10 +385,11 @@ namespace Decentralized
|
||||
AmountRow(b);
|
||||
picker(L.T("swap.pay"), _swapFrom, true);
|
||||
picker(L.T("swap.get"), _swapTo, false);
|
||||
double v;
|
||||
bool ok = double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out v) && v > 0;
|
||||
var q = ok ? m.Quote(_swapFrom, _swapTo, v) : null;
|
||||
if (q != null) b.AddLabel(L.T("swap.preview", Fmt.Num(q.Out), _swapTo, q.Hops, (q.Impact * 100).ToString("0.0", CultureInfo.InvariantCulture)), "swap_preview_dim");
|
||||
LiveLabel(b, () =>
|
||||
{
|
||||
var q = ParseAmount(out double v) ? m.Quote(_swapFrom, _swapTo, v) : null;
|
||||
return q == null ? "" : L.T("swap.preview", Fmt.Num(q.Out), _swapTo, q.Hops, (q.Impact * 100).ToString("0.0", CultureInfo.InvariantCulture));
|
||||
}, "swap_preview_dim");
|
||||
b.AddButton(L.T("swap.go"), () => Swap());
|
||||
}
|
||||
|
||||
@@ -403,6 +428,8 @@ namespace Decentralized
|
||||
{
|
||||
var p = pool;
|
||||
b.AddLabel(L.T("trade.pair", p.Base, p.Quote, L.T("venue." + p.Venue)) + " " + L.T("trade.price", Fmt.Num(p.Price)));
|
||||
LiveLabel(b, () => SpotPreview(p, true), "trade_preview_dim");
|
||||
LiveLabel(b, () => SpotPreview(p, false), "trade_preview_dim");
|
||||
b.BeginRow(4f);
|
||||
b.AddButton(L.T("trade.buy"), () => Trade(p, buy: true));
|
||||
b.AddButton(L.T("trade.sell"), () => Trade(p, buy: false));
|
||||
@@ -410,6 +437,17 @@ namespace Decentralized
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>现货模拟:按输入数量算买入或卖出能得到多少、平均成交价、相对现价的滑点(不改池子)。买入数量是计价币,卖出数量是标的币。</summary>
|
||||
private static string SpotPreview(Pool p, bool buy)
|
||||
{
|
||||
if (!ParseAmount(out double v)) return "";
|
||||
double got = buy ? p.BaseOut(v) : p.QuoteOut(v);
|
||||
if (got <= 0) return "";
|
||||
double avg = buy ? v / got : got / v;
|
||||
double slip = buy ? avg / p.Price - 1 : 1 - avg / p.Price;
|
||||
return L.T(buy ? "trade.sim.buy" : "trade.sim.sell", Fmt.Num(got), buy ? p.Base : p.Quote, Fmt.Num(avg), (Math.Max(0, slip) * 100).ToString("0.0", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private static readonly float[] HoldWBase = new[] { 80f, 100f, 110f, 110f };
|
||||
private static float[] HoldW => Sc(HoldWBase);
|
||||
private static readonly TextAnchor[] HoldAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||
@@ -641,12 +679,15 @@ namespace Decentralized
|
||||
}
|
||||
r.AddLabel(L.T("perp.margin"));
|
||||
AmountRow(r, false);
|
||||
if (held == null && double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double m) && m > 0)
|
||||
if (held == null)
|
||||
{
|
||||
double notional = m * _perpLev, spot = PerpSpot(_perpProduct), exec = bk.Exec(spot, notional, _perpLong, day);
|
||||
var ghost = new PerpPosition { Long = _perpLong, Entry = exec, Size = notional / exec, Margin = m };
|
||||
r.AddLabel(L.T("perp.preview", Fmt.Credits(notional), Fmt.Num(exec)), "perp_preview_dim");
|
||||
r.AddLabel(L.T("perp.preview2", Fmt.Num(notional * Tuning.PerpFee), Fmt.Num(ghost.LiqPrice)), "perp_preview_dim");
|
||||
LiveLabel(r, () => PerpOpenPreview(1), "perp_preview_dim");
|
||||
LiveLabel(r, () => PerpOpenPreview(2), "perp_preview_dim");
|
||||
}
|
||||
else
|
||||
{
|
||||
LiveLabel(r, () => PerpClosePreview(held, 1), "perp_preview_dim");
|
||||
LiveLabel(r, () => PerpClosePreview(held, 2), "perp_preview_dim");
|
||||
}
|
||||
r.BeginRow(6f);
|
||||
if (held == null) r.AddButton(L.T("perp.do.open"), PerpOpen);
|
||||
@@ -656,6 +697,30 @@ namespace Decentralized
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>开仓模拟:名义价值、预估平均成交价(含滑点)、手续费、强平价。line 1 是价格行,2 是费用和强平价行。</summary>
|
||||
private static string PerpOpenPreview(int line)
|
||||
{
|
||||
if (!ParseAmount(out double m)) return "";
|
||||
var bk = Session.Data.Perps;
|
||||
int day = StoreStation.GetDayCounter();
|
||||
double notional = m * _perpLev, spot = PerpSpot(_perpProduct), exec = bk.Exec(spot, notional, _perpLong, day);
|
||||
if (line == 1) return L.T("perp.preview", Fmt.Credits(notional), Fmt.Num(exec), ((exec / spot - 1) * 100).ToString("+0.0;-0.0;0.0", CultureInfo.InvariantCulture));
|
||||
var ghost = new PerpPosition { Long = _perpLong, Entry = exec, Size = notional / exec, Margin = m };
|
||||
return L.T("perp.preview2", Fmt.Num(notional * Tuning.PerpFee), Fmt.Num(ghost.LiqPrice));
|
||||
}
|
||||
|
||||
/// <summary>平仓模拟:按现价平掉整个仓位的预估成交价(含滑点)、手续费、回到账户的 CRC 和盈亏。</summary>
|
||||
private static string PerpClosePreview(PerpPosition pos, int line)
|
||||
{
|
||||
var bk = Session.Data.Perps;
|
||||
double spot = PerpSpot(pos.Product);
|
||||
double exec = bk.Exec(spot, pos.Notional(spot), !pos.Long, StoreStation.GetDayCounter());
|
||||
double fee = pos.Notional(exec) * Tuning.PerpFee;
|
||||
double payout = Math.Max(0, pos.Equity(exec) - fee);
|
||||
if (line == 1) return L.T("perp.sim.close", Fmt.Credits(pos.Notional(spot)), Fmt.Num(exec), ((exec / spot - 1) * 100).ToString("+0.0;-0.0;0.0", CultureInfo.InvariantCulture));
|
||||
return L.T("perp.sim.close2", Fmt.Num(fee), Fmt.Num(payout), (payout - pos.Margin >= 0 ? "+" : "") + Fmt.Num(payout - pos.Margin));
|
||||
}
|
||||
|
||||
private static readonly float[] PoolWBase = new[] { 110f, 50f, 64f, 64f, 50f, 78f };
|
||||
private static float[] PoolW => Sc(PoolWBase);
|
||||
private static readonly TextAnchor[] PoolAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||
|
||||
Reference in new issue
Block a user