borsajs 0.1.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/LICENSE +201 -0
- package/README.en.md +209 -0
- package/README.md +57 -0
- package/dist/cache.d.ts +24 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +40 -0
- package/dist/cache.js.map +1 -0
- package/dist/crypto.d.ts +17 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +25 -0
- package/dist/crypto.js.map +1 -0
- package/dist/exceptions.d.ts +34 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +70 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/fund.d.ts +28 -0
- package/dist/fund.d.ts.map +1 -0
- package/dist/fund.js +29 -0
- package/dist/fund.js.map +1 -0
- package/dist/fx.d.ts +16 -0
- package/dist/fx.d.ts.map +1 -0
- package/dist/fx.js +22 -0
- package/dist/fx.js.map +1 -0
- package/dist/index-class.d.ts +21 -0
- package/dist/index-class.d.ts.map +1 -0
- package/dist/index-class.js +31 -0
- package/dist/index-class.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/inflation.d.ts +12 -0
- package/dist/inflation.d.ts.map +1 -0
- package/dist/inflation.js +28 -0
- package/dist/inflation.js.map +1 -0
- package/dist/market.d.ts +7 -0
- package/dist/market.d.ts.map +1 -0
- package/dist/market.js +7 -0
- package/dist/market.js.map +1 -0
- package/dist/multi.d.ts +26 -0
- package/dist/multi.d.ts.map +1 -0
- package/dist/multi.js +46 -0
- package/dist/multi.js.map +1 -0
- package/dist/providers/base.d.ts +24 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +28 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/btcturk.d.ts +43 -0
- package/dist/providers/btcturk.d.ts.map +1 -0
- package/dist/providers/btcturk.js +94 -0
- package/dist/providers/btcturk.js.map +1 -0
- package/dist/providers/dovizcom.d.ts +42 -0
- package/dist/providers/dovizcom.d.ts.map +1 -0
- package/dist/providers/dovizcom.js +105 -0
- package/dist/providers/dovizcom.js.map +1 -0
- package/dist/providers/index.d.ts +20 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +12 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/kap.d.ts +37 -0
- package/dist/providers/kap.d.ts.map +1 -0
- package/dist/providers/kap.js +110 -0
- package/dist/providers/kap.js.map +1 -0
- package/dist/providers/paratic.d.ts +41 -0
- package/dist/providers/paratic.d.ts.map +1 -0
- package/dist/providers/paratic.js +85 -0
- package/dist/providers/paratic.js.map +1 -0
- package/dist/providers/tcmb.d.ts +49 -0
- package/dist/providers/tcmb.d.ts.map +1 -0
- package/dist/providers/tcmb.js +99 -0
- package/dist/providers/tcmb.js.map +1 -0
- package/dist/providers/tefas.d.ts +51 -0
- package/dist/providers/tefas.d.ts.map +1 -0
- package/dist/providers/tefas.js +89 -0
- package/dist/providers/tefas.js.map +1 -0
- package/dist/providers/viop.d.ts +35 -0
- package/dist/providers/viop.d.ts.map +1 -0
- package/dist/providers/viop.js +140 -0
- package/dist/providers/viop.js.map +1 -0
- package/dist/ticker.d.ts +18 -0
- package/dist/ticker.d.ts.map +1 -0
- package/dist/ticker.js +23 -0
- package/dist/ticker.js.map +1 -0
- package/dist/viop.d.ts +19 -0
- package/dist/viop.d.ts.map +1 -0
- package/dist/viop.js +31 -0
- package/dist/viop.js.map +1 -0
- package/package.json +52 -0
- package/src/cache.ts +42 -0
- package/src/crypto.ts +30 -0
- package/src/exceptions.ts +77 -0
- package/src/fund.ts +37 -0
- package/src/fx.ts +26 -0
- package/src/index-class.ts +39 -0
- package/src/index.ts +38 -0
- package/src/inflation.ts +31 -0
- package/src/market.ts +7 -0
- package/src/multi.ts +46 -0
- package/src/providers/base.ts +35 -0
- package/src/providers/btcturk.ts +100 -0
- package/src/providers/dovizcom.ts +98 -0
- package/src/providers/kap.ts +98 -0
- package/src/providers/paratic.ts +95 -0
- package/src/providers/tcmb.ts +96 -0
- package/src/providers/tefas.ts +85 -0
- package/src/ticker.ts +30 -0
- package/src/viop.ts +35 -0
- package/test/demo.ts +99 -0
- package/tsconfig.json +34 -0
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "borsajs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Turkish financial markets data library - yfinance-like API for BIST stocks, forex, crypto, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"test": "npx tsx test/demo.ts",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"finance",
|
|
22
|
+
"stocks",
|
|
23
|
+
"bist",
|
|
24
|
+
"turkey",
|
|
25
|
+
"forex",
|
|
26
|
+
"crypto",
|
|
27
|
+
"market-data",
|
|
28
|
+
"yfinance"
|
|
29
|
+
],
|
|
30
|
+
"author": "Mesut Piskin",
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/mesutpiskin/borsajs"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/mesutpiskin/borsajs/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/mesutpiskin/borsajs#readme",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"axios": "^1.6.0",
|
|
45
|
+
"cheerio": "^1.0.0-rc.12"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.10.0",
|
|
49
|
+
"tsx": "^4.21.0",
|
|
50
|
+
"typescript": "^5.3.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/cache.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL-based in-memory cache for borsajs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
interface CacheEntry<T> { value: T; expiresAt: number; }
|
|
6
|
+
|
|
7
|
+
export class Cache {
|
|
8
|
+
private store: Map<string, CacheEntry<unknown>> = new Map();
|
|
9
|
+
|
|
10
|
+
get<T>(key: string): T | null {
|
|
11
|
+
const entry = this.store.get(key);
|
|
12
|
+
if (!entry) return null;
|
|
13
|
+
if (Date.now() > entry.expiresAt) { this.store.delete(key); return null; }
|
|
14
|
+
return entry.value as T;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set<T>(key: string, value: T, ttlSeconds: number): void {
|
|
18
|
+
this.store.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
delete(key: string): boolean { return this.store.delete(key); }
|
|
22
|
+
clear(): void { this.store.clear(); }
|
|
23
|
+
get size(): number { return this.store.size; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TTL = {
|
|
27
|
+
REALTIME_PRICE: 60,
|
|
28
|
+
OHLCV_HISTORY: 3600,
|
|
29
|
+
COMPANY_INFO: 3600,
|
|
30
|
+
FINANCIAL_STATEMENTS: 86400,
|
|
31
|
+
FX_RATES: 300,
|
|
32
|
+
COMPANY_LIST: 86400,
|
|
33
|
+
FUND_DATA: 3600,
|
|
34
|
+
INFLATION_DATA: 86400,
|
|
35
|
+
VIOP: 300,
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
let globalCache: Cache | null = null;
|
|
39
|
+
export function getCache(): Cache {
|
|
40
|
+
if (!globalCache) globalCache = new Cache();
|
|
41
|
+
return globalCache;
|
|
42
|
+
}
|
package/src/crypto.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crypto class for cryptocurrency data.
|
|
3
|
+
*/
|
|
4
|
+
import { getBtcTurkProvider, TickerData, OHLCVData, HistoryOptions } from './providers/btcturk.js';
|
|
5
|
+
|
|
6
|
+
export class Crypto {
|
|
7
|
+
private readonly _pair: string;
|
|
8
|
+
private _currentCache: TickerData | null = null;
|
|
9
|
+
|
|
10
|
+
constructor(pair: string) { this._pair = pair.toUpperCase(); }
|
|
11
|
+
get pair(): string { return this._pair; }
|
|
12
|
+
get symbol(): string { return this._pair; }
|
|
13
|
+
|
|
14
|
+
async getCurrent(): Promise<TickerData> {
|
|
15
|
+
if (!this._currentCache) this._currentCache = await getBtcTurkProvider().getTicker(this._pair);
|
|
16
|
+
return this._currentCache;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getInfo(): Promise<TickerData> { return this.getCurrent(); }
|
|
20
|
+
|
|
21
|
+
async getHistory(options: HistoryOptions = {}): Promise<OHLCVData[]> {
|
|
22
|
+
return getBtcTurkProvider().getHistory(this._pair, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toString(): string { return `Crypto('${this._pair}')`; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function cryptoPairs(quote: string = 'TRY'): Promise<string[]> {
|
|
29
|
+
return getBtcTurkProvider().getPairs(quote);
|
|
30
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom exceptions for borsajs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class BorsajsError extends Error {
|
|
6
|
+
constructor(message: string) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'BorsajsError';
|
|
9
|
+
Object.setPrototypeOf(this, BorsajsError.prototype);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class TickerNotFoundError extends BorsajsError {
|
|
14
|
+
public readonly symbol: string;
|
|
15
|
+
constructor(symbol: string) {
|
|
16
|
+
super(`Ticker not found: ${symbol}`);
|
|
17
|
+
this.name = 'TickerNotFoundError';
|
|
18
|
+
this.symbol = symbol;
|
|
19
|
+
Object.setPrototypeOf(this, TickerNotFoundError.prototype);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class DataNotAvailableError extends BorsajsError {
|
|
24
|
+
constructor(message: string = 'Data not available') {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'DataNotAvailableError';
|
|
27
|
+
Object.setPrototypeOf(this, DataNotAvailableError.prototype);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class APIError extends BorsajsError {
|
|
32
|
+
public readonly statusCode?: number;
|
|
33
|
+
constructor(message: string, statusCode?: number) {
|
|
34
|
+
super(statusCode ? `API Error: ${message} (status: ${statusCode})` : `API Error: ${message}`);
|
|
35
|
+
this.name = 'APIError';
|
|
36
|
+
this.statusCode = statusCode;
|
|
37
|
+
Object.setPrototypeOf(this, APIError.prototype);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class AuthenticationError extends BorsajsError {
|
|
42
|
+
constructor(message: string = 'Authentication failed') {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = 'AuthenticationError';
|
|
45
|
+
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class RateLimitError extends BorsajsError {
|
|
50
|
+
constructor(message: string = 'Rate limit exceeded') {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = 'RateLimitError';
|
|
53
|
+
Object.setPrototypeOf(this, RateLimitError.prototype);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class InvalidPeriodError extends BorsajsError {
|
|
58
|
+
public readonly period: string;
|
|
59
|
+
public static readonly VALID_PERIODS = ['1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max'];
|
|
60
|
+
constructor(period: string) {
|
|
61
|
+
super(`Invalid period: ${period}. Valid: ${InvalidPeriodError.VALID_PERIODS.join(', ')}`);
|
|
62
|
+
this.name = 'InvalidPeriodError';
|
|
63
|
+
this.period = period;
|
|
64
|
+
Object.setPrototypeOf(this, InvalidPeriodError.prototype);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class InvalidIntervalError extends BorsajsError {
|
|
69
|
+
public readonly interval: string;
|
|
70
|
+
public static readonly VALID_INTERVALS = ['1m', '5m', '15m', '30m', '1h', '1d', '1wk', '1mo'];
|
|
71
|
+
constructor(interval: string) {
|
|
72
|
+
super(`Invalid interval: ${interval}. Valid: ${InvalidIntervalError.VALID_INTERVALS.join(', ')}`);
|
|
73
|
+
this.name = 'InvalidIntervalError';
|
|
74
|
+
this.interval = interval;
|
|
75
|
+
Object.setPrototypeOf(this, InvalidIntervalError.prototype);
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/fund.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fund class for mutual fund data.
|
|
3
|
+
*/
|
|
4
|
+
import { getTefasProvider, FundInfo, FundHistoryData, HistoryOptions, SearchResult } from './providers/tefas.js';
|
|
5
|
+
|
|
6
|
+
export interface FundPerformance { dailyReturn?: number; return1m?: number; return3m?: number; return6m?: number; returnYtd?: number; return1y?: number; return3y?: number; return5y?: number; }
|
|
7
|
+
|
|
8
|
+
export class Fund {
|
|
9
|
+
private readonly _fundCode: string;
|
|
10
|
+
private _infoCache: FundInfo | null = null;
|
|
11
|
+
|
|
12
|
+
constructor(fundCode: string) { this._fundCode = fundCode.toUpperCase(); }
|
|
13
|
+
get fundCode(): string { return this._fundCode; }
|
|
14
|
+
get symbol(): string { return this._fundCode; }
|
|
15
|
+
|
|
16
|
+
async getInfo(): Promise<FundInfo> {
|
|
17
|
+
if (!this._infoCache) this._infoCache = await getTefasProvider().getFundDetail(this._fundCode);
|
|
18
|
+
return this._infoCache;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async getDetail(): Promise<FundInfo> { return this.getInfo(); }
|
|
22
|
+
|
|
23
|
+
async getPerformance(): Promise<FundPerformance> {
|
|
24
|
+
const info = await this.getInfo();
|
|
25
|
+
return { dailyReturn: info.dailyReturn, return1m: info.return1m, return3m: info.return3m, return6m: info.return6m, returnYtd: info.returnYtd, return1y: info.return1y, return3y: info.return3y, return5y: info.return5y };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getHistory(options: HistoryOptions = {}): Promise<FundHistoryData[]> {
|
|
29
|
+
return getTefasProvider().getHistory(this._fundCode, options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
toString(): string { return `Fund('${this._fundCode}')`; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function searchFunds(query: string, limit: number = 20): Promise<SearchResult[]> {
|
|
36
|
+
return getTefasProvider().search(query, limit);
|
|
37
|
+
}
|
package/src/fx.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FX class for forex and commodity data.
|
|
3
|
+
*/
|
|
4
|
+
import { getDovizcomProvider, FXCurrentData, FXHistoryData, HistoryOptions } from './providers/dovizcom.js';
|
|
5
|
+
|
|
6
|
+
export class FX {
|
|
7
|
+
private readonly _asset: string;
|
|
8
|
+
private _currentCache: FXCurrentData | null = null;
|
|
9
|
+
|
|
10
|
+
constructor(asset: string) { this._asset = asset; }
|
|
11
|
+
get asset(): string { return this._asset; }
|
|
12
|
+
get symbol(): string { return this._asset; }
|
|
13
|
+
|
|
14
|
+
async getCurrent(): Promise<FXCurrentData> {
|
|
15
|
+
if (!this._currentCache) this._currentCache = await getDovizcomProvider().getCurrent(this._asset);
|
|
16
|
+
return this._currentCache;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getInfo(): Promise<FXCurrentData> { return this.getCurrent(); }
|
|
20
|
+
|
|
21
|
+
async getHistory(options: HistoryOptions = {}): Promise<FXHistoryData[]> {
|
|
22
|
+
return getDovizcomProvider().getHistory(this._asset, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
toString(): string { return `FX('${this._asset}')`; }
|
|
26
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index class for market index data.
|
|
3
|
+
*/
|
|
4
|
+
import { getParaticProvider, QuoteData, OHLCVData, HistoryOptions } from './providers/paratic.js';
|
|
5
|
+
|
|
6
|
+
export const INDICES: Record<string, string> = {
|
|
7
|
+
'XU100': 'BIST 100', 'XU050': 'BIST 50', 'XU030': 'BIST 30', 'XBANK': 'BIST Banka',
|
|
8
|
+
'XUSIN': 'BIST Sınai', 'XHOLD': 'BIST Holding', 'XUTEK': 'BIST Teknoloji', 'XGIDA': 'BIST Gıda',
|
|
9
|
+
'XTRZM': 'BIST Turizm', 'XULAS': 'BIST Ulaştırma', 'XSGRT': 'BIST Sigorta', 'XMANA': 'BIST Metal Ana',
|
|
10
|
+
'XKMYA': 'BIST Kimya', 'XMADN': 'BIST Maden', 'XELKT': 'BIST Elektrik', 'XTEKS': 'BIST Tekstil',
|
|
11
|
+
'XILTM': 'BIST İletişim', 'XUMAL': 'BIST Mali', 'XUTUM': 'BIST Tüm',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface IndexInfo extends QuoteData { name: string; type: 'index'; }
|
|
15
|
+
|
|
16
|
+
export class Index {
|
|
17
|
+
private readonly _symbol: string;
|
|
18
|
+
private _infoCache: IndexInfo | null = null;
|
|
19
|
+
|
|
20
|
+
constructor(symbol: string) { this._symbol = symbol.toUpperCase(); }
|
|
21
|
+
get symbol(): string { return this._symbol; }
|
|
22
|
+
|
|
23
|
+
async getInfo(): Promise<IndexInfo> {
|
|
24
|
+
if (!this._infoCache) {
|
|
25
|
+
const quote = await getParaticProvider().getQuote(this._symbol);
|
|
26
|
+
this._infoCache = { ...quote, name: INDICES[this._symbol] ?? this._symbol, type: 'index' };
|
|
27
|
+
}
|
|
28
|
+
return this._infoCache;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getHistory(options: HistoryOptions = {}): Promise<OHLCVData[]> {
|
|
32
|
+
return getParaticProvider().getHistory(this._symbol, options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
toString(): string { return `Index('${this._symbol}')`; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function indices(): string[] { return Object.keys(INDICES); }
|
|
39
|
+
export function index(symbol: string): Index { return new Index(symbol); }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* borsajs - Turkish Financial Markets Data Library
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { Ticker } from './ticker.js';
|
|
6
|
+
export type { TickerInfo } from './ticker.js';
|
|
7
|
+
|
|
8
|
+
export { Tickers, download } from './multi.js';
|
|
9
|
+
export type { DownloadOptions, MultiTickerData } from './multi.js';
|
|
10
|
+
|
|
11
|
+
export { FX } from './fx.js';
|
|
12
|
+
|
|
13
|
+
export { Crypto, cryptoPairs } from './crypto.js';
|
|
14
|
+
|
|
15
|
+
export { Fund, searchFunds } from './fund.js';
|
|
16
|
+
export type { FundPerformance } from './fund.js';
|
|
17
|
+
|
|
18
|
+
export { Index, indices, index, INDICES } from './index-class.js';
|
|
19
|
+
export type { IndexInfo } from './index-class.js';
|
|
20
|
+
|
|
21
|
+
export { Inflation } from './inflation.js';
|
|
22
|
+
|
|
23
|
+
export { VIOP } from './viop.js';
|
|
24
|
+
|
|
25
|
+
export { companies, searchCompanies } from './market.js';
|
|
26
|
+
|
|
27
|
+
export { BorsajsError, TickerNotFoundError, DataNotAvailableError, APIError, AuthenticationError, RateLimitError, InvalidPeriodError, InvalidIntervalError } from './exceptions.js';
|
|
28
|
+
|
|
29
|
+
export { Cache, TTL, getCache } from './cache.js';
|
|
30
|
+
|
|
31
|
+
export type { OHLCVData, HistoryOptions } from './providers/paratic.js';
|
|
32
|
+
export type { TickerData } from './providers/btcturk.js';
|
|
33
|
+
export type { FXCurrentData, FXHistoryData } from './providers/dovizcom.js';
|
|
34
|
+
export type { FundInfo, FundHistoryData, SearchResult } from './providers/tefas.js';
|
|
35
|
+
export type { InflationLatest, InflationData, InflationCalculation } from './providers/tcmb.js';
|
|
36
|
+
export type { ContractData, Company, Disclosure } from './providers/kap.js';
|
|
37
|
+
|
|
38
|
+
export const VERSION = '0.1.0';
|
package/src/inflation.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inflation class for TCMB inflation data.
|
|
3
|
+
*/
|
|
4
|
+
import { getTcmbProvider, InflationLatest, InflationData, InflationCalculation, InflationDataOptions } from './providers/tcmb.js';
|
|
5
|
+
|
|
6
|
+
export class Inflation {
|
|
7
|
+
async getLatest(inflationType: string = 'tufe'): Promise<InflationLatest> {
|
|
8
|
+
return getTcmbProvider().getLatest(inflationType);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getTufe(options: InflationDataOptions = {}): Promise<InflationData[]> {
|
|
12
|
+
return getTcmbProvider().getData('tufe', options);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getUfe(options: InflationDataOptions = {}): Promise<InflationData[]> {
|
|
16
|
+
return getTcmbProvider().getData('ufe', options);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async calculate(amount: number, start: string, end: string): Promise<InflationCalculation> {
|
|
20
|
+
const parseYM = (s: string): [number, number] => {
|
|
21
|
+
const [y, m] = s.split('-').map(Number);
|
|
22
|
+
if (!y || !m || m < 1 || m > 12) throw new Error(`Invalid date format: ${s}. Use YYYY-MM`);
|
|
23
|
+
return [y, m];
|
|
24
|
+
};
|
|
25
|
+
const [sy, sm] = parseYM(start);
|
|
26
|
+
const [ey, em] = parseYM(end);
|
|
27
|
+
return getTcmbProvider().calculateInflation({ startYear: sy, startMonth: sm, endYear: ey, endMonth: em, basketValue: amount });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
toString(): string { return 'Inflation()'; }
|
|
31
|
+
}
|
package/src/market.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Market-level functions for BIST data.
|
|
3
|
+
*/
|
|
4
|
+
import { getKapProvider, Company } from './providers/kap.js';
|
|
5
|
+
|
|
6
|
+
export async function companies(): Promise<Company[]> { return getKapProvider().getCompanies(); }
|
|
7
|
+
export async function searchCompanies(query: string): Promise<Company[]> { return getKapProvider().search(query); }
|
package/src/multi.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-ticker functions and classes.
|
|
3
|
+
*/
|
|
4
|
+
import { Ticker } from './ticker.js';
|
|
5
|
+
import { getParaticProvider, OHLCVData, HistoryOptions } from './providers/paratic.js';
|
|
6
|
+
|
|
7
|
+
export interface DownloadOptions extends HistoryOptions { groupBy?: 'column' | 'ticker'; progress?: boolean; }
|
|
8
|
+
export interface MultiTickerData { [ticker: string]: OHLCVData[]; }
|
|
9
|
+
|
|
10
|
+
export class Tickers {
|
|
11
|
+
private readonly _symbols: string[];
|
|
12
|
+
private readonly _tickers: Map<string, Ticker>;
|
|
13
|
+
|
|
14
|
+
constructor(symbols: string | string[]) {
|
|
15
|
+
this._symbols = (typeof symbols === 'string' ? symbols.split(/\s+/) : symbols).filter(s => s.trim()).map(s => s.trim().toUpperCase());
|
|
16
|
+
this._tickers = new Map();
|
|
17
|
+
for (const symbol of this._symbols) this._tickers.set(symbol, new Ticker(symbol));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get symbols(): string[] { return [...this._symbols]; }
|
|
21
|
+
get tickers(): Map<string, Ticker> { return this._tickers; }
|
|
22
|
+
get length(): number { return this._tickers.size; }
|
|
23
|
+
|
|
24
|
+
getTicker(symbol: string): Ticker {
|
|
25
|
+
const ticker = this._tickers.get(symbol.toUpperCase());
|
|
26
|
+
if (!ticker) throw new Error(`Symbol not found: ${symbol}`);
|
|
27
|
+
return ticker;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getHistory(options: DownloadOptions = {}): Promise<MultiTickerData> { return download(this._symbols, options); }
|
|
31
|
+
|
|
32
|
+
*[Symbol.iterator](): Iterator<[string, Ticker]> { for (const entry of this._tickers) yield entry; }
|
|
33
|
+
toString(): string { return `Tickers(${JSON.stringify(this._symbols)})`; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function download(tickers: string | string[], options: DownloadOptions = {}): Promise<MultiTickerData> {
|
|
37
|
+
const symbols = (typeof tickers === 'string' ? tickers.split(/\s+/) : tickers).filter(s => s.trim()).map(s => s.trim().toUpperCase());
|
|
38
|
+
if (symbols.length === 0) throw new Error('No symbols provided');
|
|
39
|
+
const { period, interval, start, end } = options;
|
|
40
|
+
const provider = getParaticProvider();
|
|
41
|
+
const result: MultiTickerData = {};
|
|
42
|
+
for (const symbol of symbols) {
|
|
43
|
+
try { const data = await provider.getHistory(symbol, { period, interval, start, end }); if (data.length > 0) result[symbol] = data; } catch { /* skip */ }
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base provider class for all data providers.
|
|
3
|
+
*/
|
|
4
|
+
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
5
|
+
import { Cache, getCache } from '../cache.js';
|
|
6
|
+
|
|
7
|
+
export interface ProviderOptions { timeout?: number; cache?: Cache; }
|
|
8
|
+
|
|
9
|
+
export abstract class BaseProvider {
|
|
10
|
+
protected readonly client: AxiosInstance;
|
|
11
|
+
protected readonly cache: Cache;
|
|
12
|
+
|
|
13
|
+
protected static readonly DEFAULT_HEADERS = {
|
|
14
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
15
|
+
'Accept': 'application/json, text/plain, */*',
|
|
16
|
+
'Accept-Language': 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
constructor(options: ProviderOptions = {}) {
|
|
20
|
+
const { timeout = 30000, cache } = options;
|
|
21
|
+
this.client = axios.create({ timeout, headers: BaseProvider.DEFAULT_HEADERS });
|
|
22
|
+
this.cache = cache ?? getCache();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected async get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
|
|
26
|
+
return this.client.get<T>(url, config);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
|
|
30
|
+
return this.client.post<T>(url, data, config);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected cacheGet<T>(key: string): T | null { return this.cache.get<T>(key); }
|
|
34
|
+
protected cacheSet<T>(key: string, value: T, ttl: number): void { this.cache.set(key, value, ttl); }
|
|
35
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BtcTurk provider for cryptocurrency data.
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider, ProviderOptions } from './base.js';
|
|
5
|
+
import { TTL } from '../cache.js';
|
|
6
|
+
import { APIError, DataNotAvailableError } from '../exceptions.js';
|
|
7
|
+
|
|
8
|
+
export interface TickerData {
|
|
9
|
+
symbol: string; last: number; open: number; high: number; low: number;
|
|
10
|
+
bid: number; ask: number; volume: number; change: number; changePercent: number; timestamp?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface OHLCVData { date: Date; open: number; high: number; low: number; close: number; volume: number; }
|
|
14
|
+
export interface HistoryOptions { period?: string; interval?: string; start?: Date; end?: Date; }
|
|
15
|
+
|
|
16
|
+
interface BtcTurkTickerResponse { success: boolean; message?: string; data: Array<{ pair: string; last: string | number; open: string | number; high: string | number; low: string | number; bid: string | number; ask: string | number; volume: string | number; daily: string | number; dailyPercent: string | number; timestamp: number; }>; }
|
|
17
|
+
interface GraphAPIResponse { s: string; t: number[]; o: number[]; h: number[]; l: number[]; c: number[]; v: number[]; }
|
|
18
|
+
|
|
19
|
+
export class BtcTurkProvider extends BaseProvider {
|
|
20
|
+
private static readonly BASE_URL = 'https://api.btcturk.com/api/v2';
|
|
21
|
+
private static readonly GRAPH_API_URL = 'https://graph-api.btcturk.com';
|
|
22
|
+
private static readonly RESOLUTION_MAP: Record<string, number> = { '1m': 1, '5m': 5, '15m': 15, '30m': 30, '1h': 60, '4h': 240, '1d': 1440, '1wk': 10080 };
|
|
23
|
+
private static readonly PERIOD_DAYS: Record<string, number> = { '1d': 1, '5d': 5, '1mo': 30, '3mo': 90, '6mo': 180, '1y': 365 };
|
|
24
|
+
|
|
25
|
+
constructor(options?: ProviderOptions) { super(options); }
|
|
26
|
+
|
|
27
|
+
async getTicker(pair: string): Promise<TickerData> {
|
|
28
|
+
pair = pair.toUpperCase();
|
|
29
|
+
const cacheKey = `btcturk:ticker:${pair}`;
|
|
30
|
+
const cached = this.cacheGet<TickerData>(cacheKey);
|
|
31
|
+
if (cached) return cached;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const response = await this.get<BtcTurkTickerResponse>(`${BtcTurkProvider.BASE_URL}/ticker`, { params: { pairSymbol: pair } });
|
|
35
|
+
const data = response.data;
|
|
36
|
+
if (!data.success) throw new APIError(data.message ?? 'Unknown error');
|
|
37
|
+
if (!data.data || data.data.length === 0) throw new DataNotAvailableError(`No data for pair: ${pair}`);
|
|
38
|
+
|
|
39
|
+
const ticker = data.data[0];
|
|
40
|
+
const result: TickerData = {
|
|
41
|
+
symbol: ticker.pair, last: Number(ticker.last) || 0, open: Number(ticker.open) || 0,
|
|
42
|
+
high: Number(ticker.high) || 0, low: Number(ticker.low) || 0, bid: Number(ticker.bid) || 0,
|
|
43
|
+
ask: Number(ticker.ask) || 0, volume: Number(ticker.volume) || 0, change: Number(ticker.daily) || 0,
|
|
44
|
+
changePercent: Number(ticker.dailyPercent) || 0, timestamp: ticker.timestamp,
|
|
45
|
+
};
|
|
46
|
+
this.cacheSet(cacheKey, result, TTL.REALTIME_PRICE);
|
|
47
|
+
return result;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof APIError || error instanceof DataNotAvailableError) throw error;
|
|
50
|
+
throw new APIError(`Failed to fetch ticker for ${pair}: ${error}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getHistory(pair: string, options: HistoryOptions = {}): Promise<OHLCVData[]> {
|
|
55
|
+
pair = pair.toUpperCase();
|
|
56
|
+
const { period = '1mo', interval = '1d', start, end } = options;
|
|
57
|
+
const endDt = end ?? new Date();
|
|
58
|
+
const startDt = start ?? new Date(endDt.getTime() - (BtcTurkProvider.PERIOD_DAYS[period] ?? 30) * 24 * 60 * 60 * 1000);
|
|
59
|
+
const fromTs = Math.floor(startDt.getTime() / 1000);
|
|
60
|
+
const toTs = Math.floor(endDt.getTime() / 1000);
|
|
61
|
+
|
|
62
|
+
const cacheKey = `btcturk:history:${pair}:${interval}:${fromTs}:${toTs}`;
|
|
63
|
+
const cached = this.cacheGet<OHLCVData[]>(cacheKey);
|
|
64
|
+
if (cached) return cached;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const resolution = BtcTurkProvider.RESOLUTION_MAP[interval] ?? 1440;
|
|
68
|
+
const response = await this.get<GraphAPIResponse>(`${BtcTurkProvider.GRAPH_API_URL}/v1/klines/history`, { params: { symbol: pair, resolution, from: fromTs, to: toTs } });
|
|
69
|
+
const data = response.data;
|
|
70
|
+
if (data.s !== 'ok') throw new DataNotAvailableError(`No data available for ${pair}`);
|
|
71
|
+
|
|
72
|
+
const records: OHLCVData[] = data.t.map((ts, i) => ({
|
|
73
|
+
date: new Date(ts * 1000), open: Number(data.o[i]) || 0, high: Number(data.h[i]) || 0,
|
|
74
|
+
low: Number(data.l[i]) || 0, close: Number(data.c[i]) || 0, volume: Number(data.v[i]) || 0,
|
|
75
|
+
}));
|
|
76
|
+
records.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
77
|
+
this.cacheSet(cacheKey, records, TTL.OHLCV_HISTORY);
|
|
78
|
+
return records;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error instanceof DataNotAvailableError) throw error;
|
|
81
|
+
throw new APIError(`Failed to fetch history for ${pair}: ${error}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getPairs(quote: string = 'TRY'): Promise<string[]> {
|
|
86
|
+
const cacheKey = `btcturk:pairs:${quote}`;
|
|
87
|
+
const cached = this.cacheGet<string[]>(cacheKey);
|
|
88
|
+
if (cached) return cached;
|
|
89
|
+
try {
|
|
90
|
+
const response = await this.get<BtcTurkTickerResponse>(`${BtcTurkProvider.BASE_URL}/ticker`);
|
|
91
|
+
if (!response.data.success) return [];
|
|
92
|
+
const pairs = response.data.data.map(t => t.pair).filter(p => p.endsWith(quote.toUpperCase()));
|
|
93
|
+
this.cacheSet(cacheKey, pairs, TTL.COMPANY_LIST);
|
|
94
|
+
return pairs;
|
|
95
|
+
} catch { return []; }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let provider: BtcTurkProvider | null = null;
|
|
100
|
+
export function getBtcTurkProvider(): BtcTurkProvider { if (!provider) provider = new BtcTurkProvider(); return provider; }
|