Files
zszq-trs/YLErpDAL/Modules/TradeRiskCalcModule/ExchangePositionCalcService.cs
T
2024-05-09 14:06:26 +08:00

335 lines
12 KiB
C#

using YLErp.Modules.CalculationModule;
using YLErp.Modules.CalculationModule.Abstract;
using YLErp.Modules.DataProviderModule;
namespace YLErp.Modules.TradeRiskCalcModule
{
/// <summary>
/// 实时场内持仓计算服务(用于API)
/// </summary>
public class ExchangePositionCalcService
{
readonly DateTime _valueDate;
readonly IExchangeTradeCommissionCalc _commissionCalc;
public ExchangePositionCalcService(DateTime valueDate, IExchangeTradeCommissionCalc commissionCalc)
{
_valueDate = valueDate.Date;
_commissionCalc = commissionCalc ?? throw new ArgumentNullException(nameof(commissionCalc));
}
/// <summary>
/// 计算对冲交易Pnl信息
/// </summary>
/// <param name="newHedgeTrades">当日对冲交易数据</param>
/// <param name="eodPositions">上日持仓数据</param>
public IEnumerable<ExchangePositionItem> Calculate(IEnumerable<ExchangeTrade> newHedgeTrades
, IEnumerable<EodTradePosition> eodPositions, ExchangeOptionPriceProvider exchangeOptionPriceProvider)
{
if (newHedgeTrades == null && eodPositions == null)
{
return Enumerable.Empty<ExchangePositionItem>();
}
var resultDic = new Dictionary<string, ExchangePositionItem>();
var exchangeOptionSource = DataCacheProvider.GetExchangeListOptionDataSource();
//-----------------------------------------
// 处理昨日持仓
//-----------------------------------------
if (eodPositions != null)
{
foreach (var eod in eodPositions)
{
if (eod.TradeType == "场内期权")
{
var exchangeOption = exchangeOptionSource.GetData(eod.ExchangeOptionCode);
//剔除已到期场内期权持仓
if (exchangeOption == null || exchangeOption.MaturityDate < _valueDate)
{
continue;
}
}
var uniqueCode = GetUniqueCode(eod.BookId, eod.TradeType, eod.UnderlyingCode, eod.ExchangeOptionCode);
if (!resultDic.TryGetValue(uniqueCode, out var result))
{
resultDic[uniqueCode] = result = new ExchangePositionItem()
{
//合成唯一标记
BookId = eod.BookId,
TradeType = eod.TradeType,
UnderlyingCode = eod.UnderlyingCode,
ExchangeOptionCode = eod.ExchangeOptionCode,
BookName = null,
ExchangeId = null,
LastPriceTime = null
};
}
//合并汇总
result.Commission += eod.Commission;
result.PositionProfit += eod.TotalPnL;
if (Math.Abs(eod.Amount) > 0)
{
if ("short" == eod.PositionType)
{
result.Volume_Short += eod.Amount;
//因为TotalPnL已经包括了Cost所以用PV
result.PositionCost_Short += eod.Pv;
}
else
{
result.Volume_Long += eod.Amount;
result.PositionCost_Long += eod.Pv;
}
}
}
}
//-----------------------------------------
// 处理当日交易
//-----------------------------------------
if (newHedgeTrades != null && newHedgeTrades.Any())
{
//对冲交易手续费计算
var tradeCommissionDict = _commissionCalc.GetTradeCommission(newHedgeTrades);
foreach (var newTrade in newHedgeTrades)
{
//手续费
var commission = tradeCommissionDict.GetTradeCommission(newTrade.id);
//对冲唯一编码(簿记账户ID_结构类型_持仓类型_合约代码)
var uniqueCode = GetUniqueCode(newTrade.AssetBookId, newTrade.TradeType, newTrade.UnderlyingCode, newTrade.OptionCode);
//当日交易数量/成本(根据交易方向取正负号)
var volume = newTrade.Notional * TradeCalcHelper.GetSign(newTrade.TradeSide);
var cost = newTrade.TradeSinglePrice * volume + commission;
//获取是否存在对应uniqueCode的对冲信息
if (!resultDic.TryGetValue(uniqueCode, out var result))
{
resultDic[uniqueCode] = result = new ExchangePositionItem()
{
BookId = newTrade.AssetBookId,
TradeType = newTrade.TradeType,
UnderlyingCode = newTrade.UnderlyingCode,
ExchangeOptionCode = newTrade.OptionCode,
BookName = null,
ExchangeId = null,
LastPriceTime = null
};
}
//合并汇总
result.Commission += commission;
if (newTrade.TradeSide != null && newTrade.TradeSide.Contains("空头"))
{
result.Volume_Short += volume;
result.PositionCost_Short += cost;
}
else
{
result.Volume_Long += volume;
result.PositionCost_Long += cost;
}
}
}
//-----------------------------------------
// 计算持仓盈亏
//-----------------------------------------
var underlyingSource = DataCacheProvider.GetUnderlyingDataSource();
var assetBookSource = DataCacheProvider.GetAssetUnitDataSource();
if (exchangeOptionPriceProvider == null)
{
exchangeOptionPriceProvider = new ExchangeOptionPriceProvider();
}
foreach (var result in resultDic.Values)
{
var um = underlyingSource.GetData(result.UnderlyingCode);
result.ExchangeId = um.MarketCode;
var hasPrice = false;
if (result.TradeType == "场内期权")
{
if (exchangeOptionPriceProvider.TryGetPriceModel(result.ExchangeOptionCode, out var model))
{
hasPrice = true;
result.LastPrice = model.Price;
result.LastPriceTime = model.PriceTime?.ToString("yyyy-MM-dd'T'HH:mm:ss.fff");
}
}
else if (um != null)
{
hasPrice = true;
result.LastPrice = um.Price ?? 0;
result.LastPriceTime = um.LastUpdateTime?.ToString("yyyy-MM-dd'T'HH:mm:ss.fff");
}
if (hasPrice)
{
var longProfit = result.LastPrice * result.Volume_Long - result.PositionCost_Long;
var shortProfit = result.LastPrice * result.Volume_Short - result.PositionCost_Short;
result.PositionProfit += longProfit + shortProfit;
}
result.BookName = assetBookSource.GetData(result.BookId)?.Name;
}
return resultDic.Values;
}
/// <summary>
/// 根据对冲账号 簿记账户I 结构类型 买卖方向 标的代码 场内期权代码 编制对冲唯一编码
/// </summary>
private static string GetUniqueCode(int BookId, string TradeType, string UnderlyingCode, string ExchangeOptionCode = null)
{
return $"{BookId}_{TradeType}_{("场内期权".Equals(TradeType) ? ExchangeOptionCode : UnderlyingCode)}";
}
}
/// <summary>
/// 场内持仓计算单项结果
/// </summary>
public class ExchangePositionItem
{
/// <summary>
/// [unique]簿记ID
/// </summary>
public int BookId { get; set; }
/// <summary>
/// 簿记账户
/// </summary>
public string BookName { get; set; }
/// <summary>
/// [unique]交易类型
/// </summary>
public string TradeType { get; set; }
/// <summary>
/// [unique]标的代码
/// </summary>
public string UnderlyingCode { get; set; }
/// <summary>
/// [unique]场内期权代码
/// </summary>
public string ExchangeOptionCode { get; set; }
/// <summary>
/// 持仓数量
/// </summary>
public double Volume => Volume_Long + Volume_Short;
/// <summary>
/// 手续费
/// </summary>
public double Commission { get; set; }
/// <summary>
/// 持仓成本
/// </summary>
public double PositionCost => PositionCost_Long + PositionCost_Short;
/// <summary>
/// 最新价
/// </summary>
public double LastPrice { get; set; }
/// <summary>
/// 最新价更新时间(格式:yyyy-MM-ddTHH:mm:ss.fff)
/// </summary>
public string LastPriceTime { get; set; }
/// <summary>
/// 持仓盈亏
/// </summary>
public double PositionProfit { get; set; }
/// <summary>
/// 交易所ID
/// </summary>
public string ExchangeId { get; set; }
/// <summary>
/// 多头持仓成本(使用了正号处理)
/// </summary>
public double Volume_Long { get; set; }
/// <summary>
/// 空头持仓成本(使用了负号处理)
/// </summary>
public double Volume_Short { get; set; }
/// <summary>
/// 多头持仓成本(使用了正号处理)
/// </summary>
public double PositionCost_Long { get; set; }
/// <summary>
/// 空头持仓成本(使用了负号处理)
/// </summary>
public double PositionCost_Short { get; set; }
public override string ToString()
{
return $"{BookName}--{UnderlyingCode}--{Volume}--{LastPrice}--{PositionProfit}--{PositionCost}";
}
}
/// <summary>
/// 场内持仓计算结果
/// </summary>
public class ExchangePositionCalcResult
{
/// <summary>
/// 当前结算日
/// </summary>
public DateTime ValueDate { get; set; }
/// <summary>
/// 上个结算日
/// </summary>
public DateTime LastDate { get; set; }
/// <summary>
/// 结果数据集合
/// </summary>
public IEnumerable<ExchangePositionItem> Items { get; set; }
/// <summary>
/// 开始运行时间
/// </summary>
public DateTime StartTime { get; }
/// <summary>
/// 结束运行时间
/// </summary>
public DateTime EndTime { get; } = DateTime.Now;
public ExchangePositionCalcResult(DateTime startTime)
{
StartTime = startTime;
}
}
}