@timmeck/trading-brain 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -0
- package/dist/api/server.d.ts +21 -0
- package/dist/api/server.js +157 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/colors.d.ts +46 -0
- package/dist/cli/colors.js +70 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +70 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +61 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.js +23 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +42 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/insights.d.ts +2 -0
- package/dist/cli/commands/insights.js +29 -0
- package/dist/cli/commands/insights.js.map +1 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +43 -0
- package/dist/cli/commands/network.js.map +1 -0
- package/dist/cli/commands/query.d.ts +2 -0
- package/dist/cli/commands/query.js +27 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +2 -0
- package/dist/cli/commands/rules.js +26 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +86 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +58 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.js +34 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/ipc-helper.d.ts +2 -0
- package/dist/cli/ipc-helper.js +26 -0
- package/dist/cli/ipc-helper.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +19 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrations/001_core.d.ts +2 -0
- package/dist/db/migrations/001_core.js +42 -0
- package/dist/db/migrations/001_core.js.map +1 -0
- package/dist/db/migrations/002_synapses.d.ts +2 -0
- package/dist/db/migrations/002_synapses.js +43 -0
- package/dist/db/migrations/002_synapses.js.map +1 -0
- package/dist/db/migrations/003_learning.d.ts +2 -0
- package/dist/db/migrations/003_learning.js +48 -0
- package/dist/db/migrations/003_learning.js.map +1 -0
- package/dist/db/migrations/004_research.d.ts +2 -0
- package/dist/db/migrations/004_research.js +29 -0
- package/dist/db/migrations/004_research.js.map +1 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/migrations/index.js +45 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/repositories/calibration.repository.d.ts +25 -0
- package/dist/db/repositories/calibration.repository.js +66 -0
- package/dist/db/repositories/calibration.repository.js.map +1 -0
- package/dist/db/repositories/chain.repository.d.ts +28 -0
- package/dist/db/repositories/chain.repository.js +52 -0
- package/dist/db/repositories/chain.repository.js.map +1 -0
- package/dist/db/repositories/graph.repository.d.ts +33 -0
- package/dist/db/repositories/graph.repository.js +73 -0
- package/dist/db/repositories/graph.repository.js.map +1 -0
- package/dist/db/repositories/insight.repository.d.ts +30 -0
- package/dist/db/repositories/insight.repository.js +60 -0
- package/dist/db/repositories/insight.repository.js.map +1 -0
- package/dist/db/repositories/rule.repository.d.ts +35 -0
- package/dist/db/repositories/rule.repository.js +48 -0
- package/dist/db/repositories/rule.repository.js.map +1 -0
- package/dist/db/repositories/signal.repository.d.ts +17 -0
- package/dist/db/repositories/signal.repository.js +35 -0
- package/dist/db/repositories/signal.repository.js.map +1 -0
- package/dist/db/repositories/synapse.repository.d.ts +25 -0
- package/dist/db/repositories/synapse.repository.js +50 -0
- package/dist/db/repositories/synapse.repository.js.map +1 -0
- package/dist/db/repositories/trade.repository.d.ts +36 -0
- package/dist/db/repositories/trade.repository.js +64 -0
- package/dist/db/repositories/trade.repository.js.map +1 -0
- package/dist/graph/weighted-graph.d.ts +58 -0
- package/dist/graph/weighted-graph.js +149 -0
- package/dist/graph/weighted-graph.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +16 -0
- package/dist/ipc/client.js +95 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/protocol.d.ts +8 -0
- package/dist/ipc/protocol.js +29 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/router.d.ts +32 -0
- package/dist/ipc/router.js +70 -0
- package/dist/ipc/router.js.map +1 -0
- package/dist/ipc/server.d.ts +18 -0
- package/dist/ipc/server.js +142 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/learning/calibrator.d.ts +6 -0
- package/dist/learning/calibrator.js +57 -0
- package/dist/learning/calibrator.js.map +1 -0
- package/dist/learning/chain-detector.d.ts +17 -0
- package/dist/learning/chain-detector.js +29 -0
- package/dist/learning/chain-detector.js.map +1 -0
- package/dist/learning/learning-engine.d.ts +31 -0
- package/dist/learning/learning-engine.js +85 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/learning/pattern-extractor.d.ts +14 -0
- package/dist/learning/pattern-extractor.js +40 -0
- package/dist/learning/pattern-extractor.js.map +1 -0
- package/dist/mcp/http-server.d.ts +14 -0
- package/dist/mcp/http-server.js +117 -0
- package/dist/mcp/http-server.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +67 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +7 -0
- package/dist/mcp/tools.js +158 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/research/research-engine.d.ts +21 -0
- package/dist/research/research-engine.js +204 -0
- package/dist/research/research-engine.js.map +1 -0
- package/dist/services/analytics.service.d.ts +16 -0
- package/dist/services/analytics.service.js +64 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/insight.service.d.ts +11 -0
- package/dist/services/insight.service.js +25 -0
- package/dist/services/insight.service.js.map +1 -0
- package/dist/services/signal.service.d.ts +22 -0
- package/dist/services/signal.service.js +96 -0
- package/dist/services/signal.service.js.map +1 -0
- package/dist/services/strategy.service.d.ts +29 -0
- package/dist/services/strategy.service.js +115 -0
- package/dist/services/strategy.service.js.map +1 -0
- package/dist/services/synapse.service.d.ts +20 -0
- package/dist/services/synapse.service.js +48 -0
- package/dist/services/synapse.service.js.map +1 -0
- package/dist/services/trade.service.d.ts +37 -0
- package/dist/services/trade.service.js +114 -0
- package/dist/services/trade.service.js.map +1 -0
- package/dist/signals/fingerprint.d.ts +29 -0
- package/dist/signals/fingerprint.js +98 -0
- package/dist/signals/fingerprint.js.map +1 -0
- package/dist/signals/wilson-score.d.ts +10 -0
- package/dist/signals/wilson-score.js +19 -0
- package/dist/signals/wilson-score.js.map +1 -0
- package/dist/synapses/decay.d.ts +6 -0
- package/dist/synapses/decay.js +17 -0
- package/dist/synapses/decay.js.map +1 -0
- package/dist/synapses/hebbian.d.ts +11 -0
- package/dist/synapses/hebbian.js +21 -0
- package/dist/synapses/hebbian.js.map +1 -0
- package/dist/synapses/synapse-manager.d.ts +22 -0
- package/dist/synapses/synapse-manager.js +99 -0
- package/dist/synapses/synapse-manager.js.map +1 -0
- package/dist/trading-core.d.ts +17 -0
- package/dist/trading-core.js +235 -0
- package/dist/trading-core.js.map +1 -0
- package/dist/types/config.types.d.ts +52 -0
- package/dist/types/config.types.js +2 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/ipc.types.d.ts +11 -0
- package/dist/types/ipc.types.js +2 -0
- package/dist/types/ipc.types.js.map +1 -0
- package/dist/utils/events.d.ts +48 -0
- package/dist/utils/events.js +23 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +5 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +47 -0
- package/src/api/server.ts +160 -0
- package/src/cli/colors.ts +80 -0
- package/src/cli/commands/config.ts +76 -0
- package/src/cli/commands/doctor.ts +62 -0
- package/src/cli/commands/export.ts +24 -0
- package/src/cli/commands/import.ts +44 -0
- package/src/cli/commands/insights.ts +30 -0
- package/src/cli/commands/network.ts +43 -0
- package/src/cli/commands/query.ts +28 -0
- package/src/cli/commands/rules.ts +27 -0
- package/src/cli/commands/start.ts +93 -0
- package/src/cli/commands/status.ts +64 -0
- package/src/cli/commands/stop.ts +33 -0
- package/src/cli/ipc-helper.ts +22 -0
- package/src/config.ts +103 -0
- package/src/db/connection.ts +22 -0
- package/src/db/migrations/001_core.ts +43 -0
- package/src/db/migrations/002_synapses.ts +44 -0
- package/src/db/migrations/003_learning.ts +49 -0
- package/src/db/migrations/004_research.ts +30 -0
- package/src/db/migrations/index.ts +60 -0
- package/src/db/repositories/calibration.repository.ts +86 -0
- package/src/db/repositories/chain.repository.ts +70 -0
- package/src/db/repositories/graph.repository.ts +103 -0
- package/src/db/repositories/insight.repository.ts +80 -0
- package/src/db/repositories/rule.repository.ts +67 -0
- package/src/db/repositories/signal.repository.ts +48 -0
- package/src/db/repositories/synapse.repository.ts +71 -0
- package/src/db/repositories/trade.repository.ts +97 -0
- package/src/graph/weighted-graph.ts +194 -0
- package/src/index.ts +55 -0
- package/src/ipc/client.ts +112 -0
- package/src/ipc/protocol.ts +35 -0
- package/src/ipc/router.ts +113 -0
- package/src/ipc/server.ts +150 -0
- package/src/learning/calibrator.ts +57 -0
- package/src/learning/chain-detector.ts +43 -0
- package/src/learning/learning-engine.ts +94 -0
- package/src/learning/pattern-extractor.ts +53 -0
- package/src/mcp/http-server.ts +118 -0
- package/src/mcp/server.ts +72 -0
- package/src/mcp/tools.ts +256 -0
- package/src/research/research-engine.ts +223 -0
- package/src/services/analytics.service.ts +68 -0
- package/src/services/insight.service.ts +29 -0
- package/src/services/signal.service.ts +109 -0
- package/src/services/strategy.service.ts +130 -0
- package/src/services/synapse.service.ts +58 -0
- package/src/services/trade.service.ts +139 -0
- package/src/signals/fingerprint.ts +93 -0
- package/src/signals/wilson-score.ts +17 -0
- package/src/synapses/decay.ts +19 -0
- package/src/synapses/hebbian.ts +23 -0
- package/src/synapses/synapse-manager.ts +112 -0
- package/src/trading-core.ts +285 -0
- package/src/types/config.types.ts +60 -0
- package/src/types/ipc.types.ts +8 -0
- package/src/utils/events.ts +42 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/logger.ts +48 -0
- package/src/utils/paths.ts +19 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { TradeRepository, TradeRecord } from '../db/repositories/trade.repository.js';
|
|
2
|
+
import type { SignalRepository } from '../db/repositories/signal.repository.js';
|
|
3
|
+
import type { ChainRepository } from '../db/repositories/chain.repository.js';
|
|
4
|
+
import type { SynapseManager } from '../synapses/synapse-manager.js';
|
|
5
|
+
import type { WeightedGraph } from '../graph/weighted-graph.js';
|
|
6
|
+
import type { CalibrationConfig, LearningConfig } from '../types/config.types.js';
|
|
7
|
+
import { fingerprint, decomposeFingerprint, type SignalInput } from '../signals/fingerprint.js';
|
|
8
|
+
import { NODE_TYPES } from '../graph/weighted-graph.js';
|
|
9
|
+
import { detectChain } from '../learning/chain-detector.js';
|
|
10
|
+
import { getLogger } from '../utils/logger.js';
|
|
11
|
+
import { getEventBus } from '../utils/events.js';
|
|
12
|
+
|
|
13
|
+
export interface RecordOutcomeInput {
|
|
14
|
+
signals: SignalInput;
|
|
15
|
+
regime?: string;
|
|
16
|
+
profitPct: number;
|
|
17
|
+
win: boolean;
|
|
18
|
+
botType: string;
|
|
19
|
+
pair: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class TradeService {
|
|
23
|
+
private recentTrades: TradeRecord[] = [];
|
|
24
|
+
private logger = getLogger();
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private tradeRepo: TradeRepository,
|
|
28
|
+
private signalRepo: SignalRepository,
|
|
29
|
+
private chainRepo: ChainRepository,
|
|
30
|
+
private synapseManager: SynapseManager,
|
|
31
|
+
private graph: WeightedGraph,
|
|
32
|
+
private cal: CalibrationConfig,
|
|
33
|
+
private learningConfig: LearningConfig,
|
|
34
|
+
) {
|
|
35
|
+
// Seed recent trades for chain detection
|
|
36
|
+
this.recentTrades = this.tradeRepo.getRecent(10);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
updateCalibration(cal: CalibrationConfig): void {
|
|
40
|
+
this.cal = cal;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
recordOutcome(input: RecordOutcomeInput): { tradeId: number; fingerprint: string; synapseWeight: number } {
|
|
44
|
+
const bus = getEventBus();
|
|
45
|
+
const fp = fingerprint({ ...input.signals, regime: input.regime });
|
|
46
|
+
|
|
47
|
+
// 1. Store signal combo
|
|
48
|
+
this.signalRepo.create(fp, JSON.stringify(input.signals), input.regime);
|
|
49
|
+
|
|
50
|
+
// 2. Store trade
|
|
51
|
+
const tradeId = this.tradeRepo.create({
|
|
52
|
+
fingerprint: fp,
|
|
53
|
+
pair: input.pair,
|
|
54
|
+
bot_type: input.botType,
|
|
55
|
+
regime: input.regime,
|
|
56
|
+
profit_pct: input.profitPct,
|
|
57
|
+
win: input.win,
|
|
58
|
+
signals_json: JSON.stringify(input.signals),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 3. Hebbian synapse update
|
|
62
|
+
const synapse = input.win
|
|
63
|
+
? this.synapseManager.recordWin(fp, input.profitPct)
|
|
64
|
+
: this.synapseManager.recordLoss(fp, input.profitPct);
|
|
65
|
+
|
|
66
|
+
// 4. Update weighted graph
|
|
67
|
+
const graphNodes = decomposeFingerprint(fp, input.regime, input.pair, input.botType);
|
|
68
|
+
const outcomeNodeId = input.win ? 'outcome_win' : 'outcome_loss';
|
|
69
|
+
|
|
70
|
+
for (const gn of graphNodes) {
|
|
71
|
+
this.graph.addNode(gn.id, gn.type, gn.label);
|
|
72
|
+
}
|
|
73
|
+
this.graph.addNode(outcomeNodeId, NODE_TYPES.OUTCOME, input.win ? 'win' : 'loss');
|
|
74
|
+
|
|
75
|
+
const comboNodeId = `combo_${fp}`;
|
|
76
|
+
for (const gn of graphNodes) {
|
|
77
|
+
if (gn.id !== comboNodeId) {
|
|
78
|
+
this.graph.addEdge(gn.id, comboNodeId, 0.5);
|
|
79
|
+
if (input.win) {
|
|
80
|
+
this.graph.strengthenEdge(gn.id, comboNodeId, this.cal.learningRate);
|
|
81
|
+
} else {
|
|
82
|
+
this.graph.weakenEdge(gn.id, comboNodeId, this.cal.weakenPenalty);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.graph.addEdge(comboNodeId, outcomeNodeId, 0.5);
|
|
88
|
+
if (input.win) {
|
|
89
|
+
this.graph.strengthenEdge(comboNodeId, outcomeNodeId, this.cal.learningRate);
|
|
90
|
+
} else {
|
|
91
|
+
this.graph.weakenEdge(comboNodeId, outcomeNodeId, this.cal.weakenPenalty);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Cross-connect co-occurring signals (Hebbian)
|
|
95
|
+
for (let i = 0; i < graphNodes.length; i++) {
|
|
96
|
+
for (let j = i + 1; j < graphNodes.length; j++) {
|
|
97
|
+
if (graphNodes[i]!.id !== comboNodeId && graphNodes[j]!.id !== comboNodeId) {
|
|
98
|
+
this.graph.addEdge(graphNodes[i]!.id, graphNodes[j]!.id, 0.3);
|
|
99
|
+
if (input.win) {
|
|
100
|
+
this.graph.strengthenEdge(graphNodes[i]!.id, graphNodes[j]!.id, this.cal.learningRate * 0.5);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 5. Chain detection
|
|
107
|
+
const trade = this.tradeRepo.getById(tradeId)!;
|
|
108
|
+
this.recentTrades.push(trade);
|
|
109
|
+
if (this.recentTrades.length > 10) this.recentTrades.shift();
|
|
110
|
+
|
|
111
|
+
const chain = detectChain(this.recentTrades, trade, this.learningConfig.chainMinLength);
|
|
112
|
+
if (chain) {
|
|
113
|
+
this.chainRepo.create(chain);
|
|
114
|
+
bus.emit('chain:detected', { pair: chain.pair, type: chain.type, length: chain.length });
|
|
115
|
+
this.logger.info(`Chain detected: ${chain.type} (${chain.length}x) on ${chain.pair}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
bus.emit('trade:recorded', { tradeId, fingerprint: fp, win: input.win });
|
|
119
|
+
this.logger.info(`Recorded: ${fp} → ${input.win ? 'WIN' : 'LOSS'} (${input.profitPct.toFixed(2)}%) | weight: ${synapse.weight.toFixed(3)} | graph: ${this.graph.getNodeCount()}N/${this.graph.getEdgeCount()}E`);
|
|
120
|
+
|
|
121
|
+
return { tradeId, fingerprint: fp, synapseWeight: synapse.weight };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
query(search: string, limit: number = 50): TradeRecord[] {
|
|
125
|
+
return this.tradeRepo.search(search, limit);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getRecent(limit: number = 10): TradeRecord[] {
|
|
129
|
+
return this.tradeRepo.getRecent(limit);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getByPair(pair: string): TradeRecord[] {
|
|
133
|
+
return this.tradeRepo.getByPair(pair);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
count(): number {
|
|
137
|
+
return this.tradeRepo.count();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { NODE_TYPES } from '../graph/weighted-graph.js';
|
|
2
|
+
|
|
3
|
+
export interface SignalInput {
|
|
4
|
+
rsi14?: number;
|
|
5
|
+
macd?: number;
|
|
6
|
+
trendScore?: number;
|
|
7
|
+
volatility?: number;
|
|
8
|
+
regime?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DecomposedNode {
|
|
12
|
+
id: string;
|
|
13
|
+
type: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function classifyRSI(rsi: number): string {
|
|
18
|
+
if (rsi < 25) return 'extreme_oversold';
|
|
19
|
+
if (rsi < 30) return 'oversold';
|
|
20
|
+
if (rsi < 40) return 'low';
|
|
21
|
+
if (rsi > 75) return 'extreme_overbought';
|
|
22
|
+
if (rsi > 70) return 'overbought';
|
|
23
|
+
if (rsi > 60) return 'high';
|
|
24
|
+
return 'neutral';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function classifyMACD(macd: number, trendScore: number): string {
|
|
28
|
+
if (macd > 0 && trendScore > 0) return 'bullish';
|
|
29
|
+
if (macd < 0 && trendScore < 0) return 'bearish';
|
|
30
|
+
return 'neutral';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function classifyTrend(trendScore: number): string {
|
|
34
|
+
if (trendScore > 3) return 'strong_up';
|
|
35
|
+
if (trendScore > 1) return 'up';
|
|
36
|
+
if (trendScore < -3) return 'strong_down';
|
|
37
|
+
if (trendScore < -1) return 'down';
|
|
38
|
+
return 'flat';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function classifyVolatility(volatility: number): string {
|
|
42
|
+
if (volatility > 80) return 'extreme';
|
|
43
|
+
if (volatility > 50) return 'high';
|
|
44
|
+
if (volatility > 30) return 'medium';
|
|
45
|
+
return 'low';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a signal fingerprint string from input signals.
|
|
50
|
+
* Format: rsi_class|macd_class|trend_class|vol_class[|regime]
|
|
51
|
+
*/
|
|
52
|
+
export function fingerprint(signals: SignalInput): string {
|
|
53
|
+
const parts = [
|
|
54
|
+
classifyRSI(signals.rsi14 ?? 50),
|
|
55
|
+
classifyMACD(signals.macd ?? 0, signals.trendScore ?? 0),
|
|
56
|
+
classifyTrend(signals.trendScore ?? 0),
|
|
57
|
+
classifyVolatility(signals.volatility ?? 30),
|
|
58
|
+
];
|
|
59
|
+
if (signals.regime) parts.push(signals.regime);
|
|
60
|
+
return parts.join('|');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compare two fingerprints for similarity (0-1).
|
|
65
|
+
*/
|
|
66
|
+
export function fingerprintSimilarity(fp1: string, fp2: string): number {
|
|
67
|
+
const parts1 = fp1.split('|');
|
|
68
|
+
const parts2 = fp2.split('|');
|
|
69
|
+
const maxLen = Math.max(parts1.length, parts2.length);
|
|
70
|
+
if (maxLen === 0) return 1;
|
|
71
|
+
let matches = 0;
|
|
72
|
+
for (let i = 0; i < maxLen; i++) {
|
|
73
|
+
if (parts1[i] === parts2[i]) matches++;
|
|
74
|
+
}
|
|
75
|
+
return matches / maxLen;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Decompose a fingerprint into individual graph node IDs for the weighted graph.
|
|
80
|
+
*/
|
|
81
|
+
export function decomposeFingerprint(fp: string, regime?: string, pair?: string, botType?: string): DecomposedNode[] {
|
|
82
|
+
const parts = fp.split('|');
|
|
83
|
+
const nodes: DecomposedNode[] = [];
|
|
84
|
+
if (parts[0]) nodes.push({ id: `sig_rsi_${parts[0]}`, type: NODE_TYPES.SIGNAL, label: parts[0] });
|
|
85
|
+
if (parts[1]) nodes.push({ id: `sig_macd_${parts[1]}`, type: NODE_TYPES.SIGNAL, label: parts[1] });
|
|
86
|
+
if (parts[2]) nodes.push({ id: `sig_trend_${parts[2]}`, type: NODE_TYPES.SIGNAL, label: parts[2] });
|
|
87
|
+
if (parts[3]) nodes.push({ id: `sig_vol_${parts[3]}`, type: NODE_TYPES.SIGNAL, label: parts[3] });
|
|
88
|
+
if (regime) nodes.push({ id: `regime_${regime}`, type: NODE_TYPES.REGIME, label: regime });
|
|
89
|
+
if (pair) nodes.push({ id: `pair_${pair}`, type: NODE_TYPES.PAIR, label: pair });
|
|
90
|
+
if (botType) nodes.push({ id: `bot_${botType}`, type: NODE_TYPES.BOT_TYPE, label: botType });
|
|
91
|
+
nodes.push({ id: `combo_${fp}`, type: NODE_TYPES.COMBO, label: fp });
|
|
92
|
+
return nodes;
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wilson Score lower bound — statistically sound confidence interval for win rates.
|
|
3
|
+
* Penalizes small sample sizes (wide confidence intervals).
|
|
4
|
+
*
|
|
5
|
+
* @param wins - Number of wins
|
|
6
|
+
* @param total - Total number of trades
|
|
7
|
+
* @param z - Z-score (1.64=90%, 1.80, 1.96=95%, 2.33=99%)
|
|
8
|
+
* @returns Lower bound of confidence interval (0-1)
|
|
9
|
+
*/
|
|
10
|
+
export function wilsonScore(wins: number, total: number, z: number = 1.96): number {
|
|
11
|
+
if (total === 0) return 0;
|
|
12
|
+
const p = wins / total;
|
|
13
|
+
const denominator = 1 + (z * z) / total;
|
|
14
|
+
const centre = p + (z * z) / (2 * total);
|
|
15
|
+
const spread = z * Math.sqrt((p * (1 - p) + (z * z) / (4 * total)) / total);
|
|
16
|
+
return Math.max(0, (centre - spread) / denominator);
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SynapseRecord } from '../db/repositories/synapse.repository.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apply temporal decay to a synapse.
|
|
5
|
+
* Formula: new_weight = max(0.01, weight * 0.5^(age/halfLifeMs))
|
|
6
|
+
*/
|
|
7
|
+
export function decaySynapse(synapse: Omit<SynapseRecord, 'created_at'>, halfLifeMs: number): boolean {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
const lastActivated = new Date(synapse.last_activated).getTime();
|
|
10
|
+
const age = now - lastActivated;
|
|
11
|
+
|
|
12
|
+
if (age > halfLifeMs) {
|
|
13
|
+
const periods = age / halfLifeMs;
|
|
14
|
+
const oldWeight = synapse.weight;
|
|
15
|
+
synapse.weight = Math.max(0.01, synapse.weight * Math.pow(0.5, periods));
|
|
16
|
+
return synapse.weight !== oldWeight;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SynapseRecord } from '../db/repositories/synapse.repository.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hebbian strengthen — "neurons that fire together wire together"
|
|
5
|
+
* Asymptotic approach to 1.0: weight += (1 - weight) * learningRate
|
|
6
|
+
*/
|
|
7
|
+
export function strengthen(synapse: Omit<SynapseRecord, 'created_at'>, learningRate: number): void {
|
|
8
|
+
synapse.wins++;
|
|
9
|
+
synapse.activations++;
|
|
10
|
+
synapse.weight += (1.0 - synapse.weight) * learningRate;
|
|
11
|
+
synapse.last_activated = new Date().toISOString();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hebbian weaken — multiplicative decay on loss
|
|
16
|
+
* weight *= weakenPenalty (e.g. 0.7)
|
|
17
|
+
*/
|
|
18
|
+
export function weaken(synapse: Omit<SynapseRecord, 'created_at'>, weakenPenalty: number): void {
|
|
19
|
+
synapse.losses++;
|
|
20
|
+
synapse.activations++;
|
|
21
|
+
synapse.weight *= weakenPenalty;
|
|
22
|
+
synapse.last_activated = new Date().toISOString();
|
|
23
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { SynapseRepository, SynapseRecord } from '../db/repositories/synapse.repository.js';
|
|
2
|
+
import type { CalibrationConfig } from '../types/config.types.js';
|
|
3
|
+
import { strengthen, weaken } from './hebbian.js';
|
|
4
|
+
import { decaySynapse } from './decay.js';
|
|
5
|
+
import { getLogger } from '../utils/logger.js';
|
|
6
|
+
|
|
7
|
+
export class SynapseManager {
|
|
8
|
+
private cache: Map<string, Omit<SynapseRecord, 'created_at'>> = new Map();
|
|
9
|
+
private logger = getLogger();
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private repo: SynapseRepository,
|
|
13
|
+
private cal: CalibrationConfig,
|
|
14
|
+
) {
|
|
15
|
+
this.loadCache();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private loadCache(): void {
|
|
19
|
+
const all = this.repo.getAll();
|
|
20
|
+
for (const syn of all) {
|
|
21
|
+
this.cache.set(syn.id, syn);
|
|
22
|
+
}
|
|
23
|
+
this.logger.info(`Synapse cache loaded: ${this.cache.size} synapses`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
updateCalibration(cal: CalibrationConfig): void {
|
|
27
|
+
this.cal = cal;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getOrCreate(fingerprint: string): Omit<SynapseRecord, 'created_at'> {
|
|
31
|
+
const id = `syn_${fingerprint}`;
|
|
32
|
+
let synapse = this.cache.get(id);
|
|
33
|
+
if (!synapse) {
|
|
34
|
+
synapse = {
|
|
35
|
+
id,
|
|
36
|
+
fingerprint,
|
|
37
|
+
weight: 0.5,
|
|
38
|
+
wins: 0,
|
|
39
|
+
losses: 0,
|
|
40
|
+
activations: 0,
|
|
41
|
+
total_profit: 0,
|
|
42
|
+
last_activated: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
this.cache.set(id, synapse);
|
|
45
|
+
}
|
|
46
|
+
return synapse;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get(id: string): Omit<SynapseRecord, 'created_at'> | undefined {
|
|
50
|
+
return this.cache.get(id);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getByFingerprint(fingerprint: string): Omit<SynapseRecord, 'created_at'> | undefined {
|
|
54
|
+
return this.cache.get(`syn_${fingerprint}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
recordWin(fingerprint: string, profitPct: number): Omit<SynapseRecord, 'created_at'> {
|
|
58
|
+
const synapse = this.getOrCreate(fingerprint);
|
|
59
|
+
synapse.total_profit += profitPct;
|
|
60
|
+
strengthen(synapse, this.cal.learningRate);
|
|
61
|
+
this.repo.upsert(synapse);
|
|
62
|
+
return synapse;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
recordLoss(fingerprint: string, profitPct: number): Omit<SynapseRecord, 'created_at'> {
|
|
66
|
+
const synapse = this.getOrCreate(fingerprint);
|
|
67
|
+
synapse.total_profit += profitPct;
|
|
68
|
+
weaken(synapse, this.cal.weakenPenalty);
|
|
69
|
+
this.repo.upsert(synapse);
|
|
70
|
+
return synapse;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
runDecay(): number {
|
|
74
|
+
const halfLifeMs = this.cal.decayHalfLifeDays * 86400000;
|
|
75
|
+
let decayed = 0;
|
|
76
|
+
for (const synapse of this.cache.values()) {
|
|
77
|
+
if (decaySynapse(synapse, halfLifeMs)) {
|
|
78
|
+
this.repo.upsert(synapse);
|
|
79
|
+
decayed++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (decayed > 0) {
|
|
83
|
+
this.logger.info(`Decayed ${decayed} synapses`);
|
|
84
|
+
}
|
|
85
|
+
return decayed;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getAll(): Omit<SynapseRecord, 'created_at'>[] {
|
|
89
|
+
return Array.from(this.cache.values());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
count(): number {
|
|
93
|
+
return this.cache.size;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getStrongest(limit: number = 20): Omit<SynapseRecord, 'created_at'>[] {
|
|
97
|
+
return Array.from(this.cache.values())
|
|
98
|
+
.sort((a, b) => b.weight - a.weight)
|
|
99
|
+
.slice(0, limit);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getAvgWeight(): number {
|
|
103
|
+
if (this.cache.size === 0) return 0;
|
|
104
|
+
let sum = 0;
|
|
105
|
+
for (const syn of this.cache.values()) sum += syn.weight;
|
|
106
|
+
return sum / this.cache.size;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
clear(): void {
|
|
110
|
+
this.cache.clear();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import type Database from 'better-sqlite3';
|
|
4
|
+
import { loadConfig } from './config.js';
|
|
5
|
+
import type { TradingBrainConfig } from './types/config.types.js';
|
|
6
|
+
import { createLogger, getLogger } from './utils/logger.js';
|
|
7
|
+
import { getEventBus } from './utils/events.js';
|
|
8
|
+
import { createConnection } from './db/connection.js';
|
|
9
|
+
import { runMigrations } from './db/migrations/index.js';
|
|
10
|
+
|
|
11
|
+
// Repositories
|
|
12
|
+
import { TradeRepository } from './db/repositories/trade.repository.js';
|
|
13
|
+
import { SignalRepository } from './db/repositories/signal.repository.js';
|
|
14
|
+
import { SynapseRepository } from './db/repositories/synapse.repository.js';
|
|
15
|
+
import { GraphRepository } from './db/repositories/graph.repository.js';
|
|
16
|
+
import { RuleRepository } from './db/repositories/rule.repository.js';
|
|
17
|
+
import { ChainRepository } from './db/repositories/chain.repository.js';
|
|
18
|
+
import { InsightRepository } from './db/repositories/insight.repository.js';
|
|
19
|
+
import { CalibrationRepository } from './db/repositories/calibration.repository.js';
|
|
20
|
+
|
|
21
|
+
// Graph
|
|
22
|
+
import { WeightedGraph } from './graph/weighted-graph.js';
|
|
23
|
+
|
|
24
|
+
// Synapses
|
|
25
|
+
import { SynapseManager } from './synapses/synapse-manager.js';
|
|
26
|
+
|
|
27
|
+
// Services
|
|
28
|
+
import { TradeService } from './services/trade.service.js';
|
|
29
|
+
import { SignalService } from './services/signal.service.js';
|
|
30
|
+
import { StrategyService } from './services/strategy.service.js';
|
|
31
|
+
import { SynapseService } from './services/synapse.service.js';
|
|
32
|
+
import { AnalyticsService } from './services/analytics.service.js';
|
|
33
|
+
import { InsightService } from './services/insight.service.js';
|
|
34
|
+
|
|
35
|
+
// Engines
|
|
36
|
+
import { LearningEngine } from './learning/learning-engine.js';
|
|
37
|
+
import { ResearchEngine } from './research/research-engine.js';
|
|
38
|
+
|
|
39
|
+
// IPC
|
|
40
|
+
import { IpcRouter, type Services } from './ipc/router.js';
|
|
41
|
+
import { IpcServer } from './ipc/server.js';
|
|
42
|
+
|
|
43
|
+
// API & MCP HTTP
|
|
44
|
+
import { ApiServer } from './api/server.js';
|
|
45
|
+
import { McpHttpServer } from './mcp/http-server.js';
|
|
46
|
+
|
|
47
|
+
export class TradingCore {
|
|
48
|
+
private db: Database.Database | null = null;
|
|
49
|
+
private ipcServer: IpcServer | null = null;
|
|
50
|
+
private apiServer: ApiServer | null = null;
|
|
51
|
+
private mcpHttpServer: McpHttpServer | null = null;
|
|
52
|
+
private learningEngine: LearningEngine | null = null;
|
|
53
|
+
private researchEngine: ResearchEngine | null = null;
|
|
54
|
+
private config: TradingBrainConfig | null = null;
|
|
55
|
+
private configPath?: string;
|
|
56
|
+
private restarting = false;
|
|
57
|
+
|
|
58
|
+
start(configPath?: string): void {
|
|
59
|
+
this.configPath = configPath;
|
|
60
|
+
|
|
61
|
+
// 1. Config
|
|
62
|
+
this.config = loadConfig(configPath);
|
|
63
|
+
const config = this.config;
|
|
64
|
+
|
|
65
|
+
// 2. Ensure data dir
|
|
66
|
+
fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
|
|
67
|
+
|
|
68
|
+
// 3. Logger
|
|
69
|
+
createLogger({
|
|
70
|
+
level: config.log.level,
|
|
71
|
+
file: config.log.file,
|
|
72
|
+
maxSize: config.log.maxSize,
|
|
73
|
+
maxFiles: config.log.maxFiles,
|
|
74
|
+
});
|
|
75
|
+
const logger = getLogger();
|
|
76
|
+
|
|
77
|
+
// 4. Database
|
|
78
|
+
this.db = createConnection(config.dbPath);
|
|
79
|
+
runMigrations(this.db);
|
|
80
|
+
logger.info(`Database initialized: ${config.dbPath}`);
|
|
81
|
+
|
|
82
|
+
// 5. Repositories
|
|
83
|
+
const tradeRepo = new TradeRepository(this.db);
|
|
84
|
+
const signalRepo = new SignalRepository(this.db);
|
|
85
|
+
const synapseRepo = new SynapseRepository(this.db);
|
|
86
|
+
const graphRepo = new GraphRepository(this.db);
|
|
87
|
+
const ruleRepo = new RuleRepository(this.db);
|
|
88
|
+
const chainRepo = new ChainRepository(this.db);
|
|
89
|
+
const insightRepo = new InsightRepository(this.db);
|
|
90
|
+
const calibrationRepo = new CalibrationRepository(this.db);
|
|
91
|
+
|
|
92
|
+
// 6. Synapse Manager
|
|
93
|
+
const synapseManager = new SynapseManager(synapseRepo, config.calibration);
|
|
94
|
+
|
|
95
|
+
// 7. Weighted Graph (load from DB)
|
|
96
|
+
const graph = new WeightedGraph();
|
|
97
|
+
const graphNodes = graphRepo.getAllNodes();
|
|
98
|
+
for (const node of graphNodes) {
|
|
99
|
+
graph.addNode(node.id, node.type, node.label);
|
|
100
|
+
}
|
|
101
|
+
const graphEdges = graphRepo.getAllEdges();
|
|
102
|
+
for (const edge of graphEdges) {
|
|
103
|
+
graph.addEdge(edge.source, edge.target, edge.weight);
|
|
104
|
+
}
|
|
105
|
+
logger.info(`Graph loaded: ${graphNodes.length} nodes, ${graphEdges.length} edges`);
|
|
106
|
+
|
|
107
|
+
// 8. Calibration (load current or use defaults)
|
|
108
|
+
const cal = calibrationRepo.get() ?? config.calibration;
|
|
109
|
+
const tradeCount = () => tradeRepo.count();
|
|
110
|
+
|
|
111
|
+
// 9. Services
|
|
112
|
+
const services: Services = {
|
|
113
|
+
trade: new TradeService(tradeRepo, signalRepo, chainRepo, synapseManager, graph, cal, config.learning),
|
|
114
|
+
signal: new SignalService(synapseManager, graph, cal, tradeCount),
|
|
115
|
+
strategy: new StrategyService(synapseManager, graph, cal, tradeCount),
|
|
116
|
+
synapse: new SynapseService(synapseManager, graph),
|
|
117
|
+
analytics: new AnalyticsService(tradeRepo, ruleRepo, chainRepo, insightRepo, synapseManager, graph),
|
|
118
|
+
insight: new InsightService(insightRepo),
|
|
119
|
+
ruleRepo,
|
|
120
|
+
chainRepo,
|
|
121
|
+
calRepo: calibrationRepo,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// 10. Learning Engine
|
|
125
|
+
this.learningEngine = new LearningEngine(
|
|
126
|
+
config.learning,
|
|
127
|
+
cal,
|
|
128
|
+
tradeRepo,
|
|
129
|
+
ruleRepo,
|
|
130
|
+
chainRepo,
|
|
131
|
+
calibrationRepo,
|
|
132
|
+
synapseManager,
|
|
133
|
+
graph,
|
|
134
|
+
);
|
|
135
|
+
this.learningEngine.start();
|
|
136
|
+
logger.info(`Learning engine started (interval: ${config.learning.intervalMs}ms)`);
|
|
137
|
+
|
|
138
|
+
// 11. Research Engine
|
|
139
|
+
this.researchEngine = new ResearchEngine(
|
|
140
|
+
config.research,
|
|
141
|
+
tradeRepo,
|
|
142
|
+
insightRepo,
|
|
143
|
+
);
|
|
144
|
+
this.researchEngine.start();
|
|
145
|
+
logger.info(`Research engine started (interval: ${config.research.intervalMs}ms)`);
|
|
146
|
+
|
|
147
|
+
// Expose engines to IPC
|
|
148
|
+
services.learning = this.learningEngine;
|
|
149
|
+
services.research = this.researchEngine;
|
|
150
|
+
|
|
151
|
+
// 12. IPC Server
|
|
152
|
+
const router = new IpcRouter(services);
|
|
153
|
+
this.ipcServer = new IpcServer(router, config.ipc.pipeName);
|
|
154
|
+
this.ipcServer.start();
|
|
155
|
+
|
|
156
|
+
// 13. REST API Server
|
|
157
|
+
if (config.api.enabled) {
|
|
158
|
+
this.apiServer = new ApiServer({
|
|
159
|
+
port: config.api.port,
|
|
160
|
+
router,
|
|
161
|
+
apiKey: config.api.apiKey,
|
|
162
|
+
});
|
|
163
|
+
this.apiServer.start();
|
|
164
|
+
logger.info(`REST API enabled on port ${config.api.port}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 14. MCP HTTP Server (SSE transport for Cursor, Windsurf, Cline, Continue)
|
|
168
|
+
if (config.mcpHttp.enabled) {
|
|
169
|
+
this.mcpHttpServer = new McpHttpServer(config.mcpHttp.port, router);
|
|
170
|
+
this.mcpHttpServer.start();
|
|
171
|
+
logger.info(`MCP HTTP (SSE) enabled on port ${config.mcpHttp.port}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 15. Event listeners (synapse wiring)
|
|
175
|
+
this.setupEventListeners(synapseManager);
|
|
176
|
+
|
|
177
|
+
// 16. PID file
|
|
178
|
+
const pidPath = path.join(path.dirname(config.dbPath), 'trading-brain.pid');
|
|
179
|
+
fs.writeFileSync(pidPath, String(process.pid));
|
|
180
|
+
|
|
181
|
+
// 17. Graceful shutdown
|
|
182
|
+
process.on('SIGINT', () => this.stop());
|
|
183
|
+
process.on('SIGTERM', () => this.stop());
|
|
184
|
+
|
|
185
|
+
// 18. Crash recovery
|
|
186
|
+
process.on('uncaughtException', (err) => {
|
|
187
|
+
logger.error('Uncaught exception — restarting', { error: err.message, stack: err.stack });
|
|
188
|
+
this.logCrash('uncaughtException', err);
|
|
189
|
+
this.restart();
|
|
190
|
+
});
|
|
191
|
+
process.on('unhandledRejection', (reason) => {
|
|
192
|
+
logger.error('Unhandled rejection — restarting', { reason: String(reason) });
|
|
193
|
+
this.logCrash('unhandledRejection', reason instanceof Error ? reason : new Error(String(reason)));
|
|
194
|
+
this.restart();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
logger.info(`Trading Brain daemon started (PID: ${process.pid})`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private logCrash(type: string, err: Error): void {
|
|
201
|
+
if (!this.config) return;
|
|
202
|
+
const crashLog = path.join(path.dirname(this.config.dbPath), 'crashes.log');
|
|
203
|
+
const entry = `[${new Date().toISOString()}] ${type}: ${err.message}\n${err.stack ?? ''}\n\n`;
|
|
204
|
+
try { fs.appendFileSync(crashLog, entry); } catch { /* best effort */ }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private cleanup(): void {
|
|
208
|
+
this.researchEngine?.stop();
|
|
209
|
+
this.learningEngine?.stop();
|
|
210
|
+
this.mcpHttpServer?.stop();
|
|
211
|
+
this.apiServer?.stop();
|
|
212
|
+
this.ipcServer?.stop();
|
|
213
|
+
this.db?.close();
|
|
214
|
+
|
|
215
|
+
this.db = null;
|
|
216
|
+
this.ipcServer = null;
|
|
217
|
+
this.apiServer = null;
|
|
218
|
+
this.mcpHttpServer = null;
|
|
219
|
+
this.learningEngine = null;
|
|
220
|
+
this.researchEngine = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
restart(): void {
|
|
224
|
+
if (this.restarting) return;
|
|
225
|
+
this.restarting = true;
|
|
226
|
+
|
|
227
|
+
const logger = getLogger();
|
|
228
|
+
logger.info('Restarting Trading Brain daemon...');
|
|
229
|
+
|
|
230
|
+
try { this.cleanup(); } catch { /* best effort cleanup */ }
|
|
231
|
+
|
|
232
|
+
this.restarting = false;
|
|
233
|
+
this.start(this.configPath);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
stop(): void {
|
|
237
|
+
const logger = getLogger();
|
|
238
|
+
logger.info('Shutting down...');
|
|
239
|
+
|
|
240
|
+
this.cleanup();
|
|
241
|
+
|
|
242
|
+
// Remove PID file
|
|
243
|
+
if (this.config) {
|
|
244
|
+
const pidPath = path.join(path.dirname(this.config.dbPath), 'trading-brain.pid');
|
|
245
|
+
try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
logger.info('Trading Brain daemon stopped');
|
|
249
|
+
process.exit(0);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private setupEventListeners(_synapseManager: SynapseManager): void {
|
|
253
|
+
const bus = getEventBus();
|
|
254
|
+
|
|
255
|
+
// Trade recorded → log
|
|
256
|
+
bus.on('trade:recorded', ({ tradeId, fingerprint, win }) => {
|
|
257
|
+
getLogger().info(`Trade #${tradeId} recorded: ${fingerprint} (${win ? 'WIN' : 'LOSS'})`);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Synapse updated → log at debug level
|
|
261
|
+
bus.on('synapse:updated', ({ synapseId }) => {
|
|
262
|
+
getLogger().debug(`Synapse updated: ${synapseId}`);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Rule learned → log
|
|
266
|
+
bus.on('rule:learned', ({ ruleId, pattern }) => {
|
|
267
|
+
getLogger().info(`New rule #${ruleId} learned: ${pattern}`);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Chain detected → log
|
|
271
|
+
bus.on('chain:detected', ({ pair, type, length }) => {
|
|
272
|
+
getLogger().info(`Chain: ${pair} ${type} streak (${length})`);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Insight created → log
|
|
276
|
+
bus.on('insight:created', ({ insightId, type }) => {
|
|
277
|
+
getLogger().info(`New insight #${insightId} (${type})`);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Calibration updated → log
|
|
281
|
+
bus.on('calibration:updated', ({ outcomeCount }) => {
|
|
282
|
+
getLogger().info(`Calibration updated (${outcomeCount} outcomes)`);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|