455 lines
20 KiB
C#
455 lines
20 KiB
C#
using YLErp.BLL;
|
|
using YLErp.BLL.Eod;
|
|
using YLErp.Model;
|
|
|
|
namespace YLErp.Modules.RiskListModule
|
|
{
|
|
public class RiskDailyReportService : YLBaseService
|
|
{
|
|
/// <summary>
|
|
/// 年盈亏起算日
|
|
/// </summary>
|
|
public DateTime CurrentStartDate { get; private set; }
|
|
/// <summary>
|
|
/// 年累计盈亏截止日
|
|
/// <para>最后一次收盘成功的日期</para>
|
|
/// </summary>
|
|
public DateTime CurrentEndDate { get; private set; }
|
|
/// <summary>
|
|
/// 上年累计盈亏截止日
|
|
/// </summary>
|
|
public DateTime LastYearEndDate { get; private set; }
|
|
/// <summary>
|
|
/// 前一次收盘日期
|
|
/// <para>用于计算截止日价差</para>
|
|
/// </summary>
|
|
public DateTime CurrentLastDate { get; private set; }
|
|
/// <summary>
|
|
/// 总资金
|
|
/// </summary>
|
|
public double Total { get; private set; }
|
|
/// <summary>
|
|
/// 可用资金
|
|
/// </summary>
|
|
public double Available { get; private set; }
|
|
/// <summary>
|
|
/// 客户权益
|
|
/// </summary>
|
|
public double CurrFund { get; private set; }
|
|
/// <summary>
|
|
/// 预付金占用
|
|
/// </summary>
|
|
public double CurrMargin { get; private set; }
|
|
|
|
public RiskDailyReportService(OptUserInfo userInfo, DateTime valueDate) : base(userInfo)
|
|
{
|
|
CurrentStartDate = valuedateBLL.SystemDate.AccruedTotalPnlStartDate.GetValueOrDefault();
|
|
CurrentEndDate = EodOperationBase.GetLastSettlementDate(valueDate);
|
|
LastYearEndDate = CurrentStartDate == default(DateTime) ? CurrentStartDate : valuedateBLL.GetNonHolidayDefore(CurrentStartDate.AddDays(-1));
|
|
CurrentLastDate = valuedateBLL.GetNonHolidayDefore(CurrentEndDate.AddDays(-1));
|
|
|
|
var account = DbContext.eod_exchange_account.Where(O => O.ValueDate == CurrentEndDate).ToList();
|
|
Total = account.Sum(O => O.Total);
|
|
Available = account.Sum(O => O.Available);
|
|
CurrFund = account.Sum(O => O.CurrFund);
|
|
CurrMargin = account.Sum(O => O.CurrMargin);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置累计总盈亏字段值
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="objs"></param>
|
|
private List<T> ResetOptionAccruedTotalPnL<T>(List<T> objs, DateTime valueDate, List<int> settleTradeIds) where T : EodTradePosition, new()
|
|
{
|
|
var result = objs.Select(O => (T)O.Clone()).ToList();
|
|
if (valueDate > this.LastYearEndDate)
|
|
{
|
|
var dbTable = DbContext.Set<T>();
|
|
var ids = result.Where(O => !settleTradeIds.Contains(O.TradeId)).Select(O => O.TradeId).ToList();
|
|
var list =
|
|
(from eodDb in dbTable
|
|
where eodDb.ValueDate == LastYearEndDate &&
|
|
ids.Contains(eodDb.TradeId)
|
|
select eodDb).ToList();
|
|
list.ForEach(O =>
|
|
{
|
|
var item = result.Find(B => B.TradeId == O.TradeId);
|
|
item.TotalPnL -= O.TotalPnL;
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置累计总盈亏字段值
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="objs"></param>
|
|
private List<T> ResetFutureAccruedTotalPnL<T>(List<T> objs, DateTime valueDate) where T : EodTradePosition, new()
|
|
{
|
|
var result = objs.Select(O => (T)O.Clone()).ToList();
|
|
if (valueDate > this.LastYearEndDate)
|
|
{
|
|
var dbTable = DbContext.Set<T>();
|
|
var ids = result.Select(O => $"{O.UnderlyingCode}_{O.PositionType}_{O.BookId}").ToList();
|
|
var list =
|
|
(from eodDb in dbTable
|
|
where eodDb.ValueDate == LastYearEndDate
|
|
select eodDb).ToList().FindAll(O => ids.Contains($"{O.UnderlyingCode}_{O.PositionType}_{O.BookId}"));
|
|
list.ForEach(O =>
|
|
{
|
|
var item = result.Find(B => $"{B.UnderlyingCode}_{B.PositionType}_{B.BookId}" == $"{O.UnderlyingCode}_{O.PositionType}_{O.BookId}");
|
|
item.TotalPnL -= O.TotalPnL;
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public double GetOptionPositionAccruedTotalPnL<T>(DateTime valueDate) where T : EodTradePosition, new()
|
|
{
|
|
var dbTable = DbContext.Set<T>();
|
|
var tradeIdList = (from et in DbContext.eod_trade
|
|
where et.ValueDate == valueDate &&
|
|
ConsTrade.LiveTradeStatusList.Contains(et.TradeStatus) &&
|
|
ConsTrade.OptionTradeTypes.Contains(et.TradeType)/* &&
|
|
!et.TradeJson.Contains("InValid")*/
|
|
select et.TradeId).ToArray();
|
|
var query = (from eodDb in dbTable
|
|
where eodDb.ValueDate == valueDate
|
|
&& tradeIdList.Contains(eodDb.TradeId)
|
|
select eodDb.TotalPnL).ToList().Sum();
|
|
var settlementPrice = (from cash in DbContext.trade_cash
|
|
where tradeIdList.Contains(cash.TradeId) &&
|
|
ClientCashInCashOut.PROFIT_ACTION.Contains(cash.Action) &&
|
|
cash.ValidState != ConsGlobal.InValid &&
|
|
!cash.IsDeleted &&
|
|
cash.ValueDate <= valueDate
|
|
select cash).ToList();
|
|
var tradeIds = settlementPrice.Select(O => O.TradeId).ToHashSet();
|
|
var openPrice = (from cash in DbContext.trade_cash
|
|
where tradeIds.Contains(cash.TradeId) &&
|
|
ClientCashInCashOut.系统操作_期权费 == cash.Action &&
|
|
cash.Amount != 0 &&
|
|
cash.ValidState != ConsGlobal.InValid &&
|
|
!cash.IsDeleted &&
|
|
cash.ValueDate <= valueDate
|
|
select cash).ToDictionary(K => K.TradeId, V => V);
|
|
|
|
double price = 0;
|
|
foreach (var item in settlementPrice)
|
|
{
|
|
if (openPrice.ContainsKey(item.TradeId))
|
|
{
|
|
price += (item.Amount + (item.UnwindNotional / openPrice[item.TradeId].Notional) * openPrice[item.TradeId].Amount) ?? 0;
|
|
}
|
|
else
|
|
{
|
|
price += item.Amount;
|
|
}
|
|
}
|
|
return query - price;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期权列表
|
|
/// </summary>
|
|
public List<T> GetOptionPositionList<T>(DateTime valueDate) where T : EodTradePosition, new()
|
|
{
|
|
var lastDate = valuedateBLL.GetNonHolidayDefore(valueDate.AddDays(-1));
|
|
|
|
var actions = new List<string>() { "系统操作-行权费", "系统操作-平仓费" };
|
|
|
|
var dbTable = DbContext.Set<T>();
|
|
|
|
var idList = (from et in DbContext.eod_trade
|
|
where et.ValueDate == valueDate &&
|
|
ConsTrade.LiveTradeStatusList.Contains(et.TradeStatus) &&
|
|
ConsTrade.OptionTradeTypes.Contains(et.TradeType)/* &&
|
|
!et.TradeJson.Contains("InValid")*/
|
|
select et.id).ToArray();
|
|
|
|
var tcQuery = DbContext.trade_cash.Where(tc => !tc.IsDeleted && tc.ValidState != "InValid" && actions.Contains(tc.Action) && tc.ValueDate == valueDate);
|
|
|
|
var query = from eodDb in dbTable
|
|
join eodT in DbContext.eod_trade.Where(O => idList.Contains(O.id)) on eodDb.TradeId equals eodT.TradeId
|
|
join lastTempDb in dbTable.Where(et => et.ValueDate == lastDate) on eodT.TradeId equals lastTempDb.TradeId into lastTempDb
|
|
from lastDb in lastTempDb.DefaultIfEmpty()
|
|
join tempTc in tcQuery on eodT.TradeId equals tempTc.TradeId into tempTc
|
|
from tc in tempTc.DefaultIfEmpty()
|
|
where ConsTrade.OptionTradeTypes.Contains(eodDb.TradeType) && eodDb.ValueDate == valueDate
|
|
select new { eodT, tc, lastDb, eodDb };
|
|
var temp = query.ToList();
|
|
var settleTradeId = new List<int>();
|
|
temp.ForEach(O =>
|
|
{
|
|
if (!ConsTrade.LiveTradeStatusList.Contains(O.eodT.TradeStatus))
|
|
{
|
|
O.eodDb.Pv = 0;
|
|
if (O.lastDb == null)//如果是当天开当天平的交易
|
|
{
|
|
O.eodDb.TotalPnL = -GetOptionEndPnl(valueDate, valueDate, new List<int> { O.eodDb.TradeId });
|
|
}
|
|
else//如果是当天了结的交易
|
|
{
|
|
O.eodDb.TotalPnL = -O.lastDb.TotalPnL;
|
|
}
|
|
O.eodDb.DailyPnL = O.eodDb.TotalPnL;
|
|
settleTradeId.Add(O.eodDb.TradeId);
|
|
}
|
|
else if (O.eodT.trade.HasPartialUnWind > 0)//如果交易有部分平仓过
|
|
{
|
|
O.eodDb.TotalPnL = O.eodDb.TotalPnL - GetOptionEndPnl(DateTime.MinValue, O.eodT.ValueDate, new List<int> { O.eodDb.TradeId });
|
|
if ((O.tc?.UnwindNotional ?? 0) > 0)//如果当天有部分平仓过
|
|
{ O.eodDb.DailyPnL = O.eodDb.DailyPnL - GetOptionEndPnl(valueDate, valueDate, new List<int> { O.eodDb.TradeId }); }
|
|
}
|
|
});
|
|
var list = temp.Select(O => O.eodDb).ToList();
|
|
list = ResetOptionAccruedTotalPnL(list, valueDate, settleTradeId);
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取场内期权列表
|
|
/// </summary>
|
|
public Dictionary<Exchange_Option_Trade, eod_exchange_option_price> GetExchangePositionList(DateTime valueDate)
|
|
{
|
|
var lastDate = valuedateBLL.GetNonHolidayDefore(valueDate.AddDays(-1));
|
|
|
|
var actions = new List<string>() { "系统操作-行权费", "系统操作-平仓费" };
|
|
|
|
Dictionary<Exchange_Option_Trade, eod_exchange_option_price> result = null;
|
|
using (var db = new YLContext())
|
|
{
|
|
result =
|
|
(from eodDb in db.Exchange_Option_Trade
|
|
join eodPrice in db.eod_exchange_option_price.Where(p => p.ValueDate == valueDate)
|
|
on eodDb.Code equals eodPrice.ContractCode into dbPrice
|
|
from eodPrice in dbPrice.DefaultIfEmpty()
|
|
where
|
|
(eodDb.ExerciseDate == DateTime.MinValue || eodDb.ExerciseDate > valueDate) && eodDb.TradeDate <= valueDate
|
|
select new { eodDb, eodPrice }).ToDictionary(K => K.eodDb, V => V.eodPrice);
|
|
}
|
|
foreach (var item in result)
|
|
{
|
|
if (item.Key.TradeType == "空头")
|
|
{ item.Key.TradeAmount = item.Key.TradeAmount * -1; }
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期货列表
|
|
/// </summary>
|
|
/// <param name="startDate"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <returns></returns>
|
|
public List<T> GetFutureList<T>(DateTime startDate, DateTime endDate = default(DateTime)) where T : EodTradePosition, new()
|
|
{
|
|
if (endDate < startDate) { endDate = startDate; }
|
|
var dbTable = DbContext.Set<T>();
|
|
var list =
|
|
(from eodDb in dbTable
|
|
where eodDb.TradeType == "商品期货" &&
|
|
eodDb.ValueDate >= startDate &&
|
|
eodDb.ValueDate <= endDate
|
|
select eodDb).ToList();
|
|
list = ResetFutureAccruedTotalPnL(list, endDate);
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期货总盈亏
|
|
/// </summary>
|
|
/// <param name="currentStart"></param>
|
|
/// <param name="currentEnd"></param>
|
|
/// <param name="lastDate"></param>
|
|
/// <returns></returns>
|
|
public double GetYearFutureTotlePnl<T>(DateTime currentStart, DateTime currentEnd, DateTime lastDate) where T : EodTradePosition, new()
|
|
{
|
|
double result = 0;
|
|
var currentList =
|
|
GetFutureList<T>(currentStart, currentEnd)
|
|
.GroupBy(O => new { O.UnderlyingCode, O.PositionType, O.BookId })
|
|
.ToDictionary(K =>
|
|
{
|
|
var temp = K.First();
|
|
return $"{temp.UnderlyingCode}_{temp.PositionType}_{temp.BookId}";
|
|
}, V => new List<T>(V), StringComparer.OrdinalIgnoreCase);
|
|
var lastList =
|
|
GetFutureList<T>(lastDate)
|
|
.GroupBy(O => new { O.UnderlyingCode, O.PositionType, O.BookId })
|
|
.ToDictionary(K =>
|
|
{
|
|
var temp = K.First();
|
|
return $"{temp.UnderlyingCode}_{temp.PositionType}_{temp.BookId}";
|
|
}, V => new List<T>(V), StringComparer.OrdinalIgnoreCase);
|
|
foreach (var item in currentList)
|
|
{
|
|
double lastPnl = 0;
|
|
if (lastList.ContainsKey(item.Key))
|
|
{ lastPnl = lastList[item.Key].Sum(O => O.TotalPnL); }
|
|
var maxDate = item.Value.Max(B => B.ValueDate);
|
|
result += item.Value.FindAll(O => O.ValueDate == maxDate).Sum(O => O.TotalPnL) - lastPnl;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期货持仓盈亏-年
|
|
/// </summary>
|
|
/// <param name="lastDate"></param>
|
|
/// <returns></returns>
|
|
public double GetYearFuturePositionPnl<T>(DateTime lastDate) where T : EodTradePosition, new()
|
|
{
|
|
double result = 0;
|
|
var positionList =
|
|
GetFutureList<T>(lastDate);
|
|
result = positionList.Sum(O => (double)(O.DailyPnL));
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期权已了结盈亏
|
|
/// </summary>
|
|
public double GetOptionEndPnl(DateTime currentStart, DateTime currentEnd, List<int> tradeIds = null)
|
|
{
|
|
var actions = new List<string>() { "系统操作-行权费", "系统操作-平仓费" };
|
|
var query = from tc in DbContext.trade_cash
|
|
join t in DbContext.trade
|
|
on tc.TradeId equals t.id
|
|
where t.ValidState != "InValid" && !tc.IsDeleted
|
|
&& tc.ValueDate >= currentStart
|
|
&& tc.ValueDate <= currentEnd
|
|
&& actions.Contains(tc.Action)
|
|
select new { tc, t };
|
|
if (tradeIds?.Count > 0)
|
|
{ query = query.Where(O => tradeIds.Contains(O.t.id)); }
|
|
var result = query.Select(O => O.t.BuySell == "卖出"
|
|
? O.tc.Amount - (0 - O.t.TradePrice * O.tc.UnwindPercentRate)
|
|
: O.tc.Amount - O.t.TradePrice * O.tc.UnwindPercentRate).Sum();
|
|
|
|
//double result = list.Count > 0 ? list.Sum(O => O.Amount ?? 0) : 0;
|
|
return result ?? 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取持仓DeltaCash
|
|
/// </summary>
|
|
/// <param name="startDate"></param>
|
|
/// <param name="endDate"></param>
|
|
/// <returns></returns>
|
|
public Dictionary<string, double> GetFutureDeltaCash<T>(DateTime startDate, DateTime endDate = default(DateTime)) where T : EodTradePosition, new()
|
|
{
|
|
if (endDate < startDate) { endDate = startDate; }
|
|
var dbTable = DbContext.Set<T>();
|
|
var query = from eodDb in dbTable
|
|
join underlying in DbContext.underlying_manager
|
|
on eodDb.UnderlyingCode equals underlying.UnderlyingCode
|
|
join price in DbContext.eod_commodity_future_price
|
|
on eodDb.UnderlyingCode equals price.UnderlyingCode
|
|
where eodDb.TradeType == "商品期货" &&
|
|
eodDb.ValueDate >= startDate &&
|
|
eodDb.ValueDate <= endDate &&
|
|
eodDb.Amount != 0 &&
|
|
price.ValueDate == endDate
|
|
select new
|
|
{
|
|
underlying.CommodityCode,
|
|
eodDb.UnderlyingCode,
|
|
eodDb.ValueDate,
|
|
eodDb.Amount,
|
|
price.ClosePrice
|
|
};
|
|
|
|
var list = query.AsEnumerable();
|
|
var result = list.GroupBy(O => O.CommodityCode).ToDictionary(
|
|
K => K.Key,
|
|
V => V.Sum(O => O.Amount * O.ClosePrice),
|
|
StringComparer.OrdinalIgnoreCase);
|
|
//var list =
|
|
// query.
|
|
// GroupBy(O => O.CommodityCode)
|
|
// .ToDictionary(
|
|
// K => K.Key,
|
|
// V => V.Sum(O => O.Amount * O.ClosePrice),
|
|
// StringComparer.OrdinalIgnoreCase);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取DeltaCash
|
|
/// </summary>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="sourceType"></param>
|
|
/// <returns></returns>
|
|
public Dictionary<string, double> GetOptionDeltaCash<T1, T2>(DateTime valueDate) where T1 : EodTradeRisk, new() where T2 : EodTradePosition, new()
|
|
{
|
|
var dbTable = DbContext.Set<T1>();
|
|
var pair =
|
|
(from riskDb in dbTable
|
|
join tDb in DbContext.trade
|
|
on new { id = riskDb.TradeId } equals new { tDb.id }
|
|
join underlying in DbContext.underlying_manager
|
|
on tDb.UnderlyingCode equals underlying.UnderlyingCode
|
|
where underlying.CommodityCode != null &&
|
|
tDb.ValidState != "InValid" &&
|
|
riskDb.ValueDate == valueDate
|
|
select new EodTradeRisk { Exposure = underlying.CommodityCode, DeltaCash = riskDb.DeltaCash }).ToList();
|
|
|
|
var option =
|
|
pair.GroupBy(O => O.Exposure).ToDictionary(K => K.Key, V => V.Select(O => O.DeltaCash).Sum(), StringComparer.OrdinalIgnoreCase);
|
|
return option;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取GammaCash
|
|
/// </summary>
|
|
/// <param name="valueDate"></param>
|
|
/// <returns></returns>
|
|
public double GetOptionGammaCash<T1, T2>(DateTime valueDate) where T1 : EodTradeRisk, new() where T2 : EodTradePosition, new()
|
|
{
|
|
var dbTable = DbContext.Set<T1>();
|
|
var query = (from riskDb in dbTable
|
|
join tDb in DbContext.trade
|
|
on new { id = riskDb.TradeId } equals new { id = tDb.id }
|
|
join price in DbContext.eod_commodity_future_price
|
|
on tDb.UnderlyingCode equals price.UnderlyingCode
|
|
where tDb.ValidState != "InValid" &&
|
|
riskDb.ValueDate == valueDate &&
|
|
price.ValueDate == valueDate
|
|
select new
|
|
{
|
|
riskDb.Gamma,
|
|
price.ClosePrice
|
|
});
|
|
var gammaList = query.ToList();
|
|
double gammaCash = 0;
|
|
foreach (var item in gammaList)
|
|
{
|
|
gammaCash += (double)item.Gamma * 0.5 * Math.Pow(item.ClosePrice * 0.01, 2);
|
|
}
|
|
return gammaCash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期初名义本金
|
|
/// </summary>
|
|
/// <param name="currentEndDate"></param>
|
|
/// <returns></returns>
|
|
public double GetTradePrice(DateTime currentEndDate)
|
|
{
|
|
var datas = (from tradeDb in DbContext.eod_trade
|
|
where tradeDb.ValueDate == currentEndDate &&
|
|
ConsTrade.LiveTradeStatusList.Contains(tradeDb.TradeStatus)
|
|
select tradeDb).ToList();
|
|
double result = 0;
|
|
foreach (var item in datas)
|
|
{ result += (item.trade.Notional * (item.trade.SpotPrice ?? 0)); }
|
|
return result;
|
|
}
|
|
}
|
|
}
|