test(swap): SwapDealService抽7个virtual seam + 手动结算内存测试
攻克SwapIncome/SwapUnwind零单元测试覆盖的盲区(此前仅DBRecording,CI不跑)。 SwapDealService补7个protected virtual seam(参考已验证的SwapEodPositionService模式): - FindTrade/AddClientCash/SaveSwapDeal(原private改protected virtual) - SaveAllChanges/ExecuteInTransaction/CallSaveSwapTradeClientCash/TriggerRealtimeSwapPosition 生产实现行为不变,SwapIncome/SwapUnwind/ApproveSwapTrade重构为调seam。 新增SwapDealSettlementTest.cs(4个内存测试): - SD_001 SwapIncome资金流水金额=-SwapRealizedPnL - SD_002 含预付金返息双流水 - SD_003 SwapUnwind全平仓持仓归零+状态变更 - SD_004 DealFloatPosition含费价重算(后端唯二真做计算处) 红灯验证通过(改错金额符号测试立即失败)。
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
|
||||
namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
/// <summary>
|
||||
/// SwapDealService 手动结算(SwapIncome/SwapUnwind)内存单元测试
|
||||
/// ============================================================================
|
||||
/// 背景:SwapIncome/SwapUnwind 是写客户资金流水(ClientCashInCashOut)的核心入口,
|
||||
/// 此前零单元测试(仅 DBRecording,CI 不跑)。本测试通过 7 个 virtual seam
|
||||
/// 把 DB/事务/外部服务打桩,在纯内存下验证控制流、资金流水金额、持仓状态变更。
|
||||
///
|
||||
/// 命名规范说明(见《互换价格字段命名规范决策文档》):
|
||||
/// 本测试引用现状字段(如 PosiGrossPrice/PosiNetPrice)时加对照注释,
|
||||
/// 标明其真实含义与规范名,让测试可读、可作规范示范。
|
||||
/// - PosiGrossPrice 现状名,实为"期初全价不含费",规范名 EntryDirtyPrice
|
||||
/// - PosiNetPrice 现状名,实为"期初全价含费"(非净价!),规范名 EntryDirtyFeePrice
|
||||
/// ============================================================================
|
||||
[TestClass]
|
||||
public class SwapDealSettlementTest
|
||||
{
|
||||
private const int SwapTradeId = 7700;
|
||||
private static readonly DateTime ValueDate = new(2026, 6, 15);
|
||||
private static readonly DateTime UnwindDate = new(2026, 6, 16);
|
||||
|
||||
#region Stub
|
||||
|
||||
/// <summary>
|
||||
/// 继承 SwapDealService,override 7 个 seam,把 DB/事务/外部服务替换为内存收集器。
|
||||
/// 生产路径零改动(seam 生产实现 = 原逻辑),测试可纯内存运行。
|
||||
/// </summary>
|
||||
private sealed class StubDealService : SwapDealService
|
||||
{
|
||||
private readonly trade _trade;
|
||||
public List<(double amount, string action, DateTime date)> ClientCashCalls = new();
|
||||
public List<(UnwindData data, int eventType, int clientCashId)> SaveSwapDealCalls = new();
|
||||
public int SaveAllChangesCount;
|
||||
|
||||
public StubDealService(trade td) : base(new OptUserInfo(0, nameof(SwapDealSettlementTest), OptUserFrom.UnitTest))
|
||||
{
|
||||
_trade = td;
|
||||
}
|
||||
|
||||
protected override trade FindTrade(int tradeId) => tradeId == _trade.id ? _trade : null;
|
||||
|
||||
protected override int AddClientCash(trade td, double amount, string action, DateTime valueDate)
|
||||
{
|
||||
ClientCashCalls.Add((amount, action, valueDate));
|
||||
return ClientCashCalls.Count; // 返回自增 id
|
||||
}
|
||||
|
||||
// 整体 override SaveSwapDeal:收集入参,规避内部 new SwapEventService 连库
|
||||
protected override long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
|
||||
{
|
||||
SaveSwapDealCalls.Add((unwindData, eventType, clientCashId));
|
||||
return SaveSwapDealCalls.Count; // 返回自增 eventId
|
||||
}
|
||||
|
||||
protected override void SaveAllChanges() { SaveAllChangesCount++; }
|
||||
protected override void ExecuteInTransaction(Action action) => action(); // 不包事务,直接执行
|
||||
protected override void CallSaveSwapTradeClientCash(trade td, DateTime valueDate) { } // 空操作
|
||||
protected override void TriggerRealtimeSwapPosition() { } // 空操作
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据构建
|
||||
|
||||
private static trade CreateTrade()
|
||||
{
|
||||
return new trade
|
||||
{
|
||||
id = SwapTradeId, TradeNumber = "UT-SD-001", ClientId = 888888,
|
||||
TradeType = "收益互换", StartDate = new DateTime(2026, 1, 5),
|
||||
ExerciseDate = new DateTime(2026, 6, 14), // 已到期边界(SwapIncome 判断用)
|
||||
TradeStatus = "确认成交", ValidState = "Valid",
|
||||
Notional = 1000000, StockEqvNotional = 1000000, TradeAmount = 10000
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>构造结息/平仓的 UnwindData(金额由前端算好传入,后端直接用)</summary>
|
||||
private static UnwindData CreateUnwindData(decimal swapRealizedPnL, decimal swapMarginRebatePnl = 0m,
|
||||
decimal swapMarginAmount = 0m, int closeMethod = 0, decimal closePercent = 0m,
|
||||
decimal closeQty = 0m, decimal closeNotionalValue = 0m, decimal positionQty = 0m)
|
||||
{
|
||||
return new UnwindData
|
||||
{
|
||||
SwapTradeId = SwapTradeId,
|
||||
SwapRealizedPnL = swapRealizedPnL,
|
||||
SwapMarginRebatePnl = swapMarginRebatePnl,
|
||||
SwapMarginAmount = swapMarginAmount,
|
||||
SwapCloseAmount = swapRealizedPnL,
|
||||
CloseMethod = closeMethod,
|
||||
ClosePercent = closePercent,
|
||||
CloseQty = closeQty,
|
||||
CloseNotionalValue = closeNotionalValue,
|
||||
PositionQty = positionQty,
|
||||
ValueDate = ValueDate,
|
||||
UnwindDate = UnwindDate,
|
||||
StartDate = new DateTime(2026, 1, 5)
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// ================================================================
|
||||
// SD_001:SwapIncome 正常结息 —— 验证资金流水金额正确
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_001] SwapIncome 正常结息:SwapRealizedPnL=1000 → 客户资金流水金额=-1000
|
||||
/// ------------------------------------------------------------
|
||||
/// 后端 SwapDealService.cs:1553 直接用前端传入的 SwapRealizedPnL 记账:
|
||||
/// AddClientCash(td, -SwapRealizedPnL, 系统操作_互换, ValueDate)
|
||||
/// 本测试锁定:资金流水金额 = -SwapRealizedPnL,事件类型 = 互换(3)。
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_001_SwapIncome_正常结息_资金流水金额正确()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
td.ExerciseDate = new DateTime(2026, 12, 31); // 未到期,不走"已到期"分支
|
||||
var service = new StubDealService(td);
|
||||
var unwindData = CreateUnwindData(swapRealizedPnL: 1000m);
|
||||
|
||||
service.SwapIncome(unwindData);
|
||||
|
||||
Assert.AreEqual(1, service.ClientCashCalls.Count, "应生成1条资金流水(互换)");
|
||||
Assert.AreEqual(-1000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水金额 = -SwapRealizedPnL");
|
||||
Assert.AreEqual(ClientCashInCashOut.系统操作_互换, service.ClientCashCalls[0].action, "操作类型=系统操作_互换");
|
||||
Assert.AreEqual(1, service.SaveSwapDealCalls.Count, "应调用 SaveSwapDeal 1次");
|
||||
Assert.AreEqual((int)SwapEventTypeEnum.互换, service.SaveSwapDealCalls[0].eventType, "事件类型=互换(3)");
|
||||
Console.WriteLine($"SD_001 通过:资金流水金额={service.ClientCashCalls[0].amount},事件类型=互换 ✅");
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SD_002:SwapIncome 含预付金返息 —— 两条资金流水
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_002] SwapIncome 含预付金返息:SwapRealizedPnL=1000, SwapMarginRebatePnl=200
|
||||
/// → 生成2条资金流水(互换 + 预付金返息),金额分别为 -1000、-200
|
||||
/// 后端 SwapDealService.cs:1556 条件:SwapMarginRebatePnl != 0 时追加预付金返息流水。
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_002_SwapIncome_含预付金返息_两条资金流水()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
td.ExerciseDate = new DateTime(2026, 12, 31);
|
||||
var service = new StubDealService(td);
|
||||
var unwindData = CreateUnwindData(swapRealizedPnL: 1000m, swapMarginRebatePnl: 200m);
|
||||
|
||||
service.SwapIncome(unwindData);
|
||||
|
||||
Assert.AreEqual(2, service.ClientCashCalls.Count, "应生成2条资金流水(互换+预付金返息)");
|
||||
Assert.AreEqual(-1000.0, service.ClientCashCalls[0].amount, 0.001, "第1条=互换金额 -SwapRealizedPnL");
|
||||
Assert.AreEqual(ClientCashInCashOut.系统操作_互换, service.ClientCashCalls[0].action);
|
||||
Assert.AreEqual(-200.0, service.ClientCashCalls[1].amount, 0.001, "第2条=预付金返息 -SwapMarginRebatePnl");
|
||||
Assert.AreEqual(ClientCashInCashOut.系统操作_预付金返息, service.ClientCashCalls[1].action);
|
||||
Console.WriteLine($"SD_002 通过:2条资金流水,互换={service.ClientCashCalls[0].amount},预付金返息={service.ClientCashCalls[1].amount} ✅");
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SD_003:SwapUnwind 全平仓 —— 持仓归零、资金流水、状态变更
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_003] SwapUnwind 全平仓:ClosePercent=1 → TradeStatus=已平仓、持仓扣减、资金流水正确
|
||||
/// 后端 SwapDealService.cs SwapUnwind:全平时 TradeStatus=已平仓,StockEqvNotional/TradeAmount 扣减。
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_003_SwapUnwind_正常平仓_资金流水与持仓状态正确()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var service = new StubDealService(td);
|
||||
// 全平:ClosePercent=1, CloseQty=10000, CloseNotionalValue=1000000
|
||||
var unwindData = CreateUnwindData(
|
||||
swapRealizedPnL: 5000m, swapMarginAmount: 0m,
|
||||
closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m,
|
||||
closeQty: 10000m, closeNotionalValue: 1000000m, positionQty: 10000m);
|
||||
|
||||
service.SwapUnwind(unwindData);
|
||||
|
||||
// 资金流水:平仓费 = -SwapRealizedPnL
|
||||
Assert.AreEqual(1, service.ClientCashCalls.Count, "全平无预付金时应1条资金流水");
|
||||
Assert.AreEqual(-5000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水=-SwapRealizedPnL");
|
||||
Assert.AreEqual(ClientCashInCashOut.系统操作_平仓费, service.ClientCashCalls[0].action);
|
||||
// 持仓状态
|
||||
Assert.AreEqual("已平仓", td.TradeStatus, "全平仓 TradeStatus=已平仓");
|
||||
// 全平仓走"已平仓"分支,不设 HasPartialUnWind(仅部分平仓才设=1)
|
||||
Assert.AreNotEqual(1, td.HasPartialUnWind, "全平仓不应设 HasPartialUnWind(仅部分平仓设=1)");
|
||||
// 持仓扣减:原 StockEqvNotional=1000000 - CloseNotionalValue=1000000 = 0
|
||||
Assert.AreEqual(0.0, td.StockEqvNotional, 0.001, "StockEqvNotional 扣减后=0");
|
||||
Assert.AreEqual(0.0, td.TradeAmount, 0.001, "TradeAmount 扣减后=0");
|
||||
// 事件类型
|
||||
Assert.AreEqual((int)SwapEventTypeEnum.平仓, service.SaveSwapDealCalls[0].eventType, "事件类型=平仓(2)");
|
||||
Console.WriteLine($"SD_003 通过:TradeStatus={td.TradeStatus},StockEqvNotional={td.StockEqvNotional} ✅");
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SD_004:DealFloatPosition 含费价重算正确(后端唯二真做计算的地方)
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_004] DealFloatPosition 含费价重算(SwapDealService.cs:1713-1725)
|
||||
/// ------------------------------------------------------------
|
||||
/// 平仓事件重算三个字段(规范语义,见命名文档):
|
||||
/// TradingAmountFeeAvg(ExitDirtyFeePrice)= TradingAmountAvg(ExitDirtyPrice) + TradingFeePending/CloseQty × shortRatio
|
||||
/// TradingAmountNetFeeAvg(ExitCleanFeePrice)= TradingAmountNetAvg(ExitCleanPrice) + TradingFeePending/CloseQty × shortRatio
|
||||
/// TradingAmount = TradingAmountAvg × CloseQty
|
||||
/// 这是后端少数真正做计算(而非透传前端值)的地方,需锁住。
|
||||
///
|
||||
/// 手算:ExitDirtyPrice=1.02, TradingFeePending=50, CloseQty=1000, Long(多头,shortRatio=-1)
|
||||
/// ExitDirtyFeePrice = 1.02 + 50/1000 × (-1) = 1.02 - 0.05 = 0.97
|
||||
/// ExitCleanFeePrice = 1.00 + 50/1000 × (-1) = 1.00 - 0.05 = 0.95
|
||||
/// TradingAmount = 1.02 × 1000 = 1020
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_004_DealFloatPosition_含费价重算正确()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var service = new StubDealService(td);
|
||||
|
||||
// 构造平仓事件(PositionType>0 触发重算)
|
||||
var closeEvent = new swap_flow_event
|
||||
{
|
||||
EventType = (int)SwapEventTypeEnum.平仓,
|
||||
PositionType = (int)PositionTypeFlag.Long, // 多头,shortRatio=-1
|
||||
// TradingAmountAvg 现状名,实为"期末全价不含费",规范名 ExitDirtyPrice
|
||||
TradingAmountAvg = 1.02m,
|
||||
// TradingAmountNetAvg 现状名,实为"期末净价不含费",规范名 ExitCleanPrice
|
||||
TradingAmountNetAvg = 1.00m,
|
||||
TradingFeePending = 50m,
|
||||
};
|
||||
var unwindData = CreateUnwindData(swapRealizedPnL: 0m, closeQty: 1000m);
|
||||
unwindData.FlowEvents.Add(closeEvent);
|
||||
|
||||
service.SwapUnwind(unwindData);
|
||||
|
||||
// ExitDirtyFeePrice(TradingAmountFeeAvg)= 1.02 + 50/1000×(-1) = 0.97
|
||||
Assert.AreEqual(0.97m, closeEvent.TradingAmountFeeAvg, 0.0001m,
|
||||
$"TradingAmountFeeAvg(ExitDirtyFeePrice) 应=ExitDirtyPrice(1.02)+Fee/CloseQty×(-1)=0.97,实际={closeEvent.TradingAmountFeeAvg}");
|
||||
// ExitCleanFeePrice(TradingAmountNetFeeAvg)= 1.00 + 50/1000×(-1) = 0.95
|
||||
Assert.AreEqual(0.95m, closeEvent.TradingAmountNetFeeAvg ?? 0m, 0.0001m,
|
||||
$"TradingAmountNetFeeAvg(ExitCleanFeePrice) 应=ExitCleanPrice(1.00)+Fee/CloseQty×(-1)=0.95,实际={closeEvent.TradingAmountNetFeeAvg}");
|
||||
// TradingAmount = ExitDirtyPrice × CloseQty = 1.02 × 1000 = 1020
|
||||
Assert.AreEqual(1020m, closeEvent.TradingAmount, 0.0001m,
|
||||
$"TradingAmount 应=ExitDirtyPrice(1.02)×CloseQty(1000)=1020,实际={closeEvent.TradingAmount}");
|
||||
Console.WriteLine($"SD_004 通过:ExitDirtyFeePrice={closeEvent.TradingAmountFeeAvg},ExitCleanFeePrice={closeEvent.TradingAmountNetFeeAvg},TradingAmount={closeEvent.TradingAmount} ✅");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,83 @@ namespace YLErp.Modules.SwapModule
|
||||
return EodPriceQueryService.TryGetPrice(valueDate, underlyingCode, out rate);
|
||||
}
|
||||
|
||||
#region 可测试化接缝(Seams)——override 这些虚方法可在测试中替换 DB/外部调用,生产代码行为不变
|
||||
|
||||
/// <summary>查找交易(生产: DbContext.trade.Find;测试: 返回内存对象)</summary>
|
||||
protected virtual trade FindTrade(int tradeId)
|
||||
{
|
||||
return DbContext.trade.Find(tradeId);
|
||||
}
|
||||
|
||||
/// <summary>添加资金记录(生产: AddClientCashInCashOut;测试: 计数并记录金额)</summary>
|
||||
protected virtual int AddClientCash(trade td, double amount, string action, DateTime valueDate)
|
||||
{
|
||||
return AddClientCashInCashOut(td, amount, action, valueDate);
|
||||
}
|
||||
|
||||
/// <summary>保存互换/平仓事件(生产: 落库+建事件;测试: 收集 unwindData 入内存列表)。
|
||||
/// 原 private 改 protected virtual,使测试 stub 可整体 override,规避内部 new SwapEventService 连库。</summary>
|
||||
protected virtual long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
|
||||
{
|
||||
return SaveSwapDealInternal(unwindData, eventType, clientCashId, eventResason, approve);
|
||||
}
|
||||
|
||||
/// <summary>保存所有变更(生产: DbContext.SaveChanges;测试: 空操作)</summary>
|
||||
protected virtual void SaveAllChanges()
|
||||
{
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>在事务中执行(生产: BeginTransaction/Commit/Rollback;测试: 直接执行不包事务)</summary>
|
||||
protected virtual void ExecuteInTransaction(Action action)
|
||||
{
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
try
|
||||
{
|
||||
action();
|
||||
trans.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
trans.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
trans.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>保存互换交易资金记录(生产: new ClientCashInCashOutService;测试: 空操作)。
|
||||
/// 仅 SwapUnwind 全平仓且 NeedOpenFee=false 时调用。</summary>
|
||||
protected virtual void CallSaveSwapTradeClientCash(trade td, DateTime valueDate)
|
||||
{
|
||||
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
|
||||
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
|
||||
{
|
||||
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, valueDate, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>触发互换实时持仓计算(生产: Task.Run 异步 RealtimePnlCalc;测试: 空操作)。
|
||||
/// 仅 SwapUnwind 成功后调用。</summary>
|
||||
protected virtual void TriggerRealtimeSwapPosition()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFactory.GetLogger<SwapDealService>().Error("互换实时持仓服务计算失败", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public SwapDealService(OptUserInfo optUser) : base(optUser)
|
||||
{
|
||||
|
||||
@@ -990,24 +1067,23 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <exception cref="ServiceException"></exception>
|
||||
public void SwapUnwind(UnwindData unwindData)
|
||||
{
|
||||
var td = DbContext.trade.Find(unwindData.SwapTradeId);
|
||||
var td = FindTrade(unwindData.SwapTradeId);
|
||||
if (td == null)
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
bool cofirm = false;
|
||||
try
|
||||
ExecuteInTransaction(() =>
|
||||
{
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
|
||||
int clientCashId = AddClientCash(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
|
||||
if (unwindData.SwapMarginAmount != 0)
|
||||
{
|
||||
AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate);
|
||||
AddClientCash(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate);
|
||||
}
|
||||
//if (unwindData.SwapMarginRebatePnl != 0)
|
||||
//{
|
||||
// AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
// AddClientCash(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
//}
|
||||
DealFloatPosition(unwindData);
|
||||
var flowList = new List<swap_flow_event>(unwindData.FlowEvents);
|
||||
@@ -1015,11 +1091,7 @@ namespace YLErp.Modules.SwapModule
|
||||
if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓 || unwindData.ClosePercent == 1)
|
||||
{
|
||||
td.TradeStatus = "已平仓";
|
||||
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
|
||||
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
|
||||
{
|
||||
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, unwindData.ValueDate, 0);
|
||||
}
|
||||
CallSaveSwapTradeClientCash(td, unwindData.ValueDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1028,32 +1100,12 @@ namespace YLErp.Modules.SwapModule
|
||||
td.UnWindDate = unwindData.UnwindDate;
|
||||
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
|
||||
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
|
||||
DbContext.SaveChanges();
|
||||
trans.Commit();
|
||||
SaveAllChanges();
|
||||
cofirm = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans.Rollback();
|
||||
throw new Exception(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
trans.Dispose();
|
||||
}
|
||||
});
|
||||
if (cofirm)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFactory.GetLogger<SwapDealService>().Error("互换实时持仓服务计算失败", ex);
|
||||
}
|
||||
});
|
||||
TriggerRealtimeSwapPosition();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1490,20 +1542,18 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <exception cref="ServiceException"></exception>
|
||||
public void SwapIncome(UnwindData unwindData)
|
||||
{
|
||||
var td = DbContext.trade.Find(unwindData.SwapTradeId);
|
||||
var td = FindTrade(unwindData.SwapTradeId);
|
||||
if (td == null)
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
bool confirm = false;
|
||||
try
|
||||
ExecuteInTransaction(() =>
|
||||
{
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
|
||||
int clientCashId = AddClientCash(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
|
||||
if (unwindData.SwapMarginRebatePnl != 0)
|
||||
{
|
||||
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
AddClientCash(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
}
|
||||
foreach (var item in unwindData.FlowEvents)
|
||||
{
|
||||
@@ -1517,19 +1567,8 @@ namespace YLErp.Modules.SwapModule
|
||||
td.TradeStatus = "已到期";
|
||||
}
|
||||
td.UnWindDate = unwindData.UnwindDate;
|
||||
DbContext.SaveChanges();
|
||||
trans.Commit();
|
||||
confirm = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
trans.Dispose();
|
||||
}
|
||||
SaveAllChanges();
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// 互换/平仓审核通过
|
||||
@@ -1548,20 +1587,16 @@ namespace YLErp.Modules.SwapModule
|
||||
swapEvent.unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
||||
var flowList = DbContext.swap_flow_event.Where(x => x.EventId == swapEvent.id).ToList();
|
||||
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-swapEvent.unwindData.SwapRealizedPnL), action, swapEvent.unwindData.ValueDate);
|
||||
int clientCashId = AddClientCash(td, Convert.ToDouble(-swapEvent.unwindData.SwapRealizedPnL), action, swapEvent.unwindData.ValueDate);
|
||||
if (swapEvent.unwindData.SwapMarginAmount != 0)
|
||||
{
|
||||
AddClientCashInCashOut(td, Convert.ToDouble(swapEvent.unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, swapEvent.unwindData.ValueDate);
|
||||
AddClientCash(td, Convert.ToDouble(swapEvent.unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, swapEvent.unwindData.ValueDate);
|
||||
}
|
||||
swapEvent.ClientCashId = clientCashId;
|
||||
if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓)
|
||||
{
|
||||
td.TradeStatus = "已平仓";
|
||||
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
|
||||
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
|
||||
{
|
||||
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, swapEvent.unwindData.ValueDate, 0);
|
||||
}
|
||||
CallSaveSwapTradeClientCash(td, swapEvent.unwindData.ValueDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1618,7 +1653,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// </summary>
|
||||
/// <param name="swap_Deal"></param>
|
||||
/// <param name="eventType"></param>
|
||||
private long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
|
||||
private long SaveSwapDealInternal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
|
||||
{
|
||||
var td = DbContext.trade.Find(unwindData.SwapTradeId);
|
||||
if (td == null)
|
||||
|
||||
Reference in New Issue
Block a user