money

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: MIT Imports: 3 Imported by: 0

README

kite-mcp-money

Go Reference

Currency-aware decimal money type for the algo2go ecosystem. Used by algo2go/kite-mcp-broker and consumers like Sundeepg98/kite-mcp-server.

Why a separate module?

Money is a DDD-leaf primitive shared between the broker port (which defines Order.PnL / Holding.PnL / Position.PnL as Money) and downstream consumers (riskguard checks, dashboard renders, audit projections). Hosting it in its own module:

  • Lets kite-mcp-broker's go.mod declare require kite-mcp-money as a real upstream dep — no upstream-relative replace workarounds
  • Keeps the type identity stable across module-fetch boundaries (a consumer of broker and a direct consumer of money see the same Money type, because both import the same module path)
  • Makes the type independently versionable from broker.

Stability promise

v0.x — unstable. Method signatures may break between minor versions. Pin v0.1.0 deliberately. v1.0 ships only after the public method surface (Add, Sub, Mul, Div, Float64, IsPositive, IsNegative, IsZero, MarshalJSON, etc.) is reviewed for stability.

Install

go get github.com/algo2go/kite-mcp-money@v0.1.0

License

MIT — see LICENSE.

Authors

Original DDD-leaf extraction: Sundeepg98 (Zerodha Tech). Multi-module promotion (2026-05-06): algo2go contributors.

Documentation

Overview

Package money is the leaf-layer Money value object.

Why a separate package: the existing Slice 1-6d Money sweep placed the type in kc/domain. That worked for in-process consumers but blocked Slice 6e (broker.Holding.PnL / broker.Position.PnL elevated to Money) — the broker package can't import kc/domain because kc/domain itself imports broker for its Holding / Position / Order wrapper entities, and Go forbids the resulting cycle.

Extracting Money to a leaf package (zero internal repo deps) lets both kc/domain and broker import it freely. kc/domain.Money is now a type alias to money.Money so the 65+ existing consumer files (372+ constructor sites, struct literals, method calls) continue to compile unchanged. The alias is structural, not behavioural — downstream code observes identical semantics.

Wire-format choice (MarshalJSON / UnmarshalJSON):

  • INR Money serializes as a bare JSON number (1234.56). This is the pre-Slice-6e wire shape for broker.Holding.PnL etc.; keeping it bare-float means external Kite-API-shaped consumers see no schema break.
  • Non-INR Money serializes as {"amount":N,"currency":"S"}. The cross-currency case is rare in practice (gokiteconnect emits INR-only) but the explicit shape removes ambiguity for forward- compat multi-currency accounts.
  • UnmarshalJSON accepts BOTH shapes symmetrically: a bare number rehydrates as INR; an object preserves the currency tag.

Empty Currency on a zero-value Money is the "no money set" sentinel — IsZero() / IsPositive() / IsNegative() are the canonical predicates. Empty Currency does NOT auto-INR at construction (zero-value safety), but bare-float JSON DOES auto-INR on unmarshal (wire-compat).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Money

type Money struct {
	Amount   float64
	Currency string
}

Money is a value object representing a monetary amount with currency. The default and primary currency is INR.

func NewINR

func NewINR(amount float64) Money

NewINR creates a Money value in Indian Rupees without validation. Kept for existing callers that intentionally pass zero/negative values (adjustments, PnL deltas). New call sites that must reject invalid amounts should prefer NewMoney.

func NewMoney

func NewMoney(amount float64) (Money, error)

NewMoney creates a validated Money value in INR. Rejects amounts that are not strictly positive — the canonical "price must be > 0" invariant for LIMIT/SL orders. Zero is rejected so that the zero-value Money can be detected as "no price set" via IsPositive.

func (Money) Add

func (m Money) Add(other Money) (Money, error)

Add returns a new Money that is the sum of m and other. Returns an error if the currencies differ.

func (Money) Float64

func (m Money) Float64() float64

Float64 returns the underlying amount. Boundary accessor for JSON serialization, log fields, and SQLite REAL bindings — call sites that invoke this are deliberately crossing out of the domain layer. New in-domain code should keep working with Money values directly.

func (Money) GreaterThan

func (m Money) GreaterThan(other Money) (bool, error)

GreaterThan reports whether m's amount is strictly greater than other's. Returns an error if the currencies differ — silent cross-currency comparison would defeat the type's purpose (e.g. comparing a USD limit against an INR order value would silently coerce). The riskguard per-user MaxSingleOrderINR / MaxDailyValueINR caps use this method.

func (Money) IsNegative

func (m Money) IsNegative() bool

IsNegative returns true if the amount is less than zero.

func (Money) IsPositive

func (m Money) IsPositive() bool

IsPositive returns true if the amount is greater than zero.

func (Money) IsZero

func (m Money) IsZero() bool

IsZero returns true if the amount is exactly zero.

func (Money) MarshalJSON

func (m Money) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler. Wire-compat choice (Slice 6e precondition): INR Money + zero-value Money serialize as a bare JSON number to match the pre-Money-typed broker DTO wire shape; non-INR Money serializes as an explicit {"amount", "currency"} object so cross-currency cases are unambiguous on the wire.

Justification: 372+ constructor call sites currently emit Money via the kc/domain package. Those that cross JSON boundaries (broker DTOs post-Slice-6e, billing.Subscription) use INR almost exclusively (gokiteconnect emits INR; billing tiers are INR by tier table). Bare-float emission means downstream consumers see no schema break. The non-INR object shape is reserved for the small future multi- currency surface.

func (Money) Multiply

func (m Money) Multiply(factor float64) Money

Multiply returns a new Money scaled by the given factor.

func (Money) String

func (m Money) String() string

String formats the money for display. INR is rendered as "₹1,234.56"; other currencies use the ISO code prefix.

func (Money) Sub

func (m Money) Sub(other Money) (Money, error)

Sub returns a new Money that is m minus other. Returns an error if the currencies differ.

func (*Money) UnmarshalJSON

func (m *Money) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler symmetric to MarshalJSON: a bare JSON number rehydrates as an INR Money (matches the bare-float emission); an object with "amount" + "currency" keys preserves the currency tag. This is the read-side guarantee that historical rows (pre-Slice-6e, all bare floats) round-trip correctly into the new Money-typed broker DTOs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL