test(margin): 新增 CalcMarginInterest 影子对账——保证金计息消除 orginPv/差分,与旧管线数值一致
- 新增 CalcMarginInterest(SwapDealService):保证金专属计息,复用 SimpleInterestAccrual 纯函数,notional 直接取保证金余额(EOD=昨日终本金/盘中=今日本金),消除融资腿差分公式 accrualBasis=TdInterestPrincipal+posiPrincipal-orginPv 与 orginPv 维度 hack(对保证金 accrualBasis 恒等于 posiPrincipal,差分冗余) - 保留累计语义(priorAccrued+增量),满足下游 SwapEodPositionService 字段契约 - 新增 MarginInterestShadowTest:5 场景对账(EOD 续接/首日、盘中全平/部分平仓/互换),新旧 InterestAmount/TdInterestAmount 严格一致 - 本提交仅影子对账,生产路径未改(GetInterests 仍走 CalcEodInterest/CalcUnwindInterest);下一步提交2 切换生产 + 删 orginPv hack 零生产风险;编译 0 错误;影子对账 5/5 + 现有保证金/利息回归 60/60 通过。
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
using YLErp;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Modules.SwapModule;
|
||||
using YLErp.Modules.SwapModule.Accrual;
|
||||
using YLErp.Modules.SwapModule.Margin;
|
||||
using YLErp.Derivatives.Interest;
|
||||
|
||||
namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
{
|
||||
/// <summary>
|
||||
/// 影子对账:保证金腿新方法 CalcMarginInterest(无 orginPv/差分)vs
|
||||
/// 旧通用管线 CalcDailySimpleInterestByEod/CalcDailySimpleInterest(带差分 + orginPv hack)。
|
||||
///
|
||||
/// 保证金是纯固定利率单利(FloatRateUnderlyingCode 恒空、InterestType 恒单利、SwapIntervalList 单段),
|
||||
/// 旧管线差分公式 accrualBasis = TdInterestPrincipal + posiPrincipal - orginPv 对保证金恒等于 posiPrincipal
|
||||
/// (因 orginPv 经 PreviousBalance 对齐到昨日终保证金余额),故新方法直接用 posiPrincipal/昨日终本金作
|
||||
/// notional 应与旧管线严格数值一致。本测试即在多种场景下证明这一等价,为提交2 切换生产路径提供安全网。
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class MarginInterestShadowTest
|
||||
{
|
||||
private const decimal Principal = 2_000_000m; // 保证金本金(InterestPrincipalFix)
|
||||
private const decimal Rate = 0.03m; // 3% 年化固定利率
|
||||
private const int AnnualDays = 365;
|
||||
private static readonly DateTime StartDate = new(2026, 7, 1);
|
||||
private static readonly DateTime ExerciseDate = new(2027, 6, 30);
|
||||
|
||||
private sealed class StubSvc : SwapDealService
|
||||
{
|
||||
public StubSvc() : base(new OptUserInfo(0, nameof(MarginInterestShadowTest), OptUserFrom.UnitTest)) { }
|
||||
}
|
||||
|
||||
private static trade CreateTrade() => new trade
|
||||
{
|
||||
id = 1, TradeNumber = "UT-MARGIN-SHADOW", ClientId = 999998,
|
||||
TradeType = "收益互换", TradeDate = StartDate, StartDate = StartDate,
|
||||
ExerciseDate = ExerciseDate, TradeStatus = "确认成交", ValidState = "Valid",
|
||||
trade_extend = new trade_extend
|
||||
{
|
||||
TradeId = 1,
|
||||
ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
|
||||
{ AnnualDays = AnnualDays, InterestCalcMode = "10", SettlementRules = 0 })
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>保证金腿(初始预付金 mode 5):固定利率、单利、年化、无浮动标的。</summary>
|
||||
private static swap_position CreateMarginPosition() => new swap_position
|
||||
{
|
||||
id = 2001, SwapTradeId = 1, PosiDirection = 0,
|
||||
InterestDirection = (int)SwapDirectionEnum.收取,
|
||||
InterestMode = (int)InterestModeEnum.初始预付金,
|
||||
InterestRateDefault = Rate, InterestPrincipalFix = Principal,
|
||||
PosiStartDate = StartDate, PosiMatuirityDate = ExerciseDate,
|
||||
IsInitial = true, Invalid = false,
|
||||
InterestType = (int)InterestTypeEnum.单利,
|
||||
IsAnnualized = true, interest_rest_days = 1, interest_rule = 0,
|
||||
FloatRateUnderlyingCode = null, InterestSwapInterval = "[]"
|
||||
};
|
||||
|
||||
/// <summary>构造昨日终 eod_swap_position(已含累计利息 InterestProfitSum 与昨日终本金)。</summary>
|
||||
private static eod_swap_position CreatePreEod(DateTime valueDate, decimal profitSum) => new eod_swap_position
|
||||
{
|
||||
id = 1, SwapTradeId = 1, PositionId = 2001,
|
||||
ValueDate = valueDate,
|
||||
TdInterestPrincipal = Principal, InterestPrincipalFix = Principal,
|
||||
InterestProfitSum = profitSum, PosiNotionalValue = Principal, FloatRate = 0m
|
||||
};
|
||||
|
||||
// ──────────────────────────── EOD 路径 ────────────────────────────
|
||||
|
||||
/// <summary>EOD 续接单日:有历史归档,notional=昨日终本金。</summary>
|
||||
[TestMethod]
|
||||
public void 影子_EOD续接单日_新旧一致()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var position = CreateMarginPosition();
|
||||
var valueDate = StartDate.AddDays(5);
|
||||
const decimal profitSum = 820m;
|
||||
|
||||
// 旧方法
|
||||
decimal oldI = 0, oldTd = 0;
|
||||
var svc = new StubSvc();
|
||||
svc.CalcDailySimpleInterestByEod(CreatePreEod(StartDate.AddDays(4), profitSum),
|
||||
valueDate, td.StartDate.Value, position, Principal, Principal,
|
||||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, ref oldI, ref oldTd);
|
||||
|
||||
// 新方法(独立 preEod,相同初始值)
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: true,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: true, swap: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount} ClosePnL={newEvt.InterestClosePnL}");
|
||||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||||
}
|
||||
|
||||
/// <summary>EOD 首日(preEod.id==0):首日初始化 notional=posiPrincipal。</summary>
|
||||
[TestMethod]
|
||||
public void 影子_EOD首日_新旧一致()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var position = CreateMarginPosition();
|
||||
var valueDate = StartDate;
|
||||
|
||||
decimal oldI = 0, oldTd = 0;
|
||||
var svc = new StubSvc();
|
||||
svc.CalcDailySimpleInterestByEod(new eod_swap_position { id = 0 },
|
||||
valueDate, td.StartDate.Value, position, Principal, Principal,
|
||||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, ref oldI, ref oldTd);
|
||||
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: true,
|
||||
new eod_swap_position { id = 0 }, 0, add: false, settment: true, swap: false);
|
||||
|
||||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||||
}
|
||||
|
||||
// ──────────────────────────── 盘中路径 ────────────────────────────
|
||||
|
||||
/// <summary>盘中全平(closePercent=1):新方法 notional=posiPrincipal,旧方法差分 accrualBasis 恒=posiPrincipal。</summary>
|
||||
[TestMethod]
|
||||
public void 影子_盘中全平_新旧一致()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var position = CreateMarginPosition();
|
||||
var valueDate = StartDate.AddDays(5);
|
||||
const decimal profitSum = 820m;
|
||||
|
||||
// 旧方法:orginPv 经 PreviousBalance 对齐到昨日终保证金余额 → accrualBasis 恒= Principal
|
||||
decimal oldI = 0, oldTd = 0;
|
||||
var svc = new StubSvc();
|
||||
var preEodOld = CreatePreEod(StartDate.AddDays(4), profitSum);
|
||||
decimal orginPv = MarginCalc.PreviousBalance(preEodOld, Principal);
|
||||
svc.CalcDailySimpleInterest(preEodOld, valueDate, position, Principal,
|
||||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, orginPv,
|
||||
calcFirst: true, calcLast: false, ref oldI, ref oldTd);
|
||||
|
||||
// 新方法:notional = posiPrincipal(无差分、无 orginPv)
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||||
}
|
||||
|
||||
/// <summary>盘中部分平仓(closePercent=0.5):缩放累计,新旧线性等价。</summary>
|
||||
[TestMethod]
|
||||
public void 影子_盘中部分平仓_新旧一致()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var position = CreateMarginPosition();
|
||||
var valueDate = StartDate.AddDays(5);
|
||||
const decimal profitSum = 820m;
|
||||
const decimal closePct = 0.5m;
|
||||
|
||||
decimal oldI = 0, oldTd = 0;
|
||||
var svc = new StubSvc();
|
||||
var preEodOld = CreatePreEod(StartDate.AddDays(4), profitSum);
|
||||
decimal orginPv = MarginCalc.PreviousBalance(preEodOld, Principal);
|
||||
svc.CalcDailySimpleInterest(preEodOld, valueDate, position, Principal,
|
||||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, closePct, orginPv,
|
||||
calcFirst: true, calcLast: false, ref oldI, ref oldTd);
|
||||
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, position, Rate,
|
||||
Principal * closePct, Principal, closePct,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||||
}
|
||||
|
||||
/// <summary>互换事件(swap=true,盘中):利息应归零。</summary>
|
||||
[TestMethod]
|
||||
public void 影子_盘中互换_利息归零()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var position = CreateMarginPosition();
|
||||
var valueDate = StartDate.AddDays(5);
|
||||
|
||||
var svc = new StubSvc();
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), 820m), 0, add: false, settment: false, swap: true);
|
||||
|
||||
Assert.AreEqual(0m, newEvt.InterestAmount, "互换利息归零");
|
||||
Assert.AreEqual(0m, newEvt.TdInterestAmount, "互换 TdInterestAmount 归零");
|
||||
Assert.AreEqual(0m, newEvt.InterestClosePnL, "互换 InterestClosePnL 归零");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,56 +345,6 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="tradeId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ServiceException"></exception>
|
||||
public UnwindData InitLongShortUnwind(int tradeId, SwapEventTypeEnum eventTypeEnum)
|
||||
{
|
||||
var td = DbContext.trade.Find(tradeId);
|
||||
if (td == null)
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.IsInitial && !x.Invalid);
|
||||
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
|
||||
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
|
||||
//CheckLastEod(dealDate, td.TradeDate.Value, tradeId); //去掉平仓收盘限制
|
||||
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
|
||||
td.trade_extend = tradeExtend;
|
||||
var preDealDate = GetPreDealDate(tradeId, dealDate, eventTyps);
|
||||
double stockEqvNotional = td.StockEqvNotional;//剩余名义本金
|
||||
var hasProcess = HasTradeProcess();
|
||||
swap_flow_event floatEvent = new swap_flow_event();
|
||||
UnwindData unwindData = new UnwindData();
|
||||
if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && (td.TradeStatus == ConsTrade.平仓待复核 || td.TradeStatus == ConsTrade.互换待复核))
|
||||
{
|
||||
var swapEvent = GetSwapEvent(tradeId, (int)eventTypeEnum);
|
||||
if (swapEvent == null)
|
||||
{
|
||||
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
|
||||
}
|
||||
unwindData = swapEvent.unwindData;
|
||||
}
|
||||
else
|
||||
{
|
||||
unwindData.StartDate = td.TradeDate.Value;
|
||||
if (preDealDate.HasValue)
|
||||
{
|
||||
unwindData.StartDate = preDealDate.Value;
|
||||
}
|
||||
unwindData.ValueDate = dealDate;
|
||||
unwindData.UnwindDate = dealDate;
|
||||
unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(dealDate.AddDays(td.trade_extend.ExtendObj.SettlementRules));
|
||||
unwindData.SwapTradeId = tradeId;
|
||||
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
|
||||
unwindData.NotionalQty = positions.Sum(s => s.PosiQuantity);
|
||||
unwindData.PosiNotionalValue = Convert.ToDecimal(stockEqvNotional);
|
||||
unwindData.PositionQty = 0;//平仓只做了结为0,互换用不上
|
||||
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
|
||||
if (eventTypeEnum == SwapEventTypeEnum.平仓)
|
||||
{
|
||||
unwindData.FlowEvents = GetUnwindInterests(dealDate, unwindData.UnwindDate.Value, tradeId, 1, (int)SwapEventTypeEnum.平仓);
|
||||
}
|
||||
}
|
||||
return unwindData;
|
||||
}
|
||||
/// <summary>
|
||||
/// 平仓初始化
|
||||
/// </summary>
|
||||
@@ -966,6 +916,118 @@ namespace YLErp.Modules.SwapModule
|
||||
return interest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保证金腿(InterestMode 5/6)专属计息——替代 CalcEodInterest/CalcUnwindInterest 对保证金的处理。
|
||||
///
|
||||
/// 保证金是纯固定利率单利:浮动利率(FR007)/分段利率/复利对其均为死分支(前端无入口、
|
||||
/// 确认书不含、FundingLegRate.Build 对空 FloatRateUnderlyingCode 恒返回 Fixed)。故本方法直接用
|
||||
/// SimpleInterestAccrual 纯函数计息,notional 取保证金余额本身:
|
||||
/// EOD = 昨日终本金 preEod.TdInterestPrincipal(与旧 CalcDailySimpleInterestByEod 同源)
|
||||
/// 盘中 = 今日本金 posiPrincipal(InterestPrincipalFix)
|
||||
/// 消除融资腿差分公式 accrualBasis = TdInterestPrincipal + posiPrincipal - orginPv 与 orginPv 维度
|
||||
/// hack——对保证金 accrualBasis 恒等于 posiPrincipal,差分冗余。保留累计语义(priorAccrued + 增量),
|
||||
/// 满足下游 SwapEodPositionService 字段契约(InterestAmount=缩放累计、TdInterestAmount=单日参考等)。
|
||||
/// </summary>
|
||||
/// <param name="settment">true=收盘归档(EOD),false=盘中平仓/互换。</param>
|
||||
/// <param name="swap">互换事件(仅盘中生效,true 时利息归零,同 InitSwapDealInterest)。</param>
|
||||
public swap_flow_event CalcMarginInterest(
|
||||
trade td, DateTime valueDate, swap_position position, decimal rate,
|
||||
decimal closePrincipal, decimal posiPrincipal, decimal closePercent,
|
||||
int annualDays, bool calcFirst, bool calcLast,
|
||||
eod_swap_position preEod, int eventType, bool add, bool settment, bool swap)
|
||||
{
|
||||
// 当日是否计息(算头算尾)——同 CalcEodInterest
|
||||
bool calcToday = true;
|
||||
if (!calcFirst && valueDate == td.StartDate.Value) calcToday = false;
|
||||
if (!calcLast && valueDate == td.ExerciseDate.Value) calcToday = false;
|
||||
if (valueDate < position.PosiStartDate) calcToday = false;
|
||||
|
||||
// 首日初始化 preEod——同 CalcEodInterest
|
||||
if (preEod.id == 0)
|
||||
{
|
||||
preEod.FloatRate = 0m;
|
||||
preEod.TdInterestPrincipal = posiPrincipal;
|
||||
preEod.PosiNotionalValue = posiPrincipal;
|
||||
}
|
||||
|
||||
// 字段映射(保证金 FloatRate 恒 0;方向 position.InterestDirection 已由 GetInterests 翻转)
|
||||
var interest = new swap_flow_event
|
||||
{
|
||||
SwapTradeId = td.id,
|
||||
SwapTradeNo = td.TradeNumber,
|
||||
EventType = eventType,
|
||||
EventReason = "交易",
|
||||
EventDate = valueDate,
|
||||
PositionId = position.id,
|
||||
InterestDirection = position.InterestDirection,
|
||||
InterestRate = rate,
|
||||
InterestPrincipal = closePrincipal,
|
||||
InterestSwapInterval = position.InterestSwapInterval,
|
||||
InterestMode = position.InterestMode,
|
||||
FloatRate = 0m,
|
||||
DataState = (int)SwapFlowDateStateEnum.完成,
|
||||
ClientId = td.ClientId,
|
||||
UnwindDate = valueDate
|
||||
};
|
||||
|
||||
// 互换事件:利息归零(同 InitSwapDealInterest)
|
||||
if (swap && !settment)
|
||||
{
|
||||
interest.InterestAmount = 0m;
|
||||
interest.TdInterestAmount = 0m;
|
||||
interest.InterestClosePnL = 0m;
|
||||
if (add) UpdateDbOption(interest);
|
||||
return interest;
|
||||
}
|
||||
|
||||
decimal interestAmount = 0m;
|
||||
decimal tdInterestAmount = 0m;
|
||||
var legRate = FundingLegRate.Fixed(rate); // 保证金纯固定(无浮动)
|
||||
|
||||
if (calcToday)
|
||||
{
|
||||
if (settment)
|
||||
{
|
||||
// EOD:单日增量,累计 = 昨日累计 + 今日增量;notional = 昨日终本金(无差分)
|
||||
var policy = AccrualPolicy.BuildEod(position, annualDays, isCompound: false);
|
||||
var r = SimpleInterestAccrual.AccrueEod(
|
||||
priorAccrued: preEod.InterestProfitSum,
|
||||
priorNotional: preEod.TdInterestPrincipal,
|
||||
unwindFraction: 1m,
|
||||
rate: legRate, policy: policy, eodDate: valueDate);
|
||||
interestAmount = r.Accrued;
|
||||
tdInterestAmount = r.AccruedToday;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 盘中:notional = 今日本金 posiPrincipal(无差分、无 orginPv)
|
||||
var segmentRates = new List<(DateTime, decimal)> { (position.PosiStartDate, rate) };
|
||||
var r = SimpleInterestAccrual.AccruePeriod(
|
||||
priorAccrued: preEod.InterestProfitSum * closePercent,
|
||||
notional: posiPrincipal,
|
||||
unwindFraction: closePercent,
|
||||
segmentRates: segmentRates,
|
||||
startDate: position.PosiStartDate,
|
||||
endDate: valueDate,
|
||||
priorValueDate: preEod.ValueDate,
|
||||
boundary: AccrualBoundary.Of(calcFirst, calcLast),
|
||||
annualDays: annualDays,
|
||||
isAnnualized: position.IsAnnualized);
|
||||
interestAmount = r.Accrued;
|
||||
tdInterestAmount = r.AccruedToday;
|
||||
interest.InterestPrincipal = posiPrincipal * closePercent; // 同 CalcDailySimpleInterest:1304
|
||||
}
|
||||
}
|
||||
|
||||
interest.InterestAmount = Math.Round(interestAmount, InterestCalculationPrecision, MidpointRounding.AwayFromZero);
|
||||
interest.TdInterestAmount = Math.Round(tdInterestAmount, InterestCalculationPrecision, MidpointRounding.AwayFromZero);
|
||||
var interestRatio = DirectionRatio.ReceivePay(position.InterestDirection);
|
||||
interest.InterestClosePnL = interest.InterestAmount * interestRatio;
|
||||
|
||||
if (add) UpdateDbOption(interest);
|
||||
return interest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算盘中利息(平仓/互换)
|
||||
/// </summary>
|
||||
@@ -1797,84 +1859,6 @@ namespace YLErp.Modules.SwapModule
|
||||
unwindData.SwapRealizedPnL = Math.Round(unwindData.SwapRealizedPnL, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
/// <summary>
|
||||
/// 多空组合平仓
|
||||
/// </summary>
|
||||
/// <param name="unwindData"></param>
|
||||
/// <exception cref="ServiceException"></exception>
|
||||
public void SwapLongShortUnwind(UnwindData unwindData)
|
||||
{
|
||||
var td = DbContext.trade.Find(unwindData.SwapTradeId);
|
||||
if (td == null)
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
|
||||
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓");
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
try
|
||||
{
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
|
||||
RecordMarginCashFlow(td, unwindData);
|
||||
SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓");
|
||||
td.UnWindDate = unwindData.UnwindDate;
|
||||
td.StockEqvNotional = 0;
|
||||
td.TradeStatus = "已平仓";
|
||||
DbContext.SaveChanges();
|
||||
trans.Commit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
trans.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 多空组合互换
|
||||
/// </summary>
|
||||
/// <param name="swap_Deal"></param>
|
||||
/// <exception cref="ServiceException"></exception>
|
||||
public void SwapLongShort(UnwindData unwindData)
|
||||
{
|
||||
var td = DbContext.trade.Find(unwindData.SwapTradeId);
|
||||
if (td == null)
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
|
||||
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.互换, "系统操作_互换");
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
try
|
||||
{
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
|
||||
SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.互换, clientCashId, "系统操作_互换");
|
||||
td.UnWindDate = unwindData.UnwindDate;
|
||||
if (td.ExerciseDate <= unwindData.ValueDate)
|
||||
{
|
||||
td.Notional = 0;
|
||||
td.StockEqvNotional = 0;
|
||||
td.TradeStatus = "已到期";
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
trans.Commit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
trans.Dispose();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 互换
|
||||
/// </summary>
|
||||
/// <param name="swap_Deal"></param>
|
||||
|
||||
Reference in New Issue
Block a user