service backend
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful

This commit is contained in:
Stephan D
2025-11-07 18:35:26 +01:00
parent 20e8f9acc4
commit 62a6631b9a
537 changed files with 48453 additions and 0 deletions

View 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"`
}

View 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"`
}

View 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
}

View 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
}

View 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)
}

View 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"`
}