func (self *Aggregator) TimeWeightedAvg(
	symbol string,
	start time.Time,
	lookBack time.Duration,
) (float64, float64, error) {
	resolution, err := self.resolution(symbol, start)
	if err != nil {
		return 0, 0, err
	}
	query, err := self.promqlEngine.NewInstantQuery(
		self.tsDB,
		`avg_over_time(
			`+index.ValueMetricName+`{symbol="`+format.SanitizeMetricName(symbol)+`"}
		[`+lookBack.String()+`])`,
		start,
	)
	if err != nil {
		return 0, 0, err
	}
	defer query.Close()
	_result := query.Exec(self.ctx)
	if _result.Err != nil {
		return 0, 0, errors.Wrapf(_result.Err, "error evaluating query:%v", query.Statement())
	}
	if len(_result.Value.(promql.Vector)) == 0 {
		return 0, 0, errors.Errorf("no result for TWAP vals query:%v", query.Statement())
	}
	result := _result.Value.(promql.Vector)[0].V
	query, err = self.promqlEngine.NewInstantQuery(
		self.tsDB,
		`avg(
			count_over_time(
				`+index.ValueMetricName+`{ symbol="`+format.SanitizeMetricName(symbol)+`" }
			[`+lookBack.String()+`:`+resolution.String()+`])
			/
			(`+strconv.Itoa(int(lookBack.Nanoseconds()))+` / `+strconv.Itoa(int(resolution.Nanoseconds()))+`)
		)`,
		start,
	)
	if err != nil {
		return 0, 0, err
	}
	defer query.Close()
	confidence := query.Exec(self.ctx)
	if confidence.Err != nil {
		return 0, 0, errors.Wrapf(confidence.Err, "error evaluating query:%v", query.Statement())
	}
	if len(confidence.Value.(promql.Vector)) == 0 {
		return 0, 0, errors.Errorf("no result for TWAP confidence query:%v", query.Statement())
	}
	return result, confidence.Value.(promql.Vector)[0].V * 100, err
}
TimeWeightedAvg returns price and confidence level for a given symbol.
Confidence is calculated based on maximum possible samples over the actual samples for a given period.
avg(maxPossibleSamplesCount/actualSamplesCount)
For example with 1h look back and source interval of 60sec maxPossibleSamplesCount = 36
with actualSamplesCount = 18 this is 50% confidence.
The same calculation is done for all source and the final value is the average of all.

Example for 1h.
maxDataPointCount is calculated by deviding the seconds in 1h by how often the tracker queries the APIs.
avg(count_over_time(indexTracker_value{symbol="AMPL_USD"}[1h]) / (3.6e+12/indexTracker_interval)).
package aggregator
import (
	"context"
	"encoding/json"
	"io/ioutil"
	"math"
	"os"
	"sort"
	"strconv"
	"time"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/tellor-io/telliot/pkg/format"
	"github.com/tellor-io/telliot/pkg/logging"
	"github.com/tellor-io/telliot/pkg/tracker/index"
)
const ComponentName = "aggregator"
type IAggregator interface {
	TimeWeightedAvg(symbol string, start time.Time, lookBack time.Duration) (float64, float64, error)
}
type Config struct {
	LogLevel       string
	ManualDataFile string
}
type Aggregator struct {
	logger       log.Logger
	ctx          context.Context
	tsDB         storage.SampleAndChunkQueryable
	promqlEngine *promql.Engine
	cfg          Config
}
func New(
	logger log.Logger,
	ctx context.Context,
	cfg Config,
	tsDB storage.SampleAndChunkQueryable,
) (*Aggregator, error) {
	logger, err := logging.ApplyFilter(cfg.LogLevel, logger)
	if err != nil {
		return nil, errors.Wrap(err, "apply filter logger")
	}
	opts := promql.EngineOpts{
		Logger:               logger,
		Reg:                  nil,
		MaxSamples:           30000,
		Timeout:              10 * time.Second,
		LookbackDelta:        5 * time.Minute,
		EnableAtModifier:     true,
		EnableNegativeOffset: true,
	}
	engine := promql.NewEngine(opts)
	return &Aggregator{
		logger:       log.With(logger, "component", ComponentName),
		ctx:          ctx,
		tsDB:         tsDB,
		promqlEngine: engine,
		cfg:          cfg,
	}, nil
}
func (self *Aggregator) ManualValue(oracleName string, reqID int64, ts time.Time) (float64, error) {
	jsonFile, err := os.Open(self.cfg.ManualDataFile)
	if err != nil {
		return 0, errors.Wrapf(err, "manual data file read Error")
	}
	defer jsonFile.Close()
	byteValue, _ := ioutil.ReadAll(jsonFile)
	var result map[string]map[string]map[string]float64
	err = json.Unmarshal([]byte(byteValue), &result)
	if err != nil {
		return 0, errors.Wrap(err, "unmarshal manual data file")
	}
	oracleManualVals, ok := result[oracleName]
	if !ok {
		return 0, errors.Wrapf(err, "malformatted json file for oracle:%v", oracleName)
	}
	val := oracleManualVals[strconv.FormatInt(reqID, 10)]["VALUE"]
	if val != 0 {
		_timestamp := int64(oracleManualVals[strconv.FormatInt(reqID, 10)]["DATE"])
		timestamp := time.Unix(_timestamp, 0)
		if ts.After(timestamp) {
			return 0, errors.Errorf("manual entry value has expired:%v", ts)
		}
	}
	return val, nil
}
func (self *Aggregator) MedianAt(symbol string, at time.Time) (float64, float64, error) {
	vals, confidence, err := self.valsAtWithConfidence(symbol, at)
	if err != nil {
		return 0, 0, err
	}
	if len(vals) == 0 {
		return 0, 0, errors.Errorf("no vals at:%v", at)
	}
	median, confidenceM := self.median(vals)
	if confidenceM < confidence {
		confidence = confidenceM
	}
	return median, confidence, nil
}
func (self *Aggregator) MedianAtEOD(symbol string, at time.Time) (float64, float64, error) {
	d := 24 * time.Hour
	eod := time.Now().Truncate(d)
	return self.MedianAt(symbol, eod)
}
func (self *Aggregator) MeanAt(symbol string, at time.Time) (float64, float64, error) {
	vals, confidence, err := self.valsAtWithConfidence(symbol, at)
	if err != nil {
		return 0, 0, err
	}
	price, confidenceM := self.mean(vals)
	if confidenceM < confidence {
		confidence = confidenceM
	}
	return price, confidence * 100, nil
}
func (self *Aggregator) mean(vals []float64) (float64, float64) {
	if len(vals) == 1 {
		return vals[0], 100
	}
	priceSum := 0.0
	min, max := vals[0], vals[0]
	for _, val := range vals {
		priceSum += val
		if val < min {
			min = val
		}
		if val > max {
			max = val
		}
	}
	return priceSum / float64(len(vals)), confidenceInDifference(min, max)
}