@timmeck/trading-brain 2.31.10 → 2.31.12

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.
Files changed (53) hide show
  1. package/dashboard.html +486 -486
  2. package/dist/cli/commands/paper.d.ts +2 -0
  3. package/dist/cli/commands/paper.js +108 -0
  4. package/dist/cli/commands/paper.js.map +1 -0
  5. package/dist/config.js +22 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/dashboard/renderer.d.ts +2 -0
  8. package/dist/dashboard/renderer.js +23 -0
  9. package/dist/dashboard/renderer.js.map +1 -1
  10. package/dist/db/migrations/010_paper_trading.d.ts +2 -0
  11. package/dist/db/migrations/010_paper_trading.js +71 -0
  12. package/dist/db/migrations/010_paper_trading.js.map +1 -0
  13. package/dist/db/migrations/index.js +8 -6
  14. package/dist/db/migrations/index.js.map +1 -1
  15. package/dist/db/repositories/paper.repository.d.ts +43 -0
  16. package/dist/db/repositories/paper.repository.js +201 -0
  17. package/dist/db/repositories/paper.repository.js.map +1 -0
  18. package/dist/index.js +2 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/ipc/router.d.ts +1 -0
  21. package/dist/ipc/router.js +8 -0
  22. package/dist/ipc/router.js.map +1 -1
  23. package/dist/mcp/tools.js +21 -0
  24. package/dist/mcp/tools.js.map +1 -1
  25. package/dist/paper/__tests__/indicators.test.d.ts +1 -0
  26. package/dist/paper/__tests__/indicators.test.js +126 -0
  27. package/dist/paper/__tests__/indicators.test.js.map +1 -0
  28. package/dist/paper/decision-engine.d.ts +41 -0
  29. package/dist/paper/decision-engine.js +151 -0
  30. package/dist/paper/decision-engine.js.map +1 -0
  31. package/dist/paper/indicators.d.ts +26 -0
  32. package/dist/paper/indicators.js +134 -0
  33. package/dist/paper/indicators.js.map +1 -0
  34. package/dist/paper/paper-engine.d.ts +31 -0
  35. package/dist/paper/paper-engine.js +175 -0
  36. package/dist/paper/paper-engine.js.map +1 -0
  37. package/dist/paper/paper.service.d.ts +30 -0
  38. package/dist/paper/paper.service.js +56 -0
  39. package/dist/paper/paper.service.js.map +1 -0
  40. package/dist/paper/portfolio-manager.d.ts +32 -0
  41. package/dist/paper/portfolio-manager.js +113 -0
  42. package/dist/paper/portfolio-manager.js.map +1 -0
  43. package/dist/paper/price-fetcher.d.ts +24 -0
  44. package/dist/paper/price-fetcher.js +189 -0
  45. package/dist/paper/price-fetcher.js.map +1 -0
  46. package/dist/paper/types.d.ts +82 -0
  47. package/dist/paper/types.js +2 -0
  48. package/dist/paper/types.js.map +1 -0
  49. package/dist/trading-core.d.ts +1 -0
  50. package/dist/trading-core.js +15 -0
  51. package/dist/trading-core.js.map +1 -1
  52. package/dist/types/config.types.d.ts +17 -0
  53. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { calcRSI, calcMACD, calcTrendScore, calcVolatility, calcAllIndicators } from '../indicators.js';
