mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
commit
2e9f554f9e
|
@ -71,7 +71,7 @@ const parseOrder = () => {
|
|||
case "update_time":
|
||||
case "creation_time":
|
||||
case "time":
|
||||
d[key] = new Date(d[key]);
|
||||
d[key] = moment(d[key], 'dddd, DD MMM YYYY h:mm:ss').toDate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,16 +31,25 @@ exchangeStrategies:
|
|||
stoploss: 0.3%
|
||||
source: close
|
||||
predictOffset: 2
|
||||
# the init value of takeProfitFactor Series, position avg +- takeProfitFactor * atr as take profit price
|
||||
takeProfitFactor: 6
|
||||
profitFactorWindow: 8
|
||||
noTrailingStopLoss: false
|
||||
trailingStopLossType: kline
|
||||
# stddev on high/low-source
|
||||
hlVarianceMultiplier: 0.22
|
||||
hlVarianceMultiplier: 0.23
|
||||
hlRangeWindow: 5
|
||||
smootherWindow: 2
|
||||
fisherTransformWindow: 8
|
||||
window1m: 24
|
||||
smootherWindow1m: 24
|
||||
fisherTransformWindow1m: 162
|
||||
smootherWindow: 1
|
||||
fisherTransformWindow: 9
|
||||
atrWindow: 14
|
||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||
pendingMinutes: 3
|
||||
noRebalance: true
|
||||
trendWindow: 12
|
||||
rebalanceFilter: 1.5
|
||||
|
||||
trailingActivationRatio: [0.004]
|
||||
trailingCallbackRate: [0.001]
|
||||
|
||||
generateGraph: true
|
||||
graphPNLDeductFee: true
|
||||
|
@ -79,7 +88,7 @@ sync:
|
|||
|
||||
backtest:
|
||||
startTime: "2022-01-01"
|
||||
endTime: "2022-07-29"
|
||||
endTime: "2022-08-30"
|
||||
symbols:
|
||||
- ETHBUSD
|
||||
sessions: [binance]
|
||||
|
|
|
@ -24,7 +24,7 @@ exchangeStrategies:
|
|||
- on: binance
|
||||
drift:
|
||||
canvasPath: "./output.png"
|
||||
symbol: BTCBUSD
|
||||
symbol: BTCUSDT
|
||||
# kline interval for indicators
|
||||
interval: 15m
|
||||
window: 2
|
||||
|
@ -32,18 +32,33 @@ exchangeStrategies:
|
|||
source: close
|
||||
predictOffset: 2
|
||||
noTrailingStopLoss: false
|
||||
trailingStopLossType: kline
|
||||
# stddev on high/low-source
|
||||
hlVarianceMultiplier: 0.22
|
||||
hlRangeWindow: 5
|
||||
smootherWindow: 2
|
||||
smootherWindow: 1
|
||||
fisherTransformWindow: 9
|
||||
# the init value of takeProfitFactor Series, the coefficient of ATR as TP
|
||||
takeProfitFactor: 6
|
||||
profitFactorWindow: 8
|
||||
window1m: 22
|
||||
smootherWindow1m: 18
|
||||
fisherTransformWindow1m: 162
|
||||
atrWindow: 14
|
||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||
pendingMinutes: 5
|
||||
noRebalance: true
|
||||
trendWindow: 576
|
||||
rebalanceFilter: 0
|
||||
|
||||
# ActivationRatio should be increasing order
|
||||
# when farest price from entry goes over that ratio, start using the callback ratio accordingly to do trailingstop
|
||||
#trailingActivationRatio: [0.01, 0.016, 0.05]
|
||||
trailingActivationRatio: [0.0012, 0.002, 0.01, 0.016]
|
||||
#trailingActivationRatio: []
|
||||
#trailingCallbackRate: []
|
||||
#trailingCallbackRate: [0.002, 0.01, 0.1]
|
||||
trailingCallbackRate: [0.0004, 0.0008, 0.002, 0.01]
|
||||
|
||||
generateGraph: true
|
||||
graphPNLDeductFee: true
|
||||
graphPNLDeductFee: false
|
||||
graphPNLPath: "./pnl.png"
|
||||
graphCumPNLPath: "./cumpnl.png"
|
||||
#exits:
|
||||
|
@ -100,18 +115,18 @@ sync:
|
|||
sessions:
|
||||
- binance
|
||||
symbols:
|
||||
- BTCBUSD
|
||||
- BTCUSDT
|
||||
|
||||
backtest:
|
||||
startTime: "2022-01-01"
|
||||
endTime: "2022-07-29"
|
||||
endTime: "2022-08-30"
|
||||
symbols:
|
||||
- BTCBUSD
|
||||
- BTCUSDT
|
||||
sessions: [binance]
|
||||
accounts:
|
||||
binance:
|
||||
makerFeeRate: 0.000
|
||||
takerFeeRate: 0.00075
|
||||
#takerFeeRate: 0.000
|
||||
balances:
|
||||
BTC: 1
|
||||
BUSD: 5000.0
|
||||
USDT: 5000
|
||||
|
|
5
go.mod
5
go.mod
|
@ -25,6 +25,7 @@ require (
|
|||
github.com/golang/mock v1.6.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.6
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4
|
||||
|
@ -43,7 +44,7 @@ require (
|
|||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.7.4
|
||||
github.com/valyala/fastjson v1.5.1
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0
|
||||
github.com/webview/webview v0.0.0-20210216142346-e0bfdf0e5d90
|
||||
|
@ -93,7 +94,7 @@ require (
|
|||
github.com/magiconair/properties v1.8.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
|
|
20
go.sum
20
go.sum
|
@ -81,7 +81,6 @@ github.com/c9s/requestgen v1.3.0/go.mod h1:5n9FU3hr5307IiXAmbMiZbHYaPiys1u9jCWYe
|
|||
github.com/c9s/rockhopper v1.2.2-0.20220617053729-ffdc87df194b h1:wT8c03PHLv7+nZUIGqxAzRvIfYHNxMCNVWwvdGkOXTs=
|
||||
github.com/c9s/rockhopper v1.2.2-0.20220617053729-ffdc87df194b/go.mod h1:EKObf66Cp7erWxym2de+07qNN5T1N9PXxHdh97N44EQ=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
|
@ -149,7 +148,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
|
@ -351,6 +349,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
|||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.6 h1:A6w2BuyPMtf7M82BGRBys9bAba2C26ZX9lrlrZ7uH6U=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.6/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
@ -439,8 +439,9 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
|
@ -502,6 +503,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -598,6 +600,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
@ -605,15 +609,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
|
||||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
|
||||
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.3 h1:WbFSXLxDFKVN69Sk8t+XHGzVCD7R8UoAATR8NqZgTbk=
|
||||
github.com/ugorji/go v1.2.3/go.mod h1:5l8GZ8hZvmL4uMdy+mhCO1LjswGRYco9Q3HfuisB21A=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.3 h1:/mVYEV+Jo3IZKeA5gBngN0AvNnQltEDkR+eQikkWQu0=
|
||||
|
@ -633,7 +638,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
|
@ -766,8 +770,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
|
||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
|
@ -846,7 +848,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -919,7 +920,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -51,6 +51,27 @@ func NewCoreInteraction(environment *Environment, trader *Trader) *CoreInteracti
|
|||
}
|
||||
}
|
||||
|
||||
type SimpleInteraction struct {
|
||||
Command string
|
||||
Description string
|
||||
F interface{}
|
||||
Cmd *interact.Command
|
||||
}
|
||||
|
||||
func (it *SimpleInteraction) Commands(i *interact.Interact) {
|
||||
it.Cmd = i.PrivateCommand(it.Command, it.Description, it.F)
|
||||
}
|
||||
|
||||
func RegisterCommand(command, desc string, f interface{}) *interact.Command {
|
||||
it := &SimpleInteraction{
|
||||
Command: command,
|
||||
Description: desc,
|
||||
F: f,
|
||||
}
|
||||
interact.AddCustomInteraction(it)
|
||||
return it.Cmd
|
||||
}
|
||||
|
||||
func getStrategySignatures(exchangeStrategies map[string]SingleExchangeStrategy) []string {
|
||||
var strategies []string
|
||||
for signature := range exchangeStrategies {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
|
@ -20,9 +22,19 @@ func NotifyTo(channel string, obj interface{}, args ...interface{}) {
|
|||
Notification.NotifyTo(channel, obj, args...)
|
||||
}
|
||||
|
||||
func SendPhoto(buffer *bytes.Buffer) {
|
||||
Notification.SendPhoto(buffer)
|
||||
}
|
||||
|
||||
func SendPhotoTo(channel string, buffer *bytes.Buffer) {
|
||||
Notification.SendPhotoTo(channel, buffer)
|
||||
}
|
||||
|
||||
type Notifier interface {
|
||||
NotifyTo(channel string, obj interface{}, args ...interface{})
|
||||
Notify(obj interface{}, args ...interface{})
|
||||
SendPhotoTo(channel string, buffer *bytes.Buffer)
|
||||
SendPhoto(buffer *bytes.Buffer)
|
||||
}
|
||||
|
||||
type NullNotifier struct{}
|
||||
|
@ -31,6 +43,10 @@ func (n *NullNotifier) NotifyTo(channel string, obj interface{}, args ...interfa
|
|||
|
||||
func (n *NullNotifier) Notify(obj interface{}, args ...interface{}) {}
|
||||
|
||||
func (n *NullNotifier) SendPhoto(buffer *bytes.Buffer) {}
|
||||
|
||||
func (n *NullNotifier) SendPhotoTo(channel string, buffer *bytes.Buffer) {}
|
||||
|
||||
type Notifiability struct {
|
||||
notifiers []Notifier
|
||||
SessionChannelRouter *PatternChannelRouter `json:"-"`
|
||||
|
@ -83,3 +99,15 @@ func (m *Notifiability) NotifyTo(channel string, obj interface{}, args ...interf
|
|||
n.NotifyTo(channel, obj, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Notifiability) SendPhoto(buffer *bytes.Buffer) {
|
||||
for _, n := range m.notifiers {
|
||||
n.SendPhoto(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Notifiability) SendPhotoTo(channel string, buffer *bytes.Buffer) {
|
||||
for _, n := range m.notifiers {
|
||||
n.SendPhotoTo(channel, buffer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,10 @@ func (e *GeneralOrderExecutor) SubmitOrders(ctx context.Context, submitOrders ..
|
|||
err = fmt.Errorf("can not place orders: %w", err)
|
||||
}
|
||||
}
|
||||
// FIXME: map by price and volume
|
||||
for i := 0; i < len(createdOrders); i++ {
|
||||
createdOrders[i].Tag = formattedOrders[i].Tag
|
||||
}
|
||||
|
||||
e.orderStore.Add(createdOrders...)
|
||||
e.activeMakerOrders.Add(createdOrders...)
|
||||
|
|
|
@ -104,8 +104,9 @@ func (s *OrderStore) Update(o types.Order) bool {
|
|||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
_, ok := s.orders[o.OrderID]
|
||||
old, ok := s.orders[o.OrderID]
|
||||
if ok {
|
||||
o.Tag = old.Tag
|
||||
s.orders[o.OrderID] = o
|
||||
}
|
||||
return ok
|
||||
|
|
|
@ -34,6 +34,10 @@ func (inc *FisherTransform) Update(value float64) {
|
|||
inc.prices.Update(value)
|
||||
highest := inc.prices.Highest(inc.Window)
|
||||
lowest := inc.prices.Lowest(inc.Window)
|
||||
if highest == lowest {
|
||||
inc.Values.Update(0)
|
||||
return
|
||||
}
|
||||
x := 2*((value-lowest)/(highest-lowest)) - 1
|
||||
if x == 1 {
|
||||
x = 0.9999
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package slacknotifier
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
@ -150,6 +151,14 @@ func (n *Notifier) NotifyTo(channel string, obj interface{}, args ...interface{}
|
|||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) SendPhoto(buffer *bytes.Buffer) {
|
||||
n.SendPhotoTo(n.channel, buffer)
|
||||
}
|
||||
|
||||
func (n *Notifier) SendPhotoTo(channel string, buffer *bytes.Buffer) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/*
|
||||
func (n *Notifier) NotifyTrade(trade *types.Trade) {
|
||||
_, _, err := n.client.PostMessageContext(context.Background(), n.TradeChannel,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package telegramnotifier
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
@ -129,6 +130,48 @@ func (n *Notifier) NotifyTo(channel string, obj interface{}, args ...interface{}
|
|||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) SendPhoto(buffer *bytes.Buffer) {
|
||||
n.SendPhotoTo("", buffer)
|
||||
}
|
||||
|
||||
func photoFromBuffer(buffer *bytes.Buffer) telebot.InputMedia {
|
||||
reader := bytes.NewReader(buffer.Bytes())
|
||||
return &telebot.Photo{
|
||||
File: telebot.FromReader(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) SendPhotoTo(channel string, buffer *bytes.Buffer) {
|
||||
if n.broadcast {
|
||||
if n.Subscribers == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for chatID := range n.Subscribers {
|
||||
chat, err := n.bot.ChatByID(strconv.FormatInt(chatID, 10))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("can not get chat by ID")
|
||||
continue
|
||||
}
|
||||
album := telebot.Album{
|
||||
photoFromBuffer(buffer),
|
||||
}
|
||||
if _, err := n.bot.SendAlbum(chat, album); err != nil {
|
||||
log.WithError(err).Error("failed to send message")
|
||||
}
|
||||
}
|
||||
} else if n.Chats != nil {
|
||||
for _, chat := range n.Chats {
|
||||
album := telebot.Album{
|
||||
photoFromBuffer(buffer),
|
||||
}
|
||||
if _, err := n.bot.SendAlbum(chat, album); err != nil {
|
||||
log.WithError(err).Error("telegram send error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) AddChat(c *telebot.Chat) {
|
||||
if n.Chats == nil {
|
||||
n.Chats = make(map[int64]*telebot.Chat)
|
||||
|
|
51
pkg/strategy/drift/driftma.go
Normal file
51
pkg/strategy/drift/driftma.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package drift
|
||||
|
||||
import (
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
type DriftMA struct {
|
||||
types.SeriesBase
|
||||
ma1 types.UpdatableSeriesExtend
|
||||
drift *indicator.Drift
|
||||
ma2 types.UpdatableSeriesExtend
|
||||
}
|
||||
|
||||
func (s *DriftMA) Update(value float64) {
|
||||
s.ma1.Update(value)
|
||||
s.drift.Update(s.ma1.Last())
|
||||
s.ma2.Update(s.drift.Last())
|
||||
}
|
||||
|
||||
func (s *DriftMA) Last() float64 {
|
||||
return s.ma2.Last()
|
||||
}
|
||||
|
||||
func (s *DriftMA) Index(i int) float64 {
|
||||
return s.ma2.Index(i)
|
||||
}
|
||||
|
||||
func (s *DriftMA) Length() int {
|
||||
return s.ma2.Length()
|
||||
}
|
||||
|
||||
func (s *DriftMA) ZeroPoint() float64 {
|
||||
return s.drift.ZeroPoint()
|
||||
}
|
||||
|
||||
func (s *DriftMA) Clone() *DriftMA {
|
||||
out := DriftMA{
|
||||
ma1: types.Clone(s.ma1),
|
||||
drift: s.drift.Clone(),
|
||||
ma2: types.Clone(s.ma2),
|
||||
}
|
||||
out.SeriesBase.Series = &out
|
||||
return &out
|
||||
}
|
||||
|
||||
func (s *DriftMA) TestUpdate(v float64) *DriftMA {
|
||||
out := s.Clone()
|
||||
out.Update(v)
|
||||
return out
|
||||
}
|
210
pkg/strategy/drift/output.go
Normal file
210
pkg/strategy/drift/output.go
Normal file
|
@ -0,0 +1,210 @@
|
|||
package drift
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
)
|
||||
|
||||
type jsonStruct struct {
|
||||
key string
|
||||
json string
|
||||
tp string
|
||||
value interface{}
|
||||
}
|
||||
type jsonArr []jsonStruct
|
||||
|
||||
func (a jsonArr) Len() int { return len(a) }
|
||||
func (a jsonArr) Less(i, j int) bool { return a[i].key < a[j].key }
|
||||
func (a jsonArr) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func (s *Strategy) ParamDump(f io.Writer, seriesLength ...int) {
|
||||
length := 1
|
||||
if len(seriesLength) > 0 && seriesLength[0] > 0 {
|
||||
length = seriesLength[0]
|
||||
}
|
||||
val := reflect.ValueOf(s).Elem()
|
||||
for i := 0; i < val.Type().NumField(); i++ {
|
||||
t := val.Type().Field(i)
|
||||
if ig := t.Tag.Get("ignore"); ig == "true" {
|
||||
continue
|
||||
}
|
||||
field := val.Field(i)
|
||||
if t.IsExported() || t.Anonymous || t.Type.Kind() == reflect.Func || t.Type.Kind() == reflect.Chan {
|
||||
continue
|
||||
}
|
||||
fieldName := t.Name
|
||||
typeName := field.Type().String()
|
||||
log.Infof("fieldName %s typeName %s", fieldName, typeName)
|
||||
value := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
|
||||
isSeries := true
|
||||
lastFunc := value.MethodByName("Last")
|
||||
isSeries = isSeries && lastFunc.IsValid()
|
||||
lengthFunc := value.MethodByName("Length")
|
||||
isSeries = isSeries && lengthFunc.IsValid()
|
||||
indexFunc := value.MethodByName("Index")
|
||||
isSeries = isSeries && indexFunc.IsValid()
|
||||
|
||||
stringFunc := value.MethodByName("String")
|
||||
canString := stringFunc.IsValid()
|
||||
|
||||
if isSeries {
|
||||
l := int(lengthFunc.Call(nil)[0].Int())
|
||||
if l >= length {
|
||||
fmt.Fprintf(f, "%s: Series[..., %.4f", fieldName, indexFunc.Call([]reflect.Value{reflect.ValueOf(length - 1)})[0].Float())
|
||||
for j := length - 2; j >= 0; j-- {
|
||||
fmt.Fprintf(f, ", %.4f", indexFunc.Call([]reflect.Value{reflect.ValueOf(j)})[0].Float())
|
||||
}
|
||||
fmt.Fprintf(f, "]\n")
|
||||
} else if l > 0 {
|
||||
fmt.Fprintf(f, "%s: Series[%.4f", fieldName, indexFunc.Call([]reflect.Value{reflect.ValueOf(l - 1)})[0].Float())
|
||||
for j := l - 2; j >= 0; j-- {
|
||||
fmt.Fprintf(f, ", %.4f", indexFunc.Call([]reflect.Value{reflect.ValueOf(j)})[0].Float())
|
||||
}
|
||||
fmt.Fprintf(f, "]\n")
|
||||
} else {
|
||||
fmt.Fprintf(f, "%s: Series[]\n", fieldName)
|
||||
}
|
||||
} else if canString {
|
||||
fmt.Fprintf(f, "%s: %s\n", fieldName, stringFunc.Call(nil)[0].String())
|
||||
} else if field.CanConvert(reflect.TypeOf(int(0))) {
|
||||
fmt.Fprintf(f, "%s: %d\n", fieldName, field.Int())
|
||||
} else if field.CanConvert(reflect.TypeOf(float64(0))) {
|
||||
fmt.Fprintf(f, "%s: %.4f\n", fieldName, field.Float())
|
||||
} else if field.CanInterface() {
|
||||
fmt.Fprintf(f, "%s: %v", fieldName, field.Interface())
|
||||
} else if field.Type().Kind() == reflect.Map {
|
||||
fmt.Fprintf(f, "%s: {", fieldName)
|
||||
iter := value.MapRange()
|
||||
for iter.Next() {
|
||||
k := iter.Key().Interface()
|
||||
v := iter.Value().Interface()
|
||||
fmt.Fprintf(f, "%v: %v, ", k, v)
|
||||
}
|
||||
fmt.Fprintf(f, "}\n")
|
||||
} else if field.Type().Kind() == reflect.Slice {
|
||||
fmt.Fprintf(f, "%s: [", fieldName)
|
||||
l := field.Len()
|
||||
if l > 0 {
|
||||
fmt.Fprintf(f, "%v", field.Index(0))
|
||||
}
|
||||
for j := 1; j < field.Len(); j++ {
|
||||
fmt.Fprintf(f, ", %v", field.Index(j))
|
||||
}
|
||||
fmt.Fprintf(f, "]\n")
|
||||
} else {
|
||||
fmt.Fprintf(f, "%s(%s): %s\n", fieldName, typeName, field.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Strategy) Print(f io.Writer, pretty bool, withColor ...bool) {
|
||||
//b, _ := json.MarshalIndent(s.ExitMethods, " ", " ")
|
||||
|
||||
t := table.NewWriter()
|
||||
style := table.Style{
|
||||
Name: "StyleRounded",
|
||||
Box: table.StyleBoxRounded,
|
||||
Color: table.ColorOptionsDefault,
|
||||
Format: table.FormatOptionsDefault,
|
||||
HTML: table.DefaultHTMLOptions,
|
||||
Options: table.OptionsDefault,
|
||||
Title: table.TitleOptionsDefault,
|
||||
}
|
||||
var hiyellow func(io.Writer, string, ...interface{})
|
||||
if len(withColor) > 0 && withColor[0] {
|
||||
if pretty {
|
||||
style.Color = table.ColorOptionsYellowWhiteOnBlack
|
||||
style.Color.Row = text.Colors{text.FgHiYellow, text.BgHiBlack}
|
||||
style.Color.RowAlternate = text.Colors{text.FgYellow, text.BgBlack}
|
||||
}
|
||||
hiyellow = color.New(color.FgHiYellow).FprintfFunc()
|
||||
} else {
|
||||
hiyellow = func(a io.Writer, format string, args ...interface{}) {
|
||||
fmt.Fprintf(a, format, args...)
|
||||
}
|
||||
}
|
||||
if pretty {
|
||||
t.SetOutputMirror(f)
|
||||
t.SetStyle(style)
|
||||
t.AppendHeader(table.Row{"json", "struct field name", "type", "value"})
|
||||
}
|
||||
hiyellow(f, "------ %s Settings ------\n", s.InstanceID())
|
||||
|
||||
embeddedWhiteSet := map[string]struct{}{"Window": {}, "Interval": {}, "Symbol": {}}
|
||||
redundantSet := map[string]struct{}{}
|
||||
var rows []table.Row
|
||||
val := reflect.ValueOf(*s)
|
||||
var values jsonArr
|
||||
for i := 0; i < val.Type().NumField(); i++ {
|
||||
t := val.Type().Field(i)
|
||||
if !t.IsExported() {
|
||||
continue
|
||||
}
|
||||
fieldName := t.Name
|
||||
switch jsonTag := t.Tag.Get("json"); jsonTag {
|
||||
case "-":
|
||||
case "":
|
||||
if t.Anonymous {
|
||||
var target reflect.Type
|
||||
if t.Type.Kind() == util.Pointer {
|
||||
target = t.Type.Elem()
|
||||
} else {
|
||||
target = t.Type
|
||||
}
|
||||
for j := 0; j < target.NumField(); j++ {
|
||||
tt := target.Field(j)
|
||||
if !tt.IsExported() {
|
||||
continue
|
||||
}
|
||||
fieldName := tt.Name
|
||||
if _, ok := embeddedWhiteSet[fieldName]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if jtag := tt.Tag.Get("json"); jtag != "" && jtag != "-" {
|
||||
name := strings.Split(jtag, ",")[0]
|
||||
if _, ok := redundantSet[name]; ok {
|
||||
continue
|
||||
}
|
||||
redundantSet[name] = struct{}{}
|
||||
var value interface{}
|
||||
if t.Type.Kind() == util.Pointer {
|
||||
value = val.Field(i).Elem().Field(j).Interface()
|
||||
} else {
|
||||
value = val.Field(i).Field(j).Interface()
|
||||
}
|
||||
values = append(values, jsonStruct{key: fieldName, json: name, tp: tt.Type.String(), value: value})
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
name := strings.Split(jsonTag, ",")[0]
|
||||
if _, ok := redundantSet[name]; ok {
|
||||
continue
|
||||
}
|
||||
redundantSet[name] = struct{}{}
|
||||
values = append(values, jsonStruct{key: fieldName, json: name, tp: t.Type.String(), value: val.Field(i).Interface()})
|
||||
}
|
||||
}
|
||||
sort.Sort(values)
|
||||
for _, value := range values {
|
||||
if pretty {
|
||||
rows = append(rows, table.Row{value.json, value.key, value.tp, value.value})
|
||||
} else {
|
||||
hiyellow(f, "%s: %v\n", value.json, value.value)
|
||||
}
|
||||
}
|
||||
if pretty {
|
||||
t.AppendRows(rows)
|
||||
t.Render()
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1192,16 +1192,35 @@ func NewCanvas(title string, intervals ...Interval) *Canvas {
|
|||
return out
|
||||
}
|
||||
|
||||
func (canvas *Canvas) Plot(tag string, a Series, endTime Time, length int) {
|
||||
func expand(a []float64, length int, defaultVal float64) []float64 {
|
||||
l := len(a)
|
||||
if l >= length {
|
||||
return a
|
||||
}
|
||||
for i := 0; i < length-l; i++ {
|
||||
a = append([]float64{defaultVal}, a...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (canvas *Canvas) Plot(tag string, a Series, endTime Time, length int, intervals ...Interval) {
|
||||
var timeline []time.Time
|
||||
e := endTime.Time()
|
||||
if a.Length() == 0 {
|
||||
return
|
||||
}
|
||||
oldest := a.Index(a.Length() - 1)
|
||||
interval := canvas.Interval
|
||||
if len(intervals) > 0 {
|
||||
interval = intervals[0]
|
||||
}
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
shiftedT := e.Add(-time.Duration(i*canvas.Interval.Minutes()) * time.Minute)
|
||||
shiftedT := e.Add(-time.Duration(i*interval.Minutes()) * time.Minute)
|
||||
timeline = append(timeline, shiftedT)
|
||||
}
|
||||
canvas.Series = append(canvas.Series, chart.TimeSeries{
|
||||
Name: tag,
|
||||
YValues: Reverse(a, length),
|
||||
YValues: expand(Reverse(a, length), length, oldest),
|
||||
XValues: timeline,
|
||||
})
|
||||
}
|
||||
|
@ -1211,10 +1230,14 @@ func (canvas *Canvas) PlotRaw(tag string, a Series, length int) {
|
|||
for i := 0; i < length; i++ {
|
||||
x = append(x, float64(i))
|
||||
}
|
||||
if a.Length() == 0 {
|
||||
return
|
||||
}
|
||||
oldest := a.Index(a.Length() - 1)
|
||||
canvas.Series = append(canvas.Series, chart.ContinuousSeries{
|
||||
Name: tag,
|
||||
XValues: x,
|
||||
YValues: Reverse(a, length),
|
||||
YValues: expand(Reverse(a, length), length, oldest),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
|
@ -10,13 +12,14 @@ import (
|
|||
)
|
||||
|
||||
type IntervalProfitCollector struct {
|
||||
Interval Interval `json:"interval"`
|
||||
Profits *Float64Slice `json:"profits"`
|
||||
tmpTime time.Time `json:"tmpTime"`
|
||||
Interval Interval `json:"interval"`
|
||||
Profits *Float64Slice `json:"profits"`
|
||||
Timestamp *Float64Slice `json:"timestamp"`
|
||||
tmpTime time.Time `json:"tmpTime"`
|
||||
}
|
||||
|
||||
func NewIntervalProfitCollector(i Interval, startTime time.Time) *IntervalProfitCollector {
|
||||
return &IntervalProfitCollector{Interval: i, tmpTime: startTime, Profits: &Float64Slice{1.}}
|
||||
return &IntervalProfitCollector{Interval: i, tmpTime: startTime, Profits: &Float64Slice{1.}, Timestamp: &Float64Slice{float64(startTime.Unix())}}
|
||||
}
|
||||
|
||||
// Update the collector by every traded profit
|
||||
|
@ -31,6 +34,7 @@ func (s *IntervalProfitCollector) Update(profit *Profit) {
|
|||
for {
|
||||
s.Profits.Update(1.)
|
||||
s.tmpTime = s.tmpTime.Add(duration)
|
||||
s.Timestamp.Update(float64(s.tmpTime.Unix()))
|
||||
if profit.TradedAt.Before(s.tmpTime.Add(duration)) {
|
||||
(*s.Profits)[len(*s.Profits)-1] *= 1. + profit.NetProfitMargin.Float64()
|
||||
break
|
||||
|
@ -40,6 +44,48 @@ func (s *IntervalProfitCollector) Update(profit *Profit) {
|
|||
}
|
||||
}
|
||||
|
||||
type ProfitReport struct {
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Profit float64 `json:"profit"`
|
||||
Interval Interval `json:"interval"`
|
||||
}
|
||||
|
||||
func (s ProfitReport) String() string {
|
||||
b, err := json.MarshalIndent(s, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Get all none-profitable intervals
|
||||
func (s *IntervalProfitCollector) GetNonProfitableIntervals() (result []ProfitReport) {
|
||||
if s.Profits == nil {
|
||||
return result
|
||||
}
|
||||
l := s.Profits.Length()
|
||||
for i := 0; i < l; i++ {
|
||||
if s.Profits.Index(i) <= 1. {
|
||||
result = append(result, ProfitReport{StartTime: time.Unix(int64(s.Timestamp.Index(i)), 0), Profit: s.Profits.Index(i), Interval: s.Interval})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get all profitable intervals
|
||||
func (s *IntervalProfitCollector) GetProfitableIntervals() (result []ProfitReport) {
|
||||
if s.Profits == nil {
|
||||
return result
|
||||
}
|
||||
l := s.Profits.Length()
|
||||
for i := 0; i < l; i++ {
|
||||
if s.Profits.Index(i) > 1. {
|
||||
result = append(result, ProfitReport{StartTime: time.Unix(int64(s.Timestamp.Index(i)), 0), Profit: s.Profits.Index(i), Interval: s.Interval})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get number of profitable traded intervals
|
||||
func (s *IntervalProfitCollector) GetNumOfProfitableIntervals() (profit int) {
|
||||
if s.Profits == nil {
|
||||
|
@ -226,6 +272,8 @@ func (s *TradeStats) BriefString() string {
|
|||
GrossLoss: s.GrossLoss,
|
||||
LargestProfitTrade: s.LargestProfitTrade,
|
||||
LargestLossTrade: s.LargestLossTrade,
|
||||
AverageProfitTrade: s.AverageProfitTrade,
|
||||
AverageLossTrade: s.AverageLossTrade,
|
||||
ProfitFactor: s.ProfitFactor,
|
||||
TotalNetProfit: s.TotalNetProfit,
|
||||
IntervalProfits: s.IntervalProfits,
|
||||
|
|
8
pkg/util/pointer.go
Normal file
8
pkg/util/pointer.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
//go:build !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package util
|
||||
|
||||
import "reflect"
|
||||
|
||||
const Pointer = reflect.Ptr
|
8
pkg/util/pointer_18.go
Normal file
8
pkg/util/pointer_18.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package util
|
||||
|
||||
import "reflect"
|
||||
|
||||
const Pointer = reflect.Pointer
|
Loading…
Reference in New Issue
Block a user