@ebowwa/quant-rust 0.1.0 → 0.1.1
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/dist/index.js +34 -0
- package/dist/src/index.d.ts +57 -0
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ffi.rs +151 -0
- package/src/index.ts +99 -0
- package/native/README.md +0 -62
- package/native/darwin-arm64/libquant_rust.dylib +0 -0
package/dist/index.js
CHANGED
|
@@ -126,6 +126,14 @@ var symbols = {
|
|
|
126
126
|
returns: FFIType.cstring,
|
|
127
127
|
args: [FFIType.f64, FFIType.f64]
|
|
128
128
|
},
|
|
129
|
+
quant_kelly_criterion_batch: {
|
|
130
|
+
returns: FFIType.cstring,
|
|
131
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.usize, FFIType.f64]
|
|
132
|
+
},
|
|
133
|
+
quant_detect_arbitrage_batch: {
|
|
134
|
+
returns: FFIType.cstring,
|
|
135
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.usize]
|
|
136
|
+
},
|
|
129
137
|
quant_convert_odds: {
|
|
130
138
|
returns: FFIType.cstring,
|
|
131
139
|
args: [FFIType.f64, FFIType.i32]
|
|
@@ -231,12 +239,30 @@ function kellyCriterion(yourProbability, marketPrice, bankroll) {
|
|
|
231
239
|
return parseJsonResponse(response);
|
|
232
240
|
}
|
|
233
241
|
var kelly_criterion = kellyCriterion;
|
|
242
|
+
function kellyCriterionBatch(bets, bankroll) {
|
|
243
|
+
if (bets.length === 0)
|
|
244
|
+
return [];
|
|
245
|
+
const probs = new Float64Array(bets.map((b) => b.prob));
|
|
246
|
+
const prices = new Float64Array(bets.map((b) => b.price));
|
|
247
|
+
const response = lib.symbols.quant_kelly_criterion_batch(ptr(probs), ptr(prices), bets.length, bankroll);
|
|
248
|
+
return parseJsonResponse(response);
|
|
249
|
+
}
|
|
250
|
+
var kelly_criterion_batch = kellyCriterionBatch;
|
|
234
251
|
function detectArbitrage(yesPrice, noPrice) {
|
|
235
252
|
const response = lib.symbols.quant_detect_arbitrage(yesPrice, noPrice);
|
|
236
253
|
const result = parseJsonResponse(response);
|
|
237
254
|
return { ...result, profit: result.profit_per_share };
|
|
238
255
|
}
|
|
239
256
|
var detect_arbitrage = detectArbitrage;
|
|
257
|
+
function detectArbitrageBatch(pairs) {
|
|
258
|
+
if (pairs.length === 0)
|
|
259
|
+
return [];
|
|
260
|
+
const yesPrices = new Float64Array(pairs.map((p) => p.yesPrice));
|
|
261
|
+
const noPrices = new Float64Array(pairs.map((p) => p.noPrice));
|
|
262
|
+
const response = lib.symbols.quant_detect_arbitrage_batch(ptr(yesPrices), ptr(noPrices), pairs.length);
|
|
263
|
+
return parseJsonResponse(response);
|
|
264
|
+
}
|
|
265
|
+
var detect_arbitrage_batch = detectArbitrageBatch;
|
|
240
266
|
function convertOdds(value, fromType, toType) {
|
|
241
267
|
const typeMap = {
|
|
242
268
|
probability: 0,
|
|
@@ -500,8 +526,12 @@ var src_default = {
|
|
|
500
526
|
lmsrCalculate,
|
|
501
527
|
kellyCriterion,
|
|
502
528
|
kelly_criterion,
|
|
529
|
+
kellyCriterionBatch,
|
|
530
|
+
kelly_criterion_batch,
|
|
503
531
|
detectArbitrage,
|
|
504
532
|
detect_arbitrage,
|
|
533
|
+
detectArbitrageBatch,
|
|
534
|
+
detect_arbitrage_batch,
|
|
505
535
|
convertOdds,
|
|
506
536
|
convert_odds,
|
|
507
537
|
mean,
|
|
@@ -539,12 +569,16 @@ export {
|
|
|
539
569
|
lmsrPrice,
|
|
540
570
|
lmsrCost,
|
|
541
571
|
lmsrCalculate,
|
|
572
|
+
kelly_criterion_batch,
|
|
542
573
|
kelly_criterion,
|
|
574
|
+
kellyCriterionBatch,
|
|
543
575
|
kellyCriterion,
|
|
544
576
|
getVersion,
|
|
545
577
|
getLibraryPath,
|
|
546
578
|
ema,
|
|
579
|
+
detect_arbitrage_batch,
|
|
547
580
|
detect_arbitrage,
|
|
581
|
+
detectArbitrageBatch,
|
|
548
582
|
detectArbitrage,
|
|
549
583
|
src_default as default,
|
|
550
584
|
createOHLCV,
|
package/dist/src/index.d.ts
CHANGED
|
@@ -124,6 +124,32 @@ export declare function lmsrCalculate(yesShares: number, noShares: number, liqui
|
|
|
124
124
|
*/
|
|
125
125
|
export declare function kellyCriterion(yourProbability: number, marketPrice: number, bankroll: number): KellyResult;
|
|
126
126
|
export declare const kelly_criterion: typeof kellyCriterion;
|
|
127
|
+
/**
|
|
128
|
+
* Calculate Kelly criterion for multiple bets at once (batch operation)
|
|
129
|
+
*
|
|
130
|
+
* This is significantly more efficient than calling kellyCriterion multiple times
|
|
131
|
+
* because it only crosses the FFI boundary once, eliminating FFI call overhead.
|
|
132
|
+
*
|
|
133
|
+
* @param bets - Array of betting opportunities with prob and price
|
|
134
|
+
* @param bankroll - Total bankroll in currency units (shared across all bets)
|
|
135
|
+
* @returns Array of Kelly criterion results for each bet
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* const bets = [
|
|
140
|
+
* { prob: 0.6, price: 0.5 },
|
|
141
|
+
* { prob: 0.55, price: 0.45 },
|
|
142
|
+
* { prob: 0.7, price: 0.6 },
|
|
143
|
+
* ];
|
|
144
|
+
* const results = kellyCriterionBatch(bets, 1000);
|
|
145
|
+
* // results[0].full_bet_size, results[1].half_bet_size, etc.
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export declare function kellyCriterionBatch(bets: Array<{
|
|
149
|
+
prob: number;
|
|
150
|
+
price: number;
|
|
151
|
+
}>, bankroll: number): KellyResult[];
|
|
152
|
+
export declare const kelly_criterion_batch: typeof kellyCriterionBatch;
|
|
127
153
|
/**
|
|
128
154
|
* Detect arbitrage opportunities in prediction markets
|
|
129
155
|
*
|
|
@@ -137,6 +163,33 @@ export declare function detectArbitrage(yesPrice: number, noPrice: number): Arbi
|
|
|
137
163
|
profit: number;
|
|
138
164
|
};
|
|
139
165
|
export declare const detect_arbitrage: typeof detectArbitrage;
|
|
166
|
+
/**
|
|
167
|
+
* Detect arbitrage opportunities for multiple price pairs at once (batch operation)
|
|
168
|
+
*
|
|
169
|
+
* This is significantly more efficient than calling detectArbitrage multiple times
|
|
170
|
+
* because it only crosses the FFI boundary once, eliminating FFI call overhead.
|
|
171
|
+
*
|
|
172
|
+
* @param pairs - Array of YES/NO price pairs to check for arbitrage
|
|
173
|
+
* @returns Array of arbitrage results with profit field for each pair
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const pairs = [
|
|
178
|
+
* { yesPrice: 0.45, noPrice: 0.45 }, // 10% arb opportunity
|
|
179
|
+
* { yesPrice: 0.50, noPrice: 0.50 }, // no arb
|
|
180
|
+
* { yesPrice: 0.40, noPrice: 0.40 }, // 20% arb opportunity
|
|
181
|
+
* ];
|
|
182
|
+
* const results = detectArbitrageBatch(pairs);
|
|
183
|
+
* // results[0].profit = 0.10, results[1].profit = 0, results[2].profit = 0.20
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export declare function detectArbitrageBatch(pairs: Array<{
|
|
187
|
+
yesPrice: number;
|
|
188
|
+
noPrice: number;
|
|
189
|
+
}>): Array<ArbitrageResult & {
|
|
190
|
+
profit: number;
|
|
191
|
+
}>;
|
|
192
|
+
export declare const detect_arbitrage_batch: typeof detectArbitrageBatch;
|
|
140
193
|
/** Odds input type */
|
|
141
194
|
export type OddsType = "probability" | "decimal" | "american";
|
|
142
195
|
/**
|
|
@@ -294,8 +347,12 @@ declare const _default: {
|
|
|
294
347
|
lmsrCalculate: typeof lmsrCalculate;
|
|
295
348
|
kellyCriterion: typeof kellyCriterion;
|
|
296
349
|
kelly_criterion: typeof kellyCriterion;
|
|
350
|
+
kellyCriterionBatch: typeof kellyCriterionBatch;
|
|
351
|
+
kelly_criterion_batch: typeof kellyCriterionBatch;
|
|
297
352
|
detectArbitrage: typeof detectArbitrage;
|
|
298
353
|
detect_arbitrage: typeof detectArbitrage;
|
|
354
|
+
detectArbitrageBatch: typeof detectArbitrageBatch;
|
|
355
|
+
detect_arbitrage_batch: typeof detectArbitrageBatch;
|
|
299
356
|
convertOdds: typeof convertOdds;
|
|
300
357
|
convert_odds: typeof convertOdds;
|
|
301
358
|
mean: typeof mean;
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EACV,KAAK,EACL,QAAQ,EACR,aAAa,EACb,oBAAoB,EAEpB,eAAe,EACf,WAAW,EACX,eAAe,EACf,cAAc,EACd,SAAS,EACT,cAAc,EACd,YAAY,EACb,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EACV,KAAK,EACL,QAAQ,EACR,aAAa,EACb,oBAAoB,EAEpB,eAAe,EACf,WAAW,EACX,eAAe,EACf,cAAc,EACd,SAAS,EACT,cAAc,EACd,YAAY,EACb,MAAM,mBAAmB,CAAC;AAgM3B;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,KAAK,CAUP;AAMD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAGhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,EAC/B,MAAM,EAAE,MAAM,GACb,MAAM,CAaR;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,EAC/B,MAAM,EAAE,MAAM,GACb,aAAa,CAcf;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,EAC/B,MAAM,EAAE,MAAM,GACb,oBAAoB,CAStB;AAGD,eAAO,MAAM,YAAY,yBAAmB,CAAC;AAC7C,eAAO,MAAM,kBAAkB,yBAAmB,CAAC;AACnD,eAAO,MAAM,gBAAgB,uBAAiB,CAAC;AAM/C;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,CAAC,EAAE,MAAM,GACR,eAAe,CAGjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,EAC/B,MAAM,EAAE,MAAM,GACb,aAAa,CAUf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,OAAO,GAAG,MAAM,EAC3B,OAAO,EAAE,KAAK,GAAG,IAAI,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,eAAe,GAAG,aAAa,CASjC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,WAAW,CAOb;AAGD,eAAO,MAAM,eAAe,uBAAiB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,QAAQ,EAAE,MAAM,GACf,WAAW,EAAE,CAcf;AAGD,eAAO,MAAM,qBAAqB,4BAAsB,CAAC;AAMzD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAKvG;AAGD,eAAO,MAAM,gBAAgB,wBAAkB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,KAAK,CAAC,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAa7C;AAGD,eAAO,MAAM,sBAAsB,6BAAuB,CAAC;AAM3D,sBAAsB;AACtB,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,GAAG,MAAM,CAwBzG;AAGD,eAAO,MAAM,YAAY,oBAAc,CAAC;AAMxC;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAI3C;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAI7C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAI/C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAK5D;AAGD,eAAO,MAAM,OAAO,eAAS,CAAC;AAM9B;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAY9D;AAED;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB9D;AAED;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,MAAW,GAAG,MAAM,EAAE,CAyCnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAClB,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,GAAE,MAAW,EACvB,UAAU,GAAE,MAAW,EACvB,YAAY,GAAE,MAAU,GACvB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAmC3D;AAMD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EAAE,EACjB,eAAe,GAAE,MAAa,GAC7B,SAAS,CAkBX;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,cAAc,CAyCvE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,GAAE,MAAa,EAC3B,cAAc,GAAE,MAAY,GAC3B,MAAM,CAmBR;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,GAAE,MAAa,EAC3B,cAAc,GAAE,MAAY,GAC3B,YAAY,CAyBd;AAGD,eAAO,MAAM,aAAa,qBAAe,CAAC;AAC1C,eAAO,MAAM,kBAAkB,0BAAoB,CAAC;AACpD,eAAO,MAAM,sBAAsB,6BAAuB,CAAC;AAC3D,eAAO,MAAM,WAAW,6BAAuB,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,GAAE,MAAa,EAC3B,cAAc,GAAE,MAAY,GAC3B,MAAM,CAkBR;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EAAE,EACtB,gBAAgB,EAAE,MAAM,EAAE,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA+BjC;AAGD,eAAO,MAAM,uBAAuB,8BAAwB,CAAC;AAC7D,eAAO,MAAM,oBAAoB,2BAAqB,CAAC;AAMvD,cAAc,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMlC,wBAiEE"}
|
package/package.json
CHANGED
package/src/ffi.rs
CHANGED
|
@@ -301,6 +301,71 @@ pub extern "C" fn quant_kelly_criterion(
|
|
|
301
301
|
// Arbitrage Detection
|
|
302
302
|
// ============================================================================
|
|
303
303
|
|
|
304
|
+
/// Detect arbitrage opportunities for multiple price pairs at once (batch operation)
|
|
305
|
+
/// This is much more efficient than calling quant_detect_arbitrage multiple times
|
|
306
|
+
/// because it only crosses the FFI boundary once.
|
|
307
|
+
///
|
|
308
|
+
/// # Arguments
|
|
309
|
+
/// * `yes_prices` - Pointer to array of YES prices
|
|
310
|
+
/// * `no_prices` - Pointer to array of NO prices
|
|
311
|
+
/// * `count` - Number of pairs (length of both arrays)
|
|
312
|
+
///
|
|
313
|
+
/// # Returns
|
|
314
|
+
/// JSON array of arbitrage results with profit field
|
|
315
|
+
#[no_mangle]
|
|
316
|
+
pub extern "C" fn quant_detect_arbitrage_batch(
|
|
317
|
+
yes_prices: *const f64,
|
|
318
|
+
no_prices: *const f64,
|
|
319
|
+
count: usize,
|
|
320
|
+
) -> *mut c_char {
|
|
321
|
+
if yes_prices.is_null() || no_prices.is_null() || count == 0 {
|
|
322
|
+
set_last_error("Null pointer or zero count provided");
|
|
323
|
+
return ptr::null_mut();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
let yes_slice = unsafe { std::slice::from_raw_parts(yes_prices, count) };
|
|
327
|
+
let no_slice = unsafe { std::slice::from_raw_parts(no_prices, count) };
|
|
328
|
+
|
|
329
|
+
let mut results = Vec::with_capacity(count);
|
|
330
|
+
|
|
331
|
+
for i in 0..count {
|
|
332
|
+
let yes_price = yes_slice[i];
|
|
333
|
+
let no_price = no_slice[i];
|
|
334
|
+
|
|
335
|
+
// Validate inputs
|
|
336
|
+
if yes_price < 0.0 || yes_price > 1.0 || no_price < 0.0 || no_price > 1.0 {
|
|
337
|
+
results.push(serde_json::json!({
|
|
338
|
+
"error": format!("Prices at index {} must be between 0 and 1", i),
|
|
339
|
+
"index": i,
|
|
340
|
+
"has_arbitrage": false,
|
|
341
|
+
"yes_price": yes_price,
|
|
342
|
+
"no_price": no_price,
|
|
343
|
+
"total_price": yes_price + no_price,
|
|
344
|
+
"profit_per_share": 0.0,
|
|
345
|
+
"profit_bps": 0.0,
|
|
346
|
+
"profit": 0.0
|
|
347
|
+
}));
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let total = yes_price + no_price;
|
|
352
|
+
let has_arb = total < 1.0;
|
|
353
|
+
let profit_per_share = if has_arb { 1.0 - total } else { 0.0 };
|
|
354
|
+
|
|
355
|
+
results.push(serde_json::json!({
|
|
356
|
+
"has_arbitrage": has_arb,
|
|
357
|
+
"yes_price": yes_price,
|
|
358
|
+
"no_price": no_price,
|
|
359
|
+
"total_price": total,
|
|
360
|
+
"profit_per_share": profit_per_share,
|
|
361
|
+
"profit_bps": profit_per_share * 10000.0,
|
|
362
|
+
"profit": profit_per_share
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return_json_value(serde_json::json!(results))
|
|
367
|
+
}
|
|
368
|
+
|
|
304
369
|
/// Detect arbitrage opportunity (if yes_price + no_price < 1)
|
|
305
370
|
#[no_mangle]
|
|
306
371
|
pub extern "C" fn quant_detect_arbitrage(yes_price: f64, no_price: f64) -> *mut c_char {
|
|
@@ -326,6 +391,92 @@ pub extern "C" fn quant_detect_arbitrage(yes_price: f64, no_price: f64) -> *mut
|
|
|
326
391
|
}
|
|
327
392
|
}
|
|
328
393
|
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Batch Functions (reduce FFI overhead)
|
|
396
|
+
// ============================================================================
|
|
397
|
+
|
|
398
|
+
/// Calculate Kelly criterion for multiple bets at once (batch operation)
|
|
399
|
+
/// This is much more efficient than calling quant_kelly_criterion multiple times
|
|
400
|
+
/// because it only crosses the FFI boundary once.
|
|
401
|
+
///
|
|
402
|
+
/// # Arguments
|
|
403
|
+
/// * `probs` - Pointer to array of your probabilities
|
|
404
|
+
/// * `prices` - Pointer to array of market prices
|
|
405
|
+
/// * `count` - Number of bets (length of both arrays)
|
|
406
|
+
/// * `bankroll` - Total bankroll (shared across all bets)
|
|
407
|
+
///
|
|
408
|
+
/// # Returns
|
|
409
|
+
/// JSON array of KellyResult objects
|
|
410
|
+
#[inline(never)]
|
|
411
|
+
#[no_mangle]
|
|
412
|
+
pub extern "C" fn quant_kelly_criterion_batch(
|
|
413
|
+
probs: *const f64,
|
|
414
|
+
prices: *const f64,
|
|
415
|
+
count: usize,
|
|
416
|
+
bankroll: f64,
|
|
417
|
+
) -> *mut c_char {
|
|
418
|
+
if probs.is_null() || prices.is_null() || count == 0 {
|
|
419
|
+
set_last_error("Null pointer or zero count provided");
|
|
420
|
+
return ptr::null_mut();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let probs_slice = unsafe { std::slice::from_raw_parts(probs, count) };
|
|
424
|
+
let prices_slice = unsafe { std::slice::from_raw_parts(prices, count) };
|
|
425
|
+
|
|
426
|
+
let mut results = Vec::with_capacity(count);
|
|
427
|
+
|
|
428
|
+
for i in 0..count {
|
|
429
|
+
let your_probability = probs_slice[i];
|
|
430
|
+
let market_price = prices_slice[i];
|
|
431
|
+
|
|
432
|
+
// Validate inputs
|
|
433
|
+
if your_probability <= 0.0 || your_probability >= 1.0 {
|
|
434
|
+
results.push(serde_json::json!({
|
|
435
|
+
"error": format!("Probability at index {} must be between 0 and 1", i),
|
|
436
|
+
"index": i
|
|
437
|
+
}));
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if market_price <= 0.0 || market_price >= 1.0 {
|
|
441
|
+
results.push(serde_json::json!({
|
|
442
|
+
"error": format!("Market price at index {} must be between 0 and 1", i),
|
|
443
|
+
"index": i
|
|
444
|
+
}));
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if bankroll <= 0.0 {
|
|
448
|
+
results.push(serde_json::json!({
|
|
449
|
+
"error": "Bankroll must be positive",
|
|
450
|
+
"index": i
|
|
451
|
+
}));
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
let edge = your_probability - market_price;
|
|
456
|
+
let odds = (1.0 - market_price) / market_price;
|
|
457
|
+
|
|
458
|
+
// Kelly fraction: (bp - q) / b where b = odds, p = your_prob, q = 1 - your_prob
|
|
459
|
+
let kelly = (odds * your_probability - (1.0 - your_probability)) / odds;
|
|
460
|
+
let kelly = kelly.max(0.0); // Never bet negative
|
|
461
|
+
|
|
462
|
+
let half_kelly = kelly / 2.0;
|
|
463
|
+
let quarter_kelly = kelly / 4.0;
|
|
464
|
+
|
|
465
|
+
results.push(serde_json::json!(KellyResult {
|
|
466
|
+
kelly_fraction: kelly,
|
|
467
|
+
half_kelly,
|
|
468
|
+
quarter_kelly,
|
|
469
|
+
full_bet_size: kelly * bankroll,
|
|
470
|
+
half_bet_size: half_kelly * bankroll,
|
|
471
|
+
quarter_bet_size: quarter_kelly * bankroll,
|
|
472
|
+
edge,
|
|
473
|
+
odds,
|
|
474
|
+
}));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return_json_value(serde_json::json!(results))
|
|
478
|
+
}
|
|
479
|
+
|
|
329
480
|
// ============================================================================
|
|
330
481
|
// Odds Conversion
|
|
331
482
|
// ============================================================================
|
package/src/index.ts
CHANGED
|
@@ -139,6 +139,18 @@ const symbols = {
|
|
|
139
139
|
args: [FFIType.f64, FFIType.f64],
|
|
140
140
|
},
|
|
141
141
|
|
|
142
|
+
// Batch Kelly Criterion
|
|
143
|
+
quant_kelly_criterion_batch: {
|
|
144
|
+
returns: FFIType.cstring,
|
|
145
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.usize, FFIType.f64],
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// Batch Arbitrage Detection
|
|
149
|
+
quant_detect_arbitrage_batch: {
|
|
150
|
+
returns: FFIType.cstring,
|
|
151
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.usize],
|
|
152
|
+
},
|
|
153
|
+
|
|
142
154
|
// Odds Conversion
|
|
143
155
|
quant_convert_odds: {
|
|
144
156
|
returns: FFIType.cstring,
|
|
@@ -482,6 +494,49 @@ export function kellyCriterion(
|
|
|
482
494
|
// Alias matching @ebowwa/quant API
|
|
483
495
|
export const kelly_criterion = kellyCriterion;
|
|
484
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Calculate Kelly criterion for multiple bets at once (batch operation)
|
|
499
|
+
*
|
|
500
|
+
* This is significantly more efficient than calling kellyCriterion multiple times
|
|
501
|
+
* because it only crosses the FFI boundary once, eliminating FFI call overhead.
|
|
502
|
+
*
|
|
503
|
+
* @param bets - Array of betting opportunities with prob and price
|
|
504
|
+
* @param bankroll - Total bankroll in currency units (shared across all bets)
|
|
505
|
+
* @returns Array of Kelly criterion results for each bet
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```typescript
|
|
509
|
+
* const bets = [
|
|
510
|
+
* { prob: 0.6, price: 0.5 },
|
|
511
|
+
* { prob: 0.55, price: 0.45 },
|
|
512
|
+
* { prob: 0.7, price: 0.6 },
|
|
513
|
+
* ];
|
|
514
|
+
* const results = kellyCriterionBatch(bets, 1000);
|
|
515
|
+
* // results[0].full_bet_size, results[1].half_bet_size, etc.
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
export function kellyCriterionBatch(
|
|
519
|
+
bets: Array<{ prob: number; price: number }>,
|
|
520
|
+
bankroll: number
|
|
521
|
+
): KellyResult[] {
|
|
522
|
+
if (bets.length === 0) return [];
|
|
523
|
+
|
|
524
|
+
const probs = new Float64Array(bets.map(b => b.prob));
|
|
525
|
+
const prices = new Float64Array(bets.map(b => b.price));
|
|
526
|
+
|
|
527
|
+
const response = lib.symbols.quant_kelly_criterion_batch(
|
|
528
|
+
ptr(probs),
|
|
529
|
+
ptr(prices),
|
|
530
|
+
bets.length,
|
|
531
|
+
bankroll
|
|
532
|
+
) as string;
|
|
533
|
+
|
|
534
|
+
return parseJsonResponse<KellyResult[]>(response);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Alias matching @ebowwa/quant API
|
|
538
|
+
export const kelly_criterion_batch = kellyCriterionBatch;
|
|
539
|
+
|
|
485
540
|
// ============================================================================
|
|
486
541
|
// Arbitrage Detection Functions
|
|
487
542
|
// ============================================================================
|
|
@@ -505,6 +560,46 @@ export function detectArbitrage(yesPrice: number, noPrice: number): ArbitrageRes
|
|
|
505
560
|
// Alias matching @ebowwa/quant API
|
|
506
561
|
export const detect_arbitrage = detectArbitrage;
|
|
507
562
|
|
|
563
|
+
/**
|
|
564
|
+
* Detect arbitrage opportunities for multiple price pairs at once (batch operation)
|
|
565
|
+
*
|
|
566
|
+
* This is significantly more efficient than calling detectArbitrage multiple times
|
|
567
|
+
* because it only crosses the FFI boundary once, eliminating FFI call overhead.
|
|
568
|
+
*
|
|
569
|
+
* @param pairs - Array of YES/NO price pairs to check for arbitrage
|
|
570
|
+
* @returns Array of arbitrage results with profit field for each pair
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* const pairs = [
|
|
575
|
+
* { yesPrice: 0.45, noPrice: 0.45 }, // 10% arb opportunity
|
|
576
|
+
* { yesPrice: 0.50, noPrice: 0.50 }, // no arb
|
|
577
|
+
* { yesPrice: 0.40, noPrice: 0.40 }, // 20% arb opportunity
|
|
578
|
+
* ];
|
|
579
|
+
* const results = detectArbitrageBatch(pairs);
|
|
580
|
+
* // results[0].profit = 0.10, results[1].profit = 0, results[2].profit = 0.20
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
export function detectArbitrageBatch(
|
|
584
|
+
pairs: Array<{ yesPrice: number; noPrice: number }>
|
|
585
|
+
): Array<ArbitrageResult & { profit: number }> {
|
|
586
|
+
if (pairs.length === 0) return [];
|
|
587
|
+
|
|
588
|
+
const yesPrices = new Float64Array(pairs.map(p => p.yesPrice));
|
|
589
|
+
const noPrices = new Float64Array(pairs.map(p => p.noPrice));
|
|
590
|
+
|
|
591
|
+
const response = lib.symbols.quant_detect_arbitrage_batch(
|
|
592
|
+
ptr(yesPrices),
|
|
593
|
+
ptr(noPrices),
|
|
594
|
+
pairs.length
|
|
595
|
+
) as string;
|
|
596
|
+
|
|
597
|
+
return parseJsonResponse<Array<ArbitrageResult & { profit: number }>>(response);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Alias matching @ebowwa/quant API
|
|
601
|
+
export const detect_arbitrage_batch = detectArbitrageBatch;
|
|
602
|
+
|
|
508
603
|
// ============================================================================
|
|
509
604
|
// Odds Conversion Functions
|
|
510
605
|
// ============================================================================
|
|
@@ -1035,10 +1130,14 @@ export default {
|
|
|
1035
1130
|
// Kelly
|
|
1036
1131
|
kellyCriterion,
|
|
1037
1132
|
kelly_criterion,
|
|
1133
|
+
kellyCriterionBatch,
|
|
1134
|
+
kelly_criterion_batch,
|
|
1038
1135
|
|
|
1039
1136
|
// Arbitrage
|
|
1040
1137
|
detectArbitrage,
|
|
1041
1138
|
detect_arbitrage,
|
|
1139
|
+
detectArbitrageBatch,
|
|
1140
|
+
detect_arbitrage_batch,
|
|
1042
1141
|
|
|
1043
1142
|
// Odds
|
|
1044
1143
|
convertOdds,
|
package/native/README.md
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Native Binaries
|
|
2
|
-
|
|
3
|
-
This directory contains prebuilt native binaries for the `@ebowwa/quant-rust` package.
|
|
4
|
-
|
|
5
|
-
## Directory Structure
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
native/
|
|
9
|
-
├── darwin-x64/ # macOS (Intel)
|
|
10
|
-
│ └── libquant_rust.dylib
|
|
11
|
-
├── darwin-arm64/ # macOS (Apple Silicon)
|
|
12
|
-
│ └── libquant_rust.dylib
|
|
13
|
-
├── linux-x64/ # Linux (x86_64)
|
|
14
|
-
│ └── libquant_rust.so
|
|
15
|
-
└── win32-x64/ # Windows (x86_64)
|
|
16
|
-
└── quant_rust.dll
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Building from Source
|
|
20
|
-
|
|
21
|
-
If a prebuilt binary is not available for your platform, you can build from source:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# Navigate to the package directory
|
|
25
|
-
cd node_modules/@ebowwa/quant-rust
|
|
26
|
-
|
|
27
|
-
# Build with Cargo
|
|
28
|
-
cargo build --release
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
The compiled library will be placed in `target/release/`.
|
|
32
|
-
|
|
33
|
-
## Supported Platforms
|
|
34
|
-
|
|
35
|
-
| Platform | Architecture | Binary Name |
|
|
36
|
-
|----------|--------------|-------------|
|
|
37
|
-
| macOS | x64 (Intel) | libquant_rust.dylib |
|
|
38
|
-
| macOS | arm64 (Apple Silicon) | libquant_rust.dylib |
|
|
39
|
-
| Linux | x64 | libquant_rust.so |
|
|
40
|
-
| Windows | x64 | quant_rust.dll |
|
|
41
|
-
|
|
42
|
-
## Adding Prebuilt Binaries
|
|
43
|
-
|
|
44
|
-
To add prebuilt binaries for distribution:
|
|
45
|
-
|
|
46
|
-
1. Build the library on the target platform:
|
|
47
|
-
```bash
|
|
48
|
-
cargo build --release
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
2. Copy the binary to the appropriate directory:
|
|
52
|
-
```bash
|
|
53
|
-
cp target/release/libquant_rust.dylib native/darwin-arm64/
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
3. The postinstall script will automatically detect and use the correct binary.
|
|
57
|
-
|
|
58
|
-
## Binary Requirements
|
|
59
|
-
|
|
60
|
-
- Rust 1.70 or later
|
|
61
|
-
- Target platform toolchain
|
|
62
|
-
- For cross-compilation, see the Rust cross-compilation documentation
|
|
Binary file
|