Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
549 changes: 118 additions & 431 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { WebhooksModule } from './webhooks/webhooks.module';
import { LocationModule } from './location/location.module';
import { GlobalEnergyModule } from './global-energy/global-energy.module';

// Import trading module
import { TradingModule } from './trading/trading.module';

// Import existing modules
import { PricingModule } from './pricing/pricing.module';
import { SecurityHeadersService } from './security/headers/security-headers.service';
Expand Down Expand Up @@ -76,6 +79,9 @@ import { OrderBook } from './matching/entities/order-book.entity';
LocationModule,
GlobalEnergyModule,

// Import trading module
TradingModule,

// Import existing pricing module for integration
PricingModule,

Expand Down
35 changes: 35 additions & 0 deletions src/trading/integration/stellar-integration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from '@nestjs/common';
import { Server, TransactionBuilder, Networks, Keypair, Asset } from '@stellar/stellar-sdk';

@Injectable()
export class StellarIntegrationService {
private server: Server;

constructor() {
this.server = new Server('https://horizon-testnet.stellar.org'); // Use testnet for now
}

async recordTrade(trade: any): Promise<void> {
// Create a transaction to record the trade on Stellar
const keypair = Keypair.fromSecret(process.env.STELLAR_SECRET_KEY!);

const account = await this.server.loadAccount(keypair.publicKey());

const transaction = new TransactionBuilder(account, {
fee: '100',
networkPassphrase: Networks.TESTNET,
})
.addOperation({
// Some operation to record trade, e.g., payment or data
type: 'manageData',
name: `trade_${trade.id}`,
value: JSON.stringify(trade),
})
.setTimeout(30)
.build();

transaction.sign(keypair);

await this.server.submitTransaction(transaction);
}
}
51 changes: 51 additions & 0 deletions src/trading/matching/matching-engine.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Injectable } from '@nestjs/common';
import { OrderBookService } from '../order-book/order-book.service';
import { SettlementService } from '../settlement/settlement.service';
import { Order } from '../order-management/order.service';

@Injectable()
export class MatchingEngineService {
constructor(
private readonly orderBook: OrderBookService,
private readonly settlement: SettlementService,
) {}

async submitOrder(order: Order): Promise<void> {
// Add to order book
this.orderBook.addOrder(order.id, order.side, order.price!, order.quantity);

// Try to match
await this.matchOrders();
}

async cancelOrder(orderId: string): Promise<void> {
// Remove from order book - need to know price and side
// For simplicity, assume we have a way to get order details
}

private async matchOrders(): Promise<void> {
const bestBid = this.orderBook.getBestBid();
const bestAsk = this.orderBook.getBestAsk();

if (bestBid && bestAsk && bestBid >= bestAsk) {
// Match at mid price or something
const matchPrice = (bestBid + bestAsk) / 2;

// Create trade
const trade = {
id: this.generateId(),
price: matchPrice,
quantity: Math.min(/* quantities */ 100), // placeholder
buyerId: 'buyer',
sellerId: 'seller',
timestamp: new Date(),
};

await this.settlement.settleTrade(trade);
}
}

private generateId(): string {
return Date.now().toString() + Math.random().toString(36).substr(2, 9);
}
}
51 changes: 51 additions & 0 deletions src/trading/order-book/order-book.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Injectable } from '@nestjs/common';

interface OrderBookEntry {
price: number;
quantity: number;
orders: string[]; // order ids
}

@Injectable()
export class OrderBookService {
private buyOrders: Map<number, OrderBookEntry> = new Map();
private sellOrders: Map<number, OrderBookEntry> = new Map();

addOrder(orderId: string, side: 'buy' | 'sell', price: number, quantity: number): void {
const orders = side === 'buy' ? this.buyOrders : this.sellOrders;
if (!orders.has(price)) {
orders.set(price, { price, quantity: 0, orders: [] });
}
const entry = orders.get(price)!;
entry.quantity += quantity;
entry.orders.push(orderId);
}

removeOrder(orderId: string, side: 'buy' | 'sell', price: number, quantity: number): void {
const orders = side === 'buy' ? this.buyOrders : this.sellOrders;
const entry = orders.get(price);
if (entry) {
entry.quantity -= quantity;
entry.orders = entry.orders.filter(id => id !== orderId);
if (entry.quantity <= 0) {
orders.delete(price);
}
}
}

getOrderBook() {
const buys = Array.from(this.buyOrders.values()).sort((a, b) => b.price - a.price);
const sells = Array.from(this.sellOrders.values()).sort((a, b) => a.price - b.price);
return { buys, sells };
}

getBestBid(): number | undefined {
const prices = Array.from(this.buyOrders.keys()).sort((a, b) => b - a);
return prices[0];
}

getBestAsk(): number | undefined {
const prices = Array.from(this.sellOrders.keys()).sort((a, b) => a - b);
return prices[0];
}
}
80 changes: 80 additions & 0 deletions src/trading/order-management/order.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Injectable } from '@nestjs/common';
import { MatchingEngineService } from '../matching/matching-engine.service';

