Shared Package
The nexgent-open-source-trading-engine/shared package contains code shared between the backend and frontend packages. It ensures type safety and consistency across the entire monorepo.
Overview
| Export | Purpose |
|---|---|
| Types | TypeScript interfaces for trading config, API responses |
| Validators | Zod schemas for runtime validation |
| Constants | Trading configuration defaults, DCA templates |
| Utilities | Stop loss calculators, formatting functions |
The shared package has minimal dependencies (only Zod) to keep it lightweight and prevent dependency conflicts.
Package Structure
- index.ts
- trading-config.ts
- agent-position.ts
Types
Trading Configuration
The core trading configuration types define how agents trade:
// types/trading-config.ts
/** Stop loss calculation mode */
export type StopLossMode = 'fixed' | 'exponential' | 'zones' | 'custom';
/** DCA calculation mode */
export type DCAMode = 'aggressive' | 'moderate' | 'conservative' | 'custom';
/** Complete agent trading configuration */
export interface AgentTradingConfig {
purchaseLimits: PurchaseLimits;
signals: SignalConfig;
stopLoss: StopLossConfig;
positionCalculator: PositionCalculator;
staleTrade: StaleTradeConfig;
dca: DCAConfig;
}Stop Loss Configuration
export interface StopLossConfig {
/** Whether stop loss is active */
enabled: boolean;
/** Default stop loss % (e.g., -32 = 32% loss from purchase) */
defaultPercentage: number;
/** Calculation mode: fixed, exponential, zones, or custom */
mode: StopLossMode;
/** Custom trailing levels (only used when mode === 'custom') */
trailingLevels: TrailingLevel[];
}
export interface TrailingLevel {
/** Price change % above purchase (e.g., 200 = 2x) */
change: number;
/** Stop loss % to keep (e.g., 90 = keep 90% of peak) */
stopLoss: number;
}DCA Configuration
export interface DCAConfig {
enabled: boolean;
mode: DCAMode;
levels: DCALevel[];
maxDCACount: number;
cooldownSeconds: number;
}
export interface DCALevel {
/** Price drop % from average (e.g., -15 = 15% drop) */
dropPercent: number;
/** Amount to buy as % of position (e.g., 50 = buy 50% more) */
buyPercent: number;
}Position Configuration
export interface PositionCalculator {
solBalanceThresholds: {
minimum: number;
medium: number;
large: number;
};
positionSizes: {
small: PositionSizeRange;
medium: PositionSizeRange;
large: PositionSizeRange;
};
randomization: {
enabled: boolean;
};
}
export interface PositionSizeRange {
min: number;
max: number;
}API Types
API request/response types are organized by resource:
// types/api/agents.ts
export interface Agent {
id: string;
userId: string;
name: string;
tradingMode: 'simulation' | 'live';
automatedTradingSimulation: boolean;
automatedTradingLive: boolean;
tradingConfig: AgentTradingConfig;
createdAt: Date;
updatedAt: Date;
}
// types/api/wallets.ts
export interface Wallet {
id: string;
address: string;
name: string | null;
isAssigned: boolean;
assignedAgentId: string | null;
}
// types/api/trading-signals.ts
export interface TradingSignal {
id: string;
tokenAddress: string;
tokenSymbol: string;
signalType: 'BUY' | 'SELL';
score: number;
source: string;
createdAt: Date;
}Validators (Zod)
Zod schemas provide runtime validation with TypeScript type inference.
Agent Schemas
// validators/agents.schema.ts
import { z } from 'zod';
export const CreateAgentSchema = z.object({
name: z.string()
.min(3, 'Name must be at least 3 characters')
.max(50, 'Name must be less than 50 characters'),
tradingMode: z.enum(['simulation', 'live']).default('simulation'),
});
export const UpdateAgentSchema = z.object({
name: z.string().min(3).max(50).optional(),
tradingMode: z.enum(['simulation', 'live']).optional(),
automatedTradingSimulation: z.boolean().optional(),
automatedTradingLive: z.boolean().optional(),
});
// Infer TypeScript types from schemas
export type CreateAgentInput = z.infer<typeof CreateAgentSchema>;
export type UpdateAgentInput = z.infer<typeof UpdateAgentSchema>;Wallet Schemas
// validators/wallets.schema.ts
import { z } from 'zod';
export const AssignWalletSchema = z.object({
walletAddress: z.string().min(32).max(44),
agentId: z.string().uuid(),
});
export const DepositSchema = z.object({
walletAddress: z.string(),
agentId: z.string().uuid(),
amount: z.number().positive(),
});
export const WithdrawSchema = z.object({
walletAddress: z.string(),
agentId: z.string().uuid(),
amount: z.number().positive(),
});Auth Schemas
// validators/auth.schema.ts
import { z } from 'zod';
export const LoginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
export const RegisterSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Password must contain uppercase letter')
.regex(/[0-9]/, 'Password must contain number'),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword'],
});Usage Pattern
// Backend: Validate request body
import { CreateAgentSchema } from 'nexgent-open-source-trading-engine/shared';
app.post('/api/v1/agents', async (req, res) => {
const result = CreateAgentSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
const agent = await createAgent(result.data);
res.json(agent);
});
// Frontend: Form validation with React Hook Form
import { zodResolver } from '@hookform/resolvers/zod';
import { CreateAgentSchema } from 'nexgent-open-source-trading-engine/shared';
const form = useForm({
resolver: zodResolver(CreateAgentSchema),
});Constants
Trading Configuration Defaults
// constants/trading-config-defaults.ts
export const DEFAULT_TRADING_CONFIG: AgentTradingConfig = {
purchaseLimits: {
minimumAgentBalance: 0.5,
maxPurchasePerToken: 2.0,
},
signals: {
minScore: 4,
blacklist: [],
whitelist: [],
whitelistEnabled: false,
},
stopLoss: {
enabled: true,
defaultPercentage: -32,
mode: 'fixed',
trailingLevels: [],
},
positionCalculator: {
solBalanceThresholds: {
minimum: 0.2,
medium: 5,
large: 10,
},
positionSizes: {
small: { min: 0.1, max: 0.1 },
medium: { min: 0.5, max: 1.0 },
large: { min: 1.5, max: 2.0 },
},
randomization: { enabled: true },
},
staleTrade: {
enabled: true,
minHoldTimeMinutes: 60,
minProfitPercent: 1,
maxProfitPercent: 10,
},
dca: {
enabled: false,
mode: 'moderate',
levels: [],
maxDCACount: 3,
cooldownSeconds: 30,
},
};DCA Templates
// constants/dca-templates.ts
export const DCA_TEMPLATES = {
aggressive: [
{ dropPercent: -10, buyPercent: 25 },
{ dropPercent: -20, buyPercent: 35 },
{ dropPercent: -30, buyPercent: 50 },
{ dropPercent: -40, buyPercent: 75 },
],
moderate: [
{ dropPercent: -15, buyPercent: 30 },
{ dropPercent: -30, buyPercent: 50 },
{ dropPercent: -45, buyPercent: 75 },
],
conservative: [
{ dropPercent: -20, buyPercent: 40 },
{ dropPercent: -40, buyPercent: 60 },
],
};Utilities
Stop Loss Calculator
Pure functions for calculating stop loss percentages across different modes:
// utils/stop-loss-calculator.ts
/**
* Main entry point: Calculate stop loss based on config
*/
export function calculateStopLossPercentage(
priceChangePercent: number,
config: StopLossConfig
): number {
if (priceChangePercent < 0) {
return config.defaultPercentage;
}
switch (config.mode) {
case 'fixed':
return calculateFixedStepperStopLoss(priceChangePercent, config.defaultPercentage);
case 'exponential':
return calculateExponentialStopLoss(priceChangePercent, config.defaultPercentage);
case 'zones':
return calculateZonesStopLoss(priceChangePercent, config.defaultPercentage);
case 'custom':
return calculateCustomStopLoss(priceChangePercent, config.trailingLevels)
?? config.defaultPercentage;
default:
return config.defaultPercentage;
}
}Fixed Stepper Mode
/**
* Fixed Stepper: stopLoss = change - 10 (10% increments)
* For gains below 20%, uses default percentage
*/
export function calculateFixedStepperStopLoss(
priceChangePercent: number,
defaultPercentage: number = -32
): number {
if (priceChangePercent < 20) {
return defaultPercentage;
}
return Math.max(0, priceChangePercent - 10);
}Exponential Mode
/**
* Exponential Decay: Loose at low levels, tight at high
* Uses Gaussian function for natural pullback zone
*/
export function calculateExponentialStopLoss(
priceChangePercent: number,
defaultPercentage: number = -32
): number {
if (priceChangePercent <= 20) {
return defaultPercentage;
}
const x = priceChangePercent - 20;
// Base exponential growth
const baseKeep = 1 - Math.exp(-x / (100 / 3.2));
// Gaussian pullback modulation (creates dip at ~75%)
const gaussianPullback = 0.12 * Math.exp(-Math.pow((x - 55) / 8, 2) / 2);
let keepPercentage = Math.max(0, Math.min(0.90, baseKeep - gaussianPullback));
// Acceleration after pullback zone
if (x > 65) {
const boost = 0.30 * (1 - Math.exp(-(x - 65) / 80));
keepPercentage = Math.min(0.90, keepPercentage + boost);
}
return priceChangePercent * keepPercentage;
}Zones Mode
/**
* Step-Based Zones: 5 distinct zones with different keep percentages
*/
export function calculateZonesStopLoss(
priceChangePercent: number,
defaultPercentage: number = -32
): number {
if (priceChangePercent < 20) return defaultPercentage;
let keepPercentage: number;
if (priceChangePercent <= 25) keepPercentage = 0.50; // Zone 1
else if (priceChangePercent <= 50) keepPercentage = 0.60; // Zone 2
else if (priceChangePercent <= 100) keepPercentage = 0.70; // Zone 3
else if (priceChangePercent <= 200) keepPercentage = 0.80; // Zone 4
else keepPercentage = 0.85; // Zone 5
return priceChangePercent * keepPercentage;
}💡
These calculator functions are pure (no side effects) and used by both the backend stop loss manager and frontend chart visualizations.
Usage
Backend Import
import {
AgentTradingConfig,
DEFAULT_TRADING_CONFIG,
CreateAgentSchema,
calculateStopLossPercentage,
} from 'nexgent-open-source-trading-engine/shared';Frontend Import
import {
type Agent,
CreateAgentSchema,
type CreateAgentInput,
DCA_TEMPLATES,
} from 'nexgent-open-source-trading-engine/shared';