3
+ function makeCandles(closes, base = 100) {
4
+ return closes.map((close, i) => ({
5
+ timestamp: Date.now() - (closes.length - i) * 300_000,
6
+ open: close - 0.5,
7
+ high: close + 1,
8
+ low: close - 1,
9
+ close,
10
+ volume: 1000,
11
+ }));
12
+ }
13
+ describe('calcRSI', () => {
14
+ it('returns 50 when not enough data', () => {
15
+ expect(calcRSI(makeCandles([100, 101, 102]))).toBe(50);
16
+ });
17
+ it('returns high RSI for consistent uptrend', () => {
18
+ const prices = Array.from({ length: 30 }, (_, i) => 100 + i);
19
+ const rsi = calcRSI(makeCandles(prices));
20
+ expect(rsi).toBeGreaterThan(70);
21
+ });
22
+ it('returns low RSI for consistent downtrend', () => {
23
+ const prices = Array.from({ length: 30 }, (_, i) => 130 - i);
24
+ const rsi = calcRSI(makeCandles(prices));
25
+ expect(rsi).toBeLessThan(30);
26
+ });
27
+ it('returns ~50 for flat market', () => {
28
+ const prices = Array.from({ length: 30 }, (_, i) => 100 + (i % 2 === 0 ? 0.5 : -0.5));
29
+ const rsi = calcRSI(makeCandles(prices));
30
+ expect(rsi).toBeGreaterThan(30);
31
+ expect(rsi).toBeLessThan(70);
32
+ });
33
+ it('returns 100 when all gains', () => {
34
+ const prices = Array.from({ length: 20 }, (_, i) => 100 + i * 2);
35
+ const rsi = calcRSI(makeCandles(prices));
36
+ expect(rsi).toBe(100);
37
+ });
38
+ });
39
+ describe('calcMACD', () => {
40
+ it('returns zeroes when not enough data', () => {
41
+ const result = calcMACD(makeCandles([100, 101, 102]));
42
+ expect(result.line).toBe(0);
43
+ expect(result.signal).toBe(0);
44
+ expect(result.histogram).toBe(0);
45
+ });
46
+ it('returns positive MACD for uptrend', () => {
47
+ const prices = Array.from({ length: 50 }, (_, i) => 100 + i);
48
+ const result = calcMACD(makeCandles(prices));
49
+ expect(result.line).toBeGreaterThan(0);
50
+ });
51
+ it('returns negative MACD for downtrend', () => {
52
+ const prices = Array.from({ length: 50 }, (_, i) => 150 - i);
53
+ const result = calcMACD(makeCandles(prices));
54
+ expect(result.line).toBeLessThan(0);
55
+ });
56
+ it('histogram equals line minus signal', () => {
57
+ const prices = Array.from({ length: 50 }, (_, i) => 100 + Math.sin(i / 5) * 10);
58
+ const result = calcMACD(makeCandles(prices));
59
+ expect(result.histogram).toBeCloseTo(result.line - result.signal, 10);
60
+ });
61
+ });
62
+ describe('calcTrendScore', () => {
63
+ it('returns 0 when not enough data', () => {
64
+ expect(calcTrendScore(makeCandles([100, 101]))).toBe(0);
65
+ });
66
+ it('returns positive score for uptrend', () => {
67
+ const prices = Array.from({ length: 40 }, (_, i) => 100 + i * 2);
68
+ const score = calcTrendScore(makeCandles(prices));
69
+ expect(score).toBeGreaterThan(0);
70
+ });
71
+ it('returns negative score for downtrend', () => {
72
+ const prices = Array.from({ length: 40 }, (_, i) => 200 - i * 2);
73
+ const score = calcTrendScore(makeCandles(prices));
74
+ expect(score).toBeLessThan(0);
75
+ });
76
+ it('clamps to -5..+5 range', () => {
77
+ const prices = Array.from({ length: 40 }, (_, i) => 100 + i * 20);
78
+ const score = calcTrendScore(makeCandles(prices));
79
+ expect(score).toBeLessThanOrEqual(5);
80
+ expect(score).toBeGreaterThanOrEqual(-5);
81
+ });
82
+ });
83
+ describe('calcVolatility', () => {
84
+ it('returns default when not enough data', () => {
85
+ expect(calcVolatility(makeCandles([100, 101]))).toBe(30);
86
+ });
87
+ it('returns low volatility for stable prices', () => {
88
+ const prices = Array.from({ length: 20 }, () => 100);
89
+ const candles = prices.map((close, i) => ({
90
+ timestamp: Date.now() - (prices.length - i) * 300_000,
91
+ open: close,
92
+ high: close + 0.1,
93
+ low: close - 0.1,
94
+ close,
95
+ volume: 1000,
96
+ }));
97
+ const vol = calcVolatility(candles);
98
+ expect(vol).toBeLessThan(5);
99
+ });
100
+ it('returns high volatility for wild swings', () => {
101
+ const candles = Array.from({ length: 20 }, (_, i) => ({
102
+ timestamp: Date.now() - (20 - i) * 300_000,
103
+ open: 100,
104
+ high: 120,
105
+ low: 80,
106
+ close: 100,
107
+ volume: 1000,
108
+ }));
109
+ const vol = calcVolatility(candles);
110
+ expect(vol).toBeGreaterThan(20);
111
+ });
112
+ });
113
+ describe('calcAllIndicators', () => {
114
+ it('returns all four indicators', () => {
115
+ const prices = Array.from({ length: 50 }, (_, i) => 100 + i);
116
+ const result = calcAllIndicators(makeCandles(prices));
117
+ expect(result).toHaveProperty('rsi14');
118
+ expect(result).toHaveProperty('macd');
119
+ expect(result).toHaveProperty('trendScore');
120
+ expect(result).toHaveProperty('volatility');
121
+ expect(result.macd).toHaveProperty('line');
122
+ expect(result.macd).toHaveProperty('signal');
123
+ expect(result.macd).toHaveProperty('histogram');
124
+ });
125
+ });
126
+ //# sourceMappingURL=indicators.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indicators.test.js","sourceRoot":"","sources":["../../../src/paper/__tests__/indicators.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { PaperConfig, PaperPosition, IndicatorResult } from './types.js';
2
+ import type { SignalService } from '../services/signal.service.js';
3
+ export interface EntrySignal {
4
+ symbol: string;
5
+ price: number;
6
+ indicators: IndicatorResult;
7
+ fingerprint: string;
8
+ confidence: number;
9
+ score: number;
10
+ regime: string;
11
+ signalsJson: string;
12
+ }
13
+ export interface ExitSignal {
14
+ position: PaperPosition;
15
+ reason: string;
16
+ currentPrice: number;
17
+ }
18
+ export declare class DecisionEngine {
19
+ private config;
20
+ private signalService;
21
+ private logger;
22
+ private totalClosedTrades;
23
+ constructor(config: PaperConfig, signalService: SignalService);
24
+ /** Update closed trade count for cold-start logic. */
25
+ setTradeCount(count: number): void;
26
+ /**
27
+ * Detect market regime from indicators.
28
+ */
29
+ detectRegime(indicators: IndicatorResult): string;
30
+ /**
31
+ * Check all positions for exit conditions.
32
+ */
33
+ checkExits(positions: PaperPosition[], prices: Map<string, number>): ExitSignal[];
34
+ /**
35
+ * Check which symbols qualify for entry.
36
+ */
37
+ checkEntries(symbols: string[], prices: Map<string, number>, indicatorMap: Map<string, IndicatorResult>, openSymbols: Set<string>): EntrySignal[];
38
+ private getExitReason;
39
+ private shouldEnter;
40
+ private calcScore;
41
+ }
@@ -0,0 +1,151 @@
1
+ import { fingerprint } from '../signals/fingerprint.js';
2
+ import { getLogger } from '../utils/logger.js';
3
+ export class DecisionEngine {
4
+ config;
5
+ signalService;
6
+ logger = getLogger();
7
+ totalClosedTrades = 0;
8
+ constructor(config, signalService) {
9
+ this.config = config;
10
+ this.signalService = signalService;
11
+ }
12
+ /** Update closed trade count for cold-start logic. */
13
+ setTradeCount(count) {
14
+ this.totalClosedTrades = count;
15
+ }
16
+ /**
17
+ * Detect market regime from indicators.
18
+ */
19
+ detectRegime(indicators) {
20
+ if (indicators.trendScore > 2)
21
+ return 'bullish_trend';
22
+ if (indicators.trendScore < -2)
23
+ return 'bearish_trend';
24
+ if (indicators.volatility > 60)
25
+ return 'volatile';
26
+ return 'ranging';
27
+ }
28
+ /**
29
+ * Check all positions for exit conditions.
30
+ */
31
+ checkExits(positions, prices) {
32
+ const exits = [];
33
+ for (const pos of positions) {
34
+ const currentPrice = prices.get(pos.symbol);
35
+ if (currentPrice === undefined)
36
+ continue;
37
+ const pnlPct = ((currentPrice - pos.entryPrice) / pos.entryPrice) * 100;
38
+ const reason = this.getExitReason(pos, currentPrice, pnlPct);
39
+ if (reason) {
40
+ exits.push({ position: pos, reason, currentPrice });
41
+ }
42
+ }
43
+ return exits;
44
+ }
45
+ /**
46
+ * Check which symbols qualify for entry.
47
+ */
48
+ checkEntries(symbols, prices, indicatorMap, openSymbols) {
49
+ const entries = [];
50
+ for (const symbol of symbols) {
51
+ if (openSymbols.has(symbol))
52
+ continue;
53
+ const price = prices.get(symbol);
54
+ const indicators = indicatorMap.get(symbol);
55
+ if (!price || !indicators)
56
+ continue;
57
+ const regime = this.detectRegime(indicators);
58
+ const signals = {
59
+ rsi14: indicators.rsi14,
60
+ macd: indicators.macd.line,
61
+ trendScore: indicators.trendScore,
62
+ volatility: indicators.volatility,
63
+ regime,
64
+ };
65
+ const fp = fingerprint(signals);
66
+ const confidence = this.signalService.getConfidence(signals, regime);
67
+ const weights = this.signalService.getSignalWeights(signals, regime);
68
+ const score = this.calcScore(weights);
69
+ // Entry conditions
70
+ if (!this.shouldEnter(indicators, confidence, score, regime))
71
+ continue;
72
+ entries.push({
73
+ symbol,
74
+ price,
75
+ indicators,
76
+ fingerprint: fp,
77
+ confidence,
78
+ score,
79
+ regime,
80
+ signalsJson: JSON.stringify(signals),
81
+ });
82
+ }
83
+ // Sort by confidence descending, take best ones
84
+ entries.sort((a, b) => b.confidence - a.confidence);
85
+ return entries;
86
+ }
87
+ getExitReason(pos, currentPrice, pnlPct) {
88
+ // Stop-Loss
89
+ if (pnlPct <= this.config.stopLossPct) {
90
+ return 'stop_loss';
91
+ }
92
+ // Take-Profit
93
+ if (pnlPct >= this.config.takeProfitPct) {
94
+ return 'take_profit';
95
+ }
96
+ // Trailing Stop: activate at +trailingStopActivation%, trail by trailingStopDistance%
97
+ if (pnlPct >= this.config.trailingStopActivation) {
98
+ const drawdownFromHigh = ((pos.highWaterMark - currentPrice) / pos.highWaterMark) * 100;
99
+ if (drawdownFromHigh >= this.config.trailingStopDistance) {
100
+ return 'trailing_stop';
101
+ }
102
+ }
103
+ // Time Exit
104
+ const openTime = new Date(pos.openedAt).getTime();
105
+ const holdHours = (Date.now() - openTime) / 3600000;
106
+ if (holdHours > this.config.timeExitHours) {
107
+ return 'time_exit';
108
+ }
109
+ return null;
110
+ }
111
+ shouldEnter(indicators, confidence, score, regime) {
112
+ // Don't enter in bearish regime
113
+ if (regime === 'bearish_trend')
114
+ return false;
115
+ // Cold-start mode: relaxed thresholds when Brain has <20 closed trades
116
+ const coldStart = this.totalClosedTrades < 20;
117
+ const confThreshold = coldStart ? 0.0 : this.config.confidenceThreshold;
118
+ // Confidence threshold
119
+ if (confidence < confThreshold) {
120
+ const hasBullishDiv = indicators.rsi14 < 30 && indicators.macd.line > 0;
121
+ const hasStrongTrend = indicators.trendScore > 2 && indicators.volatility < 60;
122
+ if (!hasBullishDiv && !hasStrongTrend)
123
+ return false;
124
+ }
125
+ // Score threshold (skip during cold-start — no learned data yet)
126
+ if (!coldStart && score > 0 && score < this.config.scoreThreshold) {
127
+ const hasBullishDiv = indicators.rsi14 < 30 && indicators.macd.line > 0;
128
+ if (!hasBullishDiv)
129
+ return false;
130
+ }
131
+ // At least one bullish condition must be true
132
+ // During cold-start, conditions are more relaxed to collect training data
133
+ const conditions = coldStart ? [
134
+ indicators.rsi14 < 45 && indicators.macd.line > 0, // Moderate RSI + MACD positive
135
+ indicators.trendScore > 1 && indicators.volatility < 80, // Mild trend, not too volatile
136
+ indicators.rsi14 < 35 && indicators.trendScore > 0, // Low RSI + any uptrend
137
+ indicators.macd.histogram > 0 && indicators.trendScore > 0, // MACD bullish + trend
138
+ indicators.rsi14 < 50 && indicators.trendScore > 2, // Normal RSI + strong trend
139
+ ] : [
140
+ indicators.rsi14 < 30 && indicators.macd.line > 0, // Bullish divergence
141
+ indicators.trendScore > 2 && indicators.volatility < 60, // Strong trend, low vol
142
+ indicators.rsi14 < 40 && indicators.trendScore > 1, // Low RSI + uptrend
143
+ indicators.macd.histogram > 0 && indicators.trendScore > 0, // MACD bullish + trend
144
+ ];
145
+ return conditions.some(c => c);
146
+ }
147
+ calcScore(weights) {
148
+ return Object.values(weights).reduce((sum, w) => sum + w, 0);
149
+ }
150
+ }
151
+ //# sourceMappingURL=decision-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-engine.js","sourceRoot":"","sources":["../../src/paper/decision-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAoB,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAmB/C,MAAM,OAAO,cAAc;IAKf;IACA;IALF,MAAM,GAAG,SAAS,EAAE,CAAC;IACrB,iBAAiB,GAAG,CAAC,CAAC;IAE9B,YACU,MAAmB,EACnB,aAA4B;QAD5B,WAAM,GAAN,MAAM,CAAa;QACnB,kBAAa,GAAb,aAAa,CAAe;IACnC,CAAC;IAEJ,sDAAsD;IACtD,aAAa,CAAC,KAAa;QACzB,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,UAA2B;QACtC,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC;YAAE,OAAO,eAAe,CAAC;QACtD,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC;YAAE,OAAO,eAAe,CAAC;QACvD,IAAI,UAAU,CAAC,UAAU,GAAG,EAAE;YAAE,OAAO,UAAU,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,SAA0B,EAAE,MAA2B;QAChE,MAAM,KAAK,GAAiB,EAAE,CAAC;QAE/B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,YAAY,KAAK,SAAS;gBAAE,SAAS;YAEzC,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC;YACxE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;YAE7D,IAAI,MAAM,EAAE,CAAC;gBACX,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,YAAY,CACV,OAAiB,EACjB,MAA2B,EAC3B,YAA0C,EAC1C,WAAwB;QAExB,MAAM,OAAO,GAAkB,EAAE,CAAC;QAElC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,SAAS;YAEtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACjC,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU;gBAAE,SAAS;YAEpC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAgB;gBAC3B,KAAK,EAAE,UAAU,CAAC,KAAK;gBACvB,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI;gBAC1B,UAAU,EAAE,UAAU,CAAC,UAAU;gBACjC,UAAU,EAAE,UAAU,CAAC,UAAU;gBACjC,MAAM;aACP,CAAC;YAEF,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACrE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACrE,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAEtC,mBAAmB;YACnB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC;gBAAE,SAAS;YAEvE,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM;gBACN,KAAK;gBACL,UAAU;gBACV,WAAW,EAAE,EAAE;gBACf,UAAU;gBACV,KAAK;gBACL,MAAM;gBACN,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aACrC,CAAC,CAAC;QACL,CAAC;QAED,gDAAgD;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,aAAa,CAAC,GAAkB,EAAE,YAAoB,EAAE,MAAc;QAC5E,YAAY;QACZ,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACtC,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,cAAc;QACd,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YACxC,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,sFAAsF;QACtF,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAAE,CAAC;YACjD,MAAM,gBAAgB,GAAG,CAAC,CAAC,GAAG,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;YACxF,IAAI,gBAAgB,IAAI,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;gBACzD,OAAO,eAAe,CAAC;YACzB,CAAC;QACH,CAAC;QAED,YAAY;QACZ,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAClD,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC;QACpD,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1C,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,WAAW,CACjB,UAA2B,EAC3B,UAAkB,EAClB,KAAa,EACb,MAAc;QAEd,gCAAgC;QAChC,IAAI,MAAM,KAAK,eAAe;YAAE,OAAO,KAAK,CAAC;QAE7C,uEAAuE;QACvE,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAC9C,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC;QAExE,uBAAuB;QACvB,IAAI,UAAU,GAAG,aAAa,EAAE,CAAC;YAC/B,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YACxE,MAAM,cAAc,GAAG,UAAU,CAAC,UAAU,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,GAAG,EAAE,CAAC;YAC/E,IAAI,CAAC,aAAa,IAAI,CAAC,cAAc;gBAAE,OAAO,KAAK,CAAC;QACtD,CAAC;QAED,iEAAiE;QACjE,IAAI,CAAC,SAAS,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAClE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YACxE,IAAI,CAAC,aAAa;gBAAE,OAAO,KAAK,CAAC;QACnC,CAAC;QAED,8CAA8C;QAC9C,0EAA0E;QAC1E,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;YAC7B,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAe,+BAA+B;YAC/F,UAAU,CAAC,UAAU,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,GAAG,EAAE,EAAS,+BAA+B;YAC/F,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,EAAc,wBAAwB;YACxF,UAAU,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,EAAM,uBAAuB;YACvF,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,EAAc,4BAA4B;SAC7F,CAAC,CAAC,CAAC;YACF,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAe,qBAAqB;YACrF,UAAU,CAAC,UAAU,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,GAAG,EAAE,EAAS,wBAAwB;YACxF,UAAU,CAAC,KAAK,GAAG,EAAE,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,EAAc,oBAAoB;YACpF,UAAU,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,GAAG,CAAC,EAAM,uBAAuB;SACxF,CAAC;QAEF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IAEO,SAAS,CAAC,OAA+B;QAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ import type { OHLCVCandle, IndicatorResult } from './types.js';
2
+ /**
3
+ * Wilder's smoothed RSI.
4
+ */
5
+ export declare function calcRSI(candles: OHLCVCandle[], period?: number): number;
6
+ /**
7
+ * MACD (12, 26, 9): line, signal, histogram.
8
+ */
9
+ export declare function calcMACD(candles: OHLCVCandle[], fastPeriod?: number, slowPeriod?: number, signalPeriod?: number): {
10
+ line: number;
11
+ signal: number;
12
+ histogram: number;
13
+ };
14
+ /**
15
+ * Trend score: SMA(10) vs SMA(30) crossover.
16
+ * Returns -5 to +5 range based on distance.
17
+ */
18
+ export declare function calcTrendScore(candles: OHLCVCandle[]): number;
19
+ /**
20
+ * ATR-based volatility as percentage of close price.
21
+ */
22
+ export declare function calcVolatility(candles: OHLCVCandle[], period?: number): number;
23
+ /**
24
+ * Calculate all indicators for a set of candles.
25
+ */
26
+ export declare function calcAllIndicators(candles: OHLCVCandle[]): IndicatorResult;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Wilder's smoothed RSI.
3
+ */
4
+ export function calcRSI(candles, period = 14) {
5
+ if (candles.length < period + 1)
6
+ return 50;
7
+ let avgGain = 0;
8
+ let avgLoss = 0;
9
+ // Initial average
10
+ for (let i = 1; i <= period; i++) {
11
+ const change = candles[i].close - candles[i - 1].close;
12
+ if (change > 0)
13
+ avgGain += change;
14
+ else
15
+ avgLoss += Math.abs(change);
16
+ }
17
+ avgGain /= period;
18
+ avgLoss /= period;
19
+ // Wilder smoothing
20
+ for (let i = period + 1; i < candles.length; i++) {
21
+ const change = candles[i].close - candles[i - 1].close;
22
+ if (change > 0) {
23
+ avgGain = (avgGain * (period - 1) + change) / period;
24
+ avgLoss = (avgLoss * (period - 1)) / period;
25
+ }
26
+ else {
27
+ avgGain = (avgGain * (period - 1)) / period;
28
+ avgLoss = (avgLoss * (period - 1) + Math.abs(change)) / period;
29
+ }
30
+ }
31
+ if (avgLoss === 0)
32
+ return 100;
33
+ const rs = avgGain / avgLoss;
34
+ return 100 - 100 / (1 + rs);
35
+ }
36
+ /**
37
+ * MACD (12, 26, 9): line, signal, histogram.
38
+ */
39
+ export function calcMACD(candles, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
40
+ if (candles.length < slowPeriod + signalPeriod) {
41
+ return { line: 0, signal: 0, histogram: 0 };
42
+ }
43
+ const closes = candles.map(c => c.close);
44
+ const emaFast = calcEMA(closes, fastPeriod);
45
+ const emaSlow = calcEMA(closes, slowPeriod);
46
+ const macdLine = [];
47
+ const startIdx = slowPeriod - 1;
48
+ for (let i = startIdx; i < closes.length; i++) {
49
+ const fastIdx = i - (closes.length - emaFast.length);
50
+ const slowIdx = i - (closes.length - emaSlow.length);
51
+ if (fastIdx >= 0 && slowIdx >= 0) {
52
+ macdLine.push(emaFast[fastIdx] - emaSlow[slowIdx]);
53
+ }
54
+ }
55
+ if (macdLine.length < signalPeriod) {
56
+ return { line: macdLine[macdLine.length - 1] ?? 0, signal: 0, histogram: 0 };
57
+ }
58
+ const signalLine = calcEMA(macdLine, signalPeriod);
59
+ const line = macdLine[macdLine.length - 1];
60
+ const signal = signalLine[signalLine.length - 1];
61
+ return { line, signal, histogram: line - signal };
62
+ }
63
+ /**
64
+ * Trend score: SMA(10) vs SMA(30) crossover.
65
+ * Returns -5 to +5 range based on distance.
66
+ */
67
+ export function calcTrendScore(candles) {
68
+ if (candles.length < 30)
69
+ return 0;
70
+ const closes = candles.map(c => c.close);
71
+ const sma10 = calcSMA(closes, 10);
72
+ const sma30 = calcSMA(closes, 30);
73
+ if (sma30 === 0)
74
+ return 0;
75
+ const diff = ((sma10 - sma30) / sma30) * 100;
76
+ // Clamp to -5..+5
77
+ return Math.max(-5, Math.min(5, diff));
78
+ }
79
+ /**
80
+ * ATR-based volatility as percentage of close price.
81
+ */
82
+ export function calcVolatility(candles, period = 14) {
83
+ if (candles.length < period + 1)
84
+ return 30;
85
+ let atrSum = 0;
86
+ for (let i = candles.length - period; i < candles.length; i++) {
87
+ const current = candles[i];
88
+ const prev = candles[i - 1];
89
+ const tr = Math.max(current.high - current.low, Math.abs(current.high - prev.close), Math.abs(current.low - prev.close));
90
+ atrSum += tr;
91
+ }
92
+ const atr = atrSum / period;
93
+ const lastClose = candles[candles.length - 1].close;
94
+ if (lastClose === 0)
95
+ return 30;
96
+ return (atr / lastClose) * 100;
97
+ }
98
+ /**
99
+ * Calculate all indicators for a set of candles.
100
+ */
101
+ export function calcAllIndicators(candles) {
102
+ return {
103
+ rsi14: calcRSI(candles, 14),
104
+ macd: calcMACD(candles),
105
+ trendScore: calcTrendScore(candles),
106
+ volatility: calcVolatility(candles),
107
+ };
108
+ }
109
+ // ─── Helpers ────────────────────────────────────────────
110
+ function calcEMA(values, period) {
111
+ if (values.length < period)
112
+ return [];
113
+ const k = 2 / (period + 1);
114
+ const result = [];
115
+ // Seed with SMA
116
+ let sum = 0;
117
+ for (let i = 0; i < period; i++)
118
+ sum += values[i];
119
+ result.push(sum / period);
120
+ for (let i = period; i < values.length; i++) {
121
+ result.push(values[i] * k + result[result.length - 1] * (1 - k));
122
+ }
123
+ return result;
124
+ }
125
+ function calcSMA(values, period) {
126
+ if (values.length < period)
127
+ return 0;
128
+ let sum = 0;
129
+ for (let i = values.length - period; i < values.length; i++) {
130
+ sum += values[i];
131
+ }
132
+ return sum / period;
133
+ }
134
+ //# sourceMappingURL=indicators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indicators.js","sourceRoot":"","sources":["../../src/paper/indicators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { PaperConfig } from './types.js';
2
+ import type { TradeService } from '../services/trade.service.js';
3
+ import type { SignalService } from '../services/signal.service.js';
4
+ import type { PaperRepository } from '../db/repositories/paper.repository.js';
5
+ export declare class PaperEngine {
6
+ private config;
7
+ private tradeService;
8
+ private repo;
9
+ private priceFetcher;
10
+ private portfolio;
11
+ private decision;
12
+ private timer;
13
+ private cycleCount;
14
+ private lastCycleAt;
15
+ private paused;
16
+ private running;
17
+ private logger;
18
+ constructor(config: PaperConfig, tradeService: TradeService, signalService: SignalService, repo: PaperRepository);
19
+ start(): void;
20
+ stop(): void;
21
+ pause(): void;
22
+ resume(): void;
23
+ isPaused(): boolean;
24
+ isRunning(): boolean;
25
+ getCycleCount(): number;
26
+ getLastCycleAt(): string | null;
27
+ runCycle(): Promise<{
28
+ entries: number;
29
+ exits: number;
30
+ }>;
31
+ }