using Dapper;
using YLErp.Modules.MarketRiskMoudule;
using YLErp.Modules.MarketRiskMoudule.Dto;
using YLErp.Modules.RiskExposure;
using YLErp.Modules.RiskModule;
namespace YLErp.Modules.EodModule
{
///
/// 累计总盈亏服务
///
public class AccruedTotalPnlService : YLBaseService
where TEntity : EodTradePosition
{
public AccruedTotalPnlService(OptUserInfo userInfo) : base(userInfo)
{
}
public AccruedTotalPnlService(YLBaseService baseService) : base(baseService)
{
}
private DateTime GetPreDate(DateTime date)
{
var tmpDate = DbContext.Set().Where(n => n.ValueDate < date).Max(t => (DateTime?)t.ValueDate);
return tmpDate.HasValue ? tmpDate.Value.Date : DateTime.MinValue;
}
///
/// 获取起算日前的盈亏数据(交易类型:场内期权+场外交易)
/// (在总盈亏统计中根据标的代码减去此列表结果中的响应数据)
///
public IEnumerable GetAccruedStartPnls(DateTime accruedStartDate, bool isIncludeHedge = false)
{
var preStartDate = GetPreDate(accruedStartDate);
if (preStartDate.Year < 2000)
{
return Enumerable.Empty();
}
//--------------------------------
// 获取起始累计盈亏收盘数据列表
//--------------------------------
var query1 = from n in DbContext.Set()
where n.ValueDate == preStartDate
where n.TradeType == "场内期权"
select new
{
TraderId = 0,
n.TradeType,
n.UnderlyingCode,
n.BookId,
n.ClientId,
n.TotalPnL,
n.Commission,
n.Amount
};
if (isIncludeHedge)
{
var query = from n in DbContext.Set()
where n.ValueDate == preStartDate
where n.TradeType == "商品期货"
select new
{
TraderId = 0,
n.TradeType,
n.UnderlyingCode,
n.BookId,
n.ClientId,
n.TotalPnL,
n.Commission,
n.Amount
};
query1 = query1.Concat(query);
}
var query2 = from n in DbContext.Set()
join t in DbContext.trade on n.TradeId equals t.id
where n.ValueDate == preStartDate && n.TradeId > 0
&& t.ValidState != "InValid"
&& (t.UnWindDate > preStartDate || !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus))
select new
{
t.TraderId,
n.TradeType,
UnderlyingCode = t.TradeType == ConsGlobal.TradeType.CashFlow ? "现金流" : n.UnderlyingCode,
n.BookId,
n.ClientId,
n.TotalPnL,
n.Commission,
n.Amount
};
var list = query1.Concat(query2).ToList();
//------------------------------------------------------
// 过滤并且汇总求和
//------------------------------------------------------
var sumQuery = from n in list
group n by new { n.UnderlyingCode, n.BookId, n.ClientId, n.TradeType, n.TraderId } into g
select new EodPnlStaticsDto
{
BookId = g.Key.BookId,
AssetType = g.Key.TradeType,
ClientId = g.Key.ClientId,
TraderId = g.Key.TraderId,
UnderlyingCode = g.Key.UnderlyingCode,
TotalPnl = g.Sum(t => t.TotalPnL),
TotalCommission = g.Sum(t => t.Commission),
TotalAmount = g.Sum(t => t.Amount)
};
return sumQuery.ToArray();
}
///
/// 获取历史了结交易总盈亏数据
///
/// 开始日期(包括)
/// 结束日期(包括)
/// 是否包括对冲交易
public IEnumerable GetFinishedTradePnls(DateTime startDate, DateTime endDate, bool isIncludeHedge = false)
{
var sumList = new List();
//SetDebugSqlLog();
//------------------------------------------------------
// 场内期权累计盈亏统计
//------------------------------------------------------
var exchangeOptionQuery = from a in DbContext.exchange_list_option
where a.MaturityDate >= startDate && a.MaturityDate <= endDate
select new
{
ValueDate = a.MaturityDate,
ExchangeOptionCode = a.ContractCode
};
var finishedExOptionPnlQuery = from exOption in exchangeOptionQuery
join eodPnl in DbContext.Set()
on new { exOption.ValueDate, exOption.ExchangeOptionCode }
equals new { eodPnl.ValueDate, eodPnl.ExchangeOptionCode }
where eodPnl.ValueDate >= startDate && eodPnl.TradeType == "场内期权"
group eodPnl by new { eodPnl.UnderlyingCode, eodPnl.BookId } into g
select new EodPnlStaticsDto
{
BookId = g.Key.BookId,
UnderlyingCode = g.Key.UnderlyingCode,
TotalPnl = g.Sum(n => n.TotalPnL),
TotalCommission = g.Sum(n => n.Commission),
TotalCount = g.Count()
};
var exOptionSums = finishedExOptionPnlQuery.ToArray();
foreach (var data in exOptionSums)
{
data.ValueDate = endDate;
data.AssetType = "场内期权";
}
sumList.AddRange(exOptionSums);
//------------------------------
// 商品期货累计盈亏统计(暂时不能支持股票和商品现货)
//------------------------------
if (isIncludeHedge)
{
var futureQuery = from a in DbContext.underlying_manager
where a.MaturityDate >= startDate && a.MaturityDate <= endDate
select new
{
a.UnderlyingCode,
ValueDate = a.MaturityDate.Value
};
var finishedQuery = from underlying in futureQuery
join eodPnl in DbContext.Set()
on new { underlying.ValueDate, underlying.UnderlyingCode }
equals new { eodPnl.ValueDate, eodPnl.UnderlyingCode }
where eodPnl.ValueDate >= startDate && eodPnl.TradeType == "商品期货"
group eodPnl by new { eodPnl.UnderlyingCode, eodPnl.BookId } into g
select new EodPnlStaticsDto
{
BookId = g.Key.BookId,
UnderlyingCode = g.Key.UnderlyingCode,
TotalPnl = g.Sum(n => n.TotalPnL),
TotalCommission = g.Sum(n => n.Commission),
TotalCount = g.Count()
};
var futureSums = finishedQuery.ToArray();
foreach (var data in futureSums)
{
data.ValueDate = endDate;
data.AssetType = "商品期货";
}
sumList.AddRange(futureSums);
}
//------------------------------------------------------
// 场外期权累计盈亏统计
//------------------------------------------------------
var tradQuery = from t in DbContext.trade
where t.ClientId > 0 && t.UnWindDate >= startDate && t.UnWindDate <= endDate
&& t.UnderlyingCode != null
&& ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)
&& t.TradeType != "结构化交易"
&& t.ValidState != ConsGlobal.InValid
select new
{
t.id,
t.AssetId,
t.ClientId,
AssetType = t.TradeType,
UnderlyingCode = t.TradeType == ConsGlobal.TradeType.CashFlow ? "现金流" : t.UnderlyingCode,
t.TraderId
};
var query = from t in tradQuery
join tc in DbContext.trade_cash on t.id equals tc.TradeId
where tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted
group tc by new { t.UnderlyingCode, t.AssetId, t.ClientId, t.AssetType, t.TraderId } into g
select new EodPnlStaticsDto
{
TraderId = g.Key.TraderId,
BookId = g.Key.AssetId,
ClientId = g.Key.ClientId,
AssetType = g.Key.AssetType,
UnderlyingCode = g.Key.UnderlyingCode,
TotalPnl = g.Sum(t => t.Amount),
TotalCount = g.Count()
};
var otcSums = query.ToArray();
foreach (var data in otcSums)
{
data.ValueDate = endDate;
}
sumList.AddRange(otcSums);
//返回结果
return sumList;
}
///
/// 获取区间内的场内期权pnl合计
///
/// 开始日期(包括)
/// 结束日期(包括)
public double GetTotalPnlOfExchangeOption(DateTime startDate, DateTime endDate)
{
//------------------------------
// 取值日的当前持仓统计
//------------------------------
var positionQuery = from eodPnl in DbContext.Set()
where eodPnl.ValueDate == endDate && eodPnl.TradeType == "场内期权"
group eodPnl by eodPnl.ExchangeOptionCode into g
select new
{
g.Key,
totalPnl = g.Sum(n => n.TotalPnL)
};
//------------------------------
// 区间内的过期标的统计
//------------------------------
var exchangeOptionQuery = from a in DbContext.exchange_list_option
where a.MaturityDate >= startDate && a.MaturityDate < endDate
select new
{
ValueDate = a.MaturityDate,
ExchangeOptionCode = a.ContractCode
};
var finishedQuery = from exOption in exchangeOptionQuery
join eodPnl in DbContext.Set()
on new { exOption.ValueDate, exOption.ExchangeOptionCode }
equals new { eodPnl.ValueDate, eodPnl.ExchangeOptionCode }
where eodPnl.ValueDate >= startDate && eodPnl.TradeType == "场内期权"
group eodPnl by eodPnl.ExchangeOptionCode into g
select new
{
g.Key,
totalPnl = g.Sum(n => n.TotalPnL)
};
//------------------------------
// 起算日前一交易日的累计数据
//------------------------------
var preStartDate = GetPreDate(startDate);
var preStartQuery = from n in DbContext.Set()
where n.ValueDate == preStartDate && n.TradeType == "场内期权"
select new { n.ExchangeOptionCode, TotalPnL = (double?)n.TotalPnL };
//------------------------------
// 区间内的累计数据(持仓+到期-起始)
//------------------------------
var totalPnlQuery = from t1 in positionQuery.Concat(finishedQuery)
join t2 in preStartQuery on t1.Key equals t2.ExchangeOptionCode into tt
from t2 in tt.DefaultIfEmpty()
select t1.totalPnl - (t2.TotalPnL ?? 0);
return totalPnlQuery.Sum(n => (double?)n) ?? 0;
}
///
/// 获取区间内的商品期货pnl合计
///
/// 开始日期(包括)
/// 结束日期(包括)
public double GetTotalPnlSumOfFutures(DateTime startDate, DateTime endDate)
{
//------------------------------
// 取值日的持仓统计
//------------------------------
var positionQuery = from eodPnl in DbContext.Set()
where eodPnl.ValueDate == endDate && eodPnl.TradeType == "商品期货"
group eodPnl by eodPnl.UnderlyingCode into g
select new
{
g.Key,
totalPnl = g.Sum(n => n.TotalPnL)
};
//------------------------------
// 区间内的过期标的统计
//------------------------------
var futureQuery = from a in DbContext.underlying_manager
where a.MaturityDate >= startDate && a.MaturityDate < endDate
select new
{
a.UnderlyingCode,
ValueDate = a.MaturityDate.Value
};
var finishedQuery = from underlying in futureQuery
join eodPnl in DbContext.Set()
on new { underlying.ValueDate, underlying.UnderlyingCode }
equals new { eodPnl.ValueDate, eodPnl.UnderlyingCode }
where eodPnl.ValueDate >= startDate && eodPnl.TradeType == "商品期货"
group eodPnl by eodPnl.UnderlyingCode into g
select new
{
g.Key,
totalPnl = g.Sum(n => n.TotalPnL)
};
//------------------------------
// 起算日前一交易日的累计数据
//------------------------------
var preStartDate = GetPreDate(startDate);
var preStartQuery = from n in DbContext.Set()
where n.ValueDate == preStartDate && n.TradeType == "商品期货"
select new { n.UnderlyingCode, TotalPnL = (double?)n.TotalPnL };
//------------------------------
// 区间内的数据合计(持仓+到期-起始)
//------------------------------
var totalPnlQuery = from t1 in positionQuery.Concat(finishedQuery)
join t2 in preStartQuery on t1.Key equals t2.UnderlyingCode into tt
from t2 in tt.DefaultIfEmpty()
select t1.totalPnl - (t2.TotalPnL ?? 0);
return totalPnlQuery.Sum(n => (double?)n) ?? 0;
}
///
/// 获取取值日的期权估值(公司角度)
///
public double GetTotalPvOfOptions(DateTime valueDate)
{
//当前持仓-起算前
var query = from t1 in DbContext.Set().Where(n => n.ValueDate == valueDate && n.TradeId > 0)
join t2 in DbContext.Set().Where(n => n.ValueDate == valueDate && n.TradeId > 0)
on t1.TradeId equals t2.TradeId into tt
from t2 in tt.DefaultIfEmpty()
select t1.Pv - (t2 == null ? 0 : t2.Pv);
return query.Sum(n => (double?)n) ?? 0;
}
///
/// 计算综合盈亏比例: ((收取的权利金 - 支付的权利金) + 对冲盈亏 - 期权估值)/对冲账户成本
///
/// 开始日期(包括)
/// 结束日期(包括)
public double CalcTotalPnlRate(DateTime startDate, DateTime endDate)
{
//对冲盈亏
var totalHedgePnl = GetTotalPnlOfExchangeOption(startDate, endDate) + GetTotalPnlSumOfFutures(startDate, endDate);
//公司角度期权估值(场外期权)
var totalPv = GetTotalPvOfOptions(endDate);
//公司角度(收取的权利金 - 支付的权利金)
var allTotalTradePrice = -(DbContext.ClientBalanceDaily.Where(x => x.BalanceDate == endDate)
.Sum(x => x.OptionPremiumSum) ?? 0) - (DbContext.ClientBalanceDaily.Where(x => x.BalanceDate == endDate)
.Sum(x => x.OptionPremiumSwapSum) ?? 0);
var preStartDate = GetPreDate(startDate);
var lastSettleDateStartDateTotalTradePrice = -(DbContext.ClientBalanceDaily.Where(x => x.BalanceDate == preStartDate)
.Sum(x => x.OptionPremiumSum) ?? 0) - (DbContext.ClientBalanceDaily.Where(x => x.BalanceDate == preStartDate)
.Sum(x => x.OptionPremiumSwapSum) ?? 0);
var totalTradePrice = allTotalTradePrice - lastSettleDateStartDateTotalTradePrice;
//对冲账户成本
var totalInitialCost = DbContext.exchange_account.Sum(x => x.InitialCost) ?? 0;
if (totalInitialCost < 1)
{
totalInitialCost = 1;
}
return (totalTradePrice + totalHedgePnl - Convert.ToDouble(totalPv)) / totalInitialCost;
}
///
/// 计算估算日的场内交易,商品期货股票的手续费汇总
///
/// 开始日期(包括)
/// 结束日期(包括)
///
public IEnumerable GetCommissionForHedge(DateTime startDate, DateTime endDate)
{
List tradeTypes = new List()
{
"场内期权",
"商品期货",
"股票",
"信用债"
};
var sourceQuery = from t in DbContext.Set().Where(t => t.ValueDate >= startDate && t.ValueDate <= endDate && tradeTypes.Contains(t.TradeType))
join un in DbContext.underlying_manager on t.UnderlyingCode equals un.UnderlyingCode
group t by new { t.TradeId, t.HedgeUniqueCode } into g
select new
{
g.Key.TradeId,
g.Key.HedgeUniqueCode,
maxDate = g.Max(B => B.ValueDate)
};
var finishedQuery = from source in sourceQuery
join eodPnl in DbContext.Set()
on new { ValueDate = source.maxDate, TradeId = source.TradeId + source.HedgeUniqueCode }
equals new { eodPnl.ValueDate, TradeId = eodPnl.TradeId + eodPnl.HedgeUniqueCode }
select new
{
eodPnl.UnderlyingCode,
eodPnl.BookId,
eodPnl.ClientId,
eodPnl.TradeType,
TraderId = 0,
eodPnl.Commission
};
//------------------------------------------------------
// 过滤并且汇总求和
//------------------------------------------------------
var sumQuery = from n in finishedQuery
group n by new { n.UnderlyingCode, n.BookId, n.ClientId, n.TradeType, n.TraderId } into g
select new EodPnlStaticsDto
{
BookId = g.Key.BookId,
AssetType = g.Key.TradeType,
ClientId = g.Key.ClientId,
TraderId = g.Key.TraderId,
UnderlyingCode = g.Key.UnderlyingCode,
TotalCommission = g.Sum(t => t.Commission),
ValueDate = endDate
};
return sumQuery.ToArray();
}
//todo public
public List CalcuTotalPnl(out List t2PositionDataDtos, DateTime dateT1, DateTime dateT2, RiskRequestModel reqModel)
{
// 2、查询三点数据 t1到t2已了结持仓数据
var task1 = new MarketRiskService(UserInfo).CreateGetT1DatasTask(dateT1);
string tradeWhere, onSiteTradeWhere, predicateWhere, inSiteWhere;
new MarketRiskService(UserInfo).MakeCondition(reqModel, out tradeWhere, out onSiteTradeWhere, out predicateWhere, out inSiteWhere);
var task2 = CreateGetT2DatasTask(dateT2, reqModel, predicateWhere);
tradeWhere = " UnWindDate>@StartTime and UnWindDate<@EndTime and TradeStatus in @TradeCompletedStatus and " + tradeWhere;
var task3 = new MarketRiskService(UserInfo).CreateGetT1ToT2DatasTask(reqModel, dateT1, dateT2, onSiteTradeWhere, tradeWhere, inSiteWhere);
task1.Start();
task2.Start();
task3.Start();
System.Threading.Tasks.Task.WaitAll(task1, task2, task3);
var t1DatasDic = task1.Result.ToDictionary(p => p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode), p => p);
//数据整合
if (t1DatasDic == null)
{
t1DatasDic = new Dictionary();
}
List tradePositionTotalPnLModel = new List();
t2PositionDataDtos = task2.Result;
if (task2.Result != null && task2.Result.Count > 0)
{
task2.Result.ForEach(p =>
{
var model = new TradePositionTotalPnLModel
{
ValueDate = p.ValueDate,
TradeId = p.TradeId,
ParentTradeId = p.ParentTradeId ?? 0,
UnderlyingCode = p.UnderlyingCode,
OptionCode = p.ExchangeOptionCode,
UnderlyingId = p.UnderlyingId ?? 0,
BookId = p.BookId ?? 0,
TradeType = p.StructureType == null || p.StructureType == "" ? p.TradeType : p.StructureType,
AccruedTotalPnL = p.TotalPnL,
HedgeUniqueCode = p.HedgeUniqueCode,
};
var key = p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode);
var t1Data = t1DatasDic.ContainsKey(key) ? t1DatasDic[key] : null;
if (t1Data != null)
{
model.AccruedTotalPnL = model.AccruedTotalPnL - (t1Data.TotalPnL ?? 0);
}
tradePositionTotalPnLModel.Add(model);
});
}
if (task3.Result != null && task3.Result.Count > 0)
{
task3.Result.ForEach(p =>
{
var model = new TradePositionTotalPnLModel
{
ValueDate = p.ValueDate,
TradeId = p.TradeId,
ParentTradeId = p.ParentTradeId ?? 0,
UnderlyingCode = p.UnderlyingCode,
OptionCode = p.ExchangeOptionCode,
UnderlyingId = p.UnderlyingId ?? 0,
BookId = p.BookId,
TradeType = p.StructureType == null || p.StructureType == "" ? p.TradeType : p.StructureType,
AccruedTotalPnL = p.TotalPnL,
HedgeUniqueCode = p.HedgeUniqueCode,
};
var key = p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode);
var t1Data = t1DatasDic.ContainsKey(key) ? t1DatasDic[key] : null;
if (t1Data != null)
{
model.AccruedTotalPnL = model.AccruedTotalPnL - (t1Data.TotalPnL ?? 0);
}
tradePositionTotalPnLModel.Add(model);
});
}
return tradePositionTotalPnLModel;
}
private Task> CreateGetT2DatasTask(DateTime dateT2, RiskRequestModel reqModel, string predicateWhere)
{
return new Task>(() =>
{
using (var db = DbContextFactory.GetYLDbContext())
{
var t2PositionDateSql = string.Format("select id,ValueDate,TradeId,HedgeUniqueCode,ParentTradeId,UnderlyingCode,ExchangeOptionCode,UnderlyingId,BookId,StructureType,TradeType,Amount,TotalPnL,Pv,Cost,Commission,DailyPnL,RoundedPv,PositionPnL,RoundedPositionPnL,Margin from {0} where {1}", db.GetTableName(), predicateWhere);
db.Database.SetCommandTimeout(1800);
var conn = db.Database.GetDbConnection();
var t2PositionDatas = conn.Query(t2PositionDateSql, new
{
EndTime = dateT2,
UserAssets = reqModel.UserAssets,
UserClients = reqModel.UserClients,
ClientIds = reqModel.ClientIds,
BookIds = reqModel.BookIds,
TradeTypes = reqModel.TradeTypes,
TraderIds = reqModel.TraderIds,
TagIds = reqModel.TagIds,
UnderlyingIds = reqModel.UnderlyingIds,
VarietyIds = reqModel.VarietyIds
}, commandTimeout: 1800).ToList();
return t2PositionDatas;
}
});
}
}
}
//如果数据未获取到,sum时报错,这时需要加nullable类型转换