using Qdp.Pricing.Base.Implementations;
using YLErp.Abstract.DataProviders;
using YLErp.Commons;
using YLErp.Modules;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.MarginModule;
using YLErp.QdpModule;
namespace YLErp.BLL.MarginCalculation
{
public partial class MarginCalculationBase
{
///
/// 预付金计算帮助类
///
protected class RunMarginCalculationHelper
{
public readonly RunMarginCalculationReq req;
readonly IUnderlyingDataProvider underlyingDataProvider;
HashSet _underlyingCodeSet;
Dictionary _clientDic;
readonly MarginParamProvider _mpProvider;
public RunMarginCalculationHelper(RunMarginCalculationReq req, IUnderlyingDataProvider underlyingDataProvider)
{
this.req = req;
this.underlyingDataProvider = underlyingDataProvider;
_mpProvider = new MarginParamProvider(req.UserInfo, req.settleDate);
}
#region----数据准备(内部方法)----
///
/// 交易对应客户信息
///
protected void PrepareClient()
{
if (_clientDic != null)
{
return;
}
var clientIds = req.tradeList.Select(t => t.ClientId).ToHashSet();
using (var db = DbContextFactory.GetClientDbContext(req.UserInfo))
{
var query = from c in db.client
join cl in db.clientlevel on c.LevelId equals cl.id into t_cl
from cl in t_cl.DefaultIfEmpty()
where clientIds.Contains(c.id)
select new InnerClient
{
ClientId = c.id,
Ratio = cl == null ? null : cl.Ratio,
Ratio1 = cl == null ? null : cl.Ratio1,
AddRatio = cl == null ? null : cl.AddRatio,
MarginOptionType = c.MarginOptionType,
ProperClientClass = c.ProperClientClass,
QuestionnaireScore = c.QuestionnaireScore,
RuleT0orT1 = c.RuleT0orT1,
BoundSide = c.BoundSide
};
_clientDic = query.ToDictionary(n => n.ClientId);
}
}
///
///
///
protected HashSet PrepareUnderlyingCodes()
{
if (_underlyingCodeSet != null)
{
return _underlyingCodeSet;
}
_underlyingCodeSet = new HashSet(StringComparer.OrdinalIgnoreCase);
void setUnderlyingCode(trade td)
{
if (td?.UnderlyingCode == null)
{
return;
}
_underlyingCodeSet.Add(td.UnderlyingCode);
switch (td.TradeType)
{
case "彩虹期权":
if (td.trade_rainbow_option != null)
{
_underlyingCodeSet.Add(td.trade_rainbow_option.UnderlyingAssetCode2);
}
break;
case "价差期权":
if (td.trade_spread_option != null)
{
var codes = td.trade_spread_option.UnderlyingAssetCodes();
foreach (var code in codes)
{
_underlyingCodeSet.Add(code);
}
}
break;
case "结构化交易":
if (td.SubTrades != null)
{
foreach (var std in td.SubTrades)
{
setUnderlyingCode(std);
}
}
break;
}
}
foreach (var td in req.tradeList)
{
setUnderlyingCode(td);
}
return _underlyingCodeSet;
}
#endregion
#region----提供数据(公共)----
///
/// 获取标的数据
///
public underlying_manager GetUnderlying(string underlyingCode)
{
return underlyingDataProvider.GetUnderlying(underlyingCode);
}
///
/// 获取underlying code集合
///
///
public HashSet GetUnderlyingCodes()
{
return _underlyingCodeSet;
}
///
/// 根据交易获取客户数据
///
public InnerClient GetClient(trade trade)
{
return GetClient(trade.ClientId);
}
///
/// 根据客户ID获取客户数据
///
public InnerClient GetClient(int clientId)
{
if (clientId < 1)
{
return null;
}
if (_clientDic == null)
{
PrepareClient();
}
return _clientDic.TryGetValue(clientId, out var client) ? client : null;
}
public List GetClients()
{
if (_clientDic == null)
{
PrepareClient();
}
return _clientDic.Values.ToList();
}
///
/// 获取预付金参数提供
///
public MarginParamProvider GetMarginParamProvider(MarginParamTypeEnum initTypeEnums = MarginParamTypeEnum.None)
{
if (initTypeEnums != MarginParamTypeEnum.None)
{
var umCodeSet = PrepareUnderlyingCodes();
_mpProvider.Initialize(umCodeSet, initTypeEnums);
}
return _mpProvider;
}
///
/// 根据标的ID获取涨跌停价格
///
public UpdownLimit GetUpDownLimit(string underlyingCode, double price, bool useMinPriceChange = false, double extendLimitRate = 1)
{
if (_mpProvider.TryGetUpdownLimit(underlyingCode, out var limit, out var isFixed))
{
if (!isFixed)
{
limit *= price;
}
}
else
{
limit = price * 0.05;
}
//注意:price价格可能为负值
limit = Math.Abs(limit);
if (useMinPriceChange)
{
var um = underlyingDataProvider.GetUnderlying(underlyingCode);
var minPriceChange = um?.PriceTick ?? 0.01;
var half = minPriceChange / 2;
var diff = limit % minPriceChange;
limit -= diff;
if (diff >= half)
{
limit += minPriceChange;
}
}
return new UpdownLimit
{
UpLimitPrice = price + limit * extendLimitRate,
DownLimitPrice = price - limit * extendLimitRate
};
}
///
/// 根据涨跌停比例获取张跌停价
///
///
///
///
public UpdownLimit GetUpDownLimitByRate(double price, double limitRate)
{
return new UpdownLimit
{
UpLimitPrice = price * (1 + limitRate),
DownLimitPrice = price * (1 - limitRate)
};
}
public UpdownLimit GetStockUpDownLimit(string underlyingCode, double price)
{
var limit = 0.1;
double limitPrice;
if (underlyingCode.StartsWith("30") || underlyingCode.StartsWith("688"))
{
limit = 0.2;
}
limitPrice = price * limit;
var minPriceChange = 0.01;
var half = minPriceChange / 2;
var diff = limitPrice % minPriceChange;
limitPrice -= diff;
if (diff >= half)
{
limitPrice += minPriceChange;
}
return new UpdownLimit
{
UpLimitPrice = OtcFormatHelper.FormatValue(price + limitPrice, 2),
DownLimitPrice = OtcFormatHelper.FormatValue(price - limitPrice, 2)
};
}
///
/// 获取涨跌停价格字典
///
///
///
///
/// 涨跌停额外系数,默认为1
public void GetUpDownLimitPrices(out IPriceProvider upLimitPrices, out IPriceProvider downLimitPrices, bool useMinPriceChange = false, double extendLimitRate = 1, bool isUsePriceLimit = true)
{
var umCodeSet = PrepareUnderlyingCodes();
var upLimitPrices2 = new ManualPriceProvider();
var downLimitPrices2 = new ManualPriceProvider();
//根据涨跌幅限制以及当日结算价计算涨停价以及跌停价
foreach (var code in umCodeSet)
{
var price = req.PriceProvider.GetPrice(code);
var updown = isUsePriceLimit ? GetUpDownLimit(code, price, useMinPriceChange, extendLimitRate) : GetUpDownLimitByRate(price, extendLimitRate);
upLimitPrices2.SetPrice(code, updown.UpLimitPrice);
downLimitPrices2.SetPrice(code, updown.DownLimitPrice);
}
upLimitPrices = upLimitPrices2;
downLimitPrices = downLimitPrices2;
}
///
/// 获取涨跌停价格字典
/// 可通过SplitNumber控制涨停到跌停的分割数量
/// 例如:涨停24,跌停6,splitNumber=6,
/// 则返回的数组的lenght=7,分别为24,21,18,15,12,9,6
///
/// 从涨停到跌停分割多少次
///
///
/// 涨跌停额外系数,默认为1
///
public IPriceProvider[] GetUpDownLimitPrices(int splitNumber, bool useMinPriceChange = false, double extendLimitRate = 1, bool isUsePriceLimit = true)
{
var umCodeSet = PrepareUnderlyingCodes();
var priceArr = new ManualPriceProvider[splitNumber + 1];
for (int i = 0; i < priceArr.Length; i++)
{
priceArr[i] = new ManualPriceProvider();
}
//根据涨跌幅限制以及当日结算价计算涨停价以及跌停价
foreach (var code in umCodeSet)
{
var price = req.PriceProvider.GetPrice(code);
var updown = isUsePriceLimit ? GetUpDownLimit(code, price, useMinPriceChange, extendLimitRate) : GetUpDownLimitByRate(price, extendLimitRate);
var gap = (updown.UpLimitPrice - updown.DownLimitPrice) / splitNumber;
for (int i = 0; i < priceArr.Length - 1; i++)
{
priceArr[i].SetPrice(code, (updown.UpLimitPrice - (gap * i)).FormatValue(10));
}
priceArr[priceArr.Length - 1].SetPrice(code, updown.DownLimitPrice);
}
return priceArr.Cast().ToArray();
}
///
/// 获取持仓波动率字典
///
public void GetTradVolRateDic(out Dictionary volRateDic, Func addVol = null)
{
var tradeVolRateArr = req.tradeList.Select(t => new
{
tradeId = t.id,
volRate = (_mpProvider.GetVolatilityRate(t.UnderlyingCode) ?? 0) + (addVol?.Invoke(t) ?? 0)
}).Where(d => d.volRate > 0).ToArray();
volRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate);
}
///
/// 获取持仓波动率字典
///
public void GetMarginTradVolRateDic(out Dictionary volRateDic, Func addVol = null)
{
var tradeVolRateArr = req.tradeList.Select(t => new
{
tradeId = t.id,
volRate = GetTradeVol(t.TradeNumber, req.settleDate) ?? 0
}).Where(d => d.volRate > 0).ToArray();
volRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate);
}
///
/// 获取持仓波动率字典
///
public void GetUpDownVolRateDic(out Dictionary upVolRateDic, out Dictionary downVolRateDic)
{
var tradeVolRateArr = req.tradeList.Select(t => new
{
tradeId = t.id,
volRate = _mpProvider.GetVolatilityRate(t.UnderlyingCode) ?? 0
}).Where(d => d.volRate > 0).ToArray();
upVolRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate);
downVolRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => -d.volRate);
}
#endregion
#region----实用方法(公共)----
///
/// 将BuySell换做相反的方向,
/// 交易列表变更为克隆数据,所以不需要再对交易恢复原来的方向
///
/// 强制转换
public void ReverseTradeSide(bool force = false)
{
if (!force && !req.forOtherSide)
{
return;
}
req.tradeList = req.tradeList.Select(n =>
{
var clone = n.Clone();
clone.BuySell = clone.BuySell == "买入" ? "卖出" : "买入";
if (clone.SubTrades != null && clone.SubTrades.Any())
{
clone.SubTrades = clone.SubTrades.Select(m =>
{
var xs = m.Clone();
xs.BuySell = xs.BuySell == "买入" ? "卖出" : "买入";
return xs;
}).ToArray();
}
return clone;
}).ToList();
}
///
///
///
public void SetFieldsByTradeType()
{
if (!req.hasOptionInfo)
{
tradeBLL.SetFieldsByTradeType(req.tradeList);
}
}
///
/// 获取特殊预付金(手动维护或收益互换交易)
///
public bool GetSpecialMargin(trade trade, double calcPv, out double value, bool isForOtherSide = false)
{
value = 0;
//首先获取手动维护(包括收益互换交易)的预付金值
using (var db = DbContextFactory.GetYLDbContext())
{
var predicate = PredicateBuilder.Create(x => x.TradeId == trade.id && x.Margin != null && string.IsNullOrEmpty(x.VolType));
if (trade.TradeType == "自定义交易")
{
predicate = predicate.And(x => x.ValueDate <= req.settleDate);
}
else
{
//非自定义交易只取当天保存的持仓预付金
predicate = predicate.And(x => x.ValueDate == req.settleDate);
}
var manual = db.eod_trade_risk_manual.Where(predicate)
.OrderByDescending(x => x.ValueDate)
.Select(n => new { n.Margin, n.ValueDate }).FirstOrDefault();
if (manual != null && manual.Margin != null)
{
if (isForOtherSide)
{
value = 0;
}
else
{
value = manual.Margin.Value;
}
}
//自定义交易总是返回true并有一些特殊逻辑判断
if (trade.TradeType == "自定义交易")
{
//收盘时如果自定义交易还活着且没有维护当日风险,并且收的时系统日期当日的盘,抛出exception
if ((manual == null || manual.ValueDate != req.settleDate)
&& req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin && !ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus)
&& req.settleDate == valuedateBLL.ValueDate && !PS.Config.Is润和 && !PS.Config.IsMustRiskManual)
{
throw new Exception($"交易'{trade.TradeNumber}'在{req.settleDate:yyyy-MM-dd}需先进行交易风险维护");
}
return true;
}
//非自定义交易如果有手工维护的预付金则返回true
//否则如果是收益互换交易则进行计算,其他交易返回false
if (manual != null && manual.Margin != null)
{
return true;
}
}
//获取收益互换交易的预付金值
if (trade.TradeType == "收益互换")
{
return true;
}
return false;
}
///
/// 创建trade_span实例
///
public trade_span CreateTradeSpan(trade td, underlying_manager un = null)
{
if (td is null)
{
throw new ArgumentNullException(nameof(td));
}
if (un == null || td.UnderlyingCode != un.UnderlyingCode)
{
un = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
}
return new trade_span
{
TradeId = td.id,
ClientId = td.ClientId,
ValueDate = req.settleDate,
VarietyId = un?.UnderlyingTypeId,
UnderlyingId = td.UnderlyingId,
UnderlyingCode = td.UnderlyingCode,
OptId = req.userId,
OptName = req.userName,
OptDate = DateTime.Now
};
}
///
/// 获取计算一组交易的风险指标输入参数
///
public CalculateRisksForTradesReq GetCalculateRisksForTradesReq(IPriceProvider priceProvider,
Dictionary addVolRateDic, Dictionary overrideVols, PricingRequest pricingRequest = QdpPricingRequest.PV_ONLY, bool isEodCalc = false, string pricekey = null)
{
var tlist = pricekey != null && pricekey.Contains("barPrice") ? req.tradeList.Where(l => l.UnderlyingCode == pricekey.Split(',')[1]).ToList() : req.tradeList;
var reqConv = new CalculateRisksForTradesReq
{
valueDate = req.settleDate,
tradeList = tlist,
priceProvider = priceProvider,
pricingRequest = pricingRequest,
addVolRateDic = addVolRateDic,
volType = req.volType,
settlementType = req.settlementType,
isUseTradeVol = PS.Config.IsTradeVol,
PreciseTimeMode = req.CalcMarginType != Enums.CalcMarginTypeEnum.EodMargin,
isAddVolPercent = true,
overrideVolsForTrade = overrideVols,
isMarginCalc = true,
isEodCalc = isEodCalc
};
if (req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin)
{
reqConv.calcScenario = Enums.CalcScenarioEnum.EodSettlement;
}
if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin)
{
reqConv.calcScenario = Enums.CalcScenarioEnum.InitialMargin;
}
return reqConv;
}
public double? GetTradeVol(string TradeNumber, DateTime ValueDate)
{
using (var db = DbContextFactory.GetYLDbContext())
{
var tradeVol = db.trade_vol.Where(a => a.IsValid && a.TradeNumber == TradeNumber && a.ValueDate == ValueDate).FirstOrDefault();
if (tradeVol != null)
{
return tradeVol.Vol;
}
}
return null;
}
#endregion
}
#region----内部类----
///
///
///
protected class InnerClient
{
public int ClientId { get; set; }
///
/// 维持预付金系数(R2)
///
public double? Ratio { get; set; }
///
/// 初始预付金系数(R1)
///
public double? Ratio1 { get; set; }
///
/// 商品预付金率加点
///
public double? AddRatio { get; set; }
///
/// 是否有双向预付金
///
public int? MarginOptionType { get; set; }
///
/// 适当性类型
///
public string ProperClientClass { get; set; }
///
/// 资信评估分数
///
public int? QuestionnaireScore { get; set; }
///
/// 方顿预付金模板选择
///
public string RuleT0orT1 { get; set; }
///
/// 资金流向
///
public BoundSideEnum BoundSide { get; set; }
public override string ToString()
{
return ClientId.ToString();
}
}
///
/// 涨跌停限制
///
protected struct UpdownLimit
{
///
/// 涨停价
///
public double UpLimitPrice { get; set; }
///
/// 跌停价
///
public double DownLimitPrice { get; set; }
///
/// 涨跌停幅度:创业板为20%,其他默认为10%
///
public double limit { get; set; }
}
#endregion
}
}