Skip to content

Commit

Permalink
* Performance: Improve Stream performance by downloading from start a…
Browse files Browse the repository at this point in the history
…nd end hour instead of based by day.

* Example: Add Stream example - Drawdown finder given a transaction.
  • Loading branch information
edward-yakop committed Jan 1, 2021
1 parent 52839ca commit 92e72c4
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 9 deletions.
16 changes: 9 additions & 7 deletions api/tickdata/stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package stream

import (
"ed-fx/go-duka/api/tickdata"
iTickdata "ed-fx/go-duka/internal/tickdata"
"ed-fx/go-duka/internal/bi5"
"time"
"unknwon.dev/clog/v2"
)
Expand Down Expand Up @@ -32,12 +32,14 @@ func (s Stream) EachTick(it Iterator) {

dEnd := downloadEnd(s.end)
var isContinue = true
for t := downloadStart(start); t.Before(dEnd) && isContinue; t = t.Add(24 * time.Hour) {
day, err := iTickdata.FetchDay(s.symbol, t, s.downloadFolderPath)
if err != nil && !it(t, nil, err) {
for t := downloadStart(start); t.Before(dEnd) && isContinue; t = t.Add(time.Hour) {
bi := bi5.New(t, s.symbol, s.downloadFolderPath)
err := bi.Download()
if err != nil && !it(t.In(loc), nil, err) {
return
}
day.EachTick(func(tick *tickdata.TickData, err error) bool {

bi.EachTick(func(tick *tickdata.TickData, err error) bool {
tickTime := tick.TimeInLocation(loc)
if (start.Equal(tickTime) || start.Before(tickTime)) &&
(end.Equal(tickTime) || end.After(tickTime)) {
Expand All @@ -50,13 +52,13 @@ func (s Stream) EachTick(it Iterator) {

func downloadStart(start time.Time) time.Time {
dStart := start.UTC()
dStart = time.Date(dStart.Year(), dStart.Month(), dStart.Day(), 0, 0, 0, 0, time.UTC)
dStart = time.Date(dStart.Year(), dStart.Month(), dStart.Day(), dStart.Hour(), 0, 0, 0, time.UTC)
return dStart
}

func downloadEnd(end time.Time) time.Time {
dEnd := end.UTC()
dEnd = time.Date(dEnd.Year(), dEnd.Month(), dEnd.Day(), 23, 59, 59, 0, time.UTC)
dEnd = time.Date(dEnd.Year(), dEnd.Month(), dEnd.Day(), dEnd.Hour(), 59, 59, 0, time.UTC)
return dEnd
}

Expand Down
122 changes: 122 additions & 0 deletions examples/stream/dd_finder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package stream

import (
"ed-fx/go-duka/api/tickdata"
"ed-fx/go-duka/api/tickdata/stream"
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"math"
"os"
"strconv"
"testing"
"time"
)

func Test_StreamExample_DDFinder(t *testing.T) {
est, _ := time.LoadLocation("EET")

symbol := "GBPJPY"
openTime := time.Date(2020, time.November, 3, 17, 0, 0, 0, est)
openPrice := 136.325
closeTime := time.Date(2020, time.November, 4, 00, 56, 56, 0, est)
closePrice := 136.725

openPriceDiff, maxDD, maxPositive, maxDDForMaxPositive, maxPositiveTime, closePriceDiff :=
buyDDFinder(t, symbol, openTime, closeTime, openPrice, closePrice)

println("Open price diff in [", strconv.Itoa(openPriceDiff), "] points")
println("Max DD [", strconv.Itoa(maxDD), "] points")
println("Max Positive [", strconv.Itoa(maxPositive), "] points")
println("Max Positive Time [", fmtTime(maxPositiveTime), "] Duration [", fmtDuration(maxPositiveTime.Sub(openTime)), "]")
println("Max DD for Max Positive [", strconv.Itoa(maxDDForMaxPositive), "] points")
println("Close price diff in [", strconv.Itoa(closePriceDiff), "] points")
profitInPoints := int(math.Round((closePrice - openPrice) * 1000))
println("Profit [", strconv.Itoa(profitInPoints), "] points Duration [", fmtDuration(closeTime.Sub(openTime)), "]")

// Asserts
assert.Equal(t, 0, openPriceDiff)
assert.Equal(t, -240, maxDD)
assert.Equal(t, 392, maxPositive)
assert.Equal(t, -240, maxDDForMaxPositive)
assert.Equal(t, 400, profitInPoints)

}

func buyDDFinder(t *testing.T, symbol string, openTime time.Time, closeTime time.Time, openPrice float64, closePrice float64) (
openPriceDiff int, maxDD int, maxPositive int, maxDDForMaxPositive int, maxPositiveTime time.Time, closePriceDiff int,
) {
start := openTime.Add(-1 * time.Minute)
end := closeTime.Add(time.Minute)
stream := stream.New(symbol, start, end, createEmptyDir(t))

maxDD = math.MaxInt32
openPriceDiff = math.MaxInt32
closePriceDiff = math.MaxInt32
maxPositive = math.MinInt32
stream.EachTick(func(tickTime time.Time, tick *tickdata.TickData, err error) bool {
if openTime.Sub(tickTime) > 0 {
return true
}

if openPriceDiff == math.MaxInt32 {
printTick(" open", tickTime, tick)
openPriceDiff = int(math.Round((openPrice - tick.Ask) * 1000))
}

dd := math.Round((tick.Bid - openPrice) * 1000)
maxDD = int(math.Min(float64(maxDD), dd))

ddInInt := int(dd)
if maxPositive <= ddInInt {
maxPositive = ddInInt
maxPositiveTime = tickTime
maxDDForMaxPositive = maxDD
}

if closeTime.Sub(tickTime) <= 0 && closePriceDiff == math.MaxInt32 {
printTick("close", tickTime, tick)
closePriceDiff = int(math.Round((tick.Bid - closePrice) * 1000))
return false
}

return true
})
return openPriceDiff, maxDD, maxPositive, maxDDForMaxPositive, maxPositiveTime, closePriceDiff
}

func createEmptyDir(t *testing.T) string {
dir, err := ioutil.TempDir(".", "test")
if !assert.NoError(t, err) {
t.FailNow()
}
t.Cleanup(func() {
os.RemoveAll(dir)
})
return dir
}

func printTick(op string, tickTime time.Time, tick *tickdata.TickData) {
println(op,
"date [", fmtTime(tickTime), "] timestamp [", tick.Timestamp,
"] ask [", fmtPrice(tick.Ask), "] bid [", fmtPrice(tick.Bid), "]",
)
}

func fmtDuration(d time.Duration) string {
d = d.Round(time.Minute)
h := d / time.Hour
d -= h * time.Hour
m := d / time.Minute
d -= m * time.Minute
s := d / time.Second
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}

func fmtTime(tickTime time.Time) string {
return tickTime.Format("2006-01-02 15:04:05")
}

func fmtPrice(p float64) string {
return fmt.Sprintf("%.3f", p)
}
4 changes: 2 additions & 2 deletions internal/bi5/bi5.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func (b Bi5) Symbol() string {
}

// New create an bi5 saver
func New(day time.Time, symbol, dest string) *Bi5 {
func New(day time.Time, symbol, downloadFolderPath string) *Bi5 {
y, m, d := day.Date()

biFilePath := filepath.FromSlash(fmt.Sprintf("%s/download/%s/%04d/%02d/%02d/%02dh_ticks.%s", dest, symbol, y, m, d, day.Hour(), ext))
biFilePath := filepath.FromSlash(fmt.Sprintf("%s/download/%s/%04d/%02d/%02d/%02dh_ticks.%s", downloadFolderPath, symbol, y, m, d, day.Hour(), ext))
metadata := instrument.GetMetadata(symbol)

return &Bi5{
Expand Down

0 comments on commit 92e72c4

Please sign in to comment.