bbgo/pkg/indicator/volumeprofile.go

118 lines
3.2 KiB
Go
Raw Normal View History

package indicator
import (
"math"
"time"
"git.qtrade.icu/lychiyu/bbgo/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
}