package indicator import ( "math" "time" "git.qtrade.icu/lychiyu/qbtrade/pkg/types" ) type trade struct { price float64 volume float64 // +: buy, -: sell timestamp types.Time } // The Volume Profile is a technical analysis tool that is used to visualize the distribution of trading volume at different price levels // in a security. It is typically plotted as a histogram or heatmap on the price chart, with the x-axis representing the price levels and // the y-axis representing the trading volume. The Volume Profile can be used to identify areas of support and resistance, as well as // potential entry and exit points for trades. type VolumeProfile struct { types.IntervalWindow Delta float64 profile map[float64]float64 trades []trade minPrice float64 maxPrice float64 } func (inc *VolumeProfile) Update(price, volume float64, timestamp types.Time) { inc.minPrice = math.Inf(1) inc.maxPrice = math.Inf(-1) if inc.profile == nil { inc.profile = make(map[float64]float64) } inc.profile[math.Round(price/inc.Delta)] += volume filter := timestamp.Time().Add(-time.Duration(inc.Window) * inc.Interval.Duration()) inc.trades = append(inc.trades, trade{ price: price, volume: volume, timestamp: timestamp, }) var i int for i = 0; i < len(inc.trades); i++ { td := inc.trades[i] if td.timestamp.After(filter) { inc.trades = inc.trades[i:len(inc.trades)] break } inc.profile[math.Round(td.price/inc.Delta)] -= td.volume } for i = 0; i < len(inc.trades); i++ { k := math.Round(inc.trades[i].price / inc.Delta) if k < inc.minPrice { inc.minPrice = k } if k > inc.maxPrice { inc.maxPrice = k } } } // The Point of Control (POC) is a term used in the context of Volume Profile analysis. It refers to the price level at which the most // volume has been traded in a security over a specified period of time. The POC is typically identified by looking for the highest // peak in the Volume Profile, and is considered to be an important level of support or resistance. It can be used by traders to // identify potential entry and exit points for trades, or to confirm other technical analysis signals. // Get Resistance Level by finding PoC func (inc *VolumeProfile) PointOfControlAboveEqual(price float64, limit ...float64) (resultPrice float64, vol float64) { filter := inc.maxPrice if len(limit) > 0 { filter = limit[0] } if inc.Delta == 0 { panic("Delta for volumeprofile shouldn't be zero") } start := math.Round(price / inc.Delta) vol = math.Inf(-1) if start > filter { return 0, 0 } for ; start <= filter; start += inc.Delta { abs := math.Abs(inc.profile[start]) if vol < abs { vol = abs resultPrice = start } } return resultPrice, vol } // Get Support Level by finding PoC func (inc *VolumeProfile) PointOfControlBelowEqual(price float64, limit ...float64) (resultPrice float64, vol float64) { filter := inc.minPrice if len(limit) > 0 { filter = limit[0] } if inc.Delta == 0 { panic("Delta for volumeprofile shouldn't be zero") } start := math.Round(price / inc.Delta) vol = math.Inf(-1) if start < filter { return 0, 0 } for ; start >= filter; start -= inc.Delta { abs := math.Abs(inc.profile[start]) if vol < abs { vol = abs resultPrice = start } } return resultPrice, vol }