init
This commit is contained in:
157
surge/alldown.go
Normal file
157
surge/alldown.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package surge
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
/*
|
||||
全市场瞬时暴跌做空策略。适用于十分钟跌去70%的情况。
|
||||
可使用下面脚本回测:surge/down_test.go
|
||||
E:\trade\go\bandata\backtest\bdb35c1d91\surge_alldown.log
|
||||
*/
|
||||
|
||||
type AllDownSta struct {
|
||||
volBase float64 // 平均成交量
|
||||
dirtRate float64 // K线方向倍率,范围约:[-3,3]
|
||||
barRate float64 // K线比率
|
||||
volRate float64 // 成交量倍率
|
||||
}
|
||||
|
||||
func AllDown(p *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
//var allowLog = false
|
||||
|
||||
lenAtr, lenAtrS := 20, 30
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
baseAtrLenS := baseAtrLen * 3 / 2
|
||||
|
||||
env, _ := ta.NewBarEnv("", "", "", "1m")
|
||||
lenS := env.NewSeries(nil)
|
||||
volS := env.NewSeries(nil)
|
||||
|
||||
var prevLen float64
|
||||
var volThres = 2.8
|
||||
var openDirt float64
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 150,
|
||||
BatchInOut: true,
|
||||
TimeFrames: "1m",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &AllDownSta{}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "15m", 100},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
m, _ := s.More.(*AllDownSta)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtrS)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtrS), baseAtrLenS).Get(0)
|
||||
o, c := e.Open.Get(0), e.Close.Get(0)
|
||||
h, l := e.High.Get(0), e.Low.Get(0)
|
||||
m.dirtRate = (c - o) / atrBase
|
||||
m.barRate = (c - o) / (h - l)
|
||||
m.volRate = ta.EMA(e.Volume, lenAtr).Get(0) / m.volBase
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
m, _ := s.More.(*AllDownSta)
|
||||
maVol := ta.EMA(e.Volume, lenAtr)
|
||||
m.volBase = ta.Lowest(ta.Highest(maVol, lenAtr), baseAtrLen).Get(0) / 3
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
jobNum := len(jobs)
|
||||
volRates := make([]float64, 0, jobNum)
|
||||
lenRates := make([]float64, 0, jobNum)
|
||||
solidRates := make([]float64, 0, jobNum)
|
||||
volIds := make([]*core.Int64Flt, 0, jobNum)
|
||||
var timeMS int64
|
||||
for i, j := range jobs {
|
||||
m, _ := j.More.(*AllDownSta)
|
||||
volRates = append(volRates, m.volRate)
|
||||
lenRates = append(lenRates, m.dirtRate)
|
||||
solidRates = append(solidRates, m.barRate)
|
||||
timeMS = max(timeMS, j.Env.TimeStop)
|
||||
volIds = append(volIds, &core.Int64Flt{
|
||||
Int: int64(i),
|
||||
Val: m.volRate,
|
||||
})
|
||||
}
|
||||
sort.Float64s(volRates)
|
||||
sort.Float64s(lenRates)
|
||||
sort.Float64s(solidRates)
|
||||
vol := volRates[jobNum/2]
|
||||
solid := solidRates[jobNum/2]
|
||||
lenMid := lenRates[jobNum/2]
|
||||
//len33 := lenRates[jobNum/3]
|
||||
len66 := lenRates[jobNum*2/3]
|
||||
|
||||
val := lenMid + vol
|
||||
env.TimeStop = max(timeMS, env.TimeStop)
|
||||
if math.IsNaN(val) || math.IsInf(val, 0) || lenS.Cached() {
|
||||
return
|
||||
}
|
||||
lenS.Append(lenMid)
|
||||
volS.Append(vol)
|
||||
dataLen := volS.Len()
|
||||
if dataLen > env.MaxCache*2 {
|
||||
volS.Cut(env.MaxCache)
|
||||
lenS.Cut(env.MaxCache)
|
||||
}
|
||||
volMa1 := ta.SMA(volS, 3).Get(1)
|
||||
low5len := ta.Lowest(lenS, 30).Get(1)
|
||||
if openDirt >= -1 && vol > volThres && lenMid < low5len && len66 < 0 && solid < -0.5 && prevLen < 0 {
|
||||
// 成交量突破阈值,全市场大阴线,前一个也是阴线
|
||||
prevRate := lenMid / prevLen
|
||||
if prevRate < 3 && prevRate > 1.5 && vol/volMa1 < 2 {
|
||||
// 与前一个k线比率不超过3,成交量缓慢增长
|
||||
openDirt -= 1
|
||||
costRate := 2.0
|
||||
if vol < 4 {
|
||||
costRate *= vol / 4
|
||||
}
|
||||
sort.Slice(volIds, func(i, j int) bool {
|
||||
return volIds[i].Val < volIds[j].Val
|
||||
})
|
||||
// 做空成交量最大的1/3部分,获利最丰(收益45%, 55%, 66%)
|
||||
items := volIds[jobNum*2/3:]
|
||||
for _, it := range items {
|
||||
jobs[it.Int].OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
CostRate: costRate,
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if openDirt < 0 {
|
||||
// 正在做空/多
|
||||
if lenMid*openDirt < 0 {
|
||||
openDirt = 0
|
||||
for _, job := range jobs {
|
||||
job.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit",
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
prevLen = lenMid
|
||||
//if !allowLog && vol > 2.5 {
|
||||
// allowLog = true
|
||||
//}
|
||||
//if !math.IsNaN(vol) {
|
||||
// jobs[0].Strat.WriteOutput(fmt.Sprintf("%.2f %.2f %.2f %.2f %.2f", vol, solid, lenMid, len33, len66), true)
|
||||
// if vol < 1.4 {
|
||||
// allowLog = false
|
||||
// }
|
||||
//}
|
||||
},
|
||||
}
|
||||
}
|
||||
10
surge/common.go
Normal file
10
surge/common.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package surge
|
||||
|
||||
import "github.com/banbox/banbot/strat"
|
||||
|
||||
func init() {
|
||||
strat.AddStratGroup("surge", map[string]strat.FuncMakeStrat{
|
||||
"maxVol": goMaxVol,
|
||||
"alldown": AllDown,
|
||||
})
|
||||
}
|
||||
108
surge/down_test.go
Normal file
108
surge/down_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package surge
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/core"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
测试顺势暴跌做空。应对2025-10-10
|
||||
*/
|
||||
|
||||
func TestDownQwen(t *testing.T) {
|
||||
stamps, data, err := ParseFile("E:\\trade\\go\\bandata\\backtest\\bdb35c1d91\\surge_alldown.log")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
env, _ := ta.NewBarEnv("", "", "", "1m")
|
||||
lenS := env.NewSeries(nil)
|
||||
volS := env.NewSeries(nil)
|
||||
|
||||
var prevLen float64
|
||||
var volThres = 2.8
|
||||
var holdBars int
|
||||
var holdAmt, openDirt float64
|
||||
for i, row := range data {
|
||||
timeMS := stamps[i]
|
||||
vol, solid, lenMid, _, len66 := row[0], row[1], row[2], row[3], row[4]
|
||||
if math.IsNaN(lenMid) {
|
||||
continue
|
||||
}
|
||||
env.TimeStop = timeMS
|
||||
lenS.Append(lenMid)
|
||||
volS.Append(vol)
|
||||
volMa1 := ta.SMA(volS, 3).Get(1)
|
||||
low5len := ta.Lowest(lenS, 30).Get(1)
|
||||
if openDirt != 0 {
|
||||
// 正在做空/多
|
||||
holdAmt += lenMid
|
||||
holdBars += 1
|
||||
if lenMid*openDirt < 0 {
|
||||
dateStr := btime.ToDateStr(timeMS, core.DefaultDateFmt)
|
||||
fmt.Printf("%s close, hold %d total: %.2f\n", dateStr, holdBars, holdAmt*openDirt)
|
||||
openDirt, holdBars = 0, 0
|
||||
holdAmt = 0
|
||||
}
|
||||
} else if vol > volThres && lenMid < low5len && len66 < 0 && solid < -0.5 && prevLen < 0 {
|
||||
// 成交量突破阈值,中位数阴线,3个len全部阴线,中位数创新低
|
||||
// 与前一个k线比率不超过3
|
||||
prevRate := lenMid / prevLen
|
||||
if prevRate < 3 && prevRate > 1.5 && vol/volMa1 < 2 {
|
||||
openDirt = -1
|
||||
fmt.Printf("%s short, thres: %.2f vol: %.2f len: %.2f solid: %.2f\n",
|
||||
btime.ToDateStr(timeMS, core.DefaultDateFmt), volThres, vol, lenMid, solid)
|
||||
}
|
||||
}
|
||||
prevLen = lenMid
|
||||
}
|
||||
}
|
||||
|
||||
func ParseFile(path string) ([]int64, [][5]float64, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var ts []int64
|
||||
var vals [][5]float64
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
t, v := ParseLine(scanner.Text())
|
||||
if t != 0 {
|
||||
ts = append(ts, t)
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
return ts, vals, scanner.Err()
|
||||
}
|
||||
|
||||
func ParseLine(s string) (int64, [5]float64) {
|
||||
var arr [5]float64
|
||||
f := strings.Fields(s)
|
||||
if len(f) < 7 {
|
||||
return 0, arr
|
||||
}
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05", f[0]+" "+f[1], time.UTC)
|
||||
if err != nil {
|
||||
return 0, arr
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
v, err := strconv.ParseFloat(f[2+i], 64)
|
||||
if err != nil {
|
||||
return t.UnixMilli(), arr
|
||||
}
|
||||
arr[i] = v
|
||||
}
|
||||
return t.UnixMilli(), arr
|
||||
}
|
||||
43
surge/max_vol.go
Normal file
43
surge/max_vol.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package surge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
type volSta struct {
|
||||
rate float64
|
||||
}
|
||||
|
||||
func goMaxVol(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 100,
|
||||
BatchInOut: true,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &volSta{}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
more, _ := s.More.(*volSta)
|
||||
bigVolMa := ta.SMA(e.Volume, 100).Get(0)
|
||||
more.rate = e.Volume.Get(0)/bigVolMa - 1
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
var maxIn, maxOut *strat.StratJob
|
||||
var inVal, outVal float64
|
||||
for _, j := range jobs {
|
||||
sta, _ := j.More.(*volSta)
|
||||
if sta.rate < outVal {
|
||||
outVal = sta.rate
|
||||
maxOut = j
|
||||
} else if sta.rate > inVal {
|
||||
inVal = sta.rate
|
||||
maxIn = j
|
||||
}
|
||||
}
|
||||
fmt.Printf("%v %v", maxIn, maxOut)
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user