Skip to content
Merged
21 changes: 21 additions & 0 deletions pkg/migrations/mysql/xfundingv2_20260722093545_add_fee_cost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package mysql

import (
"github.com/c9s/rockhopper/v2"
)

// This migration was compiled from pkg/strategy/xfundingv2/migrations/mysql/20260722093545_add_fee_cost.sql.
// The SQL statements are registered as data so they can be previewed in the
// console while the migration runs, exactly like a raw .sql migration.
func init() {
AddStatementMigration("xfundingv2", 20260722093545, "pkg/strategy/xfundingv2/migrations/mysql/20260722093545_add_fee_cost.sql", true,
[]rockhopper.Statement{
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_closed_rounds`\n ADD COLUMN `fee_symbol` VARCHAR(32) NOT NULL DEFAULT '' AFTER `funding_income`,\n ADD COLUMN `fee_avg_cost` DECIMAL(20, 8) NOT NULL DEFAULT 0 AFTER `fee_symbol`;"},
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_round_snapshots`\n ADD COLUMN `fee_symbol` VARCHAR(32) NOT NULL DEFAULT '' AFTER `funding_income`,\n ADD COLUMN `fee_avg_cost` DECIMAL(20, 8) NOT NULL DEFAULT 0 AFTER `fee_symbol`;"},
},
[]rockhopper.Statement{
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_round_snapshots`\n DROP COLUMN `fee_avg_cost`,\n DROP COLUMN `fee_symbol`;"},
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_closed_rounds`\n DROP COLUMN `fee_avg_cost`,\n DROP COLUMN `fee_symbol`;"},
},
)
}
25 changes: 25 additions & 0 deletions pkg/migrations/sqlite3/xfundingv2_20260722093545_add_fee_cost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package sqlite3

import (
"github.com/c9s/rockhopper/v2"
)

// This migration was compiled from pkg/strategy/xfundingv2/migrations/sqlite3/20260722093545_add_fee_cost.sql.
// The SQL statements are registered as data so they can be previewed in the
// console while the migration runs, exactly like a raw .sql migration.
func init() {
AddStatementMigration("xfundingv2", 20260722093545, "pkg/strategy/xfundingv2/migrations/sqlite3/20260722093545_add_fee_cost.sql", true,
[]rockhopper.Statement{
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_closed_rounds` ADD COLUMN `fee_symbol` TEXT NOT NULL DEFAULT '';"},
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_closed_rounds` ADD COLUMN `fee_avg_cost` REAL NOT NULL DEFAULT 0;"},
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_round_snapshots` ADD COLUMN `fee_symbol` TEXT NOT NULL DEFAULT '';"},
{Direction: rockhopper.DirectionUp, SQL: "ALTER TABLE `xfundingv2_round_snapshots` ADD COLUMN `fee_avg_cost` REAL NOT NULL DEFAULT 0;"},
},
[]rockhopper.Statement{
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_round_snapshots` DROP COLUMN `fee_avg_cost`;"},
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_round_snapshots` DROP COLUMN `fee_symbol`;"},
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_closed_rounds` DROP COLUMN `fee_avg_cost`;"},
{Direction: rockhopper.DirectionDown, SQL: "ALTER TABLE `xfundingv2_closed_rounds` DROP COLUMN `fee_symbol`;"},
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- @package xfundingv2
-- +up
-- +begin
ALTER TABLE `xfundingv2_closed_rounds`
ADD COLUMN `fee_symbol` VARCHAR(32) NOT NULL DEFAULT '' AFTER `funding_income`,
ADD COLUMN `fee_avg_cost` DECIMAL(20, 8) NOT NULL DEFAULT 0 AFTER `fee_symbol`;
-- +end

-- +begin
ALTER TABLE `xfundingv2_round_snapshots`
ADD COLUMN `fee_symbol` VARCHAR(32) NOT NULL DEFAULT '' AFTER `funding_income`,
ADD COLUMN `fee_avg_cost` DECIMAL(20, 8) NOT NULL DEFAULT 0 AFTER `fee_symbol`;
-- +end

-- +down
-- +begin
ALTER TABLE `xfundingv2_round_snapshots`
DROP COLUMN `fee_avg_cost`,
DROP COLUMN `fee_symbol`;
-- +end

-- +begin
ALTER TABLE `xfundingv2_closed_rounds`
DROP COLUMN `fee_avg_cost`,
DROP COLUMN `fee_symbol`;
-- +end
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- @package xfundingv2
-- +up
-- +begin
ALTER TABLE `xfundingv2_closed_rounds` ADD COLUMN `fee_symbol` TEXT NOT NULL DEFAULT '';
-- +end
-- +begin
ALTER TABLE `xfundingv2_closed_rounds` ADD COLUMN `fee_avg_cost` REAL NOT NULL DEFAULT 0;
-- +end
-- +begin
ALTER TABLE `xfundingv2_round_snapshots` ADD COLUMN `fee_symbol` TEXT NOT NULL DEFAULT '';
-- +end
-- +begin
ALTER TABLE `xfundingv2_round_snapshots` ADD COLUMN `fee_avg_cost` REAL NOT NULL DEFAULT 0;
-- +end

-- +down
-- +begin
ALTER TABLE `xfundingv2_round_snapshots` DROP COLUMN `fee_avg_cost`;
-- +end
-- +begin
ALTER TABLE `xfundingv2_round_snapshots` DROP COLUMN `fee_symbol`;
-- +end
-- +begin
ALTER TABLE `xfundingv2_closed_rounds` DROP COLUMN `fee_avg_cost`;
-- +end
-- +begin
ALTER TABLE `xfundingv2_closed_rounds` DROP COLUMN `fee_symbol`;
-- +end
16 changes: 7 additions & 9 deletions pkg/strategy/xfundingv2/round_pnl.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ func (r *ArbitrageRound) RealizedPnL() *RoundRealizedPnL {
}

func (r *ArbitrageRound) realizedPnL() *RoundRealizedPnL {

fundingIncome := r.totalFundingIncome()

spotMarket := r.spotWorker.Market()
futuresMarket := r.futuresWorker.Market()

spotPosition := types.NewPositionFromMarket(spotMarket)
spotPosition.UseExcludeFeeFromCostMode()
futuresPosition := types.NewPositionFromMarket(futuresMarket)
futuresPosition.UseExcludeFeeFromCostMode()
if r.spotExchangeFeeRates != nil {
spotPosition.ExchangeFeeRates = r.spotExchangeFeeRates
}
Expand Down Expand Up @@ -159,18 +160,15 @@ func (p *RoundUnrealizedPnL) TotalPnL() fixedpoint.Value {
return p.RoundRealizedPnL.TotalPnL().Add(p.UnrealizedSpotPnL).Add(p.UnrealizedFuturesPnL)
}

// legUnrealizedPnL marks one leg to market. Base is signed, so the PnL is simply
// (price - averageCost) * base for both long and short. The close-side price is the
// best bid for a long leg (sell to close) and the best ask for a short leg (buy to
// close). Returns zero when the position is flat or the relevant book side is empty.
// legUnrealizedPnL marks one leg to market.
// If the position is long, it uses the best bid price; Otherwise, it uses the best ask price.
func legUnrealizedPnL(book types.OrderBook, position *types.Position) (fixedpoint.Value, fixedpoint.Value) {
base := position.Base
if base.IsZero() {
if position.IsClosed() {
return fixedpoint.Zero, fixedpoint.Zero
}

var price fixedpoint.Value
if base.Sign() > 0 {
if position.IsLong() {
bid, ok := book.BestBid()
if !ok {
return fixedpoint.Zero, fixedpoint.Zero
Expand All @@ -184,5 +182,5 @@ func legUnrealizedPnL(book types.OrderBook, position *types.Position) (fixedpoin
price = ask.Price
}

return price, price.Sub(position.AverageCost).Mul(base)
return price, position.UnrealizedProfit(price)
}
Loading
Loading