Mosaic SDK

Ruby SDK for the Mosaic API. Provides a simple interface to manage banks, DNAs, clones, and NAVs, routed through the Fluence API gateway.

Installation

Add the gem and its fluence-gateway-client runtime dependency to your application's Gemfile (both are hosted on GitHub Packages):

source 'https://rubygems.pkg.github.com/fluence-eu' do
  gem 'mosaic-sdk'
  gem 'fluence-gateway-client'
end

Then run:

bundle install

Configuration

Credentials and gateway URLs are managed by fluence-gateway-client. Set APPCENTER_CLIENT_ID and APPCENTER_CLIENT_SECRET and you're done — the SDK targets the built-in :mosaic tenant out of the box.

For overrides (URL, profile, SSL, etc.), see the fluence-gateway-client README.

Usage

Banks

# List all banks
banks = Mosaic::Sdk.Bank.list

# Search banks by name
banks = Mosaic::Sdk.Bank.list('My Bank')

# Get a specific bank
bank = Mosaic::Sdk.Bank(123).data

DNAs

# List all DNAs
dnas = Mosaic::Sdk.Dna.list

# Get a specific DNA
dna = Mosaic::Sdk.Dna(42).data

# List clones for a DNA
clones = Mosaic::Sdk.Dna(42).clones.list

# Get a specific clone for a DNA
clone = Mosaic::Sdk.Dna(42).clones(5).data

Clones

# List all clones
clones = Mosaic::Sdk.Clone.list

# Search clones by name
clones = Mosaic::Sdk.Clone.list('My Clone')

# Filter clones by code type
clones = Mosaic::Sdk.Clone.list(type: 'isin')

# Search and filter by code type
clones = Mosaic::Sdk.Clone.list('My Clone', type: 'isin')

# Get a specific clone
clone = Mosaic::Sdk.Clone(10).data

# Create a clone
clone = Mosaic::Sdk.Clone.create(
  name: 'My Clone',
  codes: [{ type: 'isin', value: 'FR0000000001' }],
  source: 'manual',
  dna_id: 42,
  account_references: ['REF001']
)

# Create a clone within a DNA context (dna_id is inferred)
clone = Mosaic::Sdk.Dna(42).clones.create(
  name: 'My Clone',
  codes: [{ type: 'isin', value: 'FR0000000001' }],
  source: 'manual'
)

# List NAVs for a clone
navs = Mosaic::Sdk.Clone(10).navs.list
# Get the latest NAV for a clone
nav = Mosaic::Sdk.Clone(10).navs.latest.data

# Get NAV for a specific date
nav = Mosaic::Sdk.Clone(10).navs.for_date('2026-01-15').data

# Also works with Date objects
nav = Mosaic::Sdk.Clone(10).navs.for_date(Date.today).data

# Create a NAV
nav = Mosaic::Sdk.Clone(10).navs.create(
  price: 150.25,
  unit: 'price',
  date: '2026-03-11',
  pricing_method: 'net_asset_value_final',
  bank_id: 1
)

Batch reads by code

Resolve many resources by external code (ISIN, …) in a single call, via list(codes:). These return a Hash keyed by the requested code whose values are Instance objects — read attributes with [] (string or symbol keys) and chain associations directly.

# Clone metadata by code (optionally a richer view)
clones = Mosaic::Sdk.Clone.list(codes: %w[FR0000000001 LU0000000002], view: :full)
clones['FR0000000001'][:name]

# Latest NAV per code (optionally with a date ceiling, optionally a richer view)
navs = Mosaic::Sdk.Nav.list(codes: %w[FR0000000001], to: Date.today, view: 'with_documents')
navs['FR0000000001'].first[:price]

# NAVs over a date range
history = Mosaic::Sdk.Nav.list(
  codes: %w[FR0000000001],
  from: Date.new(2026, 1, 1),
  to: Date.today
)
# => { "FR0000000001" => [#<Nav::Instance>, ...] }

The top-level Mosaic::Sdk.Nav collection only supports these code-addressed reads; create NAVs under a clone (Mosaic::Sdk.Clone(10).navs.create).

Targeting a currency line

A security can have one clone per currency (a dual listing in EUR and GBX is two valuation units). A bare code entry resolves the security's unique clone — when the code is carried by several currency lines it is ambiguous and omitted from the response; target a line explicitly with a Hash carrying code: and currency:. Entries can be mixed freely, and object entries are keyed CODE|CURRENCY in the response:

Mosaic::Sdk.Clone.list(codes: ['FR0000000001', { code: 'FR0000120271', currency: 'EUR' }])
# => { "FR0000000001" => #<Clone::Instance>, "FR0000120271|EUR" => #<Clone::Instance> }

The resolved instances chain like any other:

clone = Mosaic::Sdk.Clone.list(codes: [{ code: 'FR0000120271', currency: 'EUR' }])['FR0000120271|EUR']
clone.navs.create(price: 100.0, unit: 'price', date: '2026-07-01', pricing_method: 'net_asset_value_final', bank_id: 3)

Trading venues

The trading venue (ISO 10383 MIC) is price provenance, not identity. When creating a clone, mic: records the security's default/primary listing venue (it backfills the default venue when the push resolves to an existing clone without one):

clone = Mosaic::Sdk.Clone.create(
  name: 'My Clone',
  codes: [{ type: 'isin', value: 'FR0000120271' }],
  source: 'manual',
  dna_id: 42,
  mic: 'XPAR'
)

When creating a NAV, mic: records the venue the price was observed on — the same date can carry one price per venue:

clone.navs.create(price: 100.0, unit: 'price', date: '2026-07-01',
                  pricing_method: 'net_asset_value_final', bank_id: 3, mic: 'XPAR')

Reading back, Mosaic::Sdk.Nav.list(codes:) accepts the same mic: alongside a kind: ('price' or 'percentage') in an object entry, to filter the resolved line down to a single price series — the clone resolved is still the one matching code:/currency: only, mic:/kind: never affect which line is picked:

Mosaic::Sdk.Nav.list(codes: [{ code: 'FR0000120271', currency: 'EUR', mic: 'XPAR', kind: 'price' }])
# => { "FR0000120271|EUR|XPAR|price" => [#<Nav::Instance>, ...] }

Chaining associations

Models support association chaining from DNA down to NAV:

# DNA -> Clones -> NAVs
latest_nav = Mosaic::Sdk.Dna(42).clones(5).navs.latest.data

Development

After checking out the repo, run bin/setup to install dependencies.

Use bin/console for an interactive prompt with the SDK pre-loaded. Set the APPCENTER_CLIENT_ID and APPCENTER_CLIENT_SECRET environment variables (read directly by fluence-gateway-client) before launching:

APPCENTER_CLIENT_ID=your_id APPCENTER_CLIENT_SECRET=your_secret bin/console

Run the full catalog (lint + security + codequality + tests) with:

bundle exec fluence-ci all

License

The gem is available as open source under the terms of the MIT License.