code.vegaprotocol.io/vega@v0.79.0/datanode/entities/margin_levels.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package entities 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "time" 23 24 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 25 "code.vegaprotocol.io/vega/protos/vega" 26 27 "github.com/shopspring/decimal" 28 ) 29 30 type MarginLevels struct { 31 AccountID AccountID 32 OrderMarginAccountID AccountID 33 MaintenanceMargin decimal.Decimal 34 SearchLevel decimal.Decimal 35 InitialMargin decimal.Decimal 36 CollateralReleaseLevel decimal.Decimal 37 OrderMargin decimal.Decimal 38 Timestamp time.Time 39 TxHash TxHash 40 VegaTime time.Time 41 MarginMode MarginMode 42 MarginFactor decimal.Decimal 43 } 44 45 func MarginLevelsFromProto(ctx context.Context, margin *vega.MarginLevels, accountSource AccountSource, txHash TxHash, vegaTime time.Time) (MarginLevels, error) { 46 var ( 47 maintenanceMargin, searchLevel, initialMargin, collateralReleaseLevel, orderMargin decimal.Decimal 48 err error 49 ) 50 marginFactor := decimal.NewFromInt32(0) 51 if len(margin.MarginFactor) > 0 { 52 marginFactor, err = decimal.NewFromString(margin.MarginFactor) 53 if err != nil { 54 return MarginLevels{}, fmt.Errorf("failed to obtain margin factor for margin level: %w", err) 55 } 56 } 57 marginMode := MarginModeCrossMargin 58 if margin.MarginMode == vega.MarginMode_MARGIN_MODE_ISOLATED_MARGIN { 59 marginMode = MarginMode(margin.MarginMode) 60 } 61 62 marginAccount, err := GetAccountFromMarginLevel(ctx, margin, accountSource, txHash, vegaTime) 63 if err != nil { 64 return MarginLevels{}, fmt.Errorf("failed to obtain account for margin level: %w", err) 65 } 66 67 orderMarginAccount, err := GetAccountFromOrderMarginLevel(ctx, margin, accountSource, txHash, vegaTime) 68 var orderMarginAccountID AccountID 69 if margin.MarginMode == vega.MarginMode_MARGIN_MODE_ISOLATED_MARGIN && err != nil { 70 return MarginLevels{}, fmt.Errorf("failed to obtain account for order margin level: %w", err) 71 } else { 72 orderMarginAccountID = orderMarginAccount.ID 73 } 74 75 if maintenanceMargin, err = decimal.NewFromString(margin.MaintenanceMargin); err != nil { 76 return MarginLevels{}, fmt.Errorf("invalid maintenance margin: %w", err) 77 } 78 79 if searchLevel, err = decimal.NewFromString(margin.SearchLevel); err != nil { 80 return MarginLevels{}, fmt.Errorf("invalid search level: %w", err) 81 } 82 83 if initialMargin, err = decimal.NewFromString(margin.InitialMargin); err != nil { 84 return MarginLevels{}, fmt.Errorf("invalid initial margin: %w", err) 85 } 86 87 if collateralReleaseLevel, err = decimal.NewFromString(margin.CollateralReleaseLevel); err != nil { 88 return MarginLevels{}, fmt.Errorf("invalid collateralReleaseLevel: %w", err) 89 } 90 91 if len(margin.OrderMargin) == 0 { 92 orderMargin = decimal.NewFromInt32(0) 93 } else if orderMargin, err = decimal.NewFromString(margin.OrderMargin); err != nil { 94 return MarginLevels{}, fmt.Errorf("invalid orderMarginLevel: %w", err) 95 } 96 97 return MarginLevels{ 98 AccountID: marginAccount.ID, 99 OrderMarginAccountID: orderMarginAccountID, 100 MaintenanceMargin: maintenanceMargin, 101 SearchLevel: searchLevel, 102 InitialMargin: initialMargin, 103 CollateralReleaseLevel: collateralReleaseLevel, 104 OrderMargin: orderMargin, 105 Timestamp: time.Unix(0, vegaTime.UnixNano()), 106 TxHash: txHash, 107 VegaTime: vegaTime, 108 MarginMode: marginMode, 109 MarginFactor: marginFactor, 110 }, nil 111 } 112 113 func GetAccountFromMarginLevel(ctx context.Context, margin *vega.MarginLevels, accountSource AccountSource, txHash TxHash, vegaTime time.Time) (Account, error) { 114 marginAccount := Account{ 115 ID: "", 116 PartyID: PartyID(margin.PartyId), 117 AssetID: AssetID(margin.Asset), 118 MarketID: MarketID(margin.MarketId), 119 Type: vega.AccountType_ACCOUNT_TYPE_MARGIN, 120 TxHash: txHash, 121 VegaTime: vegaTime, 122 } 123 124 err := accountSource.Obtain(ctx, &marginAccount) 125 return marginAccount, err 126 } 127 128 func GetAccountFromOrderMarginLevel(ctx context.Context, margin *vega.MarginLevels, accountSource AccountSource, txHash TxHash, vegaTime time.Time) (Account, error) { 129 orderMarginAccount := Account{ 130 ID: "", 131 PartyID: PartyID(margin.PartyId), 132 AssetID: AssetID(margin.Asset), 133 MarketID: MarketID(margin.MarketId), 134 Type: vega.AccountType_ACCOUNT_TYPE_ORDER_MARGIN, 135 TxHash: txHash, 136 VegaTime: vegaTime, 137 } 138 139 err := accountSource.Obtain(ctx, &orderMarginAccount) 140 return orderMarginAccount, err 141 } 142 143 func (ml *MarginLevels) ToProto(ctx context.Context, accountSource AccountSource) (*vega.MarginLevels, error) { 144 marginAccount, err := accountSource.GetByID(ctx, ml.AccountID) 145 if err != nil { 146 return nil, fmt.Errorf("getting from account for transfer proto:%w", err) 147 } 148 149 return &vega.MarginLevels{ 150 MaintenanceMargin: ml.MaintenanceMargin.String(), 151 SearchLevel: ml.SearchLevel.String(), 152 InitialMargin: ml.InitialMargin.String(), 153 CollateralReleaseLevel: ml.CollateralReleaseLevel.String(), 154 OrderMargin: ml.OrderMargin.String(), 155 PartyId: marginAccount.PartyID.String(), 156 MarketId: marginAccount.MarketID.String(), 157 Asset: marginAccount.AssetID.String(), 158 Timestamp: ml.Timestamp.UnixNano(), 159 MarginMode: vega.MarginMode(ml.MarginMode), 160 MarginFactor: ml.MarginFactor.String(), 161 }, nil 162 } 163 164 func (ml MarginLevels) Cursor() *Cursor { 165 cursor := MarginCursor{ 166 VegaTime: ml.VegaTime, 167 AccountID: ml.AccountID, 168 } 169 return NewCursor(cursor.String()) 170 } 171 172 func (ml MarginLevels) ToProtoEdge(input ...any) (*v2.MarginEdge, error) { 173 if len(input) != 2 { 174 return nil, fmt.Errorf("expected account source and context argument") 175 } 176 177 ctx, ok := input[0].(context.Context) 178 if !ok { 179 return nil, fmt.Errorf("first argument must be a context.Context, got: %v", input[0]) 180 } 181 182 as, ok := input[1].(AccountSource) 183 if !ok { 184 return nil, fmt.Errorf("second argument must be an AccountSource, got: %v", input[1]) 185 } 186 187 mlProto, err := ml.ToProto(ctx, as) 188 if err != nil { 189 return nil, err 190 } 191 192 return &v2.MarginEdge{ 193 Node: mlProto, 194 Cursor: ml.Cursor().Encode(), 195 }, nil 196 } 197 198 type MarginLevelsKey struct { 199 AccountID AccountID 200 VegaTime time.Time 201 } 202 203 func (ml MarginLevels) Key() MarginLevelsKey { 204 return MarginLevelsKey{ml.AccountID, ml.VegaTime} 205 } 206 207 func (ml MarginLevels) ToRow() []interface{} { 208 return []interface{}{ 209 ml.AccountID, ml.OrderMarginAccountID, ml.Timestamp, ml.MaintenanceMargin, 210 ml.SearchLevel, ml.InitialMargin, ml.CollateralReleaseLevel, ml.OrderMargin, ml.TxHash, ml.VegaTime, 211 ml.MarginMode, ml.MarginFactor, 212 } 213 } 214 215 var MarginLevelsColumns = []string{ 216 "account_id", "order_margin_account_id", "timestamp", "maintenance_margin", 217 "search_level", "initial_margin", "collateral_release_level", "order_margin", "tx_hash", 218 "vega_time", "margin_mode", "margin_factor", 219 } 220 221 type MarginCursor struct { 222 VegaTime time.Time 223 AccountID AccountID 224 } 225 226 func (mc MarginCursor) String() string { 227 bs, err := json.Marshal(mc) 228 if err != nil { 229 // This should never happen 230 panic(fmt.Errorf("failed to marshal margin cursor: %w", err)) 231 } 232 return string(bs) 233 } 234 235 func (mc *MarginCursor) Parse(cursorString string) error { 236 if cursorString == "" { 237 return nil 238 } 239 return json.Unmarshal([]byte(cursorString), mc) 240 }