- SwapDealService.ApplySwapTrade 入口补 ToRemainingClosePercent(A→B) 转换,与 SwapUnwind 保持一致;缺此转换导致 SaveSwapDealInternal 的 B→A 还原出错(GLMS-20260701-0006:第二次部分平仓50%被错误还原为32.5%) - unwindSwapTrade.js 改用 swapCalc.calcCloseQtyByOriginalPercent 的 roundHalfAwayFromZero,规避 JS 浮点精度偏差(32500000*(0.5/0.65) 应为 25000000) - SwapUnwind.cshtml 引入 swapCalc.js - 新增 ApplySwapTradeClosePercentBugTest 回归测试;补充 swapCalc.test.js 精度用例与 GLMS20260701DbDiagnoseTest 诊断测试([Ignore])
362 lines
22 KiB
C#
362 lines
22 KiB
C#
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using YLErp.DBModels.Enums;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// 线上事故诊断:GLMS-20260701-0008 多次部分平仓后,预付金返还显示仍为原始值
|
||
/// ============================================================================
|
||
/// 直连测试库,录制真实数据快照并定位根因(DB 端还是计算端)。
|
||
/// 测试结构:
|
||
/// 1) RecordSnapshot - 录 trade/position/eod_swap_position/eod_swap/flow_event
|
||
/// 2) Diagnose - 把每次部分平仓前后 InterestPrincipalFix 实际值序列打印,
|
||
/// 验证是否双重扣减;并调用 GetUnwindInterests 1.0 看后端返还值
|
||
/// 3) 期望对比 - 多次部分平仓后,1.0 closePercent 应返"剩余本金"(=已扣减后),
|
||
/// 若仍返原始值 ⇒ 后端 EOD 路径 bug (SaveAutoEodWithCloseInterestPosition 双重扣减)
|
||
/// </summary>
|
||
[TestClass]
|
||
public class GLMS20260701DbDiagnoseTest
|
||
{
|
||
private const string TradeNumber_0008 = "GLMS-20260701-0008";
|
||
private const string TradeNumber_0013 = "GLMS-20260701-0013";
|
||
|
||
#region 1) 录真实数据快照(手动跑)
|
||
|
||
[TestMethod]
|
||
[Ignore]
|
||
[TestCategory("DbDiagnose")]
|
||
public void Record_RealSnapshot()
|
||
{
|
||
YLContext db;
|
||
try { db = DbContextFactory.GetYLDbContext(); }
|
||
catch (Exception ex) { Assert.Inconclusive($"无法连接测试库:{ex.Message}"); return; }
|
||
|
||
var td = db.trade.FirstOrDefault(t => t.TradeNumber == TradeNumber_0008);
|
||
Assert.IsNotNull(td, $"测试库无交易 {TradeNumber_0008},请确认环境");
|
||
|
||
var snapshot = new JObject
|
||
{
|
||
["TradeNumber"] = td.TradeNumber,
|
||
["TradeId"] = td.id,
|
||
["StockEqvNotional"] = td.StockEqvNotional,
|
||
["Notional"] = td.Notional,
|
||
["OriginalStockEqvNotional"] = td.OriginalStockEqvNotional,
|
||
["TradeDate"] = td.TradeDate,
|
||
["StartDate"] = td.StartDate,
|
||
["ExerciseDate"] = td.ExerciseDate
|
||
};
|
||
|
||
// 1.1 当前所有仓位(含 IsInitial=初始 + !IsInitial=已平后剩余)
|
||
var positions = db.swap_position
|
||
.Where(p => p.SwapTradeId == td.id && !p.Invalid)
|
||
.OrderBy(p => p.IsInitial).ThenBy(p => p.id)
|
||
.ToList();
|
||
snapshot["Positions"] = JArray.FromObject(positions, JsonSerializer.Create(new JsonSerializerSettings
|
||
{
|
||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||
DateFormatHandling = DateFormatHandling.IsoDateFormat
|
||
}));
|
||
|
||
// 1.2 EOD 持仓序列(关键:观察 InterestPrincipalFix 逐日变化)
|
||
var eodPositions = db.eod_swap_position
|
||
.Where(e => e.SwapTradeId == td.id && !e.Invalid && e.InterestMode == 5 || e.InterestMode == 6)
|
||
.OrderBy(e => e.ValueDate).ThenBy(e => e.PositionId)
|
||
.ToList();
|
||
snapshot["EodPositions_MarginLegOnly"] = JArray.FromObject(eodPositions, JsonSerializer.Create(new JsonSerializerSettings
|
||
{
|
||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||
DateFormatHandling = DateFormatHandling.IsoDateFormat
|
||
}));
|
||
|
||
// 1.3 EOD 交易级(eod_swap.NotionalValue 应该是初始值不变)
|
||
var eodSwaps = db.eod_swap.Where(e => e.SwapTradeId == td.id).OrderBy(e => e.ValueDate).ToList();
|
||
snapshot["EodSwaps"] = JArray.FromObject(eodSwaps, JsonSerializer.Create(new JsonSerializerSettings
|
||
{
|
||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||
DateFormatHandling = DateFormatHandling.IsoDateFormat
|
||
}));
|
||
|
||
// 1.4 所有 flow_event(看平仓/互换事件序列,及 InterestPrincipal 实际写入值)
|
||
var flows = db.swap_flow_event.Where(f => f.SwapTradeId == td.id).OrderBy(f => f.EventDate).ThenBy(f => f.id).ToList();
|
||
snapshot["FlowEvents"] = JArray.FromObject(flows, JsonSerializer.Create(new JsonSerializerSettings
|
||
{
|
||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||
DateFormatHandling = DateFormatHandling.IsoDateFormat
|
||
}));
|
||
|
||
var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "DbDiagnose", "GLMS20260701");
|
||
Directory.CreateDirectory(dir);
|
||
var path = Path.Combine(dir, $"snapshot_{DateTime.Now:yyyyMMdd_HHmmss}.json");
|
||
File.WriteAllText(path, JsonConvert.SerializeObject(snapshot, Formatting.Indented,
|
||
new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.IsoDateFormat }));
|
||
Console.WriteLine($"✅ 快照已保存: {path}");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 2) 诊断:打印"预付金腿"逐日本金变化 + 后端 API 1.0 全平应返值
|
||
|
||
[TestMethod]
|
||
[TestCategory("DbDiagnose")]
|
||
public void Diagnose_InterestPrincipalFix_Progression_And_UnwindResult()
|
||
{
|
||
DiagnoseTrade(TradeNumber_0008);
|
||
}
|
||
|
||
[TestMethod]
|
||
[TestCategory("DbDiagnose")]
|
||
public void Diagnose_0013_InterestPrincipalFix_Progression_And_UnwindResult()
|
||
{
|
||
DiagnoseTrade(TradeNumber_0013);
|
||
}
|
||
|
||
private void DiagnoseTrade(string tradeNumber)
|
||
{
|
||
YLContext db;
|
||
try { db = DbContextFactory.GetYLDbContext(); }
|
||
catch (Exception ex) { Assert.Inconclusive($"无法连接测试库:{ex.Message}"); return; }
|
||
|
||
var td = db.trade.FirstOrDefault(t => t.TradeNumber == tradeNumber);
|
||
if (td == null) { Assert.Inconclusive($"测试库无 {tradeNumber}"); return; }
|
||
|
||
// 2.1 预付金腿 position.InterestPrincipalFix 当前值(多次平仓后应该已被扣减)
|
||
var marginPositions = db.swap_position
|
||
.Where(p => p.SwapTradeId == td.id && !p.Invalid
|
||
&& (p.InterestMode == (int)InterestModeEnum.初始预付金
|
||
|| p.InterestMode == (int)InterestModeEnum.追加预付金))
|
||
.ToList();
|
||
|
||
Console.WriteLine("============== 预付金腿 position 当前值(多次平仓后) ==============");
|
||
foreach (var p in marginPositions)
|
||
{
|
||
Console.WriteLine($"PositionId={p.id} Mode={p.InterestMode} Fix={p.InterestPrincipalFix} Rate={p.InterestRateDefault} Dir={p.InterestDirection} IsInitial={p.IsInitial}");
|
||
}
|
||
|
||
// 2.1b 所有 position 全景(含浮动腿),对比 IsInitial vs !IsInitial 的 PosiNotionalValue / Fix
|
||
var allPositions = db.swap_position
|
||
.Where(p => p.SwapTradeId == td.id && !p.Invalid)
|
||
.OrderBy(p => p.IsInitial).ThenBy(p => p.id)
|
||
.ToList();
|
||
|
||
Console.WriteLine("\n============== 全部 position 全景(对比 IsInitial 原始 vs !IsInitial 剩余) ==============");
|
||
Console.WriteLine($" {"Id",-8}{"Mode",-6}{"IntDir",-7}{"PosiDir",-8}{"IsInit",-8}{"Fix",-18}{"PosiNotional",-18}{"PosiQty",-12}{"UnderlyingCode",-15}");
|
||
foreach (var p in allPositions)
|
||
{
|
||
var ul = p.UnderlyingCode ?? "";
|
||
Console.WriteLine($" {p.id,-8}{p.InterestMode,-6}{p.InterestDirection,-7}{p.PosiDirection,-8}{p.IsInitial,-8}{p.InterestPrincipalFix,-18}{p.PosiNotionalValue,-18}{p.PosiQuantity,-12}{ul,-15}");
|
||
}
|
||
|
||
// 2.1c 关键诊断:GetUnwindInterests 内部 origPositions vs realPostitions 差异
|
||
var origPositions = allPositions.Where(x => x.IsInitial).ToList();
|
||
var realPostitions = allPositions.Where(x => !x.IsInitial).ToList();
|
||
Console.WriteLine("\n============== GetUnwindInterests 关键源数据对比 ==============");
|
||
Console.WriteLine($" origPositions(IsInitial=True) 浮动腿 PosiNotionalValue 总和: {origPositions.Where(x => x.PosiDirection > 0).Sum(s => s.PosiNotionalValue)}");
|
||
Console.WriteLine($" realPostitions(IsInitial=False) 浮动腿 PosiNotionalValue 总和: {realPostitions.Where(x => x.PosiDirection > 0).Sum(s => s.PosiNotionalValue)} ← 应为剩余值");
|
||
Console.WriteLine($" origPositions(IsInitial=True) 预付金腿 Fix: {string.Join(",", origPositions.Where(x => x.InterestMode == 5 || x.InterestMode == 6).Select(x => x.InterestPrincipalFix))}");
|
||
Console.WriteLine($" realPostitions(IsInitial=False) 预付金腿 Fix: {string.Join(",", realPostitions.Where(x => x.InterestMode == 5 || x.InterestMode == 6).Select(x => x.InterestPrincipalFix))} ← 应为剩余值");
|
||
|
||
// 2.1d 关键诊断:realLeg.PositionId == origPos.id 匹配校验(修复后端 Clone 是否会触发)
|
||
Console.WriteLine("\n============== realLeg.PositionId ↔ origPos.id 匹配校验(决定 Clone 是否生效)==============");
|
||
foreach (var origPos in origPositions.Where(p => p.InterestMode == 5 || p.InterestMode == 6))
|
||
{
|
||
var realLeg = realPostitions.FirstOrDefault(r => r.PositionId == origPos.id);
|
||
Console.WriteLine($" origPos.id={origPos.id} Fix={origPos.InterestPrincipalFix} | realLeg found={(realLeg != null)} | realLeg.id={realLeg?.id} realLeg.PositionId={realLeg?.PositionId} realLeg.Fix={realLeg?.InterestPrincipalFix} | 需Clone={(realLeg != null && realLeg.InterestPrincipalFix != origPos.InterestPrincipalFix)}");
|
||
}
|
||
|
||
// 2.2 EOD 持仓 InterestPrincipalFix 逐日序列
|
||
var eodMarginSeq = db.eod_swap_position
|
||
.Where(e => e.SwapTradeId == td.id && !e.Invalid
|
||
&& (e.InterestMode == (int)InterestModeEnum.初始预付金
|
||
|| e.InterestMode == (int)InterestModeEnum.追加预付金))
|
||
.OrderBy(e => e.ValueDate).ThenBy(e => e.PositionId)
|
||
.ToList();
|
||
|
||
Console.WriteLine("============== EOD 预付金腿 InterestPrincipalFix 逐日变化 ==============");
|
||
foreach (var e in eodMarginSeq)
|
||
{
|
||
Console.WriteLine($" ValueDate={e.ValueDate:yyyy-MM-dd} PositionId={e.PositionId} Fix={e.InterestPrincipalFix} TdInterestPrincipal={e.TdInterestPrincipal} PosiStatus={e.PosiStatus} Invalid={e.Invalid}");
|
||
}
|
||
|
||
// 2.3 平仓事件序列(看 InterestPrincipal 实际入库值)
|
||
var closeFlows = db.swap_flow_event
|
||
.Where(f => f.SwapTradeId == td.id && f.EventType == (int)SwapEventTypeEnum.平仓
|
||
&& f.DataState == (int)SwapFlowDateStateEnum.完成
|
||
&& (f.InterestMode == (int)InterestModeEnum.初始预付金
|
||
|| f.InterestMode == (int)InterestModeEnum.追加预付金))
|
||
.OrderBy(f => f.EventDate).ToList();
|
||
|
||
Console.WriteLine("============== 历史平仓事件-预付金腿 实际 InterestPrincipal 序列 ==============");
|
||
foreach (var f in closeFlows)
|
||
{
|
||
Console.WriteLine($" EventDate={f.EventDate:yyyy-MM-dd} PositionId={f.PositionId} InterestPrincipal={f.InterestPrincipal} InterestAmount={f.InterestAmount} Quantity={f.Quantity} TradingAmount={f.TradingAmount}");
|
||
}
|
||
|
||
// 2.3b 直接调 ResolveInterestLegPositions,验证 Clone 是否真的把 Fix 覆盖成 realLeg 值
|
||
var resolved = SwapDealService.ResolveInterestLegPositions(origPositions, realPostitions);
|
||
Console.WriteLine("\n============== ResolveInterestLegPositions 直接调用结果 ==============");
|
||
foreach (var rp in resolved.Where(x => x.InterestMode == 5 || x.InterestMode == 6))
|
||
{
|
||
Console.WriteLine($" resolved: id={rp.id} PositionId={rp.PositionId} Mode={rp.InterestMode} Fix={rp.InterestPrincipalFix} (期望=realLeg.Fix)");
|
||
}
|
||
|
||
// 2.3c 模拟前端调用 controller 完整流程:前端传 closePercent=0.7(占期初) + notionalValue/posiNotionalValue
|
||
// controller 调 ToRemainingClosePercent 转为占剩余,再调 GetUnwindInterests
|
||
// 等价于 HTTP POST /swaptrade2/GetUnwindInterestList
|
||
Console.WriteLine("\n============== 模拟 HTTP API 调用(前端 closePercent=0.7 占期初)==============");
|
||
decimal frontClosePercent = 0.7m;
|
||
decimal frontNotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0d); // 期初名义本金
|
||
decimal frontPosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional); // 剩余名义本金
|
||
Console.WriteLine($" 前端参数: closePercent={frontClosePercent} notionalValue={frontNotionalValue} posiNotionalValue={frontPosiNotionalValue}");
|
||
decimal convertedClosePercent = SwapDealService.ToRemainingClosePercent(frontClosePercent, frontNotionalValue, frontPosiNotionalValue);
|
||
Console.WriteLine($" ToRemainingClosePercent 转换后: closePercent={convertedClosePercent}(占剩余)");
|
||
var svc = new SwapDealService(new OptUserInfo(1, "UnitTest", OptUserFrom.UnitTest));
|
||
var apiInterests = svc.GetUnwindInterests(DateTime.Today, DateTime.Today, td.id, convertedClosePercent, (int)SwapEventTypeEnum.平仓);
|
||
Console.WriteLine($" GetUnwindInterests 返回 {apiInterests.Count} 条,预付金腿:");
|
||
foreach (var ai in apiInterests.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金 || x.InterestMode == (int)InterestModeEnum.追加预付金))
|
||
{
|
||
Console.WriteLine($" PositionId={ai.PositionId} Mode={ai.InterestMode} InterestPrincipal={ai.InterestPrincipal} InterestAmount={ai.InterestAmount}");
|
||
}
|
||
|
||
// 2.4 直调后端 GetUnwindInterests(closePercent=1.0) 看"按全部平仓应返"的预付金值
|
||
try
|
||
{
|
||
var user = new OptUserInfo(0, nameof(GLMS20260701DbDiagnoseTest), OptUserFrom.UnitTest);
|
||
var svcFull = new SwapDealService(user);
|
||
var interests = svcFull.GetUnwindInterests(DateTime.Today, DateTime.Today, td.id, 1.0m, (int)SwapEventTypeEnum.平仓);
|
||
|
||
Console.WriteLine("============== 后端 GetUnwindInterests(1.0) 实际返回值-预付金腿 ==============");
|
||
foreach (var it in interests.Where(i => i.InterestMode == 5 || i.InterestMode == 6))
|
||
{
|
||
Console.WriteLine($" PositionId={it.PositionId} Mode={it.InterestMode} InterestPrincipal={it.InterestPrincipal} InterestAmount={it.InterestAmount} InterestRate={it.InterestRate}");
|
||
}
|
||
|
||
// 诊断断言:1.0 全平应返 = realPostitions(剩余持仓)的 InterestPrincipalFix
|
||
// 后端为保持 eod_swap_position.PositionId 日终归档对齐,返回的 PositionId 仍是 origPositions.id,
|
||
// 但 InterestPrincipal 应等于 realPostitions[real.PositionId == orig.id].Fix(剩余值)。
|
||
// 因此对比口径:apiRet.InterestPrincipal vs realLeg.Fix(剩余值),不是 vs origPos.Fix(原始值)。
|
||
Console.WriteLine("============== 修复验证(apiRet.InterestPrincipal vs realLeg.Fix 剩余值)==============");
|
||
int okCount = 0, badCount = 0;
|
||
foreach (var origPos in marginPositions.Where(p => p.IsInitial))
|
||
{
|
||
var apiRet = interests.FirstOrDefault(i => i.PositionId == origPos.id);
|
||
if (apiRet == null) { Console.WriteLine($" ⚠ PositionId={origPos.id} 后端未返回"); continue; }
|
||
var realLeg = marginPositions.FirstOrDefault(p => !p.IsInitial && p.PositionId == origPos.id);
|
||
decimal expectedFix = realLeg?.InterestPrincipalFix ?? origPos.InterestPrincipalFix;
|
||
var diff = Math.Abs((double)(apiRet.InterestPrincipal - expectedFix));
|
||
bool ok = diff < 0.01;
|
||
if (ok) okCount++; else badCount++;
|
||
Console.WriteLine($" {(ok ? "✓" : "✗")} PositionId={origPos.id}(origFix={origPos.InterestPrincipalFix}) → realLeg.Fix={expectedFix} 后端返={apiRet.InterestPrincipal} 差={diff:F4}");
|
||
}
|
||
Console.WriteLine($"\n 结论:通过 {okCount} 条 / 失败 {badCount} 条");
|
||
Assert.IsTrue(badCount == 0, $"修复未生效:{badCount} 条预付金腿后端返还值 ≠ realLeg.Fix 剩余值");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"⚠ GetUnwindInterests 调用失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 3) 诊断 GLMS-20260701-0006:部分平仓比例显示 32.50% 而非 50%
|
||
|
||
[TestMethod]
|
||
[TestCategory("DbDiagnose")]
|
||
public void Diagnose_0006_UnwindPercentRate_Display()
|
||
{
|
||
const string tradeNumber = "GLMS-20260701-0006";
|
||
YLContext db;
|
||
try { db = DbContextFactory.GetYLDbContext(); }
|
||
catch (Exception ex) { Assert.Inconclusive($"无法连接测试库:{ex.Message}"); return; }
|
||
|
||
var td = db.trade.FirstOrDefault(t => t.TradeNumber == tradeNumber);
|
||
if (td == null) { Assert.Inconclusive($"测试库无 {tradeNumber}"); return; }
|
||
|
||
Console.WriteLine($"===== 交易 {tradeNumber} (id={td.id}) =====");
|
||
Console.WriteLine($" TradeType: {td.TradeType}");
|
||
Console.WriteLine($" TradeStatus: {td.TradeStatus}");
|
||
Console.WriteLine($" StockEqvNotional (剩余): {td.StockEqvNotional}");
|
||
Console.WriteLine($" OriginalStockEqvNotional (期初): {td.OriginalStockEqvNotional}");
|
||
Console.WriteLine($" Notional: {td.Notional}");
|
||
Console.WriteLine($" OriginalNotional: {td.OriginalNotional}");
|
||
Console.WriteLine($" TradeAmount: {td.TradeAmount}");
|
||
Console.WriteLine($" HasPartialUnWind: {td.HasPartialUnWind}");
|
||
Console.WriteLine($" 剩余比例 = StockEqvNotional/Original = {td.StockEqvNotional / td.OriginalStockEqvNotional}");
|
||
Console.WriteLine();
|
||
|
||
Console.WriteLine($"===== trade_cash 记录 =====");
|
||
var tradeCashList = db.trade_cash
|
||
.Where(t => t.TradeId == td.id && !t.IsDeleted &&
|
||
(t.Action == "系统操作-平仓费" || t.Action == "系统操作-行权费"))
|
||
.OrderBy(t => t.ValueDate).ThenBy(t => t.id)
|
||
.ToList();
|
||
|
||
foreach (var tc in tradeCashList)
|
||
{
|
||
Console.WriteLine($" [id={tc.id}] ValueDate={tc.ValueDate:yyyy-MM-dd} Action={tc.Action}");
|
||
Console.WriteLine($" UnwindType: {tc.UnwindType}");
|
||
Console.WriteLine($" UnwindPercentRate: {tc.UnwindPercentRate} (=> {tc.UnwindPercentRate * 100}%)");
|
||
Console.WriteLine($" UnwindStockEqvNotional: {tc.UnwindStockEqvNotional}");
|
||
Console.WriteLine($" UnwindNotional: {tc.UnwindNotional}");
|
||
Console.WriteLine($" UnwindTradeAmount: {tc.UnwindTradeAmount}");
|
||
Console.WriteLine($" UnwindMethod: {tc.UnwindMethod}");
|
||
Console.WriteLine($" ValidState: {tc.ValidState}");
|
||
Console.WriteLine($" IsLastAction: {tc.IsLastAction}");
|
||
Console.WriteLine($" ExerciseWay: {tc.ExerciseWay}");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
Console.WriteLine($"===== swap_event 记录 =====");
|
||
var swapEvents = db.swap_event
|
||
.Where(e => e.SwapTradeId == td.id && !e.Invalid)
|
||
.OrderBy(e => e.ValueDate).ThenBy(e => e.id)
|
||
.ToList();
|
||
|
||
foreach (var se in swapEvents)
|
||
{
|
||
Console.WriteLine($" [id={se.id}] ValueDate={se.ValueDate:yyyy-MM-dd} EventType={se.EventType}");
|
||
Console.WriteLine($" EventReason: {se.EventReason}");
|
||
Console.WriteLine($" ClientCashId: {se.ClientCashId}");
|
||
if (!string.IsNullOrEmpty(se.EventData))
|
||
{
|
||
try
|
||
{
|
||
var ud = JsonConvert.DeserializeObject<JObject>(se.EventData);
|
||
Console.WriteLine($" EventData.ClosePercent: {ud["ClosePercent"]}");
|
||
Console.WriteLine($" EventData.CloseNotionalValue: {ud["CloseNotionalValue"]}");
|
||
Console.WriteLine($" EventData.CloseQty: {ud["CloseQty"]}");
|
||
Console.WriteLine($" EventData.NotionalValue: {ud["NotionalValue"]}");
|
||
Console.WriteLine($" EventData.PosiNotionalValue: {ud["PosiNotionalValue"]}");
|
||
Console.WriteLine($" EventData.PositionQty: {ud["PositionQty"]}");
|
||
Console.WriteLine($" EventData.CloseMethod: {ud["CloseMethod"]}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($" EventData parse error: {ex.Message}");
|
||
}
|
||
}
|
||
Console.WriteLine();
|
||
}
|
||
|
||
// 查询 swap_flow_event 记录
|
||
Console.WriteLine($"===== swap_flow_event 记录 =====");
|
||
var flowEvents = db.swap_flow_event
|
||
.Where(f => f.SwapTradeId == td.id)
|
||
.OrderBy(f => f.EventDate).ThenBy(f => f.id)
|
||
.ToList();
|
||
|
||
foreach (var fe in flowEvents)
|
||
{
|
||
Console.WriteLine($" [id={fe.id}] EventDate={fe.EventDate:yyyy-MM-dd} EventType={fe.EventType}");
|
||
Console.WriteLine($" PositionId: {fe.PositionId}");
|
||
Console.WriteLine($" Quantity: {fe.Quantity}");
|
||
Console.WriteLine($" PositionQty: {fe.PositionQty}");
|
||
Console.WriteLine();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|