88 "time"
99
1010 "github.com/google/uuid"
11+ "github.com/prometheus/client_golang/prometheus"
1112 "github.com/sirupsen/logrus"
1213 "github.com/slack-go/slack"
1314
@@ -57,6 +58,7 @@ type ArbitrageRound struct {
5758 lastRebalanceTime time.Time
5859 rebalanceInterval time.Duration
5960
61+ lastFundingRate fixedpoint.Value
6062 lastFundingIncomeSyncTime time.Time
6163
6264 futuresService FuturesService
@@ -65,6 +67,14 @@ type ArbitrageRound struct {
6567 spotSession , futuresSession * bbgo.ExchangeSession
6668 retryTransferTickC chan time.Time
6769
70+ // metrics
71+ fundingRateMetric prometheus.Gauge
72+ annualizedFundingRateMetric prometheus.Gauge
73+ totalPnLMetric prometheus.Gauge
74+ spotPositionMetric , futuresPositionMetric prometheus.Gauge
75+ spotFilledRatioMetric , futuresFilledRatioMetric prometheus.Gauge
76+ quantityDeviationMetric prometheus.Gauge
77+
6878 logger logrus.FieldLogger
6979 slackAlert slackalert.SlackAlert
7080}
@@ -115,6 +125,100 @@ func NewArbitrageRound(
115125 }
116126}
117127
128+ func (r * ArbitrageRound ) SetupMetrics (s * Strategy ) {
129+ id := s .InstanceID ()
130+ symbol := r .SpotSymbol ()
131+
132+ r .fundingRateMetric = fundingRateMetrics .With (
133+ prometheus.Labels {
134+ "symbol" : symbol ,
135+ },
136+ )
137+ r .annualizedFundingRateMetric = annualizedFundingRateMetrics .With (
138+ prometheus.Labels {
139+ "symbol" : symbol ,
140+ },
141+ )
142+
143+ r .totalPnLMetric = roundTotalPnLMetrics .With (
144+ prometheus.Labels {
145+ "strategy_id" : id ,
146+ "symbol" : symbol ,
147+ },
148+ )
149+
150+ r .spotPositionMetric = roundPositionMetrics .With (
151+ prometheus.Labels {
152+ "strategy_id" : id ,
153+ "symbol" : symbol ,
154+ "accountType" : "spot" ,
155+ },
156+ )
157+ r .futuresPositionMetric = roundPositionMetrics .With (
158+ prometheus.Labels {
159+ "strategy_id" : id ,
160+ "symbol" : symbol ,
161+ "accountType" : "futures" ,
162+ },
163+ )
164+ r .spotFilledRatioMetric = roundPositionFilledRatioMetrics .With (
165+ prometheus.Labels {
166+ "strategy_id" : id ,
167+ "symbol" : symbol ,
168+ "accountType" : "spot" ,
169+ },
170+ )
171+ r .futuresFilledRatioMetric = roundPositionFilledRatioMetrics .With (
172+ prometheus.Labels {
173+ "strategy_id" : id ,
174+ "symbol" : symbol ,
175+ "accountType" : "futures" ,
176+ },
177+ )
178+
179+ r .quantityDeviationMetric = roundQuantityDeviationMetrics .With (
180+ prometheus.Labels {
181+ "strategy_id" : id ,
182+ "symbol" : symbol ,
183+ },
184+ )
185+ }
186+
187+ func (r * ArbitrageRound ) RecordMetrics (posDeviation PositionDeviation , spotPrice , futuresPrice fixedpoint.Value ) {
188+ if r .totalPnLMetric != nil {
189+ unrealizedPnL := r .UnrealizedPnL (spotPrice , futuresPrice )
190+ r .totalPnLMetric .Set (unrealizedPnL .TotalPnL ().Float64 ())
191+ }
192+
193+ if ! r .lastFundingRate .IsZero () && r .fundingRateMetric != nil && r .annualizedFundingRateMetric != nil {
194+ annualizedRate := AnnualizedRate (r .lastFundingRate , r .syncState .FundingIntervalHours )
195+ r .fundingRateMetric .Set (r .lastFundingRate .Float64 ())
196+ r .annualizedFundingRateMetric .Set (annualizedRate .Float64 ())
197+ }
198+
199+ if r .spotPositionMetric != nil && r .futuresPositionMetric != nil {
200+ r .spotPositionMetric .Set (posDeviation .SpotFilled .Float64 ())
201+ r .futuresPositionMetric .Set (posDeviation .FuturesFilled .Float64 ())
202+ }
203+
204+ if r .spotFilledRatioMetric != nil && r .futuresFilledRatioMetric != nil {
205+ spotFilledPosition := r .SpotWorker ().FilledPosition ()
206+ spotFilledRatio := spotFilledPosition .Div (r .TriggeredTargetPosition ()).Abs ()
207+ futuresFilledPosition := r .FuturesWorker ().FilledPosition ()
208+ futuresFilledRatio := futuresFilledPosition .Div (r .TriggeredTargetPosition ()).Abs ()
209+ if r .State () == RoundClosing {
210+ spotFilledRatio = fixedpoint .One .Sub (spotFilledRatio )
211+ futuresFilledRatio = fixedpoint .One .Sub (futuresFilledRatio )
212+ }
213+ r .spotFilledRatioMetric .Set (spotFilledRatio .Float64 ())
214+ r .futuresFilledRatioMetric .Set (futuresFilledRatio .Float64 ())
215+ }
216+
217+ if r .quantityDeviationMetric != nil {
218+ r .quantityDeviationMetric .Set (posDeviation .DeviatedQuantity .Float64 ())
219+ }
220+ }
221+
118222func (r * ArbitrageRound ) Halt (currentTime time.Time ) {
119223 r .mu .Lock ()
120224 defer r .mu .Unlock ()
@@ -192,6 +296,13 @@ func (r *ArbitrageRound) FuturesFeeAssetAmount() fixedpoint.Value {
192296 return r .syncState .FuturesFeeAssetAmount
193297}
194298
299+ func (r * ArbitrageRound ) SetLastFundingRate (rate fixedpoint.Value ) {
300+ r .mu .Lock ()
301+ defer r .mu .Unlock ()
302+
303+ r .lastFundingRate = rate
304+ }
305+
195306// RequiredFeeAssetAmount returns the required fee asset amount for the round based on its current state and position.
196307// The first return value is for the spot leg and the second return value is for the futures leg.
197308func (r * ArbitrageRound ) RequiredFeeAssetAmounts () (fixedpoint.Value , fixedpoint.Value ) {
0 commit comments