init
This commit is contained in:
159
arb/common.go
Normal file
159
arb/common.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package arb
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/exg"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banstagy/com"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"math"
|
||||
"slices"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func init() {
|
||||
strat.AddStratGroup("arb", map[string]strat.FuncMakeStrat{
|
||||
"stat": MeanRegressStat,
|
||||
})
|
||||
}
|
||||
|
||||
type spikeTuple struct {
|
||||
A *orm.ExSymbol
|
||||
B *orm.ExSymbol
|
||||
Score float64 // 分数,越高越好
|
||||
BigSlope float64 // 大趋势平均斜率
|
||||
PftRate float64 // (hh-ll)/minChg平均倍率
|
||||
RawXBig int // 价格比率与大周期均线交叉次数
|
||||
LastDiff float64 // 最新fast与大趋势偏移,是PftRate倍数,越小越好
|
||||
}
|
||||
|
||||
/*
|
||||
发现两个品种价格比率套利机会。
|
||||
通过此函数发现的机会实际上是交易量很小,价格变动百分比较大的小品种的波动。如XEM,最小变动0.6%,不如直接交易这个品种
|
||||
要求:EMA1333较稳定,(hh-ll)/maxChg较大,ratio和大均线交叉次数较多
|
||||
建议:pftRate>0.4,rawXBig>200,score>0.3
|
||||
*/
|
||||
func findSpikeTuples(endMS int64, tf string, barNum, hlWindow, itemMax int) ([]*spikeTuple, *errs.Error) {
|
||||
// 获取所有标的的K线
|
||||
exgId := exg.Default.Info().ID
|
||||
exsMap := orm.GetExSymbols(exgId, banexg.MarketLinear)
|
||||
exsList, barList, _, err := getExsBars(exg.Default, exsMap, tf, 0, endMS, barNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 计算周期内关键指标
|
||||
minChg := 0.0005 * 4 * 4 // 变动至少是两次开平仓手续费的4倍
|
||||
bigSlopeDist := 120 // 计算大周期斜率的距离
|
||||
items := make([]*spikeTuple, 0, len(exsList)*2)
|
||||
halfWindow := hlWindow / 2
|
||||
if hlWindow%2 > 0 {
|
||||
halfWindow += 1
|
||||
}
|
||||
for i := 0; i < len(exsList); i++ {
|
||||
barsA := barList[i]
|
||||
for j := i + 1; j < len(exsList); j++ {
|
||||
barsB := barList[j]
|
||||
barsRes := getAlignBars(barsA, barsB)
|
||||
a, b := barsRes[0], barsRes[1]
|
||||
if len(a) < barNum/2 {
|
||||
continue
|
||||
}
|
||||
var ma = com.NewEMA(0.08)
|
||||
//var maSlow = common.NewEMA(0.03)
|
||||
var maBig = com.NewEMA(0.0015)
|
||||
var rawXBig = com.NewCrossLog()
|
||||
//var rawXfa = common.NewCrossLog()
|
||||
//var vmax, vmin = float64(0), float64(0)
|
||||
var raw = make([]float64, 0, len(a))
|
||||
var bbRates = make([]float64, 0, len(a)/3)
|
||||
var spikeNum = 0
|
||||
var maBigs = make([]float64, 0, len(a))
|
||||
var bigSlopes = make([]float64, 0, 100)
|
||||
for m := 0; m < len(a); m++ {
|
||||
ratio := a[m].Close / b[m].Close
|
||||
raw = append(raw, ratio)
|
||||
maVal := ma.Update(ratio)
|
||||
maBigV := maBig.Update(ratio)
|
||||
if math.IsNaN(maBigV) {
|
||||
continue
|
||||
}
|
||||
maBigs = append(maBigs, maBigV)
|
||||
if len(maBigs)%bigSlopeDist == 0 && len(maBigs) > 0 {
|
||||
// 采样大周期斜率
|
||||
bigSlopes = append(bigSlopes, math.Abs(maBigV/maBigs[len(maBigs)-bigSlopeDist]-1))
|
||||
}
|
||||
if m%halfWindow == 0 && len(raw) > hlWindow {
|
||||
// 计算hh-ll的盈利空间
|
||||
near5 := raw[len(raw)-hlWindow:]
|
||||
bbRates = append(bbRates, (slices.Max(near5)-slices.Min(near5))/maVal/minChg)
|
||||
}
|
||||
// 计算raw与big均线交叉次数
|
||||
if ratio != maBigV {
|
||||
rawXBig.Update(ratio - maBigV)
|
||||
fxs := rawXBig.Get()
|
||||
if fxs == 1 {
|
||||
// 只记录上穿,避免交叉太多
|
||||
spikeNum += 1
|
||||
}
|
||||
}
|
||||
// 计算比率在均线附近振幅可盈利的次数
|
||||
//rawXfa.Update(ratio - maVal)
|
||||
//rxf := rawXfa.Get()
|
||||
//if rxf == 1 || rxf == -1 {
|
||||
// pft := (vmax-vmin)/maVal > minChg
|
||||
// if pft {
|
||||
// spikeNum += 1
|
||||
// }
|
||||
// vmin = maVal
|
||||
// vmax = maVal
|
||||
//}
|
||||
//if ratio > maVal {
|
||||
// vmax = max(vmax, ratio)
|
||||
//} else {
|
||||
// vmin = min(vmin, ratio)
|
||||
//}
|
||||
}
|
||||
// 平均大周期斜率
|
||||
avgSlope := floats.Sum(bigSlopes) * 50 / float64(len(bigSlopes))
|
||||
slopeRate := math.Pow(1-min(1, avgSlope), 1.5)
|
||||
// 平均盈利空间
|
||||
sort.Slice(bbRates, func(i, j int) bool {
|
||||
return bbRates[i] < bbRates[j]
|
||||
})
|
||||
bbRates = bbRates[:len(bbRates)/2]
|
||||
avgPftRate := floats.Sum(bbRates) / float64(len(bbRates))
|
||||
// 与大周期交叉的分数,交叉少于200权重降低
|
||||
hitScore := math.Log(float64(spikeNum)/200 + 1)
|
||||
val := hitScore * avgPftRate * slopeRate
|
||||
// 计算fast与大周期偏移
|
||||
lastDiff := math.Abs(ma.Val/maBig.Val-1) / minChg / avgPftRate
|
||||
items = append(items, &spikeTuple{
|
||||
A: exsList[i],
|
||||
B: exsList[j],
|
||||
Score: val,
|
||||
BigSlope: avgSlope,
|
||||
PftRate: avgPftRate,
|
||||
RawXBig: spikeNum,
|
||||
LastDiff: lastDiff,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
return items[i].Score > items[j].Score
|
||||
})
|
||||
tops := items[:min(1000, int(float64(len(items))*0.1))]
|
||||
res := make([]*spikeTuple, 0, len(tops)/2)
|
||||
hits := make(map[string]int)
|
||||
for _, t := range tops {
|
||||
numA, _ := hits[t.A.Symbol]
|
||||
numB, _ := hits[t.B.Symbol]
|
||||
if max(numA, numB) < itemMax {
|
||||
hits[t.A.Symbol] = numA + 1
|
||||
hits[t.B.Symbol] = numB + 1
|
||||
res = append(res, t)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
Reference in New Issue
Block a user