test(swap): SwapFlowEventService seam改造+MergePageEventScenarioTest(6场景全绿)
借鉴 testable 分支,只取 seam(排除 PosiPnl/NetSettmentAmount 公式改动和 MergePageEvent 拆分), 填补当前分支对开平仓合成(MergePageEvent)零覆盖的空白。 SwapFlowEventService 新增11个 protected virtual seam: - FindTrade/FindTradeExtend/FindPositions/FindAndInvalidateFutureEvents - GetUnderlying/GetNextBusinessDay/ResolvePositionId - PersistEvents(替代InitEvent内联Add+SaveChanges)/BeginTransaction/CommitTransaction/RollbackTransaction MergePageEvent/InitEvent: private→protected virtual,内联DB调用替换为seam。 InitEvent 移除 DbContext.swap_flow_event.Add(转入PersistEvents,纯函数化)。 SwapTradeBaseService.UpdateDbOption: public void→public virtual(测试override用)。 新增测试(借鉴testable分支): - TestableSwapFlowEventService.cs(集中式共享包装类) - MergePageEventScenarioTest.cs(6场景全绿): Scenario1 单条无持仓开仓 / Scenario2 同向追加 / Scenario3 反向全平 Scenario4 反向部分平+开 / Scenario5 一开一平 / Scenario6 复杂组合 **未借鉴**:SearchPositionFlowEvent的PosiPnl/NetSettmentAmount公式改动(行为变更)、 MergePageEvent拆分ProcessSingleFlow等(非必需重构)。 SwapModule 169测试全绿(+6),无回归。
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Model;
|
||||
|
||||
namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 用构造数据覆盖 MergePageEvent 全部 6 种场景
|
||||
/// 不连数据库,纯内存,秒级运行
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class MergePageEventScenarioTest
|
||||
{
|
||||
private const int TradeId = 9001;
|
||||
private const string TradeNumber = "TEST-IS-202504240001";
|
||||
private const string UnderlyingCode = "220205.IB";
|
||||
private const long PositionId = 50001;
|
||||
|
||||
#region 场景1:单条流水 + 无持仓 → 纯开仓
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario1_SingleMerge_NoPosition_ShouldOpen()
|
||||
{
|
||||
var service = CreateService(positions: new List<swap_position>());
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 100000, AvgPrice: 1.0022m, Fee: 0, FeePending: 2000)
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.开仓, result[0].EventType);
|
||||
Assert.AreEqual(1, result[0].PositionType); // 多头
|
||||
Assert.AreEqual(100000, result[0].Quantity);
|
||||
Assert.AreEqual(1.0022m, result[0].TradingAmountAvg);
|
||||
Assert.AreEqual(0, result[0].MarkClosePnl); // 开仓无平仓盈亏
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 场景2:单条流水 + 同向持仓 → 追加开仓
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario2_SingleMerge_SameDirectionPosition_ShouldOpen()
|
||||
{
|
||||
var positions = new List<swap_position>
|
||||
{
|
||||
CreatePosition(PositionType: 1, Qty: 50000, GrossPrice: 0.99m)
|
||||
};
|
||||
var service = CreateService(positions);
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 30000, AvgPrice: 1.005m)
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.开仓, result[0].EventType);
|
||||
Assert.AreEqual(1, result[0].PositionType); // 同向多头
|
||||
Assert.AreEqual(30000, result[0].Quantity); // 新开仓数量
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 场景3:单条流水 + 反向持仓(全平) → 纯平仓
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario3_SingleMerge_OppositeFullClose_ShouldCloseOnly()
|
||||
{
|
||||
var positions = new List<swap_position>
|
||||
{
|
||||
CreatePosition(PositionType: 2, Qty: 100000, GrossPrice: 0.98m)
|
||||
};
|
||||
var service = CreateService(positions);
|
||||
// 买入100000,但持仓是空头100000 → 全部平仓
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 100000, AvgPrice: 1.01m)
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.平仓, result[0].EventType);
|
||||
Assert.AreEqual(2, result[0].PositionType); // 平空头
|
||||
Assert.AreEqual(100000, result[0].Quantity);
|
||||
|
||||
// 平仓盈亏 = (平仓均价 - 持仓期初价) * 平仓数量 * 合约乘数
|
||||
var expectedPnl = (1.01m - 0.98m) * 100000 * 1;
|
||||
AssertDecimalEqual(expectedPnl, result[0].MarkClosePnl, 0.01m);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 场景4:单条流水 + 反向持仓(部分平) → 平仓+开仓
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario4_SingleMerge_OppositePartialClose_ShouldCloseAndOpen()
|
||||
{
|
||||
var positions = new List<swap_position>
|
||||
{
|
||||
CreatePosition(PositionType: 2, Qty: 30000, GrossPrice: 0.98m)
|
||||
};
|
||||
var service = CreateService(positions);
|
||||
// 买入100000,持仓空头30000 → 先平30000,再开70000
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 100000, AvgPrice: 1.01m)
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
Assert.AreEqual(2, result.Count);
|
||||
|
||||
// 第一个:平仓
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.平仓, result[0].EventType);
|
||||
Assert.AreEqual(2, result[0].PositionType);
|
||||
Assert.AreEqual(30000, result[0].Quantity);
|
||||
|
||||
var expectedClosePnl = (1.01m - 0.98m) * 30000 * 1;
|
||||
AssertDecimalEqual(expectedClosePnl, result[0].MarkClosePnl, 0.01m);
|
||||
|
||||
// 第二个:开仓
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.开仓, result[1].EventType);
|
||||
Assert.AreEqual(1, result[1].PositionType); // 剩余方向=买
|
||||
Assert.AreEqual(70000, result[1].Quantity); // 100000 - 30000
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 场景5:两条流水 + 无持仓 → 一开一平
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario5_TwoMerges_NoPosition_ShouldOpenThenClose()
|
||||
{
|
||||
var service = CreateService(positions: new List<swap_position>());
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 100000, AvgPrice: 1.00m), // 买 10万
|
||||
CreateMerge(BsType: 2, Qty: 30000, AvgPrice: 1.01m) // 卖 3万
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
Assert.AreEqual(2, result.Count);
|
||||
|
||||
// 第一个事件:开仓(大的那条)
|
||||
var openEvt = result.First(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓);
|
||||
Assert.AreEqual(1, openEvt.PositionType);
|
||||
Assert.AreEqual(100000, openEvt.Quantity);
|
||||
|
||||
// 第二个事件:平仓(小的那条平大的)
|
||||
var closeEvt = result.First(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓);
|
||||
Assert.AreEqual(1, closeEvt.PositionType); // 平的是多头的方向
|
||||
Assert.AreEqual(30000, closeEvt.Quantity);
|
||||
|
||||
// 平仓盈亏 = (卖均价 - 买均价) * 平仓数量 * 合约乘数
|
||||
var expectedPnl = (1.01m - 1.00m) * 30000 * 1;
|
||||
AssertDecimalEqual(expectedPnl, closeEvt.MarkClosePnl, 0.01m);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 场景6:两条流水 + 有持仓 → 复杂组合
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario6_TwoMerges_HasPosition_ShouldCloseThenOpen()
|
||||
{
|
||||
var positions = new List<swap_position>
|
||||
{
|
||||
CreatePosition(PositionType: 2, Qty: 30000, GrossPrice: 0.98m) // 空头持仓
|
||||
};
|
||||
var service = CreateService(positions);
|
||||
// 买10万 + 卖5万,持仓空头3万
|
||||
var merges = new List<swap_flow_merge>
|
||||
{
|
||||
CreateMerge(BsType: 1, Qty: 100000, AvgPrice: 1.00m), // 买(反向)
|
||||
CreateMerge(BsType: 2, Qty: 50000, AvgPrice: 1.01m) // 卖(同向)
|
||||
};
|
||||
|
||||
var result = service.ExecuteMergePageEvent(TradeId, merges, new DateTime(2025, 4, 24));
|
||||
|
||||
// 至少有平仓事件(买的10万 vs 空头3万)
|
||||
Assert.IsTrue(result.Count >= 2, $"Expected at least 2 events, got {result.Count}");
|
||||
|
||||
// 第一个事件应该是平仓(反向流水平空头持仓)
|
||||
Assert.AreEqual((int)SwapFlowEventTypeEnum.平仓, result[0].EventType);
|
||||
Assert.AreEqual(2, result[0].PositionType); // 平空头
|
||||
Assert.AreEqual(30000, result[0].Quantity);
|
||||
|
||||
// 后续应有开仓事件(100000-30000=70000剩余,再和卖5万处理)
|
||||
var openEvents = result.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓).ToList();
|
||||
Assert.IsTrue(openEvents.Count >= 1, "Should have at least 1 open event");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
private TestableSwapFlowEventService CreateService(List<swap_position> positions)
|
||||
{
|
||||
var user = new OptUserInfo(1, "Test", OptUserFrom.UnitTest);
|
||||
var trade = new trade
|
||||
{
|
||||
id = TradeId,
|
||||
TradeNumber = TradeNumber,
|
||||
TradeDate = new DateTime(2025, 4, 24),
|
||||
ExerciseDate = new DateTime(2025, 6, 20)
|
||||
};
|
||||
var extend = new trade_extend { TradeId = TradeId };
|
||||
extend.ExtendJson = Newtonsoft.Json.JsonConvert.SerializeObject(new TradeExtendJson
|
||||
{
|
||||
Direction = 2, // 支付
|
||||
SettlementRules = 1 // T+1
|
||||
});
|
||||
|
||||
var underlyings = new Dictionary<string, underlying_manager>
|
||||
{
|
||||
[UnderlyingCode] = new underlying_manager
|
||||
{
|
||||
UnderlyingCode = UnderlyingCode,
|
||||
UnderlyingInstrumentType = "TBonds"
|
||||
}
|
||||
};
|
||||
|
||||
return new TestableSwapFlowEventService(
|
||||
user, trade, extend, positions, underlyings,
|
||||
nextBusinessDay: d => d.AddDays(1),
|
||||
positionId: PositionId
|
||||
);
|
||||
}
|
||||
|
||||
private swap_flow_merge CreateMerge(int BsType, decimal Qty, decimal AvgPrice, decimal Fee = 0, decimal FeePending = 0)
|
||||
{
|
||||
return new swap_flow_merge
|
||||
{
|
||||
SwapTradeId = TradeId,
|
||||
SwapTradeNo = TradeNumber,
|
||||
UnderlyingCode = UnderlyingCode,
|
||||
OccurTime = new DateTime(2025, 4, 24),
|
||||
BsType = BsType,
|
||||
TradingQty = BsType == 1 ? Qty : -Qty,
|
||||
TradingAmount = Math.Abs(Qty) * AvgPrice,
|
||||
TradingAmountAvg = AvgPrice,
|
||||
TradingAmountFeeAvg = AvgPrice,
|
||||
TradingFee = Fee,
|
||||
TradingFeePending = FeePending,
|
||||
ContractSize = 1,
|
||||
ClientId = 10,
|
||||
DataState = 1
|
||||
};
|
||||
}
|
||||
|
||||
private swap_position CreatePosition(int PositionType, decimal Qty, decimal GrossPrice)
|
||||
{
|
||||
return new swap_position
|
||||
{
|
||||
PositionId = PositionId,
|
||||
SwapTradeId = TradeId,
|
||||
UnderlyingCode = UnderlyingCode,
|
||||
PositionType = PositionType,
|
||||
PosiDirection = 2, // 支付
|
||||
PosiQuantity = Qty,
|
||||
PosiGrossPrice = GrossPrice,
|
||||
PosiNetPrice = GrossPrice,
|
||||
ContractSize = 1,
|
||||
IsInitial = false,
|
||||
Invalid = false,
|
||||
PosiTradingFee = 0
|
||||
};
|
||||
}
|
||||
|
||||
private static void AssertDecimalEqual(decimal expected, decimal actual, decimal tolerance)
|
||||
{
|
||||
var diff = Math.Abs(expected - actual);
|
||||
Assert.IsTrue(diff <= tolerance,
|
||||
$"Expected {expected}, got {actual}, diff {diff} (tol {tolerance})");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Model;
|
||||
using YLErp.Modules.SwapModule;
|
||||
|
||||
namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 可测试的 SwapFlowEventService 子类
|
||||
/// override 所有外部依赖方法,用内存数据替代数据库和静态调用
|
||||
/// </summary>
|
||||
public class TestableSwapFlowEventService : SwapFlowEventService
|
||||
{
|
||||
private readonly trade _trade;
|
||||
private readonly trade_extend _tradeExtend;
|
||||
private readonly List<swap_position> _positions;
|
||||
private readonly Dictionary<string, underlying_manager> _underlyings;
|
||||
private readonly Func<DateTime, DateTime> _nextBusinessDay;
|
||||
private readonly long _positionId;
|
||||
|
||||
public List<swap_flow_event> PersistedEvents { get; } = new List<swap_flow_event>();
|
||||
|
||||
public TestableSwapFlowEventService(
|
||||
OptUserInfo optUser,
|
||||
trade trade,
|
||||
trade_extend tradeExtend,
|
||||
List<swap_position> positions,
|
||||
Dictionary<string, underlying_manager> underlyings,
|
||||
Func<DateTime, DateTime> nextBusinessDay,
|
||||
long positionId = 999999
|
||||
) : base(optUser)
|
||||
{
|
||||
_trade = trade;
|
||||
_tradeExtend = tradeExtend;
|
||||
_positions = positions ?? new List<swap_position>();
|
||||
_underlyings = underlyings ?? new Dictionary<string, underlying_manager>();
|
||||
_nextBusinessDay = nextBusinessDay ?? (d => d.AddDays(1));
|
||||
_positionId = positionId;
|
||||
}
|
||||
|
||||
protected override trade FindTrade(int swapTradeId) => _trade;
|
||||
|
||||
protected override trade_extend FindTradeExtend(int swapTradeId) => _tradeExtend;
|
||||
|
||||
protected override List<swap_position> FindPositions(int swapTradeId) => _positions;
|
||||
|
||||
protected override List<swap_flow_event> FindAndInvalidateFutureEvents(int swapTradeId, DateTime tradeDate)
|
||||
{
|
||||
// 测试环境:不需要废弃历史事件
|
||||
return new List<swap_flow_event>();
|
||||
}
|
||||
|
||||
protected override underlying_manager GetUnderlying(string underlyingCode)
|
||||
{
|
||||
return _underlyings.TryGetValue(underlyingCode, out var ul) ? ul : new underlying_manager { UnderlyingCode = underlyingCode };
|
||||
}
|
||||
|
||||
protected override DateTime GetNextBusinessDay(DateTime date) => _nextBusinessDay(date);
|
||||
|
||||
protected override long ResolvePositionId(swap_flow_merge merge, DateTime maturityDate, int direction, string tradeNumber) => _positionId;
|
||||
|
||||
protected override void PersistEvents(List<swap_flow_event> events)
|
||||
{
|
||||
// 不写 DB,收集到列表供验证
|
||||
PersistedEvents.AddRange(events);
|
||||
}
|
||||
|
||||
protected override IDisposable BeginTransaction() => new NoopDisposable();
|
||||
|
||||
protected override void CommitTransaction(IDisposable transaction) { }
|
||||
|
||||
protected override void RollbackTransaction(IDisposable transaction) { }
|
||||
|
||||
/// <summary>公开调用 protected 的 MergePageEvent,供测试使用</summary>
|
||||
public List<swap_flow_event> ExecuteMergePageEvent(int swapTradeId, List<swap_flow_merge> flowMergeList, DateTime tradeDate, bool needTrans = false)
|
||||
{
|
||||
return MergePageEvent(swapTradeId, flowMergeList, tradeDate, needTrans);
|
||||
}
|
||||
|
||||
public override void UpdateDbOption(DBModelBaseV2 dBModel)
|
||||
{
|
||||
// 测试环境不设 Opt 信息,避免依赖 UserInfo
|
||||
}
|
||||
|
||||
private class NoopDisposable : IDisposable
|
||||
{
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,53 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
}
|
||||
|
||||
#region 可测试化接缝(Seams)——借鉴 refactor-swap-event-testable 分支,override 可在测试中替换 DB/外部调用,生产代码行为不变
|
||||
|
||||
protected virtual trade FindTrade(int swapTradeId)
|
||||
=> DbContext.trade.Find(swapTradeId);
|
||||
|
||||
protected virtual trade_extend FindTradeExtend(int swapTradeId)
|
||||
=> DbContext.trade_extend.First(x => x.TradeId == swapTradeId);
|
||||
|
||||
protected virtual List<swap_position> FindPositions(int swapTradeId)
|
||||
=> DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.IsInitial && !x.Invalid).AsNoTracking().ToList();
|
||||
|
||||
protected virtual List<swap_flow_event> FindAndInvalidateFutureEvents(int swapTradeId, DateTime tradeDate)
|
||||
{
|
||||
var olds = DbContext.swap_flow_event.Where(x => x.EventDate > tradeDate && x.SwapTradeId == swapTradeId && x.DataState > 0);
|
||||
olds.ForEach(x => x.DataState = (int)SwapFlowDateStateEnum.废弃);
|
||||
return olds.ToList();
|
||||
}
|
||||
|
||||
protected virtual underlying_manager GetUnderlying(string underlyingCode)
|
||||
=> DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
||||
|
||||
protected virtual DateTime GetNextBusinessDay(DateTime date)
|
||||
=> QdpCalendarHelper.GetNonHoliday(date);
|
||||
|
||||
protected virtual long ResolvePositionId(swap_flow_merge merge, DateTime maturityDate, int direction, string tradeNumber)
|
||||
=> GetMaxPositionId(merge, maturityDate, direction, tradeNumber);
|
||||
|
||||
protected virtual void PersistEvents(List<swap_flow_event> events)
|
||||
{
|
||||
foreach (var evt in events)
|
||||
{
|
||||
DbContext.swap_flow_event.Add(evt);
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
protected virtual IDisposable BeginTransaction()
|
||||
=> DbContext.Database.BeginTransaction();
|
||||
|
||||
protected virtual void CommitTransaction(IDisposable transaction)
|
||||
=> (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Commit();
|
||||
|
||||
protected virtual void RollbackTransaction(IDisposable transaction)
|
||||
=> (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Rollback();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 互换流水开平仓事件
|
||||
/// </summary>
|
||||
@@ -70,7 +117,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// 分页处理互换流水开平仓事件,暂时只按加权平均处理
|
||||
/// </summary>
|
||||
/// <param name="pageSize"></param>
|
||||
private List<swap_flow_event> MergePageEvent(int swapTradeId, List<swap_flow_merge> flowMergeList, DateTime tradeDate, bool needTrans = true)
|
||||
protected virtual List<swap_flow_event> MergePageEvent(int swapTradeId, List<swap_flow_merge> flowMergeList, DateTime tradeDate, bool needTrans = true)
|
||||
{
|
||||
List<swap_flow_event> flowEvents = new List<swap_flow_event>();
|
||||
//按照同一互换编码、标的、买卖方向排序,一条买,一条卖//会存在买卖不在同一页
|
||||
@@ -79,26 +126,22 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
return flowEvents;
|
||||
}
|
||||
var trade = DbContext.trade.Find(swapTradeId);
|
||||
var tradeExtend = DbContext.trade_extend.First(x => x.TradeId == swapTradeId);
|
||||
var trans = needTrans ? DbContext.Database.BeginTransaction() : null;
|
||||
var trade = FindTrade(swapTradeId);
|
||||
var tradeExtend = FindTradeExtend(swapTradeId);
|
||||
var trans = needTrans ? BeginTransaction() : null;
|
||||
try
|
||||
{
|
||||
var eodPositions = DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.IsInitial && !x.Invalid).AsNoTracking().ToList();//上一日终持仓信息
|
||||
var swapFlowEventOlds = DbContext.swap_flow_event.Where(x => x.EventDate > tradeDate && x.SwapTradeId == swapTradeId && x.DataState > 0);//废弃当前清算日期及之后的开平仓事件
|
||||
swapFlowEventOlds.ForEach(x =>
|
||||
{
|
||||
x.DataState = (int)SwapFlowDateStateEnum.废弃;
|
||||
});
|
||||
var eodPositions = FindPositions(swapTradeId);//上一日终持仓信息
|
||||
FindAndInvalidateFutureEvents(swapTradeId, tradeDate);//废弃当前清算日期及之后的开平仓事件
|
||||
var mergeUnderlyingGroup = flowquery.GroupBy(g => g.UnderlyingCode);
|
||||
int direction = tradeExtend.ExtendObj.Direction;
|
||||
foreach (var underlyingGroup in mergeUnderlyingGroup)
|
||||
{
|
||||
var mergeList = underlyingGroup.OrderByDescending(o => o.TradingQty).ToList();//先按数量最大的排序
|
||||
var flowMerge = mergeList.First();
|
||||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(flowMerge.UnderlyingCode);
|
||||
var underlying = GetUnderlying(flowMerge.UnderlyingCode);
|
||||
var matuirityDate = trade.ExerciseDate;
|
||||
var positionId = GetMaxPositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber);
|
||||
var positionId = ResolvePositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber);
|
||||
var payPosition = eodPositions.FirstOrDefault(x => x.PositionId == positionId);//浮动腿 日终持仓信息
|
||||
bool hasPayPosition = payPosition != null;//是否存在日终持仓
|
||||
if (mergeList.Count == 1)//只有一条流水
|
||||
@@ -208,12 +251,12 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
}
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
trans?.Commit();
|
||||
PersistEvents(flowEvents);
|
||||
if (trans != null) CommitTransaction(trans);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans?.Rollback();
|
||||
if (trans != null) RollbackTransaction(trans);
|
||||
throw new Exception(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
@@ -238,7 +281,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="PayFeeUnwindPnl">平仓浮动费用</param>
|
||||
/// <param name="allUnwind">是否完全平仓</param>
|
||||
/// <param name="settleRules">0 T+0 1 T+1</param>
|
||||
private swap_flow_event InitEvent(
|
||||
protected virtual swap_flow_event InitEvent(
|
||||
int eventType,
|
||||
swap_flow_merge flow_merge,
|
||||
int direction,
|
||||
@@ -287,7 +330,6 @@ namespace YLErp.Modules.SwapModule
|
||||
flow_Event.MarkClosePnl = PayMarkUnwindPnl;
|
||||
flow_Event.CloseFee = PayFeeUnwindPnl;
|
||||
flow_Event.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
||||
DbContext.swap_flow_event.Add(flow_Event);
|
||||
return flow_Event;
|
||||
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ namespace YLErp.Modules.SwapModule
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UpdateDbOption(DBModelBaseV2 dBModel)
|
||||
public virtual void UpdateDbOption(DBModelBaseV2 dBModel)
|
||||
{
|
||||
dBModel.OptTime = DateTime.Now;
|
||||
dBModel.OptName = UserName;
|
||||
|
||||
Reference in New Issue
Block a user