EodSyntheticPriceSaveService 三处 DataSource="系统"、SwapFlowService 一处 DataSource="人工",均改为引用既有 EodPriceBase.系统/人工 常量,消除 eod 数据来源列值散落字面量。仅统一“数据来源”语义,不动无关的系统操作员名(OptName/UserName 等)字面量。
775 lines
37 KiB
C#
775 lines
37 KiB
C#
using BaseOUDAL;
|
|
using MoreLinq;
|
|
using NPOI.SS.Formula.Functions;
|
|
using OfficeOpenXml;
|
|
using Org.BouncyCastle.Ocsp;
|
|
using YieldChain.Security;
|
|
using YLErp.BLL.Eod;
|
|
using YLErp.DBModels;
|
|
using YLErp.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Model.Enum;
|
|
using YLErp.Modules.SwapModule.Dto;
|
|
using YLErp.Office;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.SwapModule
|
|
{
|
|
/// <summary>
|
|
/// 互换流水导入
|
|
/// </summary>
|
|
public class SwapFlowService : SwapTradeBaseService
|
|
{
|
|
public SwapFlowService(OptUserInfo optUser) : base(optUser)
|
|
{
|
|
|
|
}
|
|
public SwapFlowService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
|
|
}
|
|
/// <summary>
|
|
/// 查询今天是否有FR007的数据
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
|
|
public eod_commodity_future_price SearchTodayFRData(DateTime dateTime)
|
|
{
|
|
var data = DbContext.eod_commodity_future_price.Where(a => a.ValueDate == dateTime && a.UnderlyingCode == "FR007").FirstOrDefault();
|
|
if (data == null)
|
|
{
|
|
data = new eod_commodity_future_price();
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询选择的时间是否拥有FR007的数据
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
|
|
public List<eod_commodity_future_price> SearchdateFRData(List<DateTime> date)
|
|
{
|
|
var datafr007 = DbContext.eod_commodity_future_price.Where(a => date.Contains(a.ValueDate)).ToList();
|
|
return datafr007;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 删除的RF007数据
|
|
/// </summary>
|
|
/// <param name="id">要删除的RF007数据Id</param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public bool DeleteFRData(int id)
|
|
{
|
|
|
|
var frdata = DbContext.eod_commodity_future_price.Find(id);
|
|
if (frdata == null)
|
|
{
|
|
throw new ServiceException("未找到FR007流水");
|
|
}
|
|
DbContext.eod_commodity_future_price.Remove(frdata);
|
|
DbContext.SaveChanges();
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// 新增或者修改FR007数据
|
|
/// </summary>
|
|
/// <param name="price">FR007价格</param>
|
|
/// <param name="dateTime">新增或者修改时间</param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public bool AddOrUpdateFRdata(Double price, DateTime dateTime)
|
|
{
|
|
string beforedate = "";
|
|
var frdata = DbContext.eod_commodity_future_price.Where(a => a.ValueDate == dateTime && a.UnderlyingCode == "FR007").FirstOrDefault();
|
|
if (frdata == null)
|
|
{
|
|
frdata = new eod_commodity_future_price();
|
|
}
|
|
//修改
|
|
if (frdata != null && frdata?.UnderlyingCode != null)
|
|
{
|
|
frdata.ValueDate = dateTime;
|
|
frdata.HighPrice = 0;
|
|
frdata.LowPrice = 0;
|
|
beforedate = JsonHelper.Serialize(frdata);
|
|
}
|
|
else
|
|
{
|
|
//新增
|
|
var newestdata = DbContext.eod_commodity_future_price.OrderByDescending(a => a.ValueDate).FirstOrDefault();
|
|
if (newestdata == null)
|
|
{
|
|
var underlyingCode = DbContext.underlying_manager.Where(a => a.UnderlyingCode == "FR007").FirstOrDefault();
|
|
if (underlyingCode == null)
|
|
{
|
|
throw new ServiceException("找不到FR007的标的");
|
|
}
|
|
newestdata = new eod_commodity_future_price();
|
|
newestdata.UnderlyingId = underlyingCode.id;
|
|
}
|
|
frdata.ValueDate = dateTime;
|
|
frdata.UnderlyingCode = "FR007";
|
|
frdata.UnderlyingId = newestdata.UnderlyingId;
|
|
frdata.DataSource = EodPriceBase.人工;
|
|
DbContext.Add(frdata);
|
|
}
|
|
frdata.ClosePrice = Math.Round(price, 4);
|
|
frdata.SettlePrice = Math.Round(price, 4);
|
|
frdata.ReferencePrice = Math.Round(price, 4);
|
|
frdata.OptId = UserInfo.UserId;
|
|
frdata.OptName = UserInfo.UserName;
|
|
frdata.OptDate = DateTime.Now;
|
|
DbContext.SaveChanges();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询互换流水导入
|
|
/// </summary>
|
|
public SearchListResult<swap_flow> SearchList(SwapFlowQueryRequest req)
|
|
{
|
|
var predicate = PredicateBuilder.Create<swap_flow>(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成);
|
|
if (req.TradeDateStart.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.OccurTime >= req.TradeDateStart.Value);
|
|
}
|
|
if (req.TradeDateEnd.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.OccurTime <= req.TradeDateEnd.Value);
|
|
}
|
|
if (!string.IsNullOrEmpty(req.UnderlyingCode))
|
|
{
|
|
predicate = predicate.And(n => n.UnderlyingCode==req.UnderlyingCode);
|
|
}
|
|
if (req.TradeDate.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.OccurTime == req.TradeDate);
|
|
}
|
|
if (req.ClientId.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.ClientId == req.ClientId);
|
|
}
|
|
var flowquery = DbContext.swap_flow.Where(predicate);
|
|
if (string.IsNullOrEmpty(req.sidx))
|
|
{
|
|
req.sidx = "id";
|
|
req.sord = "desc";
|
|
}
|
|
var retListResult = flowquery.AsNoTracking().ToSearchList(req);
|
|
var trsIds= retListResult.rows.Where(x=>x.trs_deal_id>0).Select(r => r.trs_deal_id).ToList();
|
|
var underlyingCodes= retListResult.rows.Select(r => r.UnderlyingCode).ToList();
|
|
var riskChecks = DbContext.trade_risk_check_log.Where(x=> trsIds.Contains(x.flow_id)).ToList();
|
|
var underlyings=DbContext.underlying_manager.Where(x=>underlyingCodes.Contains(x.UnderlyingCode)).AsNoTracking().ToList();
|
|
foreach (var item in retListResult.rows)
|
|
{
|
|
var riskCheck = riskChecks.FirstOrDefault(x=>x.flow_id==item.trs_deal_id);
|
|
item.limit_alert_remark = riskCheck?.remark;
|
|
var um = underlyings.FirstOrDefault(x=>x.UnderlyingCode==item.UnderlyingCode);
|
|
if (um!=null&&um.IsBond())
|
|
{
|
|
// 入库小数(0.995)→展示报价(99.5),价格字段统一走 BondPriceConverter
|
|
item.TradingAmountAvg = BondPriceConverter.ToDisplay(item.TradingAmountAvg);
|
|
item.TradingAmountFeeAvg = BondPriceConverter.ToDisplay(item.TradingAmountFeeAvg);
|
|
item.TradingAmountNet = item.TradingAmountNet.HasValue ? BondPriceConverter.ToDisplay(item.TradingAmountNet.Value) : item.TradingAmountNet;
|
|
item.TradingAmountNetFee = item.TradingAmountNetFee.HasValue ? BondPriceConverter.ToDisplay(item.TradingAmountNetFee.Value) : item.TradingAmountNetFee;
|
|
// 数量÷100(手→万手展示),与价格维度无关,保留常量
|
|
item.TradingQty /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
}
|
|
return retListResult;
|
|
}
|
|
/// <summary>
|
|
/// 查询互换流水汇总
|
|
/// </summary>
|
|
public SearchListResult<SwapFlowQueryResponse> SearchMergeList(SwapFlowQueryRequest req)
|
|
{
|
|
var predicate = PredicateBuilder.Create<swap_flow_merge>(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成 && n.OccurTime == req.TradeDate);
|
|
var capitalQuery = DbContext.swap_fund_account.Where(x => x.Status == (int)SwapFoundAccountStatusEnum.正常);
|
|
if (!string.IsNullOrEmpty(req.TradeNumber))
|
|
{
|
|
capitalQuery = capitalQuery.Where(n => n.SwapTradeNo.Contains(req.TradeNumber.Trim()));
|
|
}
|
|
if (!string.IsNullOrEmpty(req.UnderlyingCode))
|
|
{
|
|
predicate = predicate.And(n => n.UnderlyingCode==req.UnderlyingCode);
|
|
}
|
|
if (req.ClientId.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.ClientId == req.ClientId);
|
|
}
|
|
var flowquery = DbContext.swap_flow_merge.Where(predicate);
|
|
var query = from flow in flowquery
|
|
join capital in capitalQuery on flow.FundAccount equals capital.FundAccount
|
|
select new SwapFlowQueryResponse
|
|
{
|
|
id = flow.id,
|
|
FundAccount = flow.FundAccount,
|
|
OccurTime = flow.OccurTime,
|
|
SwapTradeId = capital.SwapTradeId,
|
|
SwapTradeNo = capital.SwapTradeNo,
|
|
BsType = flow.BsType,
|
|
UnderlyingCode = flow.UnderlyingCode,
|
|
TradingQty = flow.TradingQty,
|
|
TradingAmount = flow.TradingAmount,
|
|
TradingFee = flow.TradingFee,
|
|
TradingAmountAvg = flow.TradingAmountAvg,
|
|
TradingAmountFeeAvg = flow.TradingAmountFeeAvg,
|
|
TradingAmountNet=flow.TradingAmountNetFeeAvg??0,
|
|
ContractSize = flow.ContractSize
|
|
};
|
|
if (string.IsNullOrEmpty(req.sidx))
|
|
{
|
|
req.sidx = "OccurTime,FundAccount,UnderlyingCode,BsType";
|
|
req.sord = "asc";
|
|
}
|
|
var retListResult = query.ToSearchList(req);
|
|
var underlyingCodes = retListResult.rows.Select(r => r.UnderlyingCode).ToList();
|
|
var underlyings = DbContext.underlying_manager.Where(x => underlyingCodes.Contains(x.UnderlyingCode)).AsNoTracking().ToList();
|
|
foreach (var item in retListResult.rows)
|
|
{
|
|
var um = underlyings.FirstOrDefault(x => x.UnderlyingCode == item.UnderlyingCode);
|
|
if (um != null && um.IsBond())
|
|
{
|
|
item.TradingAmountAvg = BondPriceConverter.ToDisplay(item.TradingAmountAvg);
|
|
item.TradingAmountFeeAvg = BondPriceConverter.ToDisplay(item.TradingAmountFeeAvg);
|
|
item.TradingAmountNet = BondPriceConverter.ToDisplay(item.TradingAmountNet);
|
|
item.TradingQty /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
}
|
|
return retListResult;
|
|
}
|
|
/// <summary>
|
|
/// 查询互换开平仓事件
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public SearchListResult<swap_flow_event> SearchEventList(SwapFlowQueryRequest req)
|
|
{
|
|
var predicate = PredicateBuilder.Create<swap_flow_event>(n =>n.PositionType>0&&n.DataState>0);
|
|
List<int> eventTypes = new List<int>() { (int)SwapEventTypeEnum.确认交易, (int)SwapEventTypeEnum.平仓 };
|
|
if (req.TradeDate.HasValue)
|
|
{
|
|
predicate = predicate.And(n=>n.EventDate==req.TradeDate);
|
|
}
|
|
if (req.TradeDateEnd.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.EventDate <= req.TradeDateEnd);
|
|
}
|
|
if (req.TradeDateStart.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.EventDate >= req.TradeDateStart);
|
|
}
|
|
if (!string.IsNullOrEmpty(req.TradeNumber))
|
|
{
|
|
predicate = predicate.And(n => n.SwapTradeNo.Contains(req.TradeNumber.Trim()));
|
|
}
|
|
if (!string.IsNullOrEmpty(req.UnderlyingCode))
|
|
{
|
|
predicate = predicate.And(n => n.UnderlyingCode.Contains(req.UnderlyingCode.Trim()));
|
|
}
|
|
if (req.ClientId.HasValue)
|
|
{
|
|
predicate = predicate.And(n => n.ClientId==req.ClientId);
|
|
}
|
|
var eventQuery =from se in DbContext.swap_flow_event.Where(predicate)
|
|
join t in DbContext.trade.Where(x=>x.ValidState==ConsGlobal.Valid) on se.SwapTradeId equals t.id
|
|
join s in DbContext.swap_event.Where(x => !x.Invalid && eventTypes.Contains(x.EventType)) on se.EventId equals s.id into stemp
|
|
from s in stemp.DefaultIfEmpty()
|
|
select se;
|
|
|
|
if (string.IsNullOrEmpty(req.sidx))
|
|
{
|
|
req.sidx = "PositionId,id";
|
|
req.sord = "asc";
|
|
}
|
|
var retListResult = eventQuery.ToSearchList(req);
|
|
List<trade_extend> extendList = null;
|
|
Dictionary<int, decimal?> ytmMap = new Dictionary<int, decimal?>();
|
|
if (retListResult != null && retListResult.rows != null && retListResult.rows.Any())
|
|
{
|
|
var tradeIds = retListResult.rows.Where(p => p.PayDate == null || p.EventType != (int)SwapEventTypeEnum.平仓).Select(p => p.SwapTradeId).Distinct().ToList();
|
|
if (tradeIds != null && tradeIds.Count > 0)
|
|
{
|
|
extendList = DbContext.trade_extend.AsNoTracking().Where(p => tradeIds.Contains(p.TradeId)).ToList();
|
|
// 查询成交收益率
|
|
ytmMap = DbContext.trade.AsNoTracking().Where(p => tradeIds.Contains(p.id)).ToList().ToDictionary(t => t.id, t => t.InitYtm);
|
|
}
|
|
|
|
|
|
}
|
|
if (extendList == null)
|
|
{
|
|
extendList = new List<trade_extend>();
|
|
}
|
|
foreach (var item in retListResult.rows)
|
|
{
|
|
item.InitYtm = ytmMap.GetValueOrDefault(item.SwapTradeId);
|
|
if (ConsGlobal.InstrumentType.IsBond(item.UnderlyingInstrumentType))
|
|
{
|
|
item.TradingAmountAvg = BondPriceConverter.ToDisplay(item.TradingAmountAvg);
|
|
item.TradingAmountFeeAvg = BondPriceConverter.ToDisplay(item.TradingAmountFeeAvg);
|
|
item.TradingAmountNetFeeAvg = item.TradingAmountNetFeeAvg.HasValue ? BondPriceConverter.ToDisplay(item.TradingAmountNetFeeAvg.Value) : item.TradingAmountNetFeeAvg;
|
|
item.TradingAmountNetAvg = item.TradingAmountNetAvg.HasValue ? BondPriceConverter.ToDisplay(item.TradingAmountNetAvg.Value) : item.TradingAmountNetAvg;
|
|
item.Quantity /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
if (item.PayDate == null || item.EventType != (int)SwapEventTypeEnum.平仓)
|
|
{
|
|
var extend = extendList.FirstOrDefault(p => p.TradeId == item.SwapTradeId);
|
|
if (extend != null&& item.PayDate == null)
|
|
{
|
|
item.PayDate = item.UnwindDate.Value.AddDays(extend.ExtendObj.SettlementRules);
|
|
}
|
|
}
|
|
}
|
|
return retListResult;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置流水
|
|
/// </summary>
|
|
/// <param name="clientId"></param>
|
|
/// <param name="valueDate"></param>
|
|
public void ResetFlows(int clientId,DateTime valueDate)
|
|
{
|
|
var flows= DbContext.swap_flow.Where(x=>x.ClientId==clientId&&x.OccurTime>=valueDate&&x.DataState==(int)SwapFlowDateStateEnum.完成);
|
|
var flowMerge= DbContext.swap_flow_merge.Where(x => x.ClientId == clientId && x.OccurTime >= valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成);
|
|
flows.ForEach(x =>
|
|
{
|
|
x.SwapTradeId = null;
|
|
x.SwapTradeNo = null;
|
|
x.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
|
});
|
|
DbContext.swap_flow_merge.RemoveRange(flowMerge);
|
|
DbContext.SaveChanges();
|
|
Task.Run(() =>
|
|
{
|
|
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 开平仓事件导出
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public byte[] exprotSwapFlowEventExcel(SwapFlowQueryRequest req)
|
|
{
|
|
//获取数据
|
|
var ret = SearchEventList(req);
|
|
List<SwapFlowEventExportModel> list = new List<SwapFlowEventExportModel>();
|
|
foreach (var item in ret.rows)
|
|
{
|
|
SwapFlowEventExportModel exportModel = new SwapFlowEventExportModel();
|
|
exportModel.EventDate = item.EventDate.OtcFormatDate();
|
|
exportModel.UnwindDate = item.UnwindDate.OtcFormatDate();
|
|
exportModel.PayDate = item.PayDate.OtcFormatDate();
|
|
exportModel.SwapTradeNo = item.SwapTradeNo;
|
|
exportModel.SwapPositionIdPadding = item.SwapPositionIdPadding;
|
|
exportModel.EventType = ((SwapFlowEventTypeEnum)item.EventType).ToString();
|
|
exportModel.EventReason = item.EventReason;
|
|
exportModel.PayDirection = ((SwapDirectionEnum)item.PayDirection).ToString();
|
|
exportModel.PositionType = item.PositionType==1?"多头":"空头";
|
|
exportModel.UnderlyingCode = item.UnderlyingCode;
|
|
exportModel.MatuirityDate = item.MatuirityDate.OtcFormatDate();
|
|
exportModel.TradingAmountAvg = item.TradingAmountAvg.OtcFormat(OtcFormatFlag.umprice);
|
|
exportModel.TradingAmountFeeAvg = item.TradingAmountFeeAvg.OtcFormat(OtcFormatFlag.umprice);
|
|
exportModel.Quantity = item.Quantity.OtcFormatMoney(false, 4);
|
|
exportModel.TradingAmount = item.TradingAmount.OtcFormatMoney(false, 4);
|
|
exportModel.ContractSize = item.ContractSize.ToString();
|
|
exportModel.TradingFee = item.TradingFee.OtcFormatMoney(false, 4);
|
|
exportModel.TradingFeePending = item.TradingFeePending.OtcFormatMoney(false, 4);
|
|
exportModel.DividendPending = item.DividendPending.OtcFormatMoney(false, 4);
|
|
exportModel.MarkClosePnl = item.MarkClosePnl.OtcFormatMoney(false, 4);
|
|
exportModel.DividendIn = item.DividendIn.OtcFormatMoney(false, 4);
|
|
exportModel.OptLog = item.OptLog;
|
|
exportModel.InitYtm = item.InitYtm;
|
|
list.Add(exportModel);
|
|
}
|
|
var tplFilePath = OtcAppContext.MapPath("/App_Docs");
|
|
var sourceFileName = Path.Combine(tplFilePath, "导出模板", "互换开平仓事件流水导出模板.xlsx");
|
|
var modelDict = new Dictionary<string, object>();
|
|
var model = new { list = list };
|
|
modelDict.Add("Sheet1", model);
|
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
|
return new ExcelTemplateGenerator().SetTemplateFile(sourceFileName).SetTemplateData(modelDict).Output();
|
|
}
|
|
/// <summary>
|
|
/// 查询互换合成持仓
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public SearchListResult<SwapPositionResponse> SearchComposeList(SwapFlowQueryRequest req)
|
|
{
|
|
var predicate = PredicateBuilder.Create<swap_position>(n => n.PosiQuantity>0 && n.PosiStartDate == req.TradeDate && !n.Invalid&&!n.IsInitial);
|
|
List<string> eventDatas = new List<string>() { "系统操作-自动合成持仓", "流水自动簿记确认交易" };
|
|
var eventDate =QdpCalendarHelper.GetNonHolidayDefore(req.TradeDate.Value.AddDays(-1));
|
|
var swapEventPredicate = PredicateBuilder.Create<swap_event>(n => eventDatas.Contains(n.EventReason)&&!n.Invalid&&n.ValueDate== eventDate);
|
|
var tradePredicate = PredicateBuilder.Create<trade>(n => n.TradeType=="收益互换"&&n.ValidState!=ConsGlobal.InValid);
|
|
if (!string.IsNullOrEmpty(req.TradeNumber))
|
|
{
|
|
tradePredicate = tradePredicate.And(n => n.TradeNumber.Contains(req.TradeNumber.Trim()));
|
|
}
|
|
if (req.ClientId.HasValue)
|
|
{
|
|
tradePredicate = tradePredicate.And(n => n.ClientId==req.ClientId);
|
|
}
|
|
if (!string.IsNullOrEmpty(req.UnderlyingCode))
|
|
{
|
|
predicate = predicate.And(n => n.UnderlyingCode.Contains(req.UnderlyingCode.Trim()));
|
|
}
|
|
var positionQuery = DbContext.swap_position.Where(predicate);
|
|
var swapTradeIds = DbContext.swap_event.Where(swapEventPredicate).Select(s => s.SwapTradeId).Distinct();
|
|
tradePredicate = tradePredicate.And(x=> swapTradeIds.Contains(x.id));
|
|
var tradeQuery = DbContext.trade.Where(tradePredicate);
|
|
var query = from position in positionQuery
|
|
join td in tradeQuery on position.SwapTradeId equals td.id
|
|
select new SwapPositionResponse
|
|
{
|
|
position = position,
|
|
TradeDate = td.StartDate.Value,
|
|
SwapTradeNo = td.TradeNumber,
|
|
StructureType = td.StructureType,
|
|
ClientName = td.ClientName
|
|
};
|
|
if (string.IsNullOrEmpty(req.sidx))
|
|
{
|
|
req.sidx = "SwapTradeNo,position.UnderlyingCode";
|
|
req.sord = "asc";
|
|
}
|
|
var retListResult = query.ToSearchList(req);
|
|
foreach (var item in retListResult.rows)
|
|
{
|
|
if (ConsGlobal.InstrumentType.IsBond(item.position.UnderlyingInstrumentType))
|
|
{
|
|
item.position.PosiNetPrice = BondPriceConverter.ToDisplay(item.position.PosiNetPrice);
|
|
item.position.PosiGrossPrice = BondPriceConverter.ToDisplay(item.position.PosiGrossPrice);
|
|
item.position.PosiNetNoFeePrice = item.position.PosiNetNoFeePrice.HasValue ? BondPriceConverter.ToDisplay(item.position.PosiNetNoFeePrice.Value) : item.position.PosiNetNoFeePrice;
|
|
item.position.PosiNetFeePrice = item.position.PosiNetFeePrice.HasValue ? BondPriceConverter.ToDisplay(item.position.PosiNetFeePrice.Value) : item.position.PosiNetFeePrice;
|
|
item.position.PosiQuantity /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
}
|
|
return retListResult;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存流水
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
public void SaveSwapFlow(swap_flow req)
|
|
{
|
|
CheckValid(req);
|
|
swap_flow swap_Flow = DbContext.swap_flow.Find(req.id);
|
|
if (swap_Flow == null)
|
|
{
|
|
swap_Flow = new swap_flow();
|
|
}
|
|
swap_Flow.OccurTime = req.OccurTime;
|
|
swap_Flow.FundAccount = req.FundAccount;
|
|
swap_Flow.TradingAmount = req.TradingAmount;
|
|
swap_Flow.TradingFee = req.TradingFee;
|
|
swap_Flow.TradingQty = req.TradingQty;
|
|
swap_Flow.UnderlyingCode = req.UnderlyingCode;
|
|
swap_Flow.BsType = req.BsType;
|
|
swap_Flow.ContractSize = req.ContractSize;
|
|
swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
|
|
swap_Flow.TradingAmountFeeAvg = TradeFeeHelper.CalcPriceWithFee(req.TradingFee,req.TradingAmountAvg,req.TradingQty,req.BsType);
|
|
swap_Flow.ClientId = req.ClientId;
|
|
swap_Flow.ytm = req.ytm;
|
|
swap_Flow.TradingAmountNet = req.TradingAmountNet;
|
|
swap_Flow.ClientName = req.ClientName;
|
|
swap_Flow.UnderlyingName = req.UnderlyingName;
|
|
swap_Flow.TradingAmountNetFee = TradeFeeHelper.CalcPriceWithFee(req.TradingFee, req.TradingAmountNet??0, req.TradingQty, req.BsType);
|
|
swap_Flow.SettleDate=req.SettleDate;
|
|
swap_Flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
|
UpdateDbOption(swap_Flow);
|
|
if (req.id == 0)
|
|
{
|
|
DbContext.swap_flow.Add(swap_Flow);
|
|
}
|
|
Task.Run(() =>
|
|
{
|
|
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
|
});
|
|
DbContext.SaveChanges();
|
|
|
|
Dictionary<long, List<string>> clientUmsDic = new Dictionary<long, List<string>>();
|
|
clientUmsDic.Add(swap_Flow.ClientId ?? 0, new List<string> { swap_Flow.UnderlyingCode });
|
|
new RiskCacheService().refreshRiskCache(clientUmsDic);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 回退删除手动簿记流水
|
|
/// </summary>
|
|
/// <param name="tradeId"></param>
|
|
/// <param name="eventDate"></param>
|
|
public void DeleteSwapFlow(int tradeId,DateTime eventDate,bool skip)
|
|
{
|
|
var swapFlows = DbContext.swap_flow.Where(x => x.SwapTradeId == tradeId && x.OccurTime >= eventDate);
|
|
if (skip)
|
|
{
|
|
swapFlows= swapFlows.Skip(1);
|
|
}
|
|
DbContext.swap_flow.RemoveRange(swapFlows);
|
|
DbContext.SaveChanges();
|
|
Task.Run(() =>
|
|
{
|
|
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 保存流水
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
public void SaveSwapFlowMerge(swap_flow req)
|
|
{
|
|
CheckValid(req);
|
|
swap_flow_merge swap_Flow = DbContext.swap_flow_merge.Find(req.id);
|
|
if (swap_Flow == null)
|
|
{
|
|
swap_Flow = new swap_flow_merge(); ;
|
|
}
|
|
swap_Flow.OccurTime = req.OccurTime.Value;
|
|
swap_Flow.ClientId = req.ClientId;
|
|
swap_Flow.FundAccount = req.FundAccount;
|
|
swap_Flow.TradingAmount = req.TradingAmount;
|
|
swap_Flow.TradingFee = req.TradingFee;
|
|
swap_Flow.TradingQty = req.TradingQty;
|
|
swap_Flow.UnderlyingCode = req.UnderlyingCode;
|
|
swap_Flow.BsType = req.BsType;
|
|
swap_Flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
|
swap_Flow.ContractSize = req.ContractSize;
|
|
swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
|
|
swap_Flow.TradingAmountFeeAvg = req.TradingAmountFeeAvg;
|
|
UpdateDbOption(swap_Flow);
|
|
if (req.id == 0)
|
|
{
|
|
DbContext.swap_flow_merge.Add(swap_Flow);
|
|
}
|
|
DbContext.SaveChanges();
|
|
}
|
|
/// <summary>
|
|
/// 删除流水
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public void DeleteSwapFlow(long id)
|
|
{
|
|
var capitalAccount = DbContext.swap_flow.Find(id);
|
|
if (capitalAccount == null)
|
|
{
|
|
throw new ServiceException("未找到该互换流水");
|
|
}
|
|
DbContext.swap_flow.Remove(capitalAccount);
|
|
DbContext.SaveChanges();
|
|
Task.Run(() =>
|
|
{
|
|
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
|
});
|
|
Dictionary<long, List<string>> clientUmsDic = new Dictionary<long, List<string>>();
|
|
clientUmsDic.Add(capitalAccount.ClientId ?? 0, new List<string> { capitalAccount.UnderlyingCode });
|
|
new RiskCacheService().refreshRiskCache(clientUmsDic);
|
|
}
|
|
/// <summary>
|
|
/// 删除流水
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public void DeleteSwapFlowMerge(long id)
|
|
{
|
|
var swap_Flow = DbContext.swap_flow_merge.Find(id);
|
|
if (swap_Flow == null)
|
|
{
|
|
throw new ServiceException("未找到该互换流水汇总");
|
|
}
|
|
DbContext.swap_flow_merge.Remove(swap_Flow);
|
|
DbContext.SaveChanges();
|
|
}
|
|
/// <summary>
|
|
/// 查看流水
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
public swap_flow GetSwapFlow(long id)
|
|
{
|
|
var swapFlow = DbContext.swap_flow.Find(id);
|
|
if (swapFlow == null)
|
|
{
|
|
throw new ServiceException("未找到该互换流水");
|
|
}
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(swapFlow.UnderlyingCode);
|
|
if (um != null && um.IsBond())
|
|
{
|
|
swapFlow.TradingAmountAvg = BondPriceConverter.ToDisplay(swapFlow.TradingAmountAvg);
|
|
swapFlow.TradingAmountFeeAvg = BondPriceConverter.ToDisplay(swapFlow.TradingAmountFeeAvg);
|
|
swapFlow.TradingAmountNet = swapFlow.TradingAmountNet.HasValue ? BondPriceConverter.ToDisplay(swapFlow.TradingAmountNet.Value) : swapFlow.TradingAmountNet;
|
|
swapFlow.TradingAmountNetFee = swapFlow.TradingAmountNetFee.HasValue ? BondPriceConverter.ToDisplay(swapFlow.TradingAmountNetFee.Value) : swapFlow.TradingAmountNetFee;
|
|
swapFlow.TradingQty /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
return swapFlow;
|
|
}
|
|
public swap_flow_merge GetSwapFlowMerge(long id)
|
|
{
|
|
var swapFlow = DbContext.swap_flow_merge.Find(id);
|
|
if (swapFlow == null)
|
|
{
|
|
throw new ServiceException("未找到该互换流水");
|
|
}
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(swapFlow.UnderlyingCode);
|
|
if (um != null && um.IsBond())
|
|
{
|
|
swapFlow.TradingAmountAvg = BondPriceConverter.ToDisplay(swapFlow.TradingAmountAvg);
|
|
swapFlow.TradingAmountFeeAvg = BondPriceConverter.ToDisplay(swapFlow.TradingAmountFeeAvg);
|
|
swapFlow.TradingAmountNetAvg = swapFlow.TradingAmountNetAvg.HasValue ? BondPriceConverter.ToDisplay(swapFlow.TradingAmountNetAvg.Value) : swapFlow.TradingAmountNetAvg;
|
|
swapFlow.TradingAmountNetFeeAvg = swapFlow.TradingAmountNetFeeAvg.HasValue ? BondPriceConverter.ToDisplay(swapFlow.TradingAmountNetFeeAvg.Value) : swapFlow.TradingAmountNetFeeAvg;
|
|
swapFlow.TradingQty /= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
return swapFlow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 流水汇总
|
|
/// </summary>
|
|
public void SwapFlowMerge(DateTime tradeDate)
|
|
{
|
|
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 21, "互换流水汇总进行中");
|
|
var predicate = PredicateBuilder.Create<swap_flow>(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成 && n.OccurTime == tradeDate && n.BsType != (int)EnumDirection.UnKnown);
|
|
var capitalQuery = DbContext.swap_fund_account.Where(x => x.Status == (int)SwapFoundAccountStatusEnum.正常);
|
|
var flowquery = DbContext.swap_flow.Where(predicate);
|
|
var tradeQuery = DbContext.trade.Where(t => t.TradeStatus == ConsTrade.确认成交
|
|
&& t.StructureType == ClientMarginTypeEnum.多空组合.ToString()
|
|
&& t.ValidState != "InValid"
|
|
&& t.StartDate <= tradeDate
|
|
&& t.ExerciseDate >= tradeDate);
|
|
List<SwapFlowQueryResponse> swapFlowSummaries = new List<SwapFlowQueryResponse>();
|
|
var query = from flow in flowquery
|
|
join capital in capitalQuery on flow.FundAccount equals capital.FundAccount
|
|
join td in tradeQuery on capital.SwapTradeId equals td.id
|
|
select new SwapFlowQueryResponse
|
|
{
|
|
id = flow.id,
|
|
FundAccount = flow.FundAccount,
|
|
OccurTime = flow.OccurTime,
|
|
SwapTradeId = capital.SwapTradeId,
|
|
SwapTradeNo = capital.SwapTradeNo,
|
|
BsType = flow.BsType,
|
|
UnderlyingCode = flow.UnderlyingCode,
|
|
TradingQty = flow.TradingQty,
|
|
TradingAmount = flow.TradingAmount,
|
|
TradingFee = flow.TradingFee,
|
|
TradingAmountAvg = flow.TradingAmountAvg,
|
|
TradingAmountFeeAvg = flow.TradingAmountFeeAvg,
|
|
ContractSize = flow.ContractSize
|
|
};
|
|
foreach (var item in query)
|
|
{
|
|
if (GetUnderlyingCode(item.UnderlyingCode))
|
|
{
|
|
swapFlowSummaries.Add(item);
|
|
}
|
|
}
|
|
if (swapFlowSummaries.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
foreach (var item in swapFlowSummaries)
|
|
{
|
|
var flow = flowquery.First(x => x.id == item.id);
|
|
flow.DataState = (int)SwapFlowDateStateEnum.完成;
|
|
}
|
|
var swapFlowGroup = swapFlowSummaries.GroupBy(g => new { g.SwapTradeId, g.OccurTime, g.UnderlyingCode, g.BsType });
|
|
foreach (var gourpItem in swapFlowGroup)
|
|
{
|
|
var swapflow = gourpItem.First();
|
|
swap_flow_merge swap_flow_summary = new swap_flow_merge()
|
|
{
|
|
OccurTime = swapflow.OccurTime.Value,
|
|
FundAccount = swapflow.FundAccount,
|
|
SwapTradeId = swapflow.SwapTradeId,
|
|
SwapTradeNo = swapflow.SwapTradeNo,
|
|
UnderlyingCode = swapflow.UnderlyingCode,
|
|
BsType = swapflow.BsType,
|
|
TradingQty = gourpItem.Sum(s => s.TradingQty),
|
|
TradingAmount = gourpItem.Sum(s => s.TradingAmount),
|
|
TradingFee = gourpItem.Sum(s => s.TradingFee),
|
|
DataState = (int)SwapFlowDateStateEnum.等待完成,
|
|
TradingAmountFeeAvg = gourpItem.Average(s => s.TradingAmountFeeAvg),
|
|
TradingAmountAvg = gourpItem.Average(s => s.TradingAmountAvg),
|
|
ContractSize = swapflow.ContractSize
|
|
};
|
|
UpdateDbOption(swap_flow_summary);
|
|
DbContext.swap_flow_merge.Add(swap_flow_summary);
|
|
}
|
|
DbContext.SaveChanges();
|
|
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 22, "互换流水汇总完成");
|
|
}
|
|
|
|
|
|
public void UpdatePayDate(UpdatePayDateDto dto)
|
|
{
|
|
var id = long.Parse(DataProtect.Decrypt(dto.Id));
|
|
var model = DbContext.swap_flow_event.FirstOrDefault(p => p.id == id);
|
|
if(model!=null)
|
|
{
|
|
model.PayDate=dto.PayDate;
|
|
model.OptId = UserInfo.UserId;
|
|
model.OptName = UserInfo.UserName;
|
|
model.OptTime = DateTime.Now;
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#region 私有方法
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 必填项校验
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
private void CheckRequired(swap_flow req)
|
|
{
|
|
if (string.IsNullOrEmpty(req.UnderlyingCode) && string.IsNullOrEmpty(req.UnderlyingCode.Trim()))
|
|
{
|
|
throw new ServiceException("标的代码不能为空");
|
|
}
|
|
req.UnderlyingCode = req.UnderlyingCode.Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 数据有效性校验
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
private void CheckValid(swap_flow req)
|
|
{
|
|
CheckRequired(req);
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
|
|
if (underlying == null)
|
|
{
|
|
throw new ServiceException("没有找到标的信息:" + req.UnderlyingCode);
|
|
}
|
|
if (underlying != null && underlying.IsBond())
|
|
{
|
|
// 债券报价(×100)转入库小数(×0.01),价格字段统一走 BondPriceConverter
|
|
req.TradingAmountAvg = BondPriceConverter.ToStorage(req.TradingAmountAvg);
|
|
req.TradingAmountFeeAvg = BondPriceConverter.ToStorage(req.TradingAmountFeeAvg);
|
|
if (req.TradingAmountNet.HasValue)
|
|
req.TradingAmountNet = BondPriceConverter.ToStorage(req.TradingAmountNet.Value);
|
|
if (req.TradingAmountNetFee.HasValue)
|
|
req.TradingAmountNetFee = BondPriceConverter.ToStorage(req.TradingAmountNetFee.Value);
|
|
// 数量×100(万手→手),与价格维度无关,保留常量
|
|
req.TradingQty *= ConsGlobal.bondShowPriceMultiple;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
}
|
|
}
|