Documentation
¶
Overview ¶
Package trader provides structured logging for the trader application using Go's standard log/slog library. It supports multiple concurrent output destinations (stdout, a log file, and syslog) and named module loggers so that log records can be filtered by subsystem (data, backtest, indicator, replay, …).
Typical usage:
// initialise once at startup (e.g. from main or cmd layer)
Setup(LogConfig{Level: "debug", Format: "text", File: "trader.log"})
// package-level helpers
Info("server started", "port", 8080)
Debug("tick received", "instrument", "EURUSD")
// module-scoped logger
logger := Module("data")
logger.Info("inventory built", "files", 42)
// or use the pre-wired module variables
Data.Info("download complete", "key", key)
Backtest.Warn("end of data reached")
Index ¶
- Constants
- Variables
- func ApproximateUSDPerUnit(currency string) (float64, bool)
- func AvgSpreadPips(spreadSum Price, spreadOpened int, inst *Instrument) float64
- func ClearEntries()
- func Debug(msg string, args ...any)
- func Error(msg string, args ...any)
- func Fatal(msg string, args ...any)
- func FormatTradeOrg(t TradeRecord) string
- func FormatTradesOrg(trades []TradeRecord) string
- func GenerateSyntheticYearTestData(basedir string, instrument string, year int, timeframe Timeframe) ([]string, error)
- func GetBoolParam(m map[string]any, key string) (bool, bool, error)
- func GetFloat64Param(m map[string]any, key string) (float64, bool, error)
- func GetInt32Param(m map[string]any, key string) (int32, bool, error)
- func GetIntParam(m map[string]any, key string) (int, bool, error)
- func GetStringParam(m map[string]any, key string) (string, bool, error)
- func Info(msg string, args ...any)
- func InstrumentPositions(lb *LotBook) map[string]Position
- func IsForexMarketClosed(t time.Time) bool
- func JournalRecordPaths(base string) (tradesPath, equityPath string)
- func MajorInstruments() []string
- func Module(name string) *slog.Logger
- func MustRegisterLiveStrategy(ctor LiveStrategyConstructor, names ...string)
- func MustRegisterStrategy(ctor StrategyConstructor, names ...string)
- func NewCSV(tradesPath, equityPath string) (*csvJournal, error)
- func NewDownloader() *downloader
- func NewJSON(tradesPath, equityPath string) (*jsonJournal, error)
- func NewULID() string
- func NormalizeInstrument(sym string) string
- func PrintSummary(w io.Writer, s BacktestReportSummary)
- func RegisterLiveStrategy(ctor LiveStrategyConstructor, names ...string) error
- func RegisterStrategy(ctor StrategyConstructor, names ...string) error
- func RegisteredLiveStrategies() []string
- func RegisteredStrategies() []string
- func RunAnalysis(ctx context.Context, itr CandleIterator, analyzers []Analyzer) (err error)
- func SetDataDir(dir string)
- func Setup(cfg LogConfig) error
- func ShortDisplayID(full string) string
- func SwapStore(s *Store) (restore func())
- func Warn(msg string, args ...any)
- func WriteOrgIndex(w io.Writer, summaries []BacktestReportSummary)
- func WriteOrgReport(w io.Writer, s BacktestReportSummary)
- type ADX
- type ATR
- type ATRPercentileFilter
- type Account
- type AnalysisStatus
- type Analyzer
- type Asset
- type AssetFlags
- type BA
- type Backtest
- type BacktestExecutor
- type BacktestReportSummary
- type BacktestReportTrade
- type BacktestRequest
- type BacktestResult
- type BacktestRun
- type BollingerBands
- func (b *BollingerBands) BandWidth() float64
- func (b *BollingerBands) Lower() float64
- func (b *BollingerBands) LowerPrice() Price
- func (b *BollingerBands) Middle() float64
- func (b *BollingerBands) MiddlePrice() Price
- func (b *BollingerBands) Name() string
- func (b *BollingerBands) PercentB(price float64) float64
- func (b *BollingerBands) PercentBPrice(price Price) float64
- func (b *BollingerBands) Period() int
- func (b *BollingerBands) Ready() bool
- func (b *BollingerBands) Reset()
- func (b *BollingerBands) StdDev() float64
- func (b *BollingerBands) StdDevPrice() Price
- func (b *BollingerBands) Update(c Candle)
- func (b *BollingerBands) Upper() float64
- func (b *BollingerBands) UpperPrice() Price
- func (b *BollingerBands) Warmup() int
- type Broker
- type BuildDecision
- type BuildStatus
- type BuildTask
- type CSVTicksFeed
- type Candle
- type CandleIndicator
- type CandleIterator
- type CandleRequest
- type CandleSource
- type CandleTime
- type CandleValidationIssue
- type CandleValidationReport
- type CandleValidationRequest
- type ChandelierExit
- func (c *ChandelierExit) InitialStop(side Side, entry Price, candle Candle) Price
- func (c *ChandelierExit) Name() string
- func (c *ChandelierExit) Ready() bool
- func (c *ChandelierExit) Tick(candle Candle)
- func (c *ChandelierExit) UpdateStop(side Side, currentStop Price, _ Price, extreme Price, candle Candle) Price
- type ChoppinessFilter
- func (f *ChoppinessFilter) AllowSide(_ Side) bool
- func (f *ChoppinessFilter) Choppiness() float64
- func (f *ChoppinessFilter) Name() string
- func (f *ChoppinessFilter) Ready() bool
- func (f *ChoppinessFilter) Tick(ct CandleTime)
- func (f *ChoppinessFilter) Trending() bool
- func (f *ChoppinessFilter) Value() float64
- type ChoppinessIndex
- type CloseMatcher
- type CloseRequest
- type CompiledBacktest
- type CompositeRegimeFilter
- type Config
- type D1ADXFilter
- type D1ChoppinessFilter
- func (f *D1ChoppinessFilter) AllowSide(_ Side) bool
- func (f *D1ChoppinessFilter) Choppiness() float64
- func (f *D1ChoppinessFilter) Name() string
- func (f *D1ChoppinessFilter) Ready() bool
- func (f *D1ChoppinessFilter) Tick(ct CandleTime)
- func (f *D1ChoppinessFilter) Trending() bool
- func (f *D1ChoppinessFilter) Value() float64
- type DataConfig
- type DataKind
- type DataManager
- func (dm *DataManager) BuildWantList(ctx context.Context) (*Wantlist, error)
- func (dm *DataManager) Candles(ctx context.Context, req CandleRequest) (CandleIterator, error)
- func (dm *DataManager) ExecuteDownloads(ctx context.Context) error
- func (dm *DataManager) Init()
- func (dm *DataManager) Plan(ctx context.Context) (*Plan, error)
- func (dm *DataManager) Sync(ctx context.Context, download, build bool) error
- type EMA
- type EquitySnapshot
- type Event
- type EventType
- type ExitConfig
- type ExitStrategy
- type FIFOMatcher
- type Float64Indicator
- type ForexAnalysis
- type GlobalConfig
- type GlobalDataConfig
- type GlobalLogConfig
- type GlobalOANDAConfig
- type Instrument
- func (inst *Instrument) AddPips(px Price, pips Pips) Price
- func (inst *Instrument) DukascopyPriceMultiplier() uint32
- func (inst *Instrument) PipSize() float64
- func (inst *Instrument) PipValueUSD(rate float64, units int64, pips float64) float64
- func (inst *Instrument) PriceDeltaFromPips(pips Pips) Price
- func (inst *Instrument) PriceUnitsPerPip() Price
- func (inst *Instrument) SubPips(px Price, pips Pips) Price
- type Inventory
- func (inv *Inventory) Delete(key Key)
- func (inv *Inventory) Get(key Key) (Asset, bool)
- func (inv *Inventory) Has(key Key) bool
- func (inv *Inventory) HasComplete(key Key) bool
- func (inv *Inventory) Keys() []Key
- func (inv *Inventory) Len() int
- func (inv *Inventory) List() []Asset
- func (inv *Inventory) MissingComplete(keys []Key) []Key
- func (inv *Inventory) Put(a Asset)
- func (inv *Inventory) TicksComplete(k Key) (complete bool, required []Key, missing []Key, err error)
- func (inv *Inventory) Update(key Key, fn func(*Asset) error) error
- func (inv *Inventory) WantReasonFor(key Key) (WantReason, bool)
- type Journal
- type Key
- type Keymap
- func (km *Keymap[V]) Delete(key Key)
- func (km *Keymap[V]) Get(key Key) (V, bool)
- func (km *Keymap[V]) Has(key Key) bool
- func (km *Keymap[V]) Keys() []Key
- func (km *Keymap[V]) Len() int
- func (km *Keymap[V]) List() []V
- func (km *Keymap[V]) Put(key Key, v V)
- func (km *Keymap[V]) Range(fn func(Key, V) bool)
- func (km *Keymap[V]) Update(key Key, fn func(*V) error) error
- type LinearCongruentialRandom
- type LiveJournal
- type LiveOpenRequest
- type LivePlan
- type LivePrice
- type LiveStrategy
- type LiveStrategyConstructor
- type LiveTrade
- type LogConfig
- type LogEntry
- type Lot
- type LotBook
- func (lb *LotBook) Add(lot *Lot) error
- func (lb *LotBook) All() map[string]*Lot
- func (lb *LotBook) Delete(id string) bool
- func (lb *LotBook) Get(id string) *Lot
- func (lb *LotBook) Has(id string) bool
- func (lb *LotBook) Len() int
- func (lb *LotBook) Range(fn func(*Lot) error) error
- func (lb *LotBook) Slice() []*Lot
- type LotMatch
- type Money
- type NoopExit
- type NoopRegime
- type OpenRequest
- type Pips
- type Plan
- type Position
- type Price
- type PriceIndicator
- type PriceSum
- type Rate
- type RawTick
- type RegimeConfig
- type RegimeFilter
- type Request
- type RequestType
- type RootConfig
- type RunConfig
- type RunDefaults
- type Scale6
- type Scale7
- type SessionAnalyzer
- type SessionFilter
- type Side
- type SpreadAnalyzer
- type Stat
- type Store
- func (s Store) Delete(k Key) error
- func (s Store) Exists(key Key) (bool, error)
- func (s *Store) IsUsableTickFile(k Key) bool
- func (s *Store) OpenTickIterator(key Key) (iterator[RawTick], error)
- func (s *Store) PathForAsset(k Key) (string, error)
- func (s *Store) PathForMonthlyCandle(k Key) string
- func (s *Store) RawCandlePath(k Key) (string, error)
- func (store *Store) ReadCSV(key Key) (cs *candleSet, err error)
- func (s *Store) RelDir(key Key) string
- func (s *Store) SaveFile(key Key, r io.ReadCloser) (path string, err error)
- func (s *Store) WriteCSV(cs *candleSet) error
- func (s *Store) WriteMonthlyCandles(source, instrument string, tf Timeframe, monthStart time.Time, ...) error
- type Strategy
- type StrategyConfig
- type StrategyConstructor
- type StrategyPlan
- type SwingAnalyzer
- type SyntheticCandleConfig
- func (cfg SyntheticCandleConfig) GenerateSyntheticMonthlyCandles(year int, month time.Month) (*candleSet, error)
- func (cfg SyntheticCandleConfig) GenerateSyntheticYearlyAndWrite(store *Store, year int) ([]string, error)
- func (cfg SyntheticCandleConfig) GenerateSyntheticYearlyCandles(year int) ([]*candleSet, error)
- type Tick
- type TimeRange
- type Timeframe
- type Timestamp
- func (t Timestamp) Add(d time.Duration) Timestamp
- func (t Timestamp) After(ts Timestamp) bool
- func (t Timestamp) Before(ts Timestamp) bool
- func (s Timestamp) FloorToHour() Timestamp
- func (s Timestamp) FloorToMinute() Timestamp
- func (t Timestamp) Int64() int64
- func (t Timestamp) IsZero() bool
- func (s Timestamp) MS() timemilli
- func (t Timestamp) Milli() timemilli
- func (t Timestamp) String() string
- func (t Timestamp) Time() time.Time
- type Trade
- type TradeCommon
- type TradeHistory
- type TradeRecord
- type Trader
- type TraderBacktestExecutor
- type TrendAnalyzer
- type Units
- type Want
- type WantReason
- type Wantlist
- func (wl *Wantlist) Delete(key Key)
- func (wl *Wantlist) Get(key Key) (Want, bool)
- func (wl *Wantlist) Has(key Key) bool
- func (wl *Wantlist) Keys() []Key
- func (wl *Wantlist) Len() int
- func (wl *Wantlist) List() []Want
- func (wl *Wantlist) Put(w Want)
- func (wl *Wantlist) PutKey(key Key, reason WantReason)
- func (wl *Wantlist) Range(fn func(Key, Want) bool)
- func (wl *Wantlist) Update(key Key, fn func(*Want) error) error
- type WeeklyEMAFilter
- func (f *WeeklyEMAFilter) AllowSide(side Side) bool
- func (f *WeeklyEMAFilter) EMA() float64
- func (f *WeeklyEMAFilter) EMAValue() float64
- func (f *WeeklyEMAFilter) Name() string
- func (f *WeeklyEMAFilter) Ready() bool
- func (f *WeeklyEMAFilter) Tick(ct CandleTime)
- func (f *WeeklyEMAFilter) Trending() bool
Constants ¶
const ( SourceDukascopy = "dukascopy" SourceOanda = "oanda" SourceCandles = "candles" )
const ( LotNone lotState = iota LotOpenRequested LotOpen LotCloseRequested LotClosed )
const ( PriceScale Scale6 = 100_000 MoneyScale Scale7 = 1_000_000 RateScale Scale7 = MoneyScale )
const ( CloseUnknown closeCause = iota CloseManual CloseStopLoss CloseTakeProfit CloseBrokerLiquidation )
const ( SecondInMS timemilli = 1_000 MinuteInSec Timestamp = 60 MinuteInMS timemilli = 60_000 HourInSec Timestamp = 3_600 HourInMS timemilli = 3_600_000 )
const ErrStatMissingInstrument = "missing instrument"
ErrStatMissingInstrument is the Value field in the error Stat returned when an analyzer has no instrument configured. Use this constant instead of a bare string literal when checking whether a Stat signals a missing instrument.
const TestDataDir = "testdata"
TestDataDir is the testdata directory path relative to workspace root.
const UnitsScale int64 = 1_000_000
UnitsScale is the fixed-point scale for Units values that represent fractional multipliers (e.g. 2.5 → Units(2_500_000)).
Variables ¶
var ( // Pre-wired module loggers. They are initialised to the default logger // in init() and remain valid across Setup calls. L *slog.Logger Data *slog.Logger BacktestLog *slog.Logger IndicatorLog *slog.Logger Strat *slog.Logger Replay *slog.Logger )
var DefaultStrategyPlan = StrategyPlan{
Reason: "hold",
}
var ErrKeyNotFound = errors.New("key not found")
var ErrTickNotFound = errors.New("tick not found")
var Version = "dev"
Version is the current build version. Set at build time via:
go build -ldflags="-X github.com/rustyeddy/trader.Version=v1.2.3"
Functions ¶
func ApproximateUSDPerUnit ¶ added in v0.2.3
ApproximateUSDPerUnit reports a rough USD conversion for a non-USD currency.
func AvgSpreadPips ¶ added in v0.2.3
func AvgSpreadPips(spreadSum Price, spreadOpened int, inst *Instrument) float64
AvgSpreadPips converts an accumulated Price spread into average pips.
func ClearEntries ¶
func ClearEntries()
ClearEntries discards all entries held in the in-memory stack.
func FormatTradeOrg ¶
func FormatTradeOrg(t TradeRecord) string
FormatTradeOrg renders a TradeRecord as an Org-mode block suitable for pasting into a journal. It purposely includes narrative placeholders (Thesis/Execution/Review) while keeping all structured facts in a PROPERTIES drawer for easy search.
func FormatTradesOrg ¶
func FormatTradesOrg(trades []TradeRecord) string
FormatTradesOrg renders multiple trades separated by blank lines.
func GenerateSyntheticYearTestData ¶
func GenerateSyntheticYearTestData(basedir string, instrument string, year int, timeframe Timeframe) ([]string, error)
GenerateSyntheticYearTestData generates a full year of synthetic test data.
func GetBoolParam ¶
GetBoolParam extracts a bool param, or returns ok=false if missing.
func GetFloat64Param ¶
GetFloat64Param extracts a float64 from a params map, widening integer types as needed. Returns (0, false, nil) when the key is absent, or an error if the value is not numeric.
func GetInt32Param ¶
GetInt32Param extracts an int32 from a params map, accepting the numeric types produced by YAML/JSON decoding. Returns (0, false, nil) when the key is absent, or an error if the value is not numeric.
func GetIntParam ¶ added in v0.2.3
GetIntParam extracts an int from a params map, accepting the numeric types produced by YAML/JSON decoding. Returns (0, false, nil) when the key is absent, or an error if the value is not numeric.
func GetStringParam ¶ added in v0.2.1
GetStringParam extracts a string param, or returns ok=false if missing.
func InstrumentPositions ¶
InstrumentPositions derives per-instrument Position from all open lots.
func IsForexMarketClosed ¶
IsForexMarketClosed is the exported form of isForexMarketClosed for use by sibling packages (e.g. data/dukascopy).
func JournalRecordPaths ¶ added in v0.2.3
func MajorInstruments ¶ added in v0.2.3
func MajorInstruments() []string
MajorInstruments returns the ordered list of seven major FX pairs tracked by this engine.
func Module ¶
Module returns a *slog.Logger pre-populated with the attribute "module"=name. The same logger is returned on subsequent calls with the same name.
func MustRegisterLiveStrategy ¶ added in v0.2.3
func MustRegisterLiveStrategy(ctor LiveStrategyConstructor, names ...string)
MustRegisterLiveStrategy registers a LiveStrategy and panics on error.
func MustRegisterStrategy ¶ added in v0.2.3
func MustRegisterStrategy(ctor StrategyConstructor, names ...string)
MustRegisterStrategy registers a strategy and panics on error. Intended for use in package init() registration paths so invalid registrations fail fast at startup.
func NewDownloader ¶
func NewDownloader() *downloader
func NewULID ¶
func NewULID() string
New returns a ULID string (time-sortable identifier).
ULIDs are lexicographically sortable by generation time, which makes them ideal for journaling/trading records and database indexes.
func NormalizeInstrument ¶
NormalizeInstrument is an internal helper for trader type processing.
func PrintSummary ¶
func PrintSummary(w io.Writer, s BacktestReportSummary)
PrintSummary writes a human-readable backtest report to w.
func RegisterLiveStrategy ¶ added in v0.2.3
func RegisterLiveStrategy(ctor LiveStrategyConstructor, names ...string) error
RegisterLiveStrategy registers a LiveStrategy constructor under one or more names. Typically called from a package's init() function.
func RegisterStrategy ¶
func RegisterStrategy(ctor StrategyConstructor, names ...string) error
RegisterStrategy adds a strategy constructor under one or more names. Typically called from an implementation package's init() function. Multiple aliases are supported (e.g. "donchian", "donchian-breakout").
func RegisteredLiveStrategies ¶ added in v0.2.3
func RegisteredLiveStrategies() []string
RegisteredLiveStrategies returns the sorted list of registered live strategy names.
func RegisteredStrategies ¶
func RegisteredStrategies() []string
RegisteredStrategies returns the sorted list of registered strategy names. Useful for help text and validation.
func RunAnalysis ¶ added in v0.2.1
func RunAnalysis(ctx context.Context, itr CandleIterator, analyzers []Analyzer) (err error)
RunAnalysis walks itr, feeding every candle to each Analyzer. It closes itr before returning.
func SetDataDir ¶
func SetDataDir(dir string)
SetDataDir overrides the global store's base directory. Call from main before any data operations.
func Setup ¶
Setup initialises (or re-initialises) the logging system according to cfg. It is safe to call multiple times; subsequent calls replace the active handler and close previously opened sinks.
func ShortDisplayID ¶ added in v0.2.3
ShortDisplayID returns a short, human-friendly prefix for headings and logs.
func SwapStore ¶
func SwapStore(s *Store) (restore func())
SwapStore replaces the global Store with the given one and returns a function that restores the previous Store. Useful in tests for sibling packages that need to point the global at a temp directory.
func WriteOrgIndex ¶
func WriteOrgIndex(w io.Writer, summaries []BacktestReportSummary)
WriteOrgIndex writes a single comparison table across all summaries to w.
func WriteOrgReport ¶
func WriteOrgReport(w io.Writer, s BacktestReportSummary)
WriteOrgReport writes a full per-run org-mode report to w.
Types ¶
type ADX ¶
type ADX struct {
// contains filtered or unexported fields
}
ADX computes the Average Directional Index (Wilder) over candle OHLC.
Readiness / warmup: - ADX needs:
- N periods to build initial smoothed TR/+DM/-DM
- N DX values to seed the initial ADX (average of first N DX)
- Practically, that's about 2N "periods" (differences between candles), plus the first candle. - We expose Warmup() as 2N to keep it simple/consistent with your other indicators.
type ATR ¶
type ATR struct {
// contains filtered or unexported fields
}
ATR computes the Average True Range (Wilder) over candle OHLC.
Warmup: needs N candle-to-candle periods (N+1 candles) before Ready() is true. ATR keeps fixed-point price units internally; Float64() is for display.
type ATRPercentileFilter ¶ added in v0.2.1
type ATRPercentileFilter struct {
// contains filtered or unexported fields
}
ATRPercentileFilter gates entries based on the percentile rank of the current ATR(atrPeriod) within a rolling window of windowSize ATR readings.
Trending() returns false when the current ATR percentile is below threshold, indicating a low-volatility ranging regime where breakout entries tend to fail. AllowSide() always returns true — this is a regime gate, not directional. Ready() becomes true as soon as ATR has warmed up and the first ATR reading has been recorded into the rolling window; the window does not need to be completely full before the filter starts classifying bars.
Default params: atrPeriod=20, windowSize=200, threshold=20.0. Registered in the factory as "atr-percentile".
func NewATRPercentileFilter ¶ added in v0.2.1
func NewATRPercentileFilter(atrPeriod, windowSize int, threshold float64, scale Scale6) (*ATRPercentileFilter, error)
func (*ATRPercentileFilter) AllowSide ¶ added in v0.2.1
func (f *ATRPercentileFilter) AllowSide(_ Side) bool
func (*ATRPercentileFilter) Name ¶ added in v0.2.1
func (f *ATRPercentileFilter) Name() string
func (*ATRPercentileFilter) Percentile ¶ added in v0.2.1
func (f *ATRPercentileFilter) Percentile() float64
Percentile exposes the current ATR percentile rank for debugging. Equal ATR values share the middle of their tie bucket, so a completely flat ATR window reports the 50th percentile instead of collapsing to 0.
func (*ATRPercentileFilter) Ready ¶ added in v0.2.1
func (f *ATRPercentileFilter) Ready() bool
func (*ATRPercentileFilter) Tick ¶ added in v0.2.1
func (f *ATRPercentileFilter) Tick(ct CandleTime)
func (*ATRPercentileFilter) Trending ¶ added in v0.2.1
func (f *ATRPercentileFilter) Trending() bool
type Account ¶
type Account struct {
ID string
Name string
Currency string // account denomination (e.g. "USD")
Balance Money // realised cash; updated on every close
Equity Money // Balance + sum of unrealised P/L across open lots
MarginUsed Money // sum of margin reserved by open lots
FreeMargin Money // Equity − MarginUsed
MarginLevel Money // Equity / MarginUsed × MoneyScale (0 when flat)
RiskFraction Rate // fraction of equity risked per trade (e.g. 0.005 = 0.5 %)
Lots LotBook
Trades []*Trade // closed trades, appended by CloseLot
}
Account holds the financial state for a single trading account. All monetary values are scaled integers (Money = int64 × MoneyScale). Invariants that must hold after every operation:
- Equity = Balance + UnrealizedPL
- FreeMargin = Equity − MarginUsed
func NewAccount ¶
NewAccount creates an Account with the given name and opening deposit. Currency defaults to "USD"; RiskFraction defaults to 0.5 %.
func (*Account) AddLot ¶
AddLot registers a newly opened lot with the account and immediately revalues all open positions at the lot's entry price.
func (*Account) CloseLot ¶
CloseLot realizes P/L for the lot, appends the trade to the account's Trades history, removes the lot from the LotBook, and revalues remaining open lots at the exit price.
func (*Account) ResolveWithMarks ¶
ResolveWithMarks recomputes all account-level derived fields (Equity, MarginUsed, FreeMargin, MarginLevel) using the provided mark prices. If a lot's instrument has no entry in marks, the lot's EntryPrice is used. Pass nil to revalue everything at entry.
func (*Account) SizePosition ¶
func (acct *Account) SizePosition(req *OpenRequest) error
SizePosition computes and sets req.Units as the lesser of:
- the units allowed by the risk budget (unitsByRisk)
- the units allowed by available margin (unitsByMargin)
Returns an error if the computed size is below the instrument's minimum trade size or if any input is invalid.
type AnalysisStatus ¶ added in v0.2.3
type AnalysisStatus string
AnalysisStatus is the action classification from a ChatGPT forex analysis row.
const ( StatusNoTrade AnalysisStatus = "No Trade" StatusWatchlist AnalysisStatus = "Watchlist" StatusTradeable AnalysisStatus = "Tradeable watch list" )
type Analyzer ¶ added in v0.2.1
type Analyzer interface {
Name() string
Update(*CandleTime)
Stats() []Stat
}
Analyzer accumulates statistics over a candle sequence.
type AssetFlags ¶
type AssetFlags uint32
const ( FlagUsable AssetFlags = 1 << iota FlagKnownClosed FlagDoNotDownload FlagDownloadFailed FlagManualSkip )
type BA ¶
BA represents a trader domain type.
type Backtest ¶
type Backtest struct {
ID string
RunConfig RunConfig // resolved config snapshot used for execution
Request *BacktestRequest
State *BacktestRun
Result *BacktestResult
}
Backtest is the executable form of one backtest run. It keeps the immutable request and mutable run-state together so strategies can inspect open lots during execution, while the final result is stored in the explicit Result field rather than anonymously merged into the run.
func (*Backtest) BuildBacktestResult ¶
func (run *Backtest) BuildBacktestResult(acct *Account) *BacktestResult
BuildBacktestResult snapshots the account state into a BacktestResult and stores it on the run's explicit Result field. It computes trade counts, returns, gross P/L, averages, risk/reward, and closed-trade drawdown from the account's closed trades. Returns nil if run or acct is nil.
func (*Backtest) Summary ¶
func (run *Backtest) Summary() BacktestReportSummary
Summary builds a fully-populated BacktestReportSummary from the run's request and result fields. It is safe to call after BuildBacktestResult. Returns a zero-value summary if any required field is nil.
type BacktestExecutor ¶ added in v0.2.3
BacktestExecutor runs an executable backtest using whatever runtime dependencies it needs. Service-layer code depends on this narrow contract instead of constructing Trader/Broker/Account directly.
type BacktestReportSummary ¶
type BacktestReportSummary struct {
Name string `json:"name"`
Strategy string `json:"strategy"`
Instrument string `json:"instrument"`
Timeframe string `json:"timeframe"`
Dataset string `json:"dataset"`
Start string `json:"start"`
End string `json:"end"`
Trades int `json:"trades"`
Wins int `json:"wins"`
Losses int `json:"losses"`
StartBalance float64 `json:"start_balance"`
EndBalance float64 `json:"end_balance"`
NetPL float64 `json:"net_pl"`
// Stored as human-friendly percentages, e.g. 12.34 means 12.34%
ReturnPct float64 `json:"return_pct"`
WinRate float64 `json:"win_rate"`
RiskPct float64 `json:"risk_pct"`
Stop string `json:"stop"`
Regime string `json:"regime"`
MaxSpread string `json:"max_spread,omitempty"`
Slippage string `json:"slippage,omitempty"`
// Execution cost stats
AvgSpreadPips float64 `json:"avg_spread_pips"`
SpreadFiltered int `json:"spread_filtered"`
RR float64 `json:"rr"`
MaxDrawdown float64 `json:"max_drawdown"` // largest peak-to-trough drop in dollars (negative)
AvgWinner float64 `json:"avg_winner"`
AvgLoser float64 `json:"avg_loser"` // negative
TradeDetails []BacktestReportTrade `json:"trade_details,omitempty"`
// Provenance links generated reports back to their origin. Older fixtures
// and manually constructed summaries may leave these fields empty.
ConfigHash string `json:"config_hash"` // 8-char SHA256 prefix of the run config params
GeneratedAt string `json:"generated_at"` // RFC3339 UTC timestamp of when the run completed
Config RunConfig `json:"config"` // full config snapshot that produced this result
}
BacktestReportSummary is a normalized machine-readable summary used for committed regression baselines and generated comparison artifacts. The Config and ConfigHash fields make every report self-describing: you can open any JSON file and see exactly what params produced it.
type BacktestReportTrade ¶
type BacktestReportTrade struct {
ID string `json:"id"`
Instrument string `json:"instrument"`
Side string `json:"side"`
Units int64 `json:"units"`
OpenPrice float64 `json:"open_price"`
ClosePrice float64 `json:"close_price"`
OpenTime string `json:"open_time"`
CloseTime string `json:"close_time"`
PNL float64 `json:"pnl"`
StopPrice float64 `json:"stop_price,omitempty"`
TakeProfitPrice float64 `json:"take_profit_price,omitempty"`
}
BacktestReportTrade is a JSON-serialisable record of a single closed trade used inside BacktestReportSummary.TradeDetails.
type BacktestRequest ¶
type BacktestRequest struct {
Name string
ConfigHash string // 8-char SHA256 prefix of execution-affecting config inputs
StartingBalance Money
RiskPct Rate // fraction of equity risked per trade (e.g. 0.005 = 0.5 %)
DefaultStopPips Pips // fallback stop distance when the strategy doesn't supply one
DefaultTakePips Pips // fallback take-profit distance
SlippagePips Pips // extra adverse fill adjustment applied on every open/close
MaxSpreadPips Pips // opens are skipped when the candle spread exceeds this
Source string // data source identifier (e.g. "candles", "dukascopy")
Instrument string // FX pair (e.g. "EUR_USD")
Strategy Strategy
Exit ExitStrategy
Regime RegimeFilter
TimeRange TimeRange
}
BacktestRequest holds all the static inputs needed to execute one backtest run. It is populated from Config/RunConfig before the run loop starts and is not modified during execution.
type BacktestResult ¶
type BacktestResult struct {
Start Timestamp
End Timestamp
StartBalance Money // starting account balance
Balance Money // final account balance, realised only
Equity Money // final equity including any open positions at run end
Trades int // total non-nil closed trades
Wins int // trades with PNL > 0
Losses int // trades with PNL < 0
Flat int // trades with PNL == 0
// Derived fields populated by BuildBacktestResult.
NetPL Money // Balance - StartBalance
ReturnPct Rate // NetPL / StartBalance, RateScale-scaled
GrossProfit Money // sum of winning trade PNL
GrossLoss Money // sum of losing trade PNL, negative
WinRate Rate // Wins / Trades, RateScale-scaled
ProfitFactor Rate // GrossProfit / abs(GrossLoss), RateScale-scaled
AvgWinner Money // average winning trade PNL
AvgLoser Money // average losing trade PNL, negative
RR Rate // AvgWinner / abs(AvgLoser), RateScale-scaled
MaxDrawdown Money // largest peak-to-trough drop in cumulative PNL, negative
MaxDrawdownPct Rate // MaxDrawdown / StartBalance, RateScale-scaled
}
BacktestResult is a lightweight, immutable summary produced at the end of a backtest run. All derived fields are computed by Backtest.BuildBacktestResult.
type BacktestRun ¶
type BacktestRun struct {
Lots *LotBook
Trades []*Trade
// Execution cost tracking — populated by the run loop.
SpreadFiltered int // opens suppressed by the max-spread filter
SpreadOpened int // opens that went through (for avg spread calc)
SpreadSum Price // sum of candle.AvgSpread at each accepted open
}
BacktestRun holds mutable state accumulated during a single backtest execution: the current lot book, the list of closed trades, and execution-cost counters updated by the run loop.
func (*BacktestRun) GetTrades ¶
func (run *BacktestRun) GetTrades() []*Trade
GetTrades returns the run's closed trade list, or nil if run is nil.
type BollingerBands ¶ added in v0.2.1
type BollingerBands struct {
// contains filtered or unexported fields
}
BollingerBands computes Bollinger Bands over candle closes. Middle = SMA(n), Upper = Middle + k×σ, Lower = Middle − k×σ where σ is the population standard deviation of the last n closes.
func NewBollingerBands ¶ added in v0.2.1
func NewBollingerBands(period int, multiplier float64, scale Scale6) (*BollingerBands, error)
func (*BollingerBands) BandWidth ¶ added in v0.2.1
func (b *BollingerBands) BandWidth() float64
BandWidth returns (upper − lower) / middle — a normalised squeeze measure.
func (*BollingerBands) Lower ¶ added in v0.2.1
func (b *BollingerBands) Lower() float64
func (*BollingerBands) LowerPrice ¶ added in v0.2.1
func (b *BollingerBands) LowerPrice() Price
func (*BollingerBands) Middle ¶ added in v0.2.1
func (b *BollingerBands) Middle() float64
func (*BollingerBands) MiddlePrice ¶ added in v0.2.1
func (b *BollingerBands) MiddlePrice() Price
func (*BollingerBands) Name ¶ added in v0.2.1
func (b *BollingerBands) Name() string
func (*BollingerBands) PercentB ¶ added in v0.2.1
func (b *BollingerBands) PercentB(price float64) float64
PercentB returns where price sits relative to the bands: 0.0 = lower, 1.0 = upper, 0.5 = middle.
func (*BollingerBands) PercentBPrice ¶ added in v0.2.3
func (b *BollingerBands) PercentBPrice(price Price) float64
func (*BollingerBands) Period ¶ added in v0.2.1
func (b *BollingerBands) Period() int
func (*BollingerBands) Ready ¶ added in v0.2.1
func (b *BollingerBands) Ready() bool
func (*BollingerBands) Reset ¶ added in v0.2.1
func (b *BollingerBands) Reset()
func (*BollingerBands) StdDev ¶ added in v0.2.1
func (b *BollingerBands) StdDev() float64
func (*BollingerBands) StdDevPrice ¶ added in v0.2.3
func (b *BollingerBands) StdDevPrice() Price
func (*BollingerBands) Update ¶ added in v0.2.1
func (b *BollingerBands) Update(c Candle)
func (*BollingerBands) Upper ¶ added in v0.2.1
func (b *BollingerBands) Upper() float64
func (*BollingerBands) UpperPrice ¶ added in v0.2.1
func (b *BollingerBands) UpperPrice() Price
func (*BollingerBands) Warmup ¶ added in v0.2.3
func (b *BollingerBands) Warmup() int
type Broker ¶
func (*Broker) SubmitClose ¶
func (b *Broker) SubmitClose(ctx context.Context, req *CloseRequest) error
func (*Broker) SubmitOpen ¶
type BuildDecision ¶
type BuildDecision struct {
Key
Status BuildStatus
Required []Key
Missing []Key
Reason string
}
type BuildStatus ¶
type BuildStatus int
const ( BuildUnknown BuildStatus = iota BuildReady BuildBlocked BuildExistsComplete )
type BuildTask ¶
BuildTask represents a single candle-aggregation job: build the candles identified by Key from the listed input Keys.
type CSVTicksFeed ¶
type CSVTicksFeed struct {
// contains filtered or unexported fields
}
CSVTicksFeed reads canonical tick CSV rows:
time,instrument,bid,ask[,event...]
where time is RFC3339 or RFC3339Nano.
It optionally filters ticks to [From, To) if provided. Header row ("time,...") is allowed. Empty/short rows are skipped.
func NewCSVTicksFeed ¶
func NewCSVTicksFeed(path string, from, to Timestamp) (*CSVTicksFeed, error)
NewCSVTicksFeed opens the CSV file at path and returns a feed that yields only ticks whose timestamp falls within [from, to). Pass zero Timestamps to disable filtering.
func (*CSVTicksFeed) Close ¶
func (f *CSVTicksFeed) Close() error
Close releases the underlying file handle.
type Candle ¶
type Candle struct {
Open Price
High Price
Low Price
Close Price
AvgSpread Price
MaxSpread Price
Ticks int32 // number of ticks per candle
}
Candle represents a trader domain type.
func (*Candle) FullString ¶
FullString is an internal helper for trader type processing.
type CandleIndicator ¶
type CandleIndicator interface {
// Name returns a stable identifier like "EMA(20)" or "RSI(14)".
Name() string
// Period returns the configured lookback length.
Period() int
// Warmup returns how many updates are needed before Ready() can be true.
// (Some indicators may become ready earlier; that's fine.)
Warmup() int
// Reset clears all internal state.
Reset()
// Update consumes the next *closed* candle and updates internal state.
Update(c Candle)
// Ready reports whether the indicator output is meaningful.
Ready() bool
}
CandleIndicator computes a single streaming value from candles. It is deterministic and safe to use in live, replay, and backtests.
type CandleIterator ¶ added in v0.2.1
type CandleIterator interface {
Next() (CandleTime, bool)
Err() error
Close() error
}
CandleIterator traverses a sequence of timestamped candles.
type CandleRequest ¶
func (CandleRequest) Key ¶
func (cr CandleRequest) Key() Key
type CandleSource ¶ added in v0.2.3
type CandleSource interface {
Candles(context.Context, CandleRequest) (CandleIterator, error)
}
CandleSource provides candle iterators for backtest and replay execution. DataManager satisfies this interface.
type CandleValidationIssue ¶ added in v0.2.3
type CandleValidationIssue struct {
Kind string `json:"kind"`
Severity string `json:"severity"`
Source string `json:"source"`
Instrument string `json:"instrument"`
Timeframe string `json:"timeframe"`
Year int `json:"year"`
Month int `json:"month"`
Path string `json:"path,omitempty"`
RawPath string `json:"raw_path,omitempty"`
Expected int `json:"expected"`
Present int `json:"present"`
Missing int `json:"missing"`
SampleMissing []string `json:"sample_missing,omitempty"`
Message string `json:"message"`
}
type CandleValidationReport ¶ added in v0.2.3
type CandleValidationReport struct {
Source string `json:"source"`
Timeframe string `json:"timeframe"`
IncludeRaw bool `json:"include_raw"`
MonthsScanned int `json:"months_scanned"`
Issues []CandleValidationIssue `json:"issues"`
}
func ValidateCandleData ¶ added in v0.2.3
func ValidateCandleData(ctx context.Context, req CandleValidationRequest) (*CandleValidationReport, error)
func (*CandleValidationReport) IssueCount ¶ added in v0.2.3
func (r *CandleValidationReport) IssueCount() int
type CandleValidationRequest ¶ added in v0.2.3
type ChandelierExit ¶
type ChandelierExit struct {
// contains filtered or unexported fields
}
ChandelierExit trails the stop from the highest-high (long) or lowest-low (short) seen since entry, offset by N×ATR. The stop only ever moves in the profitable direction — it never moves against the position.
Per-position extreme tracking lives on Lot.ExtremePrice so multiple concurrent lots each maintain their own watermark.
func NewChandelierExit ¶
func NewChandelierExit(atrPeriod int, multiplier float64, scale Scale6) (*ChandelierExit, error)
func (*ChandelierExit) InitialStop ¶
func (c *ChandelierExit) InitialStop(side Side, entry Price, candle Candle) Price
func (*ChandelierExit) Name ¶
func (c *ChandelierExit) Name() string
func (*ChandelierExit) Ready ¶
func (c *ChandelierExit) Ready() bool
func (*ChandelierExit) Tick ¶
func (c *ChandelierExit) Tick(candle Candle)
func (*ChandelierExit) UpdateStop ¶
type ChoppinessFilter ¶
type ChoppinessFilter struct {
// contains filtered or unexported fields
}
ChoppinessFilter gates entries using the Choppiness Index. When CI < threshold the market is trending; entries are allowed. When CI >= threshold the market is ranging; new opens are suppressed. The conventional threshold is 61.8. Trending() returns true before Ready() as a defensive contract, although the main callers already gate on Ready() before consulting the regime state.
func NewChoppinessFilter ¶
func NewChoppinessFilter(period int, threshold float64, scale Scale6) (*ChoppinessFilter, error)
func (*ChoppinessFilter) AllowSide ¶ added in v0.2.1
func (f *ChoppinessFilter) AllowSide(_ Side) bool
func (*ChoppinessFilter) Choppiness ¶ added in v0.2.3
func (f *ChoppinessFilter) Choppiness() float64
Choppiness exposes the raw CI value for logging/debugging.
func (*ChoppinessFilter) Name ¶
func (f *ChoppinessFilter) Name() string
func (*ChoppinessFilter) Ready ¶
func (f *ChoppinessFilter) Ready() bool
func (*ChoppinessFilter) Tick ¶
func (f *ChoppinessFilter) Tick(ct CandleTime)
func (*ChoppinessFilter) Trending ¶
func (f *ChoppinessFilter) Trending() bool
func (*ChoppinessFilter) Value ¶
func (f *ChoppinessFilter) Value() float64
Value exposes the raw CI value for logging/debugging.
type ChoppinessIndex ¶
type ChoppinessIndex struct {
// contains filtered or unexported fields
}
ChoppinessIndex measures whether price action is trending or ranging.
Formula: 100 × log10(Σ TR(1,N) / (HH(N) − LL(N))) / log10(N)
Values near 100 = choppy/consolidating; near 0 = strongly trending. Conventional threshold: 61.8 (trending below, ranging above).
func NewChoppinessIndex ¶
func NewChoppinessIndex(period int, scale Scale6) (*ChoppinessIndex, error)
func (*ChoppinessIndex) Float64 ¶ added in v0.2.3
func (c *ChoppinessIndex) Float64() float64
func (*ChoppinessIndex) Name ¶
func (c *ChoppinessIndex) Name() string
func (*ChoppinessIndex) Period ¶ added in v0.2.3
func (c *ChoppinessIndex) Period() int
func (*ChoppinessIndex) Ready ¶
func (c *ChoppinessIndex) Ready() bool
func (*ChoppinessIndex) Reset ¶
func (c *ChoppinessIndex) Reset()
func (*ChoppinessIndex) Update ¶
func (c *ChoppinessIndex) Update(candle Candle)
func (*ChoppinessIndex) Warmup ¶
func (c *ChoppinessIndex) Warmup() int
type CloseMatcher ¶
type CloseRequest ¶
CloseRequest represents a trader domain type.
func (*CloseRequest) Validate ¶ added in v0.2.3
func (r *CloseRequest) Validate() error
Validate is an internal helper for trader type processing.
type CompiledBacktest ¶ added in v0.2.3
type CompiledBacktest struct {
ID string
RunConfig RunConfig
Request BacktestRequest
}
CompiledBacktest is the construction-phase output for one backtest run. It is immutable and contains the resolved config snapshot plus the validated request used to instantiate an executable Backtest later.
func CompileBacktests ¶ added in v0.2.3
func CompileBacktests(cfg *Config) ([]CompiledBacktest, error)
CompileBacktests converts a loaded Config into validated, immutable backtest definitions. Defaults are applied during construction so execution only deals with already-compiled requests.
func (CompiledBacktest) NewRun ¶ added in v0.2.3
func (c CompiledBacktest) NewRun() Backtest
NewRun instantiates a fresh executable Backtest from a compiled definition.
type CompositeRegimeFilter ¶ added in v0.2.1
type CompositeRegimeFilter struct {
// contains filtered or unexported fields
}
CompositeRegimeFilter ANDs multiple RegimeFilters: Trending() returns true only when every sub-filter returns true. Tick() is called on all sub-filters regardless of their individual state so each indicator stays current.
func NewCompositeRegimeFilter ¶ added in v0.2.1
func NewCompositeRegimeFilter(filters []RegimeFilter) *CompositeRegimeFilter
func (*CompositeRegimeFilter) AllowSide ¶ added in v0.2.1
func (c *CompositeRegimeFilter) AllowSide(side Side) bool
func (*CompositeRegimeFilter) Name ¶ added in v0.2.1
func (c *CompositeRegimeFilter) Name() string
func (*CompositeRegimeFilter) Ready ¶ added in v0.2.1
func (c *CompositeRegimeFilter) Ready() bool
func (*CompositeRegimeFilter) Tick ¶ added in v0.2.1
func (c *CompositeRegimeFilter) Tick(ct CandleTime)
func (*CompositeRegimeFilter) Trending ¶ added in v0.2.1
func (c *CompositeRegimeFilter) Trending() bool
type Config ¶
type Config struct {
Version int `json:"version" yaml:"version"`
Defaults RunDefaults `json:"defaults" yaml:"defaults"`
Runs []RunConfig `json:"runs" yaml:"runs"`
}
Config is the top-level structure parsed from a YAML or JSON config file. It carries a set of defaults that are merged into each RunConfig before the run is executed.
func LoadConfig ¶
LoadConfig reads and parses a YAML or JSON config file from path. The file extension determines the parser (.yaml/.yml → YAML; .json → JSON). Returns an error if the file is missing, unparseable, or contains no runs.
type D1ADXFilter ¶ added in v0.2.1
type D1ADXFilter struct {
// contains filtered or unexported fields
}
D1ADXFilter is a regime filter that applies ADX at the daily timeframe while being fed sub-daily bars (e.g. H1). It aggregates intraday bars into daily OHLC and updates the ADX only when a day closes.
Trending() returns true when D1 ADX >= threshold, meaning the daily timeframe confirms a broad enough trend to allow new entries. AllowSide() always returns true because this is a regime gate, not a directional filter. Trending() returns true before Ready() as a defensive contract, although the main callers already gate on Ready() before consulting the regime state.
Registered in the factory as "adx-d1".
func NewD1ADXFilter ¶ added in v0.2.1
func NewD1ADXFilter(period int, threshold float64, scale Scale6) (*D1ADXFilter, error)
func (*D1ADXFilter) ADX ¶ added in v0.2.3
func (f *D1ADXFilter) ADX() float64
ADX exposes the raw ADX value for debugging.
func (*D1ADXFilter) ADXValue ¶ added in v0.2.1
func (f *D1ADXFilter) ADXValue() float64
ADXValue exposes the raw ADX value for debugging.
func (*D1ADXFilter) AllowSide ¶ added in v0.2.1
func (f *D1ADXFilter) AllowSide(_ Side) bool
func (*D1ADXFilter) Name ¶ added in v0.2.1
func (f *D1ADXFilter) Name() string
func (*D1ADXFilter) Ready ¶ added in v0.2.1
func (f *D1ADXFilter) Ready() bool
func (*D1ADXFilter) Tick ¶ added in v0.2.1
func (f *D1ADXFilter) Tick(ct CandleTime)
func (*D1ADXFilter) Trending ¶ added in v0.2.1
func (f *D1ADXFilter) Trending() bool
type D1ChoppinessFilter ¶ added in v0.2.1
type D1ChoppinessFilter struct {
// contains filtered or unexported fields
}
D1ChoppinessFilter is a regime filter that applies the Choppiness Index at the daily timeframe while being fed sub-daily bars (e.g. H1). It aggregates intraday bars into daily OHLC and updates the CI only when a day closes.
This avoids the correlation problem that arises when using same-timeframe CI with Donchian breakouts: a breakout bar will always look "trending" at the moment of entry when measured on its own timeframe. The daily CI captures whether the broader market context is trending over multiple days, which is independent of any individual H1 breakout signal. AllowSide() always returns true because this is a regime gate, not a directional filter. Trending() returns true before Ready() as a defensive contract, although the main callers already gate on Ready() before consulting the regime state.
Registered in the factory as "choppiness-d1".
func NewD1ChoppinessFilter ¶ added in v0.2.1
func NewD1ChoppinessFilter(period int, threshold float64, scale Scale6) (*D1ChoppinessFilter, error)
func (*D1ChoppinessFilter) AllowSide ¶ added in v0.2.1
func (f *D1ChoppinessFilter) AllowSide(_ Side) bool
func (*D1ChoppinessFilter) Choppiness ¶ added in v0.2.3
func (f *D1ChoppinessFilter) Choppiness() float64
Choppiness exposes the raw CI value for debugging.
func (*D1ChoppinessFilter) Name ¶ added in v0.2.1
func (f *D1ChoppinessFilter) Name() string
func (*D1ChoppinessFilter) Ready ¶ added in v0.2.1
func (f *D1ChoppinessFilter) Ready() bool
func (*D1ChoppinessFilter) Tick ¶ added in v0.2.1
func (f *D1ChoppinessFilter) Tick(ct CandleTime)
func (*D1ChoppinessFilter) Trending ¶ added in v0.2.1
func (f *D1ChoppinessFilter) Trending() bool
func (*D1ChoppinessFilter) Value ¶ added in v0.2.1
func (f *D1ChoppinessFilter) Value() float64
Value exposes the raw CI value for debugging.
type DataConfig ¶
type DataConfig struct {
Source string `json:"source" yaml:"source"`
Instrument string `json:"instrument" yaml:"instrument"`
Timeframe string `json:"timeframe" yaml:"timeframe"`
From string `json:"from" yaml:"from"`
To string `json:"to" yaml:"to"`
Strict *bool `json:"strict" yaml:"strict"`
}
DataConfig specifies the data source, instrument, timeframe, and date range for a run.
type DataManager ¶
type DataManager struct {
Start time.Time
End time.Time
Instruments []string
// contains filtered or unexported fields
}
DataManager is responsible for identifing data files that are missing accross all instruments. For missing datasets, ensure they are downloaded, for datasets that are downloaded, make sure they are made into candles.
func GetDataManager ¶
func GetDataManager() *DataManager
func NewDataManager ¶
func NewDataManager(instruments []string, start, end time.Time) *DataManager
NewDataManager constructs a DataManager for the given instruments and time range.
func (*DataManager) BuildWantList ¶
func (dm *DataManager) BuildWantList(ctx context.Context) (*Wantlist, error)
func (*DataManager) Candles ¶
func (dm *DataManager) Candles(ctx context.Context, req CandleRequest) (CandleIterator, error)
func (*DataManager) ExecuteDownloads ¶
func (dm *DataManager) ExecuteDownloads(ctx context.Context) error
type EMA ¶
type EMA struct {
// contains filtered or unexported fields
}
EMA computes an Exponential Moving Average over candle closes.
Pricing note:
- trader.Candle prices are scaled integers.
- EMA stores scaled price units internally; Float64 is only for display.
type EquitySnapshot ¶
type EquitySnapshot struct {
Timestamp Timestamp
Balance Money
Equity Money
MarginUsed Money
FreeMargin Money
MarginLevel Money
}
EquitySnapshot captures account state at a point in time for journal backends that persist balance/equity history alongside completed trades.
type ExitConfig ¶
type ExitConfig struct {
Kind string `json:"kind" yaml:"kind"`
Params map[string]any `json:"params" yaml:"params"`
}
ExitConfig mirrors the exit: section of a YAML backtest config.
type ExitStrategy ¶
type ExitStrategy interface {
// Name returns a human-readable description for reports.
Name() string
// Ready reports whether the exit strategy has enough history to place stops.
Ready() bool
// Tick updates internal indicators. Called every bar before strategy.Update().
Tick(c Candle)
// InitialStop returns the stop price at the moment a position is opened.
InitialStop(side Side, entry Price, c Candle) Price
// UpdateStop returns the new stop price for an open lot each bar.
// extreme is the lot's ExtremePrice (highest high for longs, lowest low for shorts).
// The implementation must never move the stop against the position.
UpdateStop(side Side, currentStop Price, entry Price, extreme Price, c Candle) Price
}
ExitStrategy manages stop placement after a position is open. It is called every bar regardless of position state (to warm up indicators), and is consulted to set/update the stop price on open lots.
func GetExitStrategy ¶
func GetExitStrategy(cfg ExitConfig, scale Scale6) (ExitStrategy, error)
GetExitStrategy constructs an ExitStrategy from cfg. If cfg.Kind is empty, NoopExit is returned (pass-through).
type Float64Indicator ¶ added in v0.2.3
type Float64Indicator interface {
// Float64 returns the current indicator value. If !Ready(), it should return 0
// (or the last computed value) — callers should always check Ready().
Float64() float64
}
type ForexAnalysis ¶ added in v0.2.3
type ForexAnalysis struct {
Group string `json:"-"`
Pair string `json:"-"`
Structure string `json:"-"`
SetupBias string `json:"-"`
Trend string `json:"-"`
Volatility string `json:"-"`
SupportLow Price `json:"-"`
SupportHigh Price `json:"-"`
ResistanceLow Price `json:"-"`
ResistanceHigh Price `json:"-"`
Status AnalysisStatus `json:"-"`
}
ForexAnalysis holds one row from a ChatGPT forex analysis CSV. Price fields are stored as scaled int32 (Price) matching the rest of the engine; JSON output converts them back to decimal via Float64().
func (ForexAnalysis) IsTradeable ¶ added in v0.2.3
func (f ForexAnalysis) IsTradeable() bool
IsTradeable reports whether the row is an active trade candidate.
func (ForexAnalysis) IsWatched ¶ added in v0.2.3
func (f ForexAnalysis) IsWatched() bool
IsWatched reports whether the row belongs on any watchlist (both Watchlist and Tradeable rows qualify).
func (ForexAnalysis) MarshalJSON ¶ added in v0.2.3
func (f ForexAnalysis) MarshalJSON() ([]byte, error)
MarshalJSON emits price fields as decimal floats.
type GlobalConfig ¶ added in v0.2.1
type GlobalConfig struct {
Log GlobalLogConfig `yaml:"log"`
Data GlobalDataConfig `yaml:"data"`
OANDA GlobalOANDAConfig `yaml:"oanda"`
DB string `yaml:"db"`
}
GlobalConfig holds settings that apply across all trader commands. It is populated by merging YAML files from the standard search path in order:
- /etc/trader/*.yml — system-wide defaults
- ~/.config/trader/*.yml — user overrides
- explicit path — passed via root --config flag
Within each directory, files are merged alphabetically. Later files override earlier ones for any non-empty field.
func LoadGlobalConfig ¶ added in v0.2.1
func LoadGlobalConfig(explicitPath string) (*GlobalConfig, error)
LoadGlobalConfig merges global config files from the standard search path plus an optional explicit file. Missing directories and files are silently skipped; a parse error in any file is returned immediately.
type GlobalDataConfig ¶ added in v0.2.1
type GlobalDataConfig struct {
Dir string `yaml:"dir"`
}
GlobalDataConfig holds data directory settings.
type GlobalLogConfig ¶ added in v0.2.1
type GlobalLogConfig struct {
Level string `yaml:"level"`
File string `yaml:"file"`
Format string `yaml:"format"`
}
GlobalLogConfig holds log-related global settings.
type GlobalOANDAConfig ¶ added in v0.2.1
type GlobalOANDAConfig struct {
Token string `yaml:"token"`
AccountID string `yaml:"account_id"`
Env string `yaml:"env"`
}
GlobalOANDAConfig holds OANDA broker credentials.
type Instrument ¶
type Instrument struct {
Name string
BaseCurrency string
QuoteCurrency string
PipLocation int
TradeUnitsPrecision int
MinimumTradeSize Units
MarginRate Rate
}
Instrument represents a trader domain type.
func GetInstrument ¶
func GetInstrument(symbol string) *Instrument
GetInstrument is an internal helper for trader type processing.
func LookupInstrument ¶ added in v0.2.3
func LookupInstrument(symbol string) (Instrument, bool)
LookupInstrument returns a copy of the instrument metadata and whether it exists.
func (*Instrument) AddPips ¶
func (inst *Instrument) AddPips(px Price, pips Pips) Price
AddPips is an internal helper for trader type processing.
func (*Instrument) DukascopyPriceMultiplier ¶
func (inst *Instrument) DukascopyPriceMultiplier() uint32
DukascopyPriceMultiplier returns the factor needed to convert a raw Dukascopy bi5 price integer into a Price value at the current PriceScale.
Dukascopy stores prices with (−PipLocation + 1) decimal places:
- 5-decimal pairs (EURUSD, PipLocation=−4): native scale 100,000 → multiplier = 1
- 3-decimal pairs (USDJPY, PipLocation=−2): native scale 1,000 → multiplier = 100
func (*Instrument) PipSize ¶
func (inst *Instrument) PipSize() float64
PipSize is an internal helper for trader type processing.
func (*Instrument) PipValueUSD ¶ added in v0.2.1
func (inst *Instrument) PipValueUSD(rate float64, units int64, pips float64) float64
PipValueUSD returns the USD value of pips pips for a position of units units.
For USD-quoted pairs (EURUSD, GBPUSD, AUDUSD, NZDUSD) the result is exact and rate is ignored. For USD-base pairs (USDJPY, USDCHF, USDCAD) the pip value is denominated in the quote currency, so rate (the current pair price) is required to convert back to USD. Returns 0 if rate ≤ 0.
func (*Instrument) PriceDeltaFromPips ¶
func (inst *Instrument) PriceDeltaFromPips(pips Pips) Price
PriceDeltaFromPips is an internal helper for trader type processing.
func (*Instrument) PriceUnitsPerPip ¶
func (inst *Instrument) PriceUnitsPerPip() Price
PriceUnitsPerPip is an internal helper for trader type processing.
type Inventory ¶
type Inventory struct {
// contains filtered or unexported fields
}
func NewInventory ¶
func NewInventory() *Inventory
func (*Inventory) HasComplete ¶
func (*Inventory) MissingComplete ¶
func (*Inventory) TicksComplete ¶
func (*Inventory) WantReasonFor ¶ added in v0.2.3
func (inv *Inventory) WantReasonFor(key Key) (WantReason, bool)
type Journal ¶
type Journal interface {
RecordTrade(TradeRecord) error
RecordEquity(EquitySnapshot) error
Close() error
}
Journal is the storage contract used by live trading and replay code to persist completed trades and optional equity snapshots.
type Key ¶
type Key struct {
Instrument string
Source string
Kind DataKind
TF Timeframe
Year int
Month int
Day int
Hour int
}
func (Key) IsHourlyTick ¶
func (Key) IsMonthlyCandle ¶
func (Key) Time ¶
Time returns the UTC time represented by the key. Missing fields are normalized to the earliest valid value. Use Validate() first when invalid keys should be rejected instead of coerced.
Examples:
Year=2024, Month=0, Day=0, Hour=0 -> 2024-01-01 00:00:00 UTC Year=2024, Month=5, Day=0, Hour=0 -> 2024-05-01 00:00:00 UTC Year=2024, Month=5, Day=7, Hour=13 -> 2024-05-07 13:00:00 UTC
type LinearCongruentialRandom ¶
type LinearCongruentialRandom struct {
// contains filtered or unexported fields
}
LinearCongruentialRandom is a simple deterministic RNG.
func NewLCRandom ¶
func NewLCRandom(seed int64) *LinearCongruentialRandom
NewLCRandom creates a new LCR with a seed.
func (*LinearCongruentialRandom) NextGaussian ¶
func (r *LinearCongruentialRandom) NextGaussian() float64
NextGaussian returns a pseudo-random number from a normal distribution (Box-Muller).
func (*LinearCongruentialRandom) NextUniform ¶
func (r *LinearCongruentialRandom) NextUniform() float64
NextUniform returns a pseudo-random number in [0, 1).
type LiveJournal ¶
type LiveJournal struct {
// contains filtered or unexported fields
}
LiveJournal subscribes to an OANDA transaction stream and writes complete TradeRecord rows to the configured Journal as trades close.
Open ORDER_FILL events are buffered in memory (keyed by tradeID) until the matching close ORDER_FILL arrives. The close fill provides the realized P/L; we look up the buffered open to fill in entry side and open time, then RecordTrade(...) writes the complete row.
Heartbeats advance an in-memory "lastSeenTxID" cursor so callers can reconnect (or poll for gap recovery) from a known point.
func NewLiveJournal ¶
func NewLiveJournal(client *oanda.Client, accountID string, journal Journal, log *slog.Logger) *LiveJournal
NewLiveJournal creates a journal worker. Call Run to start the subscription.
func (*LiveJournal) Backfill ¶
func (lj *LiveJournal) Backfill(ctx context.Context, sinceID int64) error
Backfill polls GetTransactions from sinceID forward and replays them into the same handler used for streamed events. Call before Run to recover anything missed during downtime.
func (*LiveJournal) LastSeenTxID ¶
func (lj *LiveJournal) LastSeenTxID() int64
LastSeenTxID returns the highest transaction ID we've processed (via heartbeat or actual transaction). Persist this for resume on restart.
func (*LiveJournal) Run ¶
func (lj *LiveJournal) Run(ctx context.Context) error
Run subscribes to the transaction stream and processes events until ctx is cancelled or the stream ends. Returns the final error from the stream (nil on clean ctx-cancel exit).
func (*LiveJournal) SetBotIDLookup ¶ added in v0.2.3
func (lj *LiveJournal) SetBotIDLookup(fn func(tradeID string) string)
SetBotIDLookup provides a function the journal calls on each close to find which bot opened a given OANDA trade ID. This lets the centralized journal (one per serve process) tag TradeRecords with the correct bot.
type LiveOpenRequest ¶
type LiveOpenRequest struct {
Side string // "long" or "short"
StopPips float64 // stop-loss distance in pips
TakePips float64 // take-profit distance in pips (0 = none)
RiskPct float64 // percent of account NAV to risk
Reason string // strategy signal reason, e.g. "donchian-v6-breakout-down"
}
LiveOpenRequest carries the parameters for a new live position.
type LivePlan ¶
type LivePlan struct {
// Open describes a new position to open. Nil means hold.
Open *LiveOpenRequest
// CloseIDs lists trade IDs the strategy wants to close.
CloseIDs []string
// Reason is a human-readable note logged by the runner.
Reason string
}
LivePlan is what the strategy asks the runner to do this tick. At most one new position is opened per tick; zero or more are closed.
type LiveStrategy ¶
type LiveStrategy interface {
Name() string
// Tick is called once per poll interval. price is the current bid/ask snapshot.
// openTrades lists all tracked open positions for this strategy's instrument.
// Returns a plan (open one new position and/or close a set of existing ones).
Tick(ctx context.Context, price LivePrice, openTrades []LiveTrade) *LivePlan
}
LiveStrategy is implemented by strategies that drive live (non-backtest) trading. Tick is called on each price poll; the runner tracks position ages and passes them in so the strategy can decide what to open or close.
func GetLiveStrategy ¶ added in v0.2.3
func GetLiveStrategy(scfg StrategyConfig) (LiveStrategy, error)
GetLiveStrategy looks up and constructs a LiveStrategy by kind.
type LiveStrategyConstructor ¶ added in v0.2.3
type LiveStrategyConstructor func(params map[string]any) (LiveStrategy, error)
LiveStrategyConstructor builds a LiveStrategy from a params map.
func LookupLiveStrategy ¶ added in v0.2.3
func LookupLiveStrategy(name string) LiveStrategyConstructor
LookupLiveStrategy returns the constructor registered under name, or nil.
type LiveTrade ¶
type LiveTrade struct {
ID string
Instrument string
Units int64 // positive = long, negative = short
EntryPrice float64
UnrealizedPL float64
OpenTime time.Time // when OANDA opened the trade
TicksOpen int // estimated ticks elapsed, seeded from OpenTime on restart
}
LiveTrade describes an open position as seen by the live runner.
type LogConfig ¶
type LogConfig struct {
// Level is the minimum log level to emit. Accepted values (case-
// insensitive): "debug", "info", "warn" / "warning", "error".
// Defaults to "info" when empty or unrecognised.
Level string
// Format selects the handler format: "json" for JSON output, anything
// else (or empty) for human-readable text.
Format string
// File is an optional path to a log file. When non-empty, log records
// are written to both stdout and this file. When empty and no other sink
// is configured, Setup falls back to a default log file.
File string
// Syslog enables forwarding of log records to the system logger.
// Has no effect on Windows (syslog is not available there).
Syslog bool
// Stdout enables log output to stdout
Stdout bool
// Memory enables in-memory capture of log entries, accessible via
// Entries() and ClearEntries(). Useful for testing and diagnostics.
Memory bool
}
LogConfig holds the logging configuration that is typically populated from the application's RootConfig (RootConfig.LogLevel, etc.).
type Lot ¶
type Lot struct {
*TradeCommon
EntryPrice Price
EntryTime Timestamp
OriginalUnits Units
RemainingUnits Units
State lotState
// ExtremePrice tracks the highest-high (long) or lowest-low (short) seen
// since entry. Used by trailing/chandelier exit strategies.
ExtremePrice Price
}
Lot represents a trader domain type.
type LotBook ¶
type LotBook struct {
// contains filtered or unexported fields
}
LotBook represents a trader domain type.
type Money ¶
type Money int64
Money represents a trader domain type.
func MoneyFromFloat ¶
MoneyFromFloat is an internal helper for trader type processing.
type NoopExit ¶
type NoopExit struct{}
NoopExit is a pass-through exit strategy. It never moves stops; the entry strategy is responsible for setting an initial stop via the OpenRequest.
type NoopRegime ¶
type NoopRegime struct{}
NoopRegime is a pass-through filter that always allows trading.
func (NoopRegime) AllowSide ¶ added in v0.2.1
func (NoopRegime) AllowSide(_ Side) bool
func (NoopRegime) Name ¶
func (NoopRegime) Name() string
func (NoopRegime) Ready ¶
func (NoopRegime) Ready() bool
func (NoopRegime) Tick ¶
func (NoopRegime) Tick(_ CandleTime)
func (NoopRegime) Trending ¶
func (NoopRegime) Trending() bool
type OpenRequest ¶
type OpenRequest struct {
Request
}
OpenRequest represents a trader domain type.
func NewOpenRequest ¶
func NewOpenRequest( instr string, c *CandleTime, side Side, stop Price, take Price, reason string) *OpenRequest
NewOpenRequest is an internal helper for trader type processing.
func (*OpenRequest) Validate ¶ added in v0.2.3
func (r *OpenRequest) Validate() error
Validate is an internal helper for trader type processing.
type Pips ¶
type Pips int32
Pips stores tenths of a pip (deci-pips): 1 == 0.1 pip and 20 == 2.0 pips.
func PipsFromFloat ¶
PipsFromFloat converts a whole/decimal pip count into internal deci-pips.
type Plan ¶
Plan describes the data-preparation work that must be completed before a backtest can run: files to download and candle aggregations to build at each timeframe.
func (*Plan) BuildTasks ¶ added in v0.2.3
func (*Plan) Log ¶
func (p *Plan) Log()
Log emits a structured summary of the plan (download and build counts) at info level.
func (*Plan) TotalBuilds ¶ added in v0.2.3
type Position ¶
type Position struct {
Instrument string
LongUnits Units
LongAvgEntryPrice Price
ShortUnits Units
ShortAvgEntryPrice Price
NetUnits Units
}
Position is the computed aggregate view of all open lots for one instrument. Hedged books keep separate long/short exposure and entry prices.
type Price ¶
type Price int32
Price represents a trader domain type.
func PriceFromFloat ¶
PriceFromFloat is an internal helper for trader type processing.
type PriceIndicator ¶ added in v0.2.3
type PriceIndicator interface {
Price() Price
}
type PriceSum ¶ added in v0.2.3
type PriceSum int64
PriceSum represents an accumulated sum of Price values.
type Rate ¶
type Rate int64
Rate represents a trader domain type.
func RateFromFloat ¶
RateFromFloat is an internal helper for trader type processing.
type RawTick ¶
type RawTick struct {
Ask Price
Bid Price
AskVol float32
BidVol float32
// contains filtered or unexported fields
}
func (RawTick) FloorToHour ¶
func (ms RawTick) FloorToHour() timemilli
FloorToHour is an internal helper for trader type processing.
func (RawTick) FloorToMinute ¶
func (ms RawTick) FloorToMinute() timemilli
FloorToMinute is an internal helper for trader type processing.
type RegimeConfig ¶
type RegimeConfig struct {
Kind string `json:"kind" yaml:"kind"`
Params map[string]any `json:"params" yaml:"params"`
Filters []RegimeConfig `json:"filters" yaml:"filters"` // for composite kind
}
RegimeConfig mirrors the regime: section of a YAML backtest config.
type RegimeFilter ¶
type RegimeFilter interface {
// Name returns a human-readable label for reports.
Name() string
// Ready reports whether the filter has enough history to classify.
Ready() bool
// Tick updates internal indicators with the current bar. The full
// CandleTime is provided so implementations can use the timestamp
// (e.g. to aggregate sub-daily bars into daily bars).
Tick(ct CandleTime)
// Trending returns true when the market is in a trending regime and
// new entries should be allowed. Returns true while not yet ready so
// warmup bars are not suppressed.
Trending() bool
// AllowSide returns true when new entries on the given side are permitted.
// Trending() == false already blocks all opens; AllowSide provides
// directional filtering when Trending() == true.
AllowSide(side Side) bool
}
RegimeFilter classifies the current market as trending or ranging. The bar loop calls Tick() every bar and suppresses new position opens when Trending() returns false.
func GetRegimeFilter ¶
func GetRegimeFilter(cfg RegimeConfig, scale Scale6) (RegimeFilter, error)
GetRegimeFilter constructs a RegimeFilter from cfg. If cfg.Kind is empty, NoopRegime is returned (no filtering).
type Request ¶
type Request struct {
*TradeCommon
RequestType
Price
Timestamp
Reason string
Candle Candle
}
Request represents a trader domain type.
type RequestType ¶
type RequestType uint8
RequestType represents a trader domain type.
const ( RequestNone RequestType = iota RequestMarketOpen RequestLimitOpen RequestClose )
func (RequestType) String ¶ added in v0.2.3
func (t RequestType) String() string
String is an internal helper for trader type processing.
type RootConfig ¶
type RootConfig struct {
ConfigPath string
GlobalPath string
DBPath string
ReportPath string
DataDir string
LogLevel string
LogFile string
LogFormat string
NoColor bool
// OANDA credentials populated from global config; individual commands
// may override via their own --token / --account-id / --env flags.
OANDAToken string
OANDAAccountID string
OANDAEnv string
}
type RunConfig ¶
type RunConfig struct {
Name string `json:"name" yaml:"name"`
Data DataConfig `json:"data" yaml:"data"`
Strategy StrategyConfig `json:"strategy" yaml:"strategy"`
Exit ExitConfig `json:"exit" yaml:"exit"`
Regime RegimeConfig `json:"regime" yaml:"regime"`
}
RunConfig describes a single backtest run: what data to load, which strategy to use, and optional exit and regime-filter overrides.
type RunDefaults ¶
type RunDefaults struct {
StartingBalance float64 `json:"starting-balance" yaml:"starting-balance"`
AccountCCY string `json:"account-ccy" yaml:"account-ccy"`
Scale int64 `json:"scale" yaml:"scale"`
Strict bool `json:"strict" yaml:"strict"`
RiskPct float64 `json:"risk-pct" yaml:"risk-pct"`
StopPips int32 `json:"stop-pips" yaml:"stop-pips"`
TakePips int32 `json:"take-pips" yaml:"take-pips"`
RR float64 `json:"rr" yaml:"rr"`
Units int32 `json:"units" yaml:"units"`
SlippagePips float64 `json:"slippage-pips" yaml:"slippage-pips"`
MaxSpreadPips float64 `json:"max-spread-pips" yaml:"max-spread-pips"`
Source string `json:"source" yaml:"source"`
}
RunDefaults holds account-level and execution-cost settings that apply to every run in the config unless overridden at the run level.
type SessionAnalyzer ¶ added in v0.2.1
type SessionAnalyzer struct {
// contains filtered or unexported fields
}
SessionAnalyzer breaks down candle activity and average range by UTC hour. Ranges are stored as Price (scaled int) and converted to pips only at output.
func NewSessionAnalyzer ¶ added in v0.2.1
func NewSessionAnalyzer(inst *Instrument) *SessionAnalyzer
NewSessionAnalyzer creates a SessionAnalyzer for the given instrument.
func (*SessionAnalyzer) Name ¶ added in v0.2.1
func (a *SessionAnalyzer) Name() string
func (*SessionAnalyzer) Stats ¶ added in v0.2.1
func (a *SessionAnalyzer) Stats() []Stat
func (*SessionAnalyzer) Update ¶ added in v0.2.1
func (a *SessionAnalyzer) Update(ct *CandleTime)
type SessionFilter ¶ added in v0.2.1
type SessionFilter struct {
// contains filtered or unexported fields
}
SessionFilter is a regime filter that restricts entries to a specified UTC hour window. Bars outside the window return Trending() = false so the strategy skips new opens. Session windows must stay within a single UTC day; overnight windows like 22:00-06:00 are not supported. Trending() returns true before Ready() as a defensive contract, although the main callers already gate on Ready() before consulting the regime state.
Default window: 07:00–17:00 UTC (London open through NY afternoon). Registered in the factory as "session".
func NewSessionFilter ¶ added in v0.2.1
func NewSessionFilter(start, end int) (*SessionFilter, error)
func (*SessionFilter) AllowSide ¶ added in v0.2.1
func (f *SessionFilter) AllowSide(_ Side) bool
func (*SessionFilter) Name ¶ added in v0.2.1
func (f *SessionFilter) Name() string
func (*SessionFilter) Ready ¶ added in v0.2.1
func (f *SessionFilter) Ready() bool
func (*SessionFilter) Tick ¶ added in v0.2.1
func (f *SessionFilter) Tick(ct CandleTime)
func (*SessionFilter) Trending ¶ added in v0.2.1
func (f *SessionFilter) Trending() bool
type Side ¶
type Side int
Side represents a trader domain type.
type SpreadAnalyzer ¶ added in v0.2.1
type SpreadAnalyzer struct {
// contains filtered or unexported fields
}
SpreadAnalyzer measures the AvgSpread value of each candle. AvgSpread values are stored as Price (scaled int) and converted to pips only at output. Candles with zero AvgSpread are skipped (tick data may not carry spread).
func NewSpreadAnalyzer ¶ added in v0.2.1
func NewSpreadAnalyzer(inst *Instrument) *SpreadAnalyzer
NewSpreadAnalyzer creates a SpreadAnalyzer for the given instrument.
func (*SpreadAnalyzer) Name ¶ added in v0.2.1
func (a *SpreadAnalyzer) Name() string
func (*SpreadAnalyzer) Stats ¶ added in v0.2.1
func (a *SpreadAnalyzer) Stats() []Stat
func (*SpreadAnalyzer) Update ¶ added in v0.2.1
func (a *SpreadAnalyzer) Update(ct *CandleTime)
type Stat ¶ added in v0.2.1
Stat is a single labeled measurement returned by an Analyzer. Pips is the raw pip count when Value is a pip measurement; zero otherwise. Callers can use Pips to convert to a currency amount without re-parsing Value.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store manages candle CSVs and raw tick files under a pair of symmetric directory trees that share a common root:
/srv/trading/data/ ├── candles/<provider>/<instrument>/<year>/<month>/<filename>.csv └── raw/<provider>/<instrument>/<year>/<month>/<day>/<hh>h_ticks.bi5
The "candles" tree is rooted at basedir; the "raw" tree is its sibling (rawRoot = filepath.Dir(basedir) + "/raw"). Providers are source names such as "oanda" or "dukascopy".
Candle filenames embed every identifying dimension so the file is self-describing without its path:
gbpusd-2026-01-h1.csv (instrument-year-month-tf) eurusd-2025-08-m1.csv usdchf-2024-12-d1.csv
Raw tick files follow the Dukascopy bi5 naming convention:
/srv/trading/data/raw/dukascopy/EURUSD/2025/01/02/13h_ticks.bi5
func GetStore ¶
func GetStore() *Store
GetStore returns the global Store. Used by sibling packages (e.g. data/dukascopy) that need direct store access.
func NewStoreAt ¶
NewStoreAt returns a fresh Store rooted at basedir. Useful for tests.
func (*Store) IsUsableTickFile ¶
func (*Store) OpenTickIterator ¶
func (*Store) PathForMonthlyCandle ¶ added in v0.2.3
PathForMonthlyCandle returns the file path for a monthly candle CSV.
func (*Store) RawCandlePath ¶ added in v0.2.3
RawCandlePath returns the path for a monthly candle CSV under the raw tree. It mirrors PathForAsset but roots in rawRoot instead of basedir.
func (*Store) WriteMonthlyCandles ¶
func (s *Store) WriteMonthlyCandles(source, instrument string, tf Timeframe, monthStart time.Time, candles []Candle) error
WriteMonthlyCandles writes a slice of Candle as a monthly CSV file in the canonical trader format. The candles should be dense (one slot per timeframe step within the month); zero-valued candles are treated as gaps.
Source is the data source name (e.g. "oanda", "dukascopy") and ends up in the path: <basedir>/<source>/<instrument>/<year>/<month>/<instr>-<year>-<month>-<tf>.csv
type Strategy ¶
type Strategy interface {
Name() string
Reset()
Ready() bool
Update(context.Context, *CandleTime, *Backtest) *StrategyPlan
// StopDescription returns a human-readable description of how this strategy
// places stops, e.g. "ATR(14)×1.5", "25 pips", or "" if none.
StopDescription() string
}
Strategy is the single backtest strategy interface used across the repo.
func GetStrategy ¶
func GetStrategy(scfg StrategyConfig) (Strategy, error)
GetStrategy is the public dispatcher used by config-driven backtest setup. It looks the strategy up in the registry; implementations register themselves via init() in their own packages.
type StrategyConfig ¶
type StrategyConfig struct {
Kind string `json:"kind" yaml:"kind"`
Params map[string]any `json:"params" yaml:"params"`
}
StrategyConfig names the strategy and carries arbitrary key/value parameters that are passed to the strategy constructor at build time.
type StrategyConstructor ¶
StrategyConstructor builds a Strategy from a config's Params map. Each implementation owns its own param parsing.
func LookupStrategy ¶
func LookupStrategy(name string) StrategyConstructor
LookupStrategy returns the constructor registered under name, or nil.
type StrategyPlan ¶
type StrategyPlan struct {
Opens []*OpenRequest
Closes []*CloseRequest
Cancel []string
Reason string
}
func DefaultPlan ¶ added in v0.2.3
func DefaultPlan() *StrategyPlan
DefaultPlan returns a fresh no-op plan with the default hold reason.
func HoldPlan ¶ added in v0.2.3
func HoldPlan(reason string) *StrategyPlan
HoldPlan returns a fresh no-op plan with the provided reason. An empty reason falls back to the default hold reason.
func (*StrategyPlan) Empty ¶ added in v0.2.3
func (p *StrategyPlan) Empty() bool
Empty reports whether the plan has no actions to execute.
type SwingAnalyzer ¶ added in v0.2.1
type SwingAnalyzer struct {
// contains filtered or unexported fields
}
SwingAnalyzer measures the high-low range of each candle. Ranges are stored as Price (scaled int) and converted to pips only at output.
func NewSwingAnalyzer ¶ added in v0.2.1
func NewSwingAnalyzer(inst *Instrument) *SwingAnalyzer
NewSwingAnalyzer creates a SwingAnalyzer for the given instrument.
func (*SwingAnalyzer) Name ¶ added in v0.2.1
func (a *SwingAnalyzer) Name() string
func (*SwingAnalyzer) Stats ¶ added in v0.2.1
func (a *SwingAnalyzer) Stats() []Stat
func (*SwingAnalyzer) Update ¶ added in v0.2.1
func (a *SwingAnalyzer) Update(ct *CandleTime)
type SyntheticCandleConfig ¶
type SyntheticCandleConfig struct {
Instrument string // e.g., "EURUSD"
Timeframe Timeframe // e.g., H1 (hourly)
StartPrice Price // Starting price in scale units
Volatility float64 // Volatility as percentage (e.g., 0.005 = 0.5%)
Trend float64 // Trend as log return per candle (e.g., 0.0001 = +0.01%)
Seed int64 // Random seed for reproducibility
TicksPerBar int32 // Number of ticks per candle
}
SyntheticCandleConfig holds parameters for generating synthetic candle data.
func DefaultSyntheticConfig ¶
func DefaultSyntheticConfig(instrument string) SyntheticCandleConfig
DefaultSyntheticConfig returns a sensible default configuration for EUR/USD.
func (SyntheticCandleConfig) GenerateSyntheticMonthlyCandles ¶
func (cfg SyntheticCandleConfig) GenerateSyntheticMonthlyCandles(year int, month time.Month) (*candleSet, error)
GenerateSyntheticMonthlyCandles generates a full month of synthetic OHLC data.
func (SyntheticCandleConfig) GenerateSyntheticYearlyAndWrite ¶
func (cfg SyntheticCandleConfig) GenerateSyntheticYearlyAndWrite(store *Store, year int) ([]string, error)
GenerateSyntheticYearlyAndWrite generates a year of synthetic data and writes it to CSV files.
func (SyntheticCandleConfig) GenerateSyntheticYearlyCandles ¶
func (cfg SyntheticCandleConfig) GenerateSyntheticYearlyCandles(year int) ([]*candleSet, error)
GenerateSyntheticYearlyCandles generates a full year of monthly candle sets.
type Tick ¶
Tick represents a trader domain type.
type TimeRange ¶
type TimeRange struct {
Start Timestamp // inclusive
End Timestamp // exclusive
TF Timeframe // m1, h1, d1
}
TimeRange represents a trader domain type.
func ParseTimeRange ¶ added in v0.2.0
ParseTimeRange parses a TimeRange from "YYYY-MM-DD" from/to strings and a timeframe string ("M1", "H1", "D1"). Exported for use by sibling packages.
func (TimeRange) MonthsInRange ¶
func (r TimeRange) MonthsInRange() []yearMonth
MonthsInRange is an internal helper for trader type processing.
type Timeframe ¶
type Timeframe int64
******************************************************************** Timeframe ********************************************************************
func ParseTimeframe ¶ added in v0.2.3
ParseTimeframe parses a timeframe string into its canonical Timeframe value. It accepts common aliases and returns an error for unknown values.
type Timestamp ¶
type Timestamp int64
Timestamp represents a trader domain type.
func FromString ¶
FromString is an internal helper for trader type processing.
func ParseDateTimestamp ¶ added in v0.2.3
ParseDateTimestamp parses a YYYY-MM-DD date string into a UTC midnight Timestamp.
func (Timestamp) FloorToHour ¶
FloorToHour is an internal helper for trader type processing.
func (Timestamp) MS ¶
func (s Timestamp) MS() timemilli
MS is an internal helper for trader type processing.
func (Timestamp) Milli ¶
func (t Timestamp) Milli() timemilli
Milli is an internal helper for trader type processing.
type Trade ¶
type Trade struct {
*TradeCommon
EntryPrice Price
EntryTime Timestamp
ExitPrice Price
ExitTime Timestamp
PNL Money // account currency (best-effort)
CloseCause closeCause
}
Trade represents a trader domain type.
type TradeCommon ¶
type TradeCommon struct {
ID string
Instrument string
Side // Long or Short
Units
Stop Price
Take Price
}
TradeCommon represents a trader domain type.
func (*TradeCommon) Clone ¶ added in v0.2.3
func (tc *TradeCommon) Clone() *TradeCommon
Clone is an internal helper for trader type processing.
type TradeHistory ¶
type TradeHistory struct {
*TradeCommon
*OpenRequest
}
TradeHistory represents a trader domain type.
func NewTradeHistory ¶
func NewTradeHistory(inst string) *TradeHistory
NewTradeHistory is an internal helper for trader type processing.
type TradeRecord ¶
type TradeRecord struct {
TradeID string
BotID string // set by the bot manager; empty for backtest/journal-only runs
Instrument string
Units Units
EntryPrice Price
ExitPrice Price
OpenTime Timestamp
CloseTime Timestamp
RealizedPL Money
Reason string
}
TradeRecord is the canonical persisted representation of a completed trade. It is shared by live journaling, replay/sim journaling, and export formats such as CSV, JSONL, and Org output.
func ReadTradesJSONL ¶ added in v0.2.3
func ReadTradesJSONL(path string) ([]TradeRecord, error)
ReadTradesJSONL reads all TradeRecords from a JSONL file. Malformed/invalid lines are silently skipped (forward-compatible with mixed journal data).
type Trader ¶
type Trader struct {
DataManager CandleSource
*Broker
*Store
}
type TraderBacktestExecutor ¶ added in v0.2.3
type TraderBacktestExecutor struct {
DataManager CandleSource
BrokerFactory func() *Broker
AccountFactory func(name string, balance Money) *Account
}
TraderBacktestExecutor executes a Backtest by wiring it through Trader with factory-provided runtime dependencies.
func NewTraderBacktestExecutor ¶ added in v0.2.3
func NewTraderBacktestExecutor(dm CandleSource) *TraderBacktestExecutor
NewTraderBacktestExecutor returns a BacktestExecutor that uses Trader as the concrete execution engine.
type TrendAnalyzer ¶ added in v0.2.1
type TrendAnalyzer struct {
// contains filtered or unexported fields
}
TrendAnalyzer measures the body/range ratio as a proxy for trending vs consolidating bars. ratio = |Close−Open| / (High−Low).
Thresholds: >0.6 → trending; <0.3 → consolidating.
func NewTrendAnalyzer ¶ added in v0.2.1
func NewTrendAnalyzer() *TrendAnalyzer
NewTrendAnalyzer creates a TrendAnalyzer.
func (*TrendAnalyzer) Name ¶ added in v0.2.1
func (a *TrendAnalyzer) Name() string
func (*TrendAnalyzer) Stats ¶ added in v0.2.1
func (a *TrendAnalyzer) Stats() []Stat
func (*TrendAnalyzer) Update ¶ added in v0.2.1
func (a *TrendAnalyzer) Update(ct *CandleTime)
type Units ¶
type Units int64
Units represents a trader domain type.
func UnitsFromFloat ¶ added in v0.2.3
UnitsFromFloat converts a float64 multiplier to a fixed-point Units value.
func (Units) Float64 ¶ added in v0.2.3
Float64 converts a fixed-point Units multiplier back to float64. Use only at output boundaries (display, broker API).
type Want ¶
type Want struct {
Key
WantReason
}
type WantReason ¶
type WantReason string
const ( WantMissing WantReason = "missing" WantIncomplete WantReason = "incomplete" WantStale WantReason = "stale" )
func (WantReason) Valid ¶ added in v0.2.3
func (wr WantReason) Valid() bool
type Wantlist ¶
type Wantlist struct {
// contains filtered or unexported fields
}
func NewWantlist ¶
func NewWantlist() *Wantlist
func (*Wantlist) PutKey ¶ added in v0.2.3
func (wl *Wantlist) PutKey(key Key, reason WantReason)
type WeeklyEMAFilter ¶ added in v0.2.1
type WeeklyEMAFilter struct {
// contains filtered or unexported fields
}
WeeklyEMAFilter is a directional regime filter that aggregates sub-daily bars into ISO weekly bars and runs an EMA(period) over weekly closes.
Trending() always returns true — this is a direction-only filter. AllowSide(Long) returns true when the current week's in-progress close is above the EMA computed from completed weekly closes. AllowSide(Short) returns true when that in-progress close is below the EMA, so directional permission can change within a week as the partial weekly close moves.
During warmup (EMA not yet ready) AllowSide returns true as a defensive contract so no entries are suppressed before enough weekly data has accumulated, although the main callers already gate on Ready() before consulting directional permission.
Registered in the factory as "weekly-ema".
func NewWeeklyEMAFilter ¶ added in v0.2.1
func NewWeeklyEMAFilter(period int, scale Scale6) (*WeeklyEMAFilter, error)
func (*WeeklyEMAFilter) AllowSide ¶ added in v0.2.1
func (f *WeeklyEMAFilter) AllowSide(side Side) bool
func (*WeeklyEMAFilter) EMA ¶ added in v0.2.3
func (f *WeeklyEMAFilter) EMA() float64
EMA exposes the current weekly EMA value for debugging.
func (*WeeklyEMAFilter) EMAValue ¶ added in v0.2.1
func (f *WeeklyEMAFilter) EMAValue() float64
EMAValue exposes the current EMA value for debugging.
func (*WeeklyEMAFilter) Name ¶ added in v0.2.1
func (f *WeeklyEMAFilter) Name() string
func (*WeeklyEMAFilter) Ready ¶ added in v0.2.1
func (f *WeeklyEMAFilter) Ready() bool
func (*WeeklyEMAFilter) Tick ¶ added in v0.2.1
func (f *WeeklyEMAFilter) Tick(ct CandleTime)
func (*WeeklyEMAFilter) Trending ¶ added in v0.2.1
func (f *WeeklyEMAFilter) Trending() bool
Trending always returns true; direction is enforced via AllowSide.
Source Files
¶
- account.go
- account_close.go
- account_sizing.go
- analysis.go
- analysis_distribution.go
- analysis_session.go
- analysis_spread.go
- analysis_stats.go
- analysis_swing.go
- analysis_trend.go
- app_config.go
- backtest.go
- backtest_candle_helpers.go
- backtest_config.go
- backtest_executor.go
- backtest_feed.go
- backtest_report.go
- backtest_report_org.go
- backtest_result.go
- backtest_run.go
- backtest_summary.go
- broker.go
- broker_event.go
- candle_agg.go
- candle_gap.go
- candle_iter.go
- candle_set.go
- candle_types.go
- config_global.go
- data_downloader.go
- data_iterator.go
- data_manager.go
- data_plan.go
- data_ticks.go
- data_validation.go
- data_wants.go
- data_writer.go
- indicators.go
- indicators_adx.go
- indicators_atr.go
- indicators_bollinger.go
- indicators_choppiness.go
- indicators_ema.go
- indicators_fixed.go
- journal.go
- journal_csv.go
- journal_json.go
- journal_live.go
- journal_org.go
- live_strategy.go
- live_strategy_registry.go
- log_logger.go
- log_stack.go
- log_syslog_unix.go
- regime_filter.go
- regime_filter_atr_percentile.go
- regime_filter_choppiness.go
- regime_filter_d1adx.go
- regime_filter_d1choppiness.go
- regime_filter_daily.go
- regime_filter_factory.go
- regime_filter_session.go
- regime_filter_weekly_ema.go
- store.go
- store_inventory.go
- store_key.go
- store_keymap.go
- strategy.go
- strategy_exit.go
- strategy_exit_chandelier.go
- strategy_exit_factory.go
- strategy_plan.go
- strategy_registry.go
- testdata_generator.go
- testdata_helper.go
- trader.go
- trader_globals.go
- types_analysis.go
- types_id.go
- types_instruments.go
- types_lot.go
- types_math.go
- types_money.go
- types_param.go
- types_position.go
- types_request.go
- types_tick.go
- types_time.go
- types_trade.go
- types_trade_history.go
- types_units.go
- types_utils.go
- version.go
Directories
¶
| Path | Synopsis |
|---|---|
|
api
|
|
|
mcp
Package mcp implements an MCP (Model Context Protocol) server over the service layer.
|
Package mcp implements an MCP (Model Context Protocol) server over the service layer. |
|
rest
Package rest is the HTTP presentation layer over the service package.
|
Package rest is the HTTP presentation layer over the service package. |
|
brokers
|
|
|
api
Package api hosts the CLI command for starting the REST API server.
|
Package api hosts the CLI command for starting the REST API server. |
|
bot
Package bot hosts CLI subcommands for managing live strategy bots running inside a trader serve process.
|
Package bot hosts CLI subcommands for managing live strategy bots running inside a trader serve process. |
|
gen-newsdays
command
gen-newsdays generates a news-days file for use with the donchian-v5/v6 strategies (news_days_file param).
|
gen-newsdays generates a news-days file for use with the donchian-v5/v6 strategies (news_days_file param). |
|
gen-testdata
command
|
|
|
health
Package health provides CLI commands that query the trader serve REST API for health and version information.
|
Package health provides CLI commands that query the trader serve REST API for health and version information. |
|
live
Package live hosts CLI commands for the live trading subsystem.
|
Package live hosts CLI commands for the live trading subsystem. |
|
mcp
Package mcp hosts the CLI command for starting the MCP server.
|
Package mcp hosts the CLI command for starting the MCP server. |
|
order
Package order hosts CLI subcommands for live order management.
|
Package order hosts CLI subcommands for live order management. |
|
serve
Package serve implements "trader serve" — the long-running daemon mode.
|
Package serve implements "trader serve" — the long-running daemon mode. |
|
Package data defines the Provider interface implemented by every market-data source (Dukascopy, OANDA, future Polygon/IBKR, etc.).
|
Package data defines the Provider interface implemented by every market-data source (Dukascopy, OANDA, future Polygon/IBKR, etc.). |
|
dukascopy
Package dukascopy implements the data.Provider interface for Dukascopy historical tick files.
|
Package dukascopy implements the data.Provider interface for Dukascopy historical tick files. |
|
Package service is the protocol-agnostic business-logic layer.
|
Package service is the protocol-agnostic business-logic layer. |
|
strategies
|
|
|
bollingerfade
Package bollingerfade implements a Bollinger Band mean-reversion strategy.
|
Package bollingerfade implements a Bollinger Band mean-reversion strategy. |
|
donchian
Package donchian implements the Donchian breakout strategy with close-strength confirmation.
|
Package donchian implements the Donchian breakout strategy with close-strength confirmation. |
|
donchianv2
Package donchianv2 is Donchian breakout v2: adds a consecutive-close confirmation filter (confirm_bars, default 2) on top of the v1 close-strength filter.
|
Package donchianv2 is Donchian breakout v2: adds a consecutive-close confirmation filter (confirm_bars, default 2) on top of the v1 close-strength filter. |
|
donchianv3
Package donchianv3 is Donchian breakout v3: adds a same-day re-entry block on top of the v2 consecutive-close confirmation filter.
|
Package donchianv3 is Donchian breakout v3: adds a same-day re-entry block on top of the v2 consecutive-close confirmation filter. |
|
donchianv4
Package donchianv4 is Donchian breakout v4: adds an ADX directional-strength gate on top of the v2 consecutive-close confirmation filter.
|
Package donchianv4 is Donchian breakout v4: adds an ADX directional-strength gate on top of the v2 consecutive-close confirmation filter. |
|
donchianv5
Package donchianv5 is Donchian breakout v5: adds a high-impact news-day filter on top of the v4 ADX directional-strength gate.
|
Package donchianv5 is Donchian breakout v5: adds a high-impact news-day filter on top of the v4 ADX directional-strength gate. |
|
donchianv6
Package donchianv6 is Donchian breakout v6: adds a Monday/week-open entry block on top of the v5 news-day filter.
|
Package donchianv6 is Donchian breakout v6: adds a Monday/week-open entry block on top of the v5 news-day filter. |
|
emacross
Package emacross implements the fast/slow EMA crossover strategy.
|
Package emacross implements the fast/slow EMA crossover strategy. |
|
emacrossadx
Package emacrossadx implements the EMA-cross strategy with an ADX trend-strength gate.
|
Package emacrossadx implements the EMA-cross strategy with an ADX trend-strength gate. |
|
fake
Package fake contains canned deterministic strategies used by trader's integration and lifecycle tests.
|
Package fake contains canned deterministic strategies used by trader's integration and lifecycle tests. |
|
lifecycle
Package lifecycle is a deterministic canned strategy used to regression-test the full config→candles→strategy→Trader→Broker→Account→Trades→Result pipeline.
|
Package lifecycle is a deterministic canned strategy used to regression-test the full config→candles→strategy→Trader→Broker→Account→Trades→Result pipeline. |
|
noop
Package noop implements a do-nothing strategy.
|
Package noop implements a do-nothing strategy. |
|
pulse
Package pulse provides a mechanical live-trading strategy that opens and closes positions on a fixed schedule.
|
Package pulse provides a mechanical live-trading strategy that opens and closes positions on a fixed schedule. |
|
scalper
Package scalper implements a "buy the dip" M1 scalper for live broker integration testing and incremental strategy development.
|
Package scalper implements a "buy the dip" M1 scalper for live broker integration testing and incremental strategy development. |
|
stress
Package stress implements an unconditional mechanical strategy that opens a trade every N candles with no indicator warmup.
|
Package stress implements an unconditional mechanical strategy that opens a trade every N candles with no indicator warmup. |
|
tmpl
Package tmpl is a strategy template / starting point for new strategy implementations.
|
Package tmpl is a strategy template / starting point for new strategy implementations. |
|
Package ui exposes the compiled SvelteKit front-end as an embed.FS.
|
Package ui exposes the compiled SvelteKit front-end as an embed.FS. |