|
@@ -4,6 +4,7 @@ import (
|
|
"eta/eta_api/models"
|
|
"eta/eta_api/models"
|
|
"eta/eta_api/utils"
|
|
"eta/eta_api/utils"
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "math"
|
|
"sync"
|
|
"sync"
|
|
"time"
|
|
"time"
|
|
)
|
|
)
|
|
@@ -175,3 +176,341 @@ func CheckAssessmentFormButton(item *models.AssessmentForm, sysAdminId int, auth
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// GetVarietyPriceAndForecastComment 获取品种价格详情及观点评价
|
|
|
|
+func GetVarietyPriceAndForecastComment(forms []*models.AssessmentForm) (varietyPrice []*models.AssessmentFormVarietyPrice, forecastComment []*models.AssessmentFormForecastComment, err error) {
|
|
|
|
+ if len(forms) == 0 {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ baseDate := forms[0].BaseDate
|
|
|
|
+ var varietyIds []int
|
|
|
|
+ for _, v := range forms {
|
|
|
|
+ varietyIds = append(varietyIds, v.VarietyId)
|
|
|
|
+ }
|
|
|
|
+ if len(varietyIds) == 0 || baseDate.IsZero() {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 获取品种信息
|
|
|
|
+ varietyOb := new(models.AssessmentVariety)
|
|
|
|
+ varietyMatch := make(map[int]*models.AssessmentVariety)
|
|
|
|
+ {
|
|
|
|
+ cond := fmt.Sprintf(` AND %s IN (?)`, varietyOb.Cols().PrimaryId)
|
|
|
|
+ pars := make([]interface{}, 0)
|
|
|
|
+ pars = append(pars, varietyIds)
|
|
|
|
+ list, e := varietyOb.GetItemsByCondition(cond, pars, []string{}, "")
|
|
|
|
+ if e != nil {
|
|
|
|
+ err = fmt.Errorf("获取品种信息失败, %v", e)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ for _, v := range list {
|
|
|
|
+ varietyMatch[v.AssessmentVarietyId] = v
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ varietyLatestData := make(map[int]*models.AssessmentVarietyData)
|
|
|
|
+ varietyBaseDateData := make(map[int]*models.AssessmentVarietyData)
|
|
|
|
+ varietyNextWeekData := make(map[int]*models.AssessmentVarietyData)
|
|
|
|
+ varietyNextMonthData := make(map[int]*models.AssessmentVarietyData)
|
|
|
|
+
|
|
|
|
+ // 获取最新日期和数据
|
|
|
|
+ dataOb := new(models.AssessmentVarietyData)
|
|
|
|
+ latestData, e := dataOb.GetVarietyMaxDateData(varietyIds)
|
|
|
|
+ if e != nil {
|
|
|
|
+ err = fmt.Errorf("获取品种最新数据失败, %v", e)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ for _, v := range latestData {
|
|
|
|
+ varietyLatestData[v.VarietyId] = v
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 获取基准日期N至N+4周的数据
|
|
|
|
+ var dateArr []string
|
|
|
|
+ nextWeek := baseDate.AddDate(0, 0, 7)
|
|
|
|
+ nextMonth := baseDate.AddDate(0, 0, 28)
|
|
|
|
+ dateArr = append(dateArr, baseDate.Format(utils.FormatDate), nextWeek.Format(utils.FormatDate), nextMonth.Format(utils.FormatDate))
|
|
|
|
+ {
|
|
|
|
+ cond := fmt.Sprintf(` AND %s IN (?) AND %s IN (?)`, dataOb.Cols().VarietyId, dataOb.Cols().WeekDate)
|
|
|
|
+ pars := make([]interface{}, 0)
|
|
|
|
+ pars = append(pars, varietyIds, dateArr)
|
|
|
|
+ list, e := dataOb.GetItemsByCondition(cond, pars, []string{}, "")
|
|
|
|
+ if e != nil {
|
|
|
|
+ err = fmt.Errorf("获取基准日至N+4周数据失败, %v", e)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ for _, v := range list {
|
|
|
|
+ if varietyMatch[v.VarietyId] == nil {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ if baseDate.Equal(v.WeekDate) {
|
|
|
|
+ varietyBaseDateData[v.VarietyId] = v
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ if baseDate.AddDate(0, 0, 7).Equal(v.WeekDate) {
|
|
|
|
+ varietyNextWeekData[v.VarietyId] = v
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ if baseDate.AddDate(0, 0, 28).Equal(v.WeekDate) {
|
|
|
|
+ varietyNextMonthData[v.VarietyId] = v
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ varietyPrice = make([]*models.AssessmentFormVarietyPrice, 0)
|
|
|
|
+ forecastComment = make([]*models.AssessmentFormForecastComment, 0)
|
|
|
|
+ for _, v := range forms {
|
|
|
|
+ vat := varietyMatch[v.VarietyId]
|
|
|
|
+ if vat == nil {
|
|
|
|
+ utils.FileLog.Info(fmt.Sprintf("GetVarietyPriceAndForecastComment, 品种不存在: %d", v.VarietyId))
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 品种价格评价
|
|
|
|
+ vp := new(models.AssessmentFormVarietyPrice)
|
|
|
|
+ vp.VarietyId = v.VarietyId
|
|
|
|
+ vp.VarietyCode = v.VarietyCode
|
|
|
|
+ vp.VarietyName = v.VarietyName
|
|
|
|
+ if varietyLatestData[v.VarietyId] != nil {
|
|
|
|
+ vp.EndDate = varietyLatestData[v.VarietyId].WeekDate.Format(utils.FormatDate)
|
|
|
|
+ vp.LatestValue = fmt.Sprint(varietyLatestData[v.VarietyId].CloseValue)
|
|
|
|
+ }
|
|
|
|
+ var (
|
|
|
|
+ baseDataPrice *float64
|
|
|
|
+ nextMonthPrice *float64
|
|
|
|
+ nextWeekHighPrice *float64
|
|
|
|
+ nextWeekLowPrice *float64
|
|
|
|
+ )
|
|
|
|
+ if varietyBaseDateData[v.VarietyId] != nil {
|
|
|
|
+ baseDataPrice = &varietyBaseDateData[v.VarietyId].CloseValue
|
|
|
|
+ vp.BaseDatePrice = fmt.Sprint(varietyBaseDateData[v.VarietyId].CloseValue)
|
|
|
|
+ }
|
|
|
|
+ if varietyNextWeekData[v.VarietyId] != nil {
|
|
|
|
+ nextWeekHighPrice = &varietyNextWeekData[v.VarietyId].HighValue
|
|
|
|
+ nextWeekLowPrice = &varietyNextWeekData[v.VarietyId].LowValue
|
|
|
|
+ vp.NextWeekPrice = fmt.Sprint(varietyNextWeekData[v.VarietyId].CloseValue)
|
|
|
|
+ }
|
|
|
|
+ if varietyNextMonthData[v.VarietyId] != nil {
|
|
|
|
+ nextMonthPrice = &varietyNextMonthData[v.VarietyId].CloseValue
|
|
|
|
+ vp.NextMonthPrice = fmt.Sprint(varietyNextMonthData[v.VarietyId].CloseValue)
|
|
|
|
+ }
|
|
|
|
+ varietyPrice = append(varietyPrice, vp)
|
|
|
|
+
|
|
|
|
+ // 观点评价
|
|
|
|
+ fc := new(models.AssessmentFormForecastComment)
|
|
|
|
+ fc.VarietyId = v.VarietyId
|
|
|
|
+ fc.VarietyCode = v.VarietyCode
|
|
|
|
+ fc.VarietyName = v.VarietyName
|
|
|
|
+ fc.WeekTime = v.WeekTime
|
|
|
|
+ if !v.SubmitTime.IsZero() {
|
|
|
|
+ fc.SubmitTime = v.SubmitTime.Format(utils.FormatDateUnSpace)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 月度涨跌评价
|
|
|
|
+ if baseDataPrice != nil && nextMonthPrice != nil {
|
|
|
|
+ _, tips, right := calculateMonthlyPriceTrend(*baseDataPrice, *nextMonthPrice, vat.MonthlyFluctuate, v.MonthlyPriceForecast)
|
|
|
|
+ fc.MonthlyPriceComment = tips
|
|
|
|
+ fc.MonthlyPriceForecastRight = right
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 周度上下行风险
|
|
|
|
+ if baseDataPrice != nil && nextWeekHighPrice != nil {
|
|
|
|
+ _, tips, right := calculateWeekUpDownTrend(*baseDataPrice, *nextWeekHighPrice, vat.WeeklyFluctuate, v.WeeklyUpForecast, true)
|
|
|
|
+ fc.WeeklyUpComment = tips
|
|
|
|
+ fc.WeeklyUpForecastRight = right
|
|
|
|
+ }
|
|
|
|
+ if baseDataPrice != nil && nextWeekLowPrice != nil {
|
|
|
|
+ _, tips, right := calculateWeekUpDownTrend(*baseDataPrice, *nextWeekLowPrice, vat.WeeklyFluctuate, v.WeeklyUpForecast, false)
|
|
|
|
+ fc.WeeklyDownComment = tips
|
|
|
|
+ fc.WeeklyDownForecastRight = right
|
|
|
|
+ }
|
|
|
|
+ forecastComment = append(forecastComment, fc)
|
|
|
|
+ }
|
|
|
|
+ return
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// calculateMonthly 判断月度涨跌趋势
|
|
|
|
+func calculateMonthlyPriceTrend(basePrice, monthPrice, monthlyFluctuate float64, forecast string) (result, tips string, right bool) {
|
|
|
|
+ if basePrice <= 0 || monthPrice <= 0 || monthlyFluctuate <= 0 {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ // 计算月度价格变化比例:月度价格/当周价格-1
|
|
|
|
+ percent := (monthPrice/basePrice - 1) * 100
|
|
|
|
+
|
|
|
|
+ // 判断价格趋势
|
|
|
|
+ switch {
|
|
|
|
+ case percent > monthlyFluctuate:
|
|
|
|
+ result = models.AssessmentFormMonthlyPriceUp
|
|
|
|
+ case percent < -monthlyFluctuate:
|
|
|
|
+ result = models.AssessmentFormMonthlyPriceDown
|
|
|
|
+ default:
|
|
|
|
+ result = models.AssessmentFormMonthlyPriceShake
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 如果有进行预测,判断是否正确,返回提示语句
|
|
|
|
+ if forecast == "" {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ tips = fmt.Sprintf("判断 %s,实际 %s", forecast, result)
|
|
|
|
+ if forecast == result {
|
|
|
|
+ right = true
|
|
|
|
+ }
|
|
|
|
+ return
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// calculateWeekUpDownTrend 判断周度上下行风险
|
|
|
|
+func calculateWeekUpDownTrend(basePrice, weekPrice, weekFluctuate float64, forecast string, calculateUp bool) (result, tips string, right bool) {
|
|
|
|
+ if basePrice <= 0 || weekPrice <= 0 || weekFluctuate <= 0 {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ percent := (weekPrice/basePrice - 1) * 100
|
|
|
|
+
|
|
|
|
+ // 上下行
|
|
|
|
+ result = models.AssessmentFormWeekUpNo
|
|
|
|
+ if calculateUp {
|
|
|
|
+ if percent > weekFluctuate {
|
|
|
|
+ result = models.AssessmentFormWeekUpYes
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if percent < -weekFluctuate {
|
|
|
|
+ result = models.AssessmentFormWeekUpYes
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 如果有进行预测,判断是否正确,返回提示语句
|
|
|
|
+ if forecast == "" {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if forecast == result {
|
|
|
|
+ right = true
|
|
|
|
+ }
|
|
|
|
+ forecastMap := map[string]string{models.AssessmentFormWeekUpYes: "提示风险", models.AssessmentFormWeekUpNo: "未提示风险"}
|
|
|
|
+ resultMap := map[string]string{models.AssessmentFormWeekUpYes: "风险发生", models.AssessmentFormWeekUpNo: "风险未发生"}
|
|
|
|
+ tips = fmt.Sprint(forecastMap[forecast], " ", resultMap[result])
|
|
|
|
+ return
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func CalculateResultStatistic(forms []*models.AssessmentForm, varietyData []*models.AssessmentVarietyData, results []*models.AssessmentFormResultStatisticItem) (resp []*models.AssessmentFormResultStatisticItem, err error) {
|
|
|
|
+ if len(forms) == 0 {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ calculateMappingQ := make(map[string]int) // 月度趋势分子Q(即月度涨跌趋势判断正确的次数)
|
|
|
|
+ calculateMappingP := make(map[string]int) // 月度趋势分母P(即月度涨跌趋势判断总次数)
|
|
|
|
+ calculateMappingT := make(map[string]int) // 周度风险分子T(即周度上下行风险判断正确的次数)
|
|
|
|
+ calculateMappingS := make(map[string]int) // 周度风险分母S(即周度上下行风险判断的总次数)
|
|
|
|
+
|
|
|
|
+ // 品种数据
|
|
|
|
+ varietyDateData := make(map[string]*models.AssessmentVarietyData)
|
|
|
|
+ for _, v := range varietyData {
|
|
|
|
+ k := fmt.Sprintf("%d-%s", v.VarietyId, v.WeekDate.Format(utils.FormatDate))
|
|
|
|
+ varietyDateData[k] = v
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 获取品种信息
|
|
|
|
+ varietyMatch := make(map[int]*models.AssessmentVariety)
|
|
|
|
+ {
|
|
|
|
+ varietyOb := new(models.AssessmentVariety)
|
|
|
|
+ list, e := varietyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
|
|
|
|
+ if e != nil {
|
|
|
|
+ err = fmt.Errorf("获取品种信息失败, %v", e)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ for _, v := range list {
|
|
|
|
+ varietyMatch[v.AssessmentVarietyId] = v
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 对填报单的[研究员ID-品种ID]进行QPTS的计数
|
|
|
|
+ for _, v := range forms {
|
|
|
|
+ vat := varietyMatch[v.VarietyId]
|
|
|
|
+ if vat == nil {
|
|
|
|
+ utils.FileLog.Info(fmt.Sprintf("CalculateResultStatistic, 品种不存在: %d", v.VarietyId))
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ key := fmt.Sprintf("%d-%d", v.ResearcherId, v.VarietyId)
|
|
|
|
+
|
|
|
|
+ // 找出填报单品种对应的基准日期数据、N+1周的最高最低价格、N+4周的价格
|
|
|
|
+ var (
|
|
|
|
+ baseDataPrice *float64
|
|
|
|
+ nextMonthPrice *float64
|
|
|
|
+ nextWeekHighPrice *float64
|
|
|
|
+ nextWeekLowPrice *float64
|
|
|
|
+ )
|
|
|
|
+ kb := fmt.Sprintf("%d-%s", v.VarietyId, v.BaseDate.Format(utils.FormatDate))
|
|
|
|
+ if varietyDateData[kb] != nil {
|
|
|
|
+ baseDataPrice = &varietyDateData[kb].CloseValue
|
|
|
|
+ }
|
|
|
|
+ kw := fmt.Sprintf("%d-%s", v.VarietyId, v.BaseDate.AddDate(0, 0, 7).Format(utils.FormatDate))
|
|
|
|
+ if varietyDateData[kw] != nil {
|
|
|
|
+ nextWeekHighPrice = &varietyDateData[kw].HighValue
|
|
|
|
+ nextWeekLowPrice = &varietyDateData[kw].LowValue
|
|
|
|
+ }
|
|
|
|
+ km := fmt.Sprintf("%d-%s", v.VarietyId, v.BaseDate.AddDate(0, 0, 28).Format(utils.FormatDate))
|
|
|
|
+ if varietyDateData[km] != nil {
|
|
|
|
+ nextMonthPrice = &varietyDateData[km].CloseValue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 月度涨跌评价
|
|
|
|
+ if baseDataPrice != nil && nextMonthPrice != nil {
|
|
|
|
+ _, _, right := calculateMonthlyPriceTrend(*baseDataPrice, *nextMonthPrice, vat.MonthlyFluctuate, v.MonthlyPriceForecast)
|
|
|
|
+ calculateMappingP[key] += 1
|
|
|
|
+ if right {
|
|
|
|
+ calculateMappingQ[key] += 1
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 周度上下行风险
|
|
|
|
+ if baseDataPrice != nil && nextWeekHighPrice != nil {
|
|
|
|
+ _, _, right := calculateWeekUpDownTrend(*baseDataPrice, *nextWeekHighPrice, vat.WeeklyFluctuate, v.WeeklyUpForecast, true)
|
|
|
|
+ calculateMappingS[key] += 1
|
|
|
|
+ if right {
|
|
|
|
+ calculateMappingT[key] += 1
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if baseDataPrice != nil && nextWeekLowPrice != nil {
|
|
|
|
+ _, _, right := calculateWeekUpDownTrend(*baseDataPrice, *nextWeekLowPrice, vat.WeeklyFluctuate, v.WeeklyUpForecast, false)
|
|
|
|
+ calculateMappingS[key] += 1
|
|
|
|
+ if right {
|
|
|
|
+ calculateMappingT[key] += 1
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 计算正确率,结果取整
|
|
|
|
+ for _, v := range results {
|
|
|
|
+ k := fmt.Sprintf("%d-%d", v.ResearcherId, v.VarietyId)
|
|
|
|
+ // 月趋势正确率:Q/P
|
|
|
|
+ q := calculateMappingQ[k]
|
|
|
|
+ p := calculateMappingP[k]
|
|
|
|
+ if p > 0 {
|
|
|
|
+ v.MonthlyTrendAccuracy = math.Round(float64(q) / float64(p) * 100)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 周度预警正确率:T/S
|
|
|
|
+ t := calculateMappingT[k]
|
|
|
|
+ s := calculateMappingS[k]
|
|
|
|
+ if s > 0 {
|
|
|
|
+ v.WeeklyWarningAccuracy = math.Round(float64(t) / float64(s) * 100)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 综合正确率1:(Q+2T)/(P+2S)
|
|
|
|
+ a := q + 2*t
|
|
|
|
+ b := p + 2*s
|
|
|
|
+ if b > 0 {
|
|
|
|
+ v.TotalAccuracyA = math.Round(float64(a) / float64(b) * 100)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 综合正确率2:Q/2P+T/2S
|
|
|
|
+ var c, d float64
|
|
|
|
+ if p > 0 {
|
|
|
|
+ c = math.Round(float64(q) / float64(2*p) * 100)
|
|
|
|
+ }
|
|
|
|
+ if s > 0 {
|
|
|
|
+ d = math.Round(float64(t) / float64(2*s) * 100)
|
|
|
|
+ }
|
|
|
|
+ v.TotalAccuracyB = c + d
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ resp = results
|
|
|
|
+ return
|
|
|
|
+}
|