powerball-quantum 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 +80 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +76 -0
- package/dist/data.d.ts +17 -0
- package/dist/data.js +116 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +110 -0
- package/dist/quantum.d.ts +45 -0
- package/dist/quantum.js +239 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.js +6 -0
- package/package.json +41 -0
- package/powerball_data.csv +1880 -0
- package/src/cli.ts +82 -0
- package/src/data.ts +83 -0
- package/src/index.ts +142 -0
- package/src/quantum.ts +277 -0
- package/src/types.ts +25 -0
- package/tsconfig.json +17 -0
package/dist/quantum.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateMomentum = calculateMomentum;
|
|
4
|
+
exports.calculateZScores = calculateZScores;
|
|
5
|
+
exports.calculateRecentMomentum = calculateRecentMomentum;
|
|
6
|
+
exports.analyzePairFrequency = analyzePairFrequency;
|
|
7
|
+
exports.weightedSample = weightedSample;
|
|
8
|
+
exports.quantumSelect = quantumSelect;
|
|
9
|
+
exports.checkSumRange = checkSumRange;
|
|
10
|
+
exports.checkOddEvenRatio = checkOddEvenRatio;
|
|
11
|
+
exports.checkHighLowBalance = checkHighLowBalance;
|
|
12
|
+
exports.checkDecadeBalance = checkDecadeBalance;
|
|
13
|
+
exports.checkEndingDiversity = checkEndingDiversity;
|
|
14
|
+
exports.checkNoTripleConsecutive = checkNoTripleConsecutive;
|
|
15
|
+
exports.isInHistory = isInHistory;
|
|
16
|
+
exports.buildHistorySet = buildHistorySet;
|
|
17
|
+
const types_1 = require("./types");
|
|
18
|
+
/**
|
|
19
|
+
* Calculate exponential decay momentum scores
|
|
20
|
+
*/
|
|
21
|
+
function calculateMomentum(draws, decayAlpha = 0.03) {
|
|
22
|
+
const whiteScores = {};
|
|
23
|
+
const redScores = {};
|
|
24
|
+
// Initialize scores
|
|
25
|
+
for (let n = types_1.WHITE_BALL_RANGE.min; n <= types_1.WHITE_BALL_RANGE.max; n++) {
|
|
26
|
+
whiteScores[n] = 0.1;
|
|
27
|
+
}
|
|
28
|
+
for (let n = types_1.RED_BALL_RANGE.min; n <= types_1.RED_BALL_RANGE.max; n++) {
|
|
29
|
+
redScores[n] = 0.1;
|
|
30
|
+
}
|
|
31
|
+
// Sort by date ascending
|
|
32
|
+
const sorted = [...draws].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
33
|
+
const total = sorted.length;
|
|
34
|
+
sorted.forEach((draw, idx) => {
|
|
35
|
+
const t = total - 1 - idx; // t=0 for most recent
|
|
36
|
+
const weight = Math.exp(-decayAlpha * t);
|
|
37
|
+
draw.whiteBalls.forEach(w => {
|
|
38
|
+
if (whiteScores[w] !== undefined) {
|
|
39
|
+
whiteScores[w] += weight;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (redScores[draw.powerball] !== undefined) {
|
|
43
|
+
redScores[draw.powerball] += weight;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return { white: whiteScores, red: redScores };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Calculate Z-scores for gap analysis (mean reversion)
|
|
50
|
+
*/
|
|
51
|
+
function calculateZScores(draws) {
|
|
52
|
+
const whiteGaps = {};
|
|
53
|
+
const redGaps = {};
|
|
54
|
+
const lastSeenWhite = {};
|
|
55
|
+
const lastSeenRed = {};
|
|
56
|
+
// Initialize
|
|
57
|
+
for (let n = types_1.WHITE_BALL_RANGE.min; n <= types_1.WHITE_BALL_RANGE.max; n++) {
|
|
58
|
+
whiteGaps[n] = [];
|
|
59
|
+
lastSeenWhite[n] = -1;
|
|
60
|
+
}
|
|
61
|
+
for (let n = types_1.RED_BALL_RANGE.min; n <= types_1.RED_BALL_RANGE.max; n++) {
|
|
62
|
+
redGaps[n] = [];
|
|
63
|
+
lastSeenRed[n] = -1;
|
|
64
|
+
}
|
|
65
|
+
const sorted = [...draws].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
66
|
+
sorted.forEach((draw, idx) => {
|
|
67
|
+
// White balls
|
|
68
|
+
draw.whiteBalls.forEach(w => {
|
|
69
|
+
if (lastSeenWhite[w] !== -1) {
|
|
70
|
+
whiteGaps[w].push(idx - lastSeenWhite[w]);
|
|
71
|
+
}
|
|
72
|
+
lastSeenWhite[w] = idx;
|
|
73
|
+
});
|
|
74
|
+
// Red ball
|
|
75
|
+
if (lastSeenRed[draw.powerball] !== -1) {
|
|
76
|
+
redGaps[draw.powerball].push(idx - lastSeenRed[draw.powerball]);
|
|
77
|
+
}
|
|
78
|
+
lastSeenRed[draw.powerball] = idx;
|
|
79
|
+
});
|
|
80
|
+
const total = sorted.length;
|
|
81
|
+
const whiteZ = {};
|
|
82
|
+
const redZ = {};
|
|
83
|
+
// Calculate Z-scores
|
|
84
|
+
for (let n = types_1.WHITE_BALL_RANGE.min; n <= types_1.WHITE_BALL_RANGE.max; n++) {
|
|
85
|
+
const currentGap = total - 1 - lastSeenWhite[n];
|
|
86
|
+
const gaps = whiteGaps[n];
|
|
87
|
+
if (gaps.length === 0) {
|
|
88
|
+
whiteZ[n] = 0;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const mean = gaps.reduce((a, b) => a + b, 0) / gaps.length;
|
|
92
|
+
const variance = gaps.reduce((sum, g) => sum + Math.pow(g - mean, 2), 0) / gaps.length;
|
|
93
|
+
const stdev = Math.sqrt(variance) || 1;
|
|
94
|
+
whiteZ[n] = (currentGap - mean) / stdev;
|
|
95
|
+
}
|
|
96
|
+
for (let n = types_1.RED_BALL_RANGE.min; n <= types_1.RED_BALL_RANGE.max; n++) {
|
|
97
|
+
const currentGap = total - 1 - lastSeenRed[n];
|
|
98
|
+
const gaps = redGaps[n];
|
|
99
|
+
if (gaps.length === 0) {
|
|
100
|
+
redZ[n] = 0;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const mean = gaps.reduce((a, b) => a + b, 0) / gaps.length;
|
|
104
|
+
const variance = gaps.reduce((sum, g) => sum + Math.pow(g - mean, 2), 0) / gaps.length;
|
|
105
|
+
const stdev = Math.sqrt(variance) || 1;
|
|
106
|
+
redZ[n] = (currentGap - mean) / stdev;
|
|
107
|
+
}
|
|
108
|
+
return { white: whiteZ, red: redZ };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Calculate recent momentum (last N draws)
|
|
112
|
+
*/
|
|
113
|
+
function calculateRecentMomentum(draws, nRecent = 15, decay = 0.15) {
|
|
114
|
+
const whiteScores = {};
|
|
115
|
+
const redScores = {};
|
|
116
|
+
for (let n = types_1.WHITE_BALL_RANGE.min; n <= types_1.WHITE_BALL_RANGE.max; n++) {
|
|
117
|
+
whiteScores[n] = 0.1;
|
|
118
|
+
}
|
|
119
|
+
for (let n = types_1.RED_BALL_RANGE.min; n <= types_1.RED_BALL_RANGE.max; n++) {
|
|
120
|
+
redScores[n] = 0.1;
|
|
121
|
+
}
|
|
122
|
+
const sorted = [...draws].sort((a, b) => b.date.getTime() - a.date.getTime()).slice(0, nRecent);
|
|
123
|
+
sorted.forEach((draw, idx) => {
|
|
124
|
+
const weight = Math.exp(-decay * idx) * 2;
|
|
125
|
+
draw.whiteBalls.forEach(w => {
|
|
126
|
+
if (whiteScores[w] !== undefined) {
|
|
127
|
+
whiteScores[w] += weight;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
if (redScores[draw.powerball] !== undefined) {
|
|
131
|
+
redScores[draw.powerball] += weight;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
return { white: whiteScores, red: redScores };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Analyze pair frequency
|
|
138
|
+
*/
|
|
139
|
+
function analyzePairFrequency(draws, topN = 50) {
|
|
140
|
+
const pairCounts = new Map();
|
|
141
|
+
draws.forEach(draw => {
|
|
142
|
+
const whites = [...draw.whiteBalls].sort((a, b) => a - b);
|
|
143
|
+
for (let i = 0; i < whites.length; i++) {
|
|
144
|
+
for (let j = i + 1; j < whites.length; j++) {
|
|
145
|
+
const key = `${whites[i]}-${whites[j]}`;
|
|
146
|
+
pairCounts.set(key, (pairCounts.get(key) || 0) + 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
const pairBonus = {};
|
|
151
|
+
const sorted = [...pairCounts.entries()]
|
|
152
|
+
.sort((a, b) => b[1] - a[1])
|
|
153
|
+
.slice(0, topN);
|
|
154
|
+
sorted.forEach(([pair, count]) => {
|
|
155
|
+
const [a, b] = pair.split('-').map(Number);
|
|
156
|
+
pairBonus[a] = (pairBonus[a] || 0) + count * 0.1;
|
|
157
|
+
pairBonus[b] = (pairBonus[b] || 0) + count * 0.1;
|
|
158
|
+
});
|
|
159
|
+
return pairBonus;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Weighted random selection
|
|
163
|
+
*/
|
|
164
|
+
function weightedSample(candidates, weights, k) {
|
|
165
|
+
const selected = new Set();
|
|
166
|
+
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
167
|
+
while (selected.size < k) {
|
|
168
|
+
let r = Math.random() * totalWeight;
|
|
169
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
170
|
+
r -= weights[i];
|
|
171
|
+
if (r <= 0) {
|
|
172
|
+
selected.add(candidates[i]);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return [...selected];
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Quantum selector combining all signals
|
|
181
|
+
*/
|
|
182
|
+
function quantumSelect(momentum, zScores, recentMomentum, pairBonus, nPicks) {
|
|
183
|
+
const candidates = Object.keys(momentum).map(Number);
|
|
184
|
+
const weights = candidates.map(n => {
|
|
185
|
+
const base = momentum[n] || 0;
|
|
186
|
+
const zBoost = Math.max(0, zScores[n] || 0) * 5;
|
|
187
|
+
const recentBoost = (recentMomentum[n] || 0) * 1.5;
|
|
188
|
+
const pairB = (pairBonus[n] || 0) * 0.5;
|
|
189
|
+
return Math.max(0.1, base + zBoost + recentBoost + pairB);
|
|
190
|
+
});
|
|
191
|
+
return weightedSample(candidates, weights, nPicks);
|
|
192
|
+
}
|
|
193
|
+
// --- FILTERS ---
|
|
194
|
+
function checkSumRange(whiteBalls, min = 130, max = 220) {
|
|
195
|
+
const sum = whiteBalls.reduce((a, b) => a + b, 0);
|
|
196
|
+
return sum >= min && sum <= max;
|
|
197
|
+
}
|
|
198
|
+
function checkOddEvenRatio(whiteBalls) {
|
|
199
|
+
const odds = whiteBalls.filter(x => x % 2 !== 0).length;
|
|
200
|
+
const evens = 5 - odds;
|
|
201
|
+
return (odds === 3 && evens === 2) || (odds === 2 && evens === 3);
|
|
202
|
+
}
|
|
203
|
+
function checkHighLowBalance(whiteBalls, threshold = 35) {
|
|
204
|
+
const lows = whiteBalls.filter(x => x < threshold).length;
|
|
205
|
+
const highs = 5 - lows;
|
|
206
|
+
return (lows === 3 && highs === 2) || (lows === 2 && highs === 3);
|
|
207
|
+
}
|
|
208
|
+
function checkDecadeBalance(whiteBalls) {
|
|
209
|
+
const decades = new Set(whiteBalls.map(n => Math.floor(n / 10)));
|
|
210
|
+
return decades.size >= 3;
|
|
211
|
+
}
|
|
212
|
+
function checkEndingDiversity(whiteBalls) {
|
|
213
|
+
const endings = new Set(whiteBalls.map(n => n % 10));
|
|
214
|
+
return endings.size >= 4;
|
|
215
|
+
}
|
|
216
|
+
function checkNoTripleConsecutive(whiteBalls) {
|
|
217
|
+
const sorted = [...whiteBalls].sort((a, b) => a - b);
|
|
218
|
+
const diffs = [];
|
|
219
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
220
|
+
diffs.push(sorted[i + 1] - sorted[i]);
|
|
221
|
+
}
|
|
222
|
+
const consecutiveOnes = diffs.filter(d => d === 1).length;
|
|
223
|
+
return consecutiveOnes < 2;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if combination exists in history
|
|
227
|
+
*/
|
|
228
|
+
function isInHistory(whiteBalls, powerball, history) {
|
|
229
|
+
const key = [...whiteBalls].sort((a, b) => a - b).join(',') + '-' + powerball;
|
|
230
|
+
return history.has(key);
|
|
231
|
+
}
|
|
232
|
+
function buildHistorySet(draws) {
|
|
233
|
+
const history = new Set();
|
|
234
|
+
draws.forEach(draw => {
|
|
235
|
+
const key = [...draw.whiteBalls].sort((a, b) => a - b).join(',') + '-' + draw.powerball;
|
|
236
|
+
history.add(key);
|
|
237
|
+
});
|
|
238
|
+
return history;
|
|
239
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface Draw {
|
|
2
|
+
date: Date;
|
|
3
|
+
whiteBalls: number[];
|
|
4
|
+
powerball: number;
|
|
5
|
+
multiplier: number;
|
|
6
|
+
}
|
|
7
|
+
export interface Pick {
|
|
8
|
+
whiteBalls: number[];
|
|
9
|
+
powerball: number;
|
|
10
|
+
score?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Scores {
|
|
13
|
+
[key: number]: number;
|
|
14
|
+
}
|
|
15
|
+
export interface PredictOptions {
|
|
16
|
+
count?: number;
|
|
17
|
+
showAnalysis?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare const WHITE_BALL_RANGE: {
|
|
20
|
+
min: number;
|
|
21
|
+
max: number;
|
|
22
|
+
};
|
|
23
|
+
export declare const RED_BALL_RANGE: {
|
|
24
|
+
min: number;
|
|
25
|
+
max: number;
|
|
26
|
+
};
|
|
27
|
+
export declare const DATA_URL = "https://data.ny.gov/api/views/d6yy-54nr/rows.csv?accessType=DOWNLOAD";
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DATA_URL = exports.RED_BALL_RANGE = exports.WHITE_BALL_RANGE = void 0;
|
|
4
|
+
exports.WHITE_BALL_RANGE = { min: 1, max: 69 };
|
|
5
|
+
exports.RED_BALL_RANGE = { min: 1, max: 26 };
|
|
6
|
+
exports.DATA_URL = "https://data.ny.gov/api/views/d6yy-54nr/rows.csv?accessType=DOWNLOAD";
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powerball-quantum",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Powerball number predictor using quantum-inspired algorithm with momentum, mean reversion, and statistical filters",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"powerball-quantum": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"predict": "node dist/cli.js predict",
|
|
13
|
+
"update": "node dist/cli.js update",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"powerball",
|
|
18
|
+
"lottery",
|
|
19
|
+
"predictor",
|
|
20
|
+
"quantum",
|
|
21
|
+
"statistics",
|
|
22
|
+
"probability"
|
|
23
|
+
],
|
|
24
|
+
"author": "seochan99",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/seochan99/Powerball-Predictor-Quantum"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"axios": "^1.6.0",
|
|
32
|
+
"commander": "^11.1.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.10.0",
|
|
36
|
+
"typescript": "^5.3.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=16.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|