service backend
This commit is contained in:
18
api/fx/storage/model/cross.go
Normal file
18
api/fx/storage/model/cross.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
// CrossRateConfig describes how to synthetically derive a currency pair using
|
||||
// two other pairs connected by a pivot currency.
|
||||
type CrossRateConfig struct {
|
||||
Enabled bool `bson:"enabled" json:"enabled"`
|
||||
PivotCurrency string `bson:"pivotCurrency,omitempty" json:"pivotCurrency,omitempty"`
|
||||
BaseLeg CrossRateLeg `bson:"baseLeg" json:"baseLeg"`
|
||||
QuoteLeg CrossRateLeg `bson:"quoteLeg" json:"quoteLeg"`
|
||||
}
|
||||
|
||||
// CrossRateLeg identifies a supporting currency pair and optional overrides to
|
||||
// fetch or orient its pricing data for cross-rate calculations.
|
||||
type CrossRateLeg struct {
|
||||
Pair CurrencyPair `bson:"pair" json:"pair"`
|
||||
Invert bool `bson:"invert,omitempty" json:"invert,omitempty"`
|
||||
Provider string `bson:"provider,omitempty" json:"provider,omitempty"`
|
||||
}
|
||||
27
api/fx/storage/model/currency.go
Normal file
27
api/fx/storage/model/currency.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package model
|
||||
|
||||
import "github.com/tech/sendico/pkg/db/storable"
|
||||
|
||||
// Currency captures rounding metadata for a given currency code.
|
||||
type Currency struct {
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
|
||||
Code string `bson:"code" json:"code"`
|
||||
Decimals uint32 `bson:"decimals" json:"decimals"`
|
||||
Rounding RoundingMode `bson:"rounding" json:"rounding"`
|
||||
DisplayName string `bson:"displayName,omitempty" json:"displayName,omitempty"`
|
||||
Symbol string `bson:"symbol,omitempty" json:"symbol,omitempty"`
|
||||
MinUnit string `bson:"minUnit,omitempty" json:"minUnit,omitempty"`
|
||||
}
|
||||
|
||||
// Collection implements storable.Storable.
|
||||
func (*Currency) Collection() string {
|
||||
return CurrenciesCollection
|
||||
}
|
||||
|
||||
// CurrencySettings embeds precision details inside a Pair document.
|
||||
type CurrencySettings struct {
|
||||
Code string `bson:"code" json:"code"`
|
||||
Decimals uint32 `bson:"decimals" json:"decimals"`
|
||||
Rounding RoundingMode `bson:"rounding" json:"rounding"`
|
||||
}
|
||||
26
api/fx/storage/model/pair.go
Normal file
26
api/fx/storage/model/pair.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package model
|
||||
|
||||
import "github.com/tech/sendico/pkg/db/storable"
|
||||
|
||||
// Pair describes a supported FX currency pair and related metadata.
|
||||
type Pair struct {
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
|
||||
Pair CurrencyPair `bson:"pair" json:"pair"`
|
||||
BaseMeta CurrencySettings `bson:"baseMeta" json:"baseMeta"`
|
||||
QuoteMeta CurrencySettings `bson:"quoteMeta" json:"quoteMeta"`
|
||||
Providers []string `bson:"providers,omitempty" json:"providers,omitempty"`
|
||||
IsEnabled bool `bson:"isEnabled" json:"isEnabled"`
|
||||
TenantRef string `bson:"tenantRef,omitempty" json:"tenantRef,omitempty"`
|
||||
DefaultProvider string `bson:"defaultProvider,omitempty" json:"defaultProvider,omitempty"`
|
||||
Attributes map[string]any `bson:"attributes,omitempty" json:"attributes,omitempty"`
|
||||
SupportedSides []QuoteSide `bson:"supportedSides,omitempty" json:"supportedSides,omitempty"`
|
||||
FallbackProviders []string `bson:"fallbackProviders,omitempty" json:"fallbackProviders,omitempty"`
|
||||
Tags []string `bson:"tags,omitempty" json:"tags,omitempty"`
|
||||
Cross *CrossRateConfig `bson:"cross,omitempty" json:"cross,omitempty"`
|
||||
}
|
||||
|
||||
// Collection implements storable.Storable.
|
||||
func (*Pair) Collection() string {
|
||||
return PairsCollection
|
||||
}
|
||||
63
api/fx/storage/model/quote.go
Normal file
63
api/fx/storage/model/quote.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
)
|
||||
|
||||
// Quote represents a firm or indicative quote persisted by the oracle.
|
||||
type Quote struct {
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
|
||||
QuoteRef string `bson:"quoteRef" json:"quoteRef"`
|
||||
Firm bool `bson:"firm" json:"firm"`
|
||||
Status QuoteStatus `bson:"status" json:"status"`
|
||||
Pair CurrencyPair `bson:"pair" json:"pair"`
|
||||
Side QuoteSide `bson:"side" json:"side"`
|
||||
Price string `bson:"price" json:"price"`
|
||||
BaseAmount Money `bson:"baseAmount" json:"baseAmount"`
|
||||
QuoteAmount Money `bson:"quoteAmount" json:"quoteAmount"`
|
||||
AmountType QuoteAmountType `bson:"amountType" json:"amountType"`
|
||||
ExpiresAtUnixMs int64 `bson:"expiresAtUnixMs" json:"expiresAtUnixMs"`
|
||||
ExpiresAt *time.Time `bson:"expiresAt,omitempty" json:"expiresAt,omitempty"`
|
||||
RateRef string `bson:"rateRef" json:"rateRef"`
|
||||
Provider string `bson:"provider" json:"provider"`
|
||||
PreferredProvider string `bson:"preferredProvider,omitempty" json:"preferredProvider,omitempty"`
|
||||
RequestedTTLMs int64 `bson:"requestedTtlMs,omitempty" json:"requestedTtlMs,omitempty"`
|
||||
MaxAgeToleranceMs int64 `bson:"maxAgeToleranceMs,omitempty" json:"maxAgeToleranceMs,omitempty"`
|
||||
ConsumedByLedgerTxnRef string `bson:"consumedByLedgerTxnRef,omitempty" json:"consumedByLedgerTxnRef,omitempty"`
|
||||
ConsumedAtUnixMs *int64 `bson:"consumedAtUnixMs,omitempty" json:"consumedAtUnixMs,omitempty"`
|
||||
Meta *QuoteMeta `bson:"meta,omitempty" json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// Collection implements storable.Storable.
|
||||
func (*Quote) Collection() string {
|
||||
return QuotesCollection
|
||||
}
|
||||
|
||||
// MarkConsumed switches the quote to consumed status and links it to a ledger transaction.
|
||||
func (q *Quote) MarkConsumed(ledgerTxnRef string, consumedAt time.Time) {
|
||||
if ledgerTxnRef == "" {
|
||||
return
|
||||
}
|
||||
q.Status = QuoteStatusConsumed
|
||||
q.ConsumedByLedgerTxnRef = ledgerTxnRef
|
||||
ts := consumedAt.UnixMilli()
|
||||
q.ConsumedAtUnixMs = &ts
|
||||
q.Base.Update()
|
||||
}
|
||||
|
||||
// MarkExpired marks the quote as expired.
|
||||
func (q *Quote) MarkExpired() {
|
||||
q.Status = QuoteStatusExpired
|
||||
q.Base.Update()
|
||||
}
|
||||
|
||||
// IsExpired reports whether the quote has passed its expiration instant.
|
||||
func (q *Quote) IsExpired(now time.Time) bool {
|
||||
if q.ExpiresAtUnixMs == 0 {
|
||||
return false
|
||||
}
|
||||
return now.UnixMilli() >= q.ExpiresAtUnixMs
|
||||
}
|
||||
34
api/fx/storage/model/rate.go
Normal file
34
api/fx/storage/model/rate.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
)
|
||||
|
||||
// RateSnapshot stores a normalized FX rate observation.
|
||||
type RateSnapshot struct {
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
|
||||
RateRef string `bson:"rateRef" json:"rateRef"`
|
||||
Pair CurrencyPair `bson:"pair" json:"pair"`
|
||||
Provider string `bson:"provider" json:"provider"`
|
||||
Mid string `bson:"mid,omitempty" json:"mid,omitempty"`
|
||||
Bid string `bson:"bid,omitempty" json:"bid,omitempty"`
|
||||
Ask string `bson:"ask,omitempty" json:"ask,omitempty"`
|
||||
SpreadBps string `bson:"spreadBps,omitempty" json:"spreadBps,omitempty"`
|
||||
AsOfUnixMs int64 `bson:"asOfUnixMs" json:"asOfUnixMs"`
|
||||
AsOf *time.Time `bson:"asOf,omitempty" json:"asOf,omitempty"`
|
||||
Source string `bson:"source,omitempty" json:"source,omitempty"`
|
||||
ProviderRef string `bson:"providerRef,omitempty" json:"providerRef,omitempty"`
|
||||
}
|
||||
|
||||
// Collection implements storable.Storable.
|
||||
func (*RateSnapshot) Collection() string {
|
||||
return RatesCollection
|
||||
}
|
||||
|
||||
// AsOfTime converts the stored millisecond timestamp to time.Time.
|
||||
func (r *RateSnapshot) AsOfTime() time.Time {
|
||||
return time.UnixMilli(r.AsOfUnixMs)
|
||||
}
|
||||
68
api/fx/storage/model/types.go
Normal file
68
api/fx/storage/model/types.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package model
|
||||
|
||||
import "github.com/tech/sendico/pkg/model"
|
||||
|
||||
// Collection names used by the FX oracle persistence layer.
|
||||
const (
|
||||
RatesCollection = "rates"
|
||||
QuotesCollection = "quotes"
|
||||
CurrenciesCollection = "currencies"
|
||||
PairsCollection = "pairs"
|
||||
)
|
||||
|
||||
// QuoteStatus tracks the lifecycle state of a quote.
|
||||
type QuoteStatus string
|
||||
|
||||
const (
|
||||
QuoteStatusIssued QuoteStatus = "issued"
|
||||
QuoteStatusConsumed QuoteStatus = "consumed"
|
||||
QuoteStatusExpired QuoteStatus = "expired"
|
||||
)
|
||||
|
||||
// QuoteSide expresses the trade direction for the requested quote.
|
||||
type QuoteSide string
|
||||
|
||||
const (
|
||||
QuoteSideBuyBaseSellQuote QuoteSide = "buy_base_sell_quote"
|
||||
QuoteSideSellBaseBuyQuote QuoteSide = "sell_base_buy_quote"
|
||||
)
|
||||
|
||||
// QuoteAmountType indicates which leg amount was provided by the caller.
|
||||
type QuoteAmountType string
|
||||
|
||||
const (
|
||||
QuoteAmountTypeBase QuoteAmountType = "base"
|
||||
QuoteAmountTypeQuote QuoteAmountType = "quote"
|
||||
)
|
||||
|
||||
// RoundingMode describes how rounding should be applied for a currency.
|
||||
type RoundingMode string
|
||||
|
||||
const (
|
||||
RoundingModeUnspecified RoundingMode = "unspecified"
|
||||
RoundingModeHalfEven RoundingMode = "half_even"
|
||||
RoundingModeHalfUp RoundingMode = "half_up"
|
||||
RoundingModeDown RoundingMode = "down"
|
||||
)
|
||||
|
||||
// CurrencyPair identifies an FX pair.
|
||||
type CurrencyPair struct {
|
||||
Base string `bson:"base" json:"base"`
|
||||
Quote string `bson:"quote" json:"quote"`
|
||||
}
|
||||
|
||||
// Money represents an exact decimal amount with its currency.
|
||||
type Money struct {
|
||||
Currency string `bson:"currency" json:"currency"`
|
||||
Amount string `bson:"amount" json:"amount"`
|
||||
}
|
||||
|
||||
// QuoteMeta carries request-scoped metadata associated with a quote.
|
||||
type QuoteMeta struct {
|
||||
model.OrganizationBoundBase `bson:",inline" json:",inline"`
|
||||
|
||||
RequestRef string `bson:"requestRef,omitempty" json:"requestRef,omitempty"`
|
||||
TenantRef string `bson:"tenantRef,omitempty" json:"tenantRef,omitempty"`
|
||||
TraceRef string `bson:"traceRef,omitempty" json:"traceRef,omitempty"`
|
||||
IdempotencyKey string `bson:"idempotencyKey,omitempty" json:"idempotencyKey,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user