export interface Order {
id: string;
type: 'market' | 'limit' | 'stop' | 'stop-limit';
side: 'buy' | 'sell';
quantity: number;
price?: number;
stopPrice?: number;
status: 'pending' | 'open' | 'filled' | 'cancelled';
userId: string;
createdAt: Date;
updatedAt: Date;
}

@Injectable()
export class OrderService {
private orders: Map<string, Order> = new Map();

constructor(private readonly matchingEngine: MatchingEngineService) {}

async createOrder(orderData: Partial<Order>): Promise<Order> {
const order: Order = {
id: this.generateId(),
type: orderData.type || 'limit',
side: orderData.side,
quantity: orderData.quantity,
price: orderData.price,
stopPrice: orderData.stopPrice,
status: 'pending',
userId: orderData.userId,
createdAt: new Date(),
updatedAt: new Date(),
};

// Validate order
this.validateOrder(order);

this.orders.set(order.id, order);

// Submit to matching engine
await this.matchingEngine.submitOrder(order);

return order;
}

async getOrder(id: string): Promise<Order | undefined> {
return this.orders.get(id);
}

async cancelOrder(id: string): Promise<boolean> {
const order = this.orders.get(id);
if (order && order.status === 'open') {
order.status = 'cancelled';
order.updatedAt = new Date();
// Notify matching engine
await this.matchingEngine.cancelOrder(id);
return true;
}
return false;
}

private validateOrder(order: Order): void {
if (!order.side || !['buy', 'sell'].includes(order.side)) {
throw new Error('Invalid order side');
}
if (order.quantity <= 0) {
throw new Error('Invalid quantity');
}
if (order.type === 'limit' && !order.price) {
throw new Error('Limit order requires price');
}
// Add more validations
}

private generateId(): string {
return Date.now().toString() + Math.random().toString(36).substr(2, 9);
}
}
17 changes: 17 additions & 0 deletions src/trading/settlement/settlement.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Injectable } from '@nestjs/common';

@Injectable()
export class SettlementService {
async settleTrade(trade: any): Promise<void> {
// Implement settlement logic
// Update positions, transfer assets, etc.
console.log('Settling trade:', trade);

// Simulate settlement
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 seconds as per requirements
}

async clearTrades(): Promise<void> {
// Batch clearing
}
}
36 changes: 36 additions & 0 deletions src/trading/trading.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Controller, Post, Get, Body, Param, Delete } from '@nestjs/common';
import { TradingService } from './trading.service';
import { OrderService } from './order-management/order.service';

@Controller('trading')
export class TradingController {
constructor(
private readonly tradingService: TradingService,
private readonly orderService: OrderService,
) {}

@Post('orders')
async placeOrder(@Body() orderData: any) {
return this.orderService.createOrder(orderData);
}

@Get('orders/:id')
async getOrder(@Param('id') id: string) {
return this.orderService.getOrder(id);
}

@Delete('orders/:id')
async cancelOrder(@Param('id') id: string) {
return this.orderService.cancelOrder(id);
}

@Get('order-book')
async getOrderBook() {
return this.tradingService.getOrderBook();
}

@Get('trades')
async getTrades() {
return this.tradingService.getTrades();
}
}
29 changes: 29 additions & 0 deletions src/trading/trading.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TradingController } from './trading.controller';
import { TradingService } from './trading.service';
import { OrderService } from './order-management/order.service';
import { OrderBookService } from './order-book/order-book.service';
import { MatchingEngineService } from './matching/matching-engine.service';
import { SettlementService } from './settlement/settlement.service';
import { StellarIntegrationService } from './integration/stellar-integration.service';
// Import entities if needed
// import { Order } from './entities/order.entity';
// import { Trade } from './entities/trade.entity';

@Module({
imports: [
// TypeOrmModule.forFeature([Order, Trade]),
],
controllers: [TradingController],
providers: [
TradingService,
OrderService,
OrderBookService,
MatchingEngineService,
SettlementService,
StellarIntegrationService,
],
exports: [TradingService],
})
export class TradingModule {}
30 changes: 30 additions & 0 deletions src/trading/trading.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { OrderBookService } from './order-book/order-book.service';
import { MatchingEngineService } from './matching/matching-engine.service';
import { SettlementService } from './settlement/settlement.service';
import { StellarIntegrationService } from './integration/stellar-integration.service';

@Injectable()
export class TradingService {
constructor(
private readonly orderBookService: OrderBookService,
private readonly matchingEngineService: MatchingEngineService,
private readonly settlementService: SettlementService,
private readonly stellarService: StellarIntegrationService,
) {}

async getOrderBook() {
return this.orderBookService.getOrderBook();
}

async getTrades() {
// Implement logic to get recent trades
return [];
}

async processTrade(trade: any) {
// Validate and execute trade
await this.settlementService.settleTrade(trade);
await this.stellarService.recordTrade(trade);
}
}