├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── babel.config.js
├── dist
├── Bot.d.ts
├── Bot.js
├── Bot.js.map
├── algorithms
│ ├── Algorithm.d.ts
│ ├── Algorithm.js
│ ├── Algorithm.js.map
│ ├── ArbitrageBetweenExchanges.d.ts
│ ├── ArbitrageBetweenExchanges.js
│ ├── ArbitrageBetweenExchanges.js.map
│ ├── ArbitrageTriangleWithinExchange.d.ts
│ ├── ArbitrageTriangleWithinExchange.js
│ ├── ArbitrageTriangleWithinExchange.js.map
│ ├── ArbitrageTriangularBetweenExchanges.d.ts
│ ├── ArbitrageTriangularBetweenExchanges.js
│ └── ArbitrageTriangularBetweenExchanges.js.map
├── common
│ ├── config.d.ts
│ ├── config.js
│ ├── config.js.map
│ ├── constants.d.ts
│ ├── constants.js
│ ├── constants.js.map
│ ├── errors.d.ts
│ ├── errors.js
│ ├── errors.js.map
│ ├── helpers.d.ts
│ ├── helpers.js
│ ├── helpers.js.map
│ ├── interfaces.d.ts
│ ├── interfaces.js
│ ├── interfaces.js.map
│ ├── types.d.ts
│ ├── types.js
│ └── types.js.map
├── config.json
├── config.local.example.json
├── example.d.ts
├── example.js
├── example.js.map
├── index.d.ts
├── index.js
├── index.js.map
└── misc
│ ├── Telegram.d.ts
│ ├── Telegram.js
│ └── Telegram.js.map
├── docs
└── example.png
├── jest.config.js
├── misc
└── proxy.js
├── nodemon.json
├── package-lock.json
├── package.json
├── src
├── Bot.ts
├── algorithms
│ ├── Algorithm.ts
│ ├── ArbitrageBetweenExchanges.ts
│ ├── ArbitrageTriangleWithinExchange.ts
│ └── ArbitrageTriangularBetweenExchanges.ts
├── common
│ ├── config.ts
│ ├── constants.ts
│ ├── errors.ts
│ ├── helpers.ts
│ ├── interfaces.ts
│ └── types.ts
├── config.json
├── config.local.example.json
├── example.ts
├── index.ts
└── misc
│ └── Telegram.ts
├── test
├── ArbitrageTriangleWithinExchange.spec.ts
└── common
│ └── helpers.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # config
2 | config.local.json
3 | src/config.local.json
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # Diagnostic reports (https://nodejs.org/api/report.html)
14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | node_modules/
46 | jspm_packages/
47 |
48 | # Snowpack dependency directory (https://snowpack.dev/)
49 | web_modules/
50 |
51 | # TypeScript cache
52 | *.tsbuildinfo
53 |
54 | # Optional npm cache directory
55 | .npm
56 |
57 | # Optional eslint cache
58 | .eslintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variables file
76 | .env
77 | .env.test
78 |
79 | # parcel-bundler cache (https://parceljs.org/)
80 | .cache
81 | .parcel-cache
82 |
83 | # Next.js build output
84 | .next
85 | out
86 |
87 | # Nuxt.js build / generate output
88 | .nuxt
89 |
90 | # Gatsby files
91 | .cache/
92 | # Comment in the public line in if your project uses Gatsby and not Next.js
93 | # https://nextjs.org/blog/next-9-1#public-directory-support
94 | # public
95 |
96 | # vuepress build output
97 | .vuepress/dist
98 |
99 | # Serverless directories
100 | .serverless/
101 |
102 | # FuseBox cache
103 | .fusebox/
104 |
105 | # DynamoDB Local files
106 | .dynamodb/
107 |
108 | # TernJS port file
109 | .tern-port
110 |
111 | # Stores VSCode versions used for testing VSCode extensions
112 | .vscode-test
113 |
114 | # yarn v2
115 | .yarn/cache
116 | .yarn/unplugged
117 | .yarn/build-state.yml
118 | .yarn/install-state.gz
119 | .pnp.*
120 |
121 | # Other
122 | notes
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, religion, or sexual identity
11 | and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the
27 | overall community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or
32 | advances of any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email
36 | address, without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | zvvolinski.g@gmail.com.
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series
87 | of actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or
94 | permanent ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within
114 | the community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.0, available at
120 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127 | at [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 |
135 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Looks like there's already a license file for this project.
2 | [?25l[2K[1G[36m?[39m [1mDo you want to replace it?[22m [90m»[39m [90m(y/N)[39m[2K[1G[2K[1G[32m√[39m [1mDo you want to replace it?[22m [90m...[39m no
3 | [?25hExiting...
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Arbitrage Algorithms Framework Prototype
2 | For making trading bots on top of [CCXT](https://github.com/ccxt/ccxt/), with simple lifecycle. In TypeScript.
3 |
4 |
5 |

6 |
7 |
8 | Feel free to contribute.
9 |
10 | Configuration
11 | ===================================
12 | ```
13 | {
14 | keys: {
15 | [key: string /* exchange */]: {
16 | apiKey: string;
17 | secret: string;
18 | }
19 | };
20 | exchangesToWatch: string[];
21 | orderBookLimit: number;
22 | exchangeOptions: {
23 | [key: string /* exchange */]: any
24 | };
25 | defaultExchangeOptions: any;
26 | currenciesToWatch: string[];
27 |
28 | makeOrders: boolean;
29 | parallelOrders: boolean;
30 |
31 | profile: boolean;
32 | logDetails: boolean;
33 | logAdditionalDetails: boolean;
34 | logWarnings: boolean;
35 | logAdditionalWarnings: boolean;
36 | logError: boolean;
37 | logErrorDetails: boolean;
38 |
39 | // ccxt calculate_fees correction
40 | feesRate: number;
41 | zeroesFeesCorrection: boolean;
42 | correctAllFees: boolean;
43 | feesRoundType: 'ceil' | 'floor' |'round';
44 |
45 | orderOptionsByExchange: {
46 | [key: string /* exchange */]: any
47 | };
48 | defaultOrderOptions: any;
49 |
50 | enableProxy: boolean;
51 | changeProxyAfterEveryOrder: boolean;
52 | changeProxyAfterAnyNetworkError: boolean;
53 | proxies: string[];
54 |
55 | telegram?: {
56 | token: string;
57 | startPhrase: string;
58 | stopPhrase: string;
59 | chats: string[];
60 | logErrors: boolean;
61 | }
62 | }
63 | ```
64 |
65 | Usage
66 | ===================================
67 | ```
68 | import Bot from "arbitrage-algorithms-framework";
69 | import { config, errorLogTemplate, log } from "arbitrage-algorithms-framework";
70 |
71 | const bot = new Bot(config(require("path/to/config.local.json")));
72 | const toIterate = [];
73 | bot.init().then(
74 | () => {
75 | bot.printProfileTime();
76 | bot.cycle(
77 | toIterate,
78 | (params) => {
79 | // class extending Algorithm base class to be used in cycle
80 | return new CustomAlgorithm(params)
81 | }, iteratedElement => ({
82 | // function for element adaptation
83 | ...iteratedElement
84 | } as CustomAlgorithmParams),
85 | () => {
86 | log('Algorithm cycle stared');
87 | bot.printProfileTime();
88 | bot.telegram.sendMessage('Algorithm cycle stared');
89 | }
90 | );
91 | },
92 | err => log(errorLogTemplate(err))
93 | );
94 | ```
95 | Check `src/example.ts` for more details.
96 |
97 | Todo
98 | ===================================
99 | - "fluid" fees (depends on fees.currency, other than default)
100 | - use prepared error handler
101 | - bot method for placing orders with retries
102 | - WS adapter class compatible with ccxt (for updating iterating elements in rl time, making orders, use with proxy?)
103 | - limits, precisions exceptions (per exchange, coin, exchange + coin and order side)
104 | - make triangle orders at once via proxy or one by one
105 | - fill order at once or chunk (depends on balance and/or agresivness)
106 | - ArbitrageBetweenExchanges
107 | - ArbitrageTriangularBetweenExchanges
108 | - handle fees in BNB on binance
109 | - handle other ordersand fees types (eg. oco, precision rounding types)
110 | - ignore (exchange, market, coin) lists
111 | - readme, docs
112 |
113 | Proxy
114 | ===================================
115 | To start proxy on another server:
116 | ```
117 | npm i arbitrage-algorithms-framework
118 | npm i -g cors-anywhere
119 | node node_modules/arbitrage-algorithms-framework/misc/proxy.js PORT HOST
120 | ```
121 |
122 | Then add HOST:PORT in config proxies list.
123 |
124 | ArbitrageTriangleWithinExchange
125 | ===================================
126 | ```
127 | MARKET: BASE/QUOTE
128 |
129 | M0
130 | /\
131 | / \
132 | D0 |/_ \_
133 | / |\
134 | _/ \
135 | /| _\| D1
136 | / \
137 | /____\____/____\
138 | M2 / \ M1
139 |
140 | D - DIRECTION,
141 | M - MARKET,
142 | C - COIN
143 |
144 | M0 C0_C1
145 | M1 C1_C2
146 | M2 C0_C2
147 |
148 | Direction 0:
149 | M0 BUY (C0 for C1) | +C0 -C1
150 | M1 BUY (C1 for C2) | +C1 -C2
151 | M2 SELL (C0 for C2) | -C0 +C2
152 |
153 | Direction 1:
154 | M0 SELL (C0 for C1) | -C0 +C1
155 | M1 SELL (C1 for C2) | -C1 +C2
156 | M2 BUY (C0 for C2) | +C0 -C2
157 | ```
158 | Temporary assumptions:
159 | - don't use BNB for fees on Binance
160 | - every exchange has limit orders
161 |
162 | Known issues:
163 | - handle minimum fees on Bleutrade
164 |
165 |
166 | ArbitrageBetweenExchanges
167 | ===================================
168 | TBD
169 |
170 | BUY C0 FOR C1 -> TRANSFER C0 -> SELL C0 FOR C1 -> TRANSFER C1
171 | SELL C0 FOR C1 -> TRANSFER C1 -> BUY C0 FOR C1 -> TRANSFER C0
172 |
173 |
174 | ArbitrageTriangularBetweenExchanges
175 | ===================================
176 | TBD
177 |
178 | ```
179 | MARKET: BASE/QUOTE
180 |
181 | M0E0
182 | /\
183 | / \
184 | |/_ \_
185 | / |\
186 | _/ \
187 | /| _\|
188 | / \
189 | /____\____/____\
190 | M2E1 / \ M1E0
191 |
192 | D - DIRECTION,
193 | M - MARKET,
194 | C - COIN,
195 | E - EXCHANGE
196 |
197 | M0 C0_C1
198 | M1 C1_C2
199 | M2 C0_C2
200 |
201 | Direction 0:
202 | M0E0 BUY (C0 for C1) | +C0 -C1 | E0
203 | M2E0 SELL (C0 for C2) | -C0 +C2 | E0
204 | TRANSFER C2 FROM E0 TO E1 | -E0 +E1
205 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
206 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
207 |
208 | Direction 1:
209 | M0E0 SELL (C0 for C1) | -C0 +C1 | E0
210 | M1E0 BUY (C2 for C1) | +C2 -C1 | E0
211 | TRANSFER C2 FROM E0 TO E1 | -E0 +E1
212 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
213 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
214 |
215 | Direction 2:
216 | M0E0 BUY (C0 for C1) | +C0 -C1 | E0
217 | TRANSFER C0 FROM E0 TO E1 | -E0 +E1
218 | M2E1 SELL (C0 for C2) | -C0 +C2 | E1
219 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
220 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
221 |
222 | Direction 3:
223 | M0E0 SELL (C0 for C1) | -C0 +C1 | E0
224 | TRANSFER C1 FROM E0 TO E1 | -E0 +E1
225 | M1E1 BUY (C2 for C1) | +C2 -C1 | E1
226 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
227 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
228 |
229 | Direction 4:
230 | M0E0 BUY (C0 for C1) | +C0 -C1 | E0
231 | TRANSFER C0 FROM E0 TO E1 | -E0 +E1
232 | M2E1 SELL (C0 for C2) | -C0 +C2 | E1
233 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
234 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
235 |
236 | Direction 5:
237 | M0E0 SELL (C0 for C1) | -C0 +C1 | E0
238 | TRANSFER C1 FROM E0 TO E1 | -E0 +E1
239 | M1E1 BUY (C2 for C1) | +C2 -C1 | E1
240 | M2E1 BUY (C0 for C2) | +C0 -C2 | E1
241 | TRANSFER C0 FROM E1 TO E0 | -E1 +E0
242 | ```
243 |
244 | Direction 0 is exemplary. Transfers can be between any cone, cones can be vary within three markets (not every occures in every direction).
245 | ```
246 | m m m t t
247 | m t t m m
248 | t m m m t
249 | m m t t m
250 | t t m m m *
251 | ```
252 | @TODO: GET EVERY POSSIBLE DIRECTION (* take into account bids/asks variation)
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', {targets: {node: 'current'}}],
4 | '@babel/preset-typescript',
5 | ],
6 | };
--------------------------------------------------------------------------------
/dist/Bot.d.ts:
--------------------------------------------------------------------------------
1 | import ccxt, { Balances, Exchange } from 'ccxt';
2 | import Algorithm from './algorithms/Algorithm';
3 | import Telegram, { TelegramParams } from './misc/Telegram';
4 | export interface BotConfig {
5 | keys: {
6 | [key: string]: {
7 | apiKey: string;
8 | secret: string;
9 | };
10 | };
11 | exchangesToWatch: string[];
12 | orderBookLimit: number;
13 | exchangeOptions: {
14 | [key: string]: any;
15 | };
16 | defaultExchangeOptions: any;
17 | currenciesToWatch: string[];
18 | makeOrders: boolean;
19 | parallelOrders: boolean;
20 | profile: boolean;
21 | logDetails: boolean;
22 | logAdditionalDetails: boolean;
23 | logWarnings: boolean;
24 | logAdditionalWarnings: boolean;
25 | logError: boolean;
26 | logErrorDetails: boolean;
27 | feesRate: number;
28 | zeroesFeesCorrection: boolean;
29 | correctAllFees: boolean;
30 | feesRoundType: 'ceil' | 'floor' | 'round';
31 | orderOptionsByExchange: {
32 | [key: string]: any;
33 | };
34 | defaultOrderOptions: any;
35 | enableProxy: boolean;
36 | changeProxyAfterEveryOrder: boolean;
37 | changeProxyAfterAnyNetworkError: boolean;
38 | proxies: string[];
39 | telegram?: TelegramParams;
40 | }
41 | export default class Bot {
42 | config: BotConfig;
43 | exchanges: {
44 | [key: string]: Exchange;
45 | };
46 | balances: {
47 | [key: string]: Balances;
48 | };
49 | cycleIndex: number;
50 | proxyIndex: number;
51 | telegram: Telegram;
52 | constructor(config: BotConfig);
53 | printProfileTime(): void;
54 | init(): Promise;
55 | runAlgorithm(algorithm: Algorithm): Promise;
56 | runAlgorithmOnIteratedElement(elementsArray: any, algorithm: any, paramsFromElement: any): Promise;
57 | cycle: (toIterate: any[], algorithm: (params: any) => Algorithm, paramsFromElement: (element: any) => any, onCycleRun: () => any) => void;
58 | validateExchange(exchange: string): void;
59 | fetchBalanceAsync(exchange: Exchange): Promise;
60 | fetchBalance(exchange: Exchange): Promise;
61 | setExchangeProxy(exchange: Exchange, index?: number | null): void;
62 | makeOrder(exchange: Exchange, market: any, side: any, amount: any, price: any, additionalParams?: {}): any;
63 | }
64 |
--------------------------------------------------------------------------------
/dist/Bot.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
13 | };
14 | Object.defineProperty(exports, "__esModule", { value: true });
15 | const helpers_1 = require("./common/helpers");
16 | const ccxt_1 = __importDefault(require("ccxt"));
17 | const Telegram_1 = __importDefault(require("./misc/Telegram"));
18 | const constants_1 = require("./common/constants");
19 | class Bot {
20 | constructor(config) {
21 | this.exchanges = {};
22 | this.balances = {};
23 | this.cycleIndex = 0;
24 | this.proxyIndex = 0;
25 | this.cycle = (toIterate, algorithm, paramsFromElement, onCycleRun = () => null) => __awaiter(this, void 0, void 0, function* () {
26 | this.cycleIndex = 0;
27 | const cycleMaxIndex = toIterate.length;
28 | onCycleRun();
29 | do {
30 | yield this.runAlgorithmOnIteratedElement(toIterate, algorithm, paramsFromElement);
31 | } while (this.cycleIndex < cycleMaxIndex);
32 | process.nextTick(() => {
33 | this.cycle(toIterate, algorithm, paramsFromElement, onCycleRun);
34 | });
35 | });
36 | this.config = config;
37 | this.telegram = new Telegram_1.default(this.config.telegram);
38 | this.telegram.sendMessage('Bot started');
39 | }
40 | printProfileTime() {
41 | if (!this.config.profile) {
42 | return;
43 | }
44 | console.log("\x1b[47m\x1b[30m%s\x1b[0m", new Date().toLocaleString().padEnd(100, ' '));
45 | }
46 | init() {
47 | return __awaiter(this, void 0, void 0, function* () {
48 | const self = this;
49 | return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
50 | yield Promise.all(ccxt_1.default.exchanges.map((exchange) => (function () {
51 | return __awaiter(this, void 0, void 0, function* () {
52 | if (self.config.exchangesToWatch.includes(exchange)) {
53 | self.validateExchange(exchange);
54 | self.exchanges[exchange] = new (ccxt_1.default)[exchange](Object.assign(Object.assign({}, self.config.defaultExchangeOptions), self.config.exchangeOptions[exchange]));
55 | self.exchanges[exchange].apiKey = self.config.keys[exchange].apiKey;
56 | self.exchanges[exchange].secret = self.config.keys[exchange].secret;
57 | try {
58 | self.balances[exchange] = yield self.fetchBalance(self.exchanges[exchange]);
59 | }
60 | catch (err) {
61 | helpers_1.log(helpers_1.errorLogTemplate(err));
62 | }
63 | }
64 | });
65 | })()));
66 | resolve();
67 | }));
68 | });
69 | }
70 | runAlgorithm(algorithm) {
71 | return __awaiter(this, void 0, void 0, function* () {
72 | return new Promise(resolve => {
73 | algorithm.run().then(res => resolve(res));
74 | });
75 | });
76 | }
77 | runAlgorithmOnIteratedElement(elementsArray, algorithm, paramsFromElement) {
78 | return __awaiter(this, void 0, void 0, function* () {
79 | const element = elementsArray[this.cycleIndex];
80 | console.log('\x1b[45m%s\x1b[0m', `RUNNING: ${element}`.padEnd(100, ' '));
81 | let runningAlgorithm;
82 | try {
83 | runningAlgorithm = algorithm(paramsFromElement(element));
84 | }
85 | catch (err) {
86 | helpers_1.log(helpers_1.errorLogTemplate(err));
87 | }
88 | let result = runningAlgorithm ? yield this.runAlgorithm(runningAlgorithm) : false;
89 | if (!result) {
90 | this.cycleIndex = this.cycleIndex + 1;
91 | }
92 | });
93 | }
94 | validateExchange(exchange) {
95 | // @TODO: add validaiton (check if exchange has every required method)
96 | if (!this.config.exchangesToWatch.includes(exchange)) {
97 | helpers_1.validationException('EXCHANGE', `${exchange} IS IGNORED`);
98 | }
99 | }
100 | fetchBalanceAsync(exchange) {
101 | return __awaiter(this, void 0, void 0, function* () {
102 | let balance;
103 | try {
104 | balance = yield exchange.fetchBalance();
105 | }
106 | catch (err) {
107 | helpers_1.log(helpers_1.errorLogTemplate(err));
108 | yield exports.fetchBalance(exchange);
109 | }
110 | return balance;
111 | });
112 | }
113 | fetchBalance(exchange) {
114 | return exchange.fetchBalance();
115 | }
116 | setExchangeProxy(exchange, index = null) {
117 | this.proxyIndex = index === null ? this.proxyIndex + 1 : index;
118 | this.proxyIndex = this.proxyIndex === this.config.proxies.length ? 0 : this.proxyIndex;
119 | exchange.proxy = this.config.proxies[this.proxyIndex];
120 | }
121 | makeOrder(exchange, market, side, amount, price, additionalParams = {}) {
122 | // @wip
123 | if (!this.config.makeOrders)
124 | false;
125 | // @TODO: send Telegram notification after order
126 | // @TODO: setExchangeProxy if config.changeProxyAfterEveryOrder
127 | if (side === constants_1.BUY) {
128 | return exchange.createLimitBuyOrder(market, amount, price, Object.assign(Object.assign(Object.assign({}, additionalParams), this.config.defaultOrderOptions), this.config.orderOptionsByExchange[exchange.id]));
129 | }
130 | if (side === constants_1.SELL) {
131 | return exchange.createLimitSellOrder(market, amount, price, Object.assign(Object.assign(Object.assign({}, additionalParams), this.config.defaultOrderOptions), this.config.orderOptionsByExchange[exchange.id]));
132 | }
133 | return false;
134 | }
135 | }
136 | exports.default = Bot;
137 | //# sourceMappingURL=Bot.js.map
--------------------------------------------------------------------------------
/dist/Bot.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Bot.js","sourceRoot":"","sources":["../src/Bot.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/algorithms/Algorithm.d.ts:
--------------------------------------------------------------------------------
1 | import { Balances, Market } from "ccxt";
2 | import Bot from "../Bot";
3 | export interface AlgorithmCommonParams {
4 | bot: Bot;
5 | markets: Market[];
6 | balances: Balances;
7 | logWarnings: boolean;
8 | }
9 | export default class Algorithm {
10 | onRun: (params: any) => Promise;
11 | run: (params?: any) => Promise;
12 | }
13 |
--------------------------------------------------------------------------------
/dist/algorithms/Algorithm.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | Object.defineProperty(exports, "__esModule", { value: true });
12 | class Algorithm {
13 | constructor() {
14 | this.onRun = () => new Promise(() => false);
15 | this.run = (params) => __awaiter(this, void 0, void 0, function* () {
16 | return yield this.onRun(params);
17 | });
18 | }
19 | }
20 | exports.default = Algorithm;
21 | //# sourceMappingURL=Algorithm.js.map
--------------------------------------------------------------------------------
/dist/algorithms/Algorithm.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Algorithm.js","sourceRoot":"","sources":["../../src/algorithms/Algorithm.ts"],"names":[],"mappings":";;;;;;;;;;;AAUA,MAAqB,SAAS;IAA9B;QACC,UAAK,GAAkC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QAEtE,QAAG,GAAG,CAAO,MAAY,EAAoB,EAAE;YAC9C,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAA,CAAA;IACF,CAAC;CAAA;AAND,4BAMC"}
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageBetweenExchanges.d.ts:
--------------------------------------------------------------------------------
1 | import Algorithm from './Algorithm';
2 | export default class ArbitrageBetweenExchanges extends Algorithm {
3 | }
4 |
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageBetweenExchanges.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const Algorithm_1 = __importDefault(require("./Algorithm"));
7 | class ArbitrageBetweenExchanges extends Algorithm_1.default {
8 | }
9 | exports.default = ArbitrageBetweenExchanges;
10 | //# sourceMappingURL=ArbitrageBetweenExchanges.js.map
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageBetweenExchanges.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"ArbitrageBetweenExchanges.js","sourceRoot":"","sources":["../../src/algorithms/ArbitrageBetweenExchanges.ts"],"names":[],"mappings":";;;;;AAAA,4DAAoC;AAEpC,MAAqB,yBAA0B,SAAQ,mBAAS;CAK/D;AALD,4CAKC"}
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangleWithinExchange.d.ts:
--------------------------------------------------------------------------------
1 | import { Balances, Exchange, Market, MinMax } from 'ccxt';
2 | import Bot from '../Bot';
3 | import { Amount } from '../common/interfaces';
4 | import { BidsOrAsks, BuyOrSell, OrderType } from '../common/types';
5 | import Algorithm from './Algorithm';
6 | import { AlgorithmCommonParams } from './Algorithm';
7 | import { BigNumber } from "bignumber.js";
8 | export interface ArbitrageTriangleWithinExchangeParams extends AlgorithmCommonParams {
9 | exchange: Exchange;
10 | validateMarkets: boolean;
11 | minimumsCorrectionTries?: number;
12 | }
13 | interface DIRECTIONS_SEQUENCE {
14 | orders: BuyOrSell[];
15 | ordersbookSide: BidsOrAsks[];
16 | }
17 | export default class ArbitrageTriangleWithinExchange extends Algorithm {
18 | static ALGORITHM_TYPE: string;
19 | DIRECTIONS_SEQUENCES: DIRECTIONS_SEQUENCE[];
20 | availableDirections: DIRECTIONS_SEQUENCE[];
21 | bot: Bot;
22 | exchange: Exchange;
23 | marketsTriplet: Market[];
24 | availableBalances: Balances;
25 | validateMarkets: boolean;
26 | orderBooks: {
27 | [key: string]: {
28 | bids: number[][];
29 | asks: number[][];
30 | };
31 | };
32 | minimum: {
33 | amount: Amount[];
34 | cost: Amount[];
35 | market: string;
36 | }[];
37 | minimumsCorrectionTries: any;
38 | constructor(params: ArbitrageTriangleWithinExchangeParams);
39 | static validateMarkets(markets: Market[]): boolean;
40 | isBalanceSufficientForOrder(order: BuyOrSell, balance: number, marketLimits: {
41 | amount: MinMax;
42 | price: MinMax;
43 | cost?: MinMax;
44 | }): boolean;
45 | validateBalances(markets: Market[], balances: Balances): boolean;
46 | static getValidatedTripletsOnExchange(exchange: Exchange, toWatch?: string[], showLoadingStatus?: boolean, showLoadingErrors?: boolean, checkBalances?: boolean): string[][];
47 | static throwValidationException(message: any): void;
48 | validate(markets: Market[], balances: Balances): void;
49 | getOrderBooks(): Promise;
50 | getPrecision(market: Market, of: string): any;
51 | getQuantity(market: Market, total: BigNumber, ordersbookSide: BidsOrAsks): {
52 | quantity: BigNumber;
53 | price: number;
54 | };
55 | onArbitrageTriangleWithinExchangeRun(): Promise;
56 | makeOrder(market: any, side: any, amount: any, price: any, additionalParams?: {}): any;
57 | getCost(market: Market, amount: BigNumber, ordersbookSide: BidsOrAsks): {
58 | total: BigNumber;
59 | price: number;
60 | };
61 | getMinCost(market: Market, ordersbookSide: BidsOrAsks): {
62 | total: BigNumber;
63 | price: number;
64 | };
65 | fees(market: Market, amount: number, price: number, buyOrSell: BuyOrSell, orderType?: OrderType): any;
66 | calculateFeeFallback(fees: any, market: any, side: any, amount: any, price: any, takerOrMaker?: string): any;
67 | }
68 | export {};
69 |
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangleWithinExchange.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
13 | };
14 | Object.defineProperty(exports, "__esModule", { value: true });
15 | const constants_1 = require("../common/constants");
16 | const helpers_1 = require("../common/helpers");
17 | const Algorithm_1 = __importDefault(require("./Algorithm"));
18 | const bignumber_js_1 = require("bignumber.js");
19 | class ArbitrageTriangleWithinExchange extends Algorithm_1.default {
20 | constructor(params) {
21 | super();
22 | this.DIRECTIONS_SEQUENCES = [
23 | {
24 | orders: [constants_1.BUY, constants_1.BUY, constants_1.SELL],
25 | ordersbookSide: [constants_1.ASKS, constants_1.ASKS, constants_1.BIDS]
26 | },
27 | {
28 | orders: [constants_1.SELL, constants_1.SELL, constants_1.BUY],
29 | ordersbookSide: [constants_1.BIDS, constants_1.BIDS, constants_1.ASKS]
30 | }
31 | ];
32 | this.availableDirections = [];
33 | this.validateMarkets = true;
34 | this.orderBooks = {};
35 | params.bot.printProfileTime();
36 | const { bot, markets, balances, validateMarkets, exchange, minimumsCorrectionTries } = Object.assign({}, params);
37 | this.bot = bot;
38 | this.exchange = exchange;
39 | this.validateMarkets = validateMarkets;
40 | this.minimumsCorrectionTries = minimumsCorrectionTries || 1000;
41 | this.validate(markets, balances);
42 | this.marketsTriplet = markets;
43 | this.availableBalances = balances;
44 | this.onRun = this.onArbitrageTriangleWithinExchangeRun;
45 | params.bot.printProfileTime();
46 | }
47 | static validateMarkets(markets) {
48 | if (markets.length !== 3) {
49 | this.throwValidationException('InvalidMarketsLength (should be equal to 3)');
50 | }
51 | if (markets[0].quote !== markets[1].base) {
52 | this.throwValidationException('InvalidMarkets (first market quote should be secound market base)');
53 | }
54 | if (markets[1].quote !== markets[2].quote) {
55 | this.throwValidationException('InvalidMarkets (second market quote should be third market base)');
56 | }
57 | if (markets[2].base !== markets[0].base) {
58 | this.throwValidationException('InvalidMarkets (third market base should be first market base)');
59 | }
60 | return true;
61 | }
62 | isBalanceSufficientForOrder(order, balance, marketLimits) {
63 | return order === constants_1.BUY
64 | ? balance >= (marketLimits.cost && marketLimits.cost.min ? marketLimits.cost.min : 0)
65 | : balance >= marketLimits.amount.min;
66 | }
67 | validateBalances(markets, balances) {
68 | // @TODO: check again after getting orders and price
69 | // log('validateBalances');
70 | let isDirAvailable = {
71 | 0: true,
72 | 1: true
73 | };
74 | [0, 1].forEach(dirIndex => {
75 | this.DIRECTIONS_SEQUENCES[dirIndex].orders.forEach((order, index) => {
76 | const isBuy = order === constants_1.BUY;
77 | const quoteOrBase = isBuy ? 'quote' : 'base';
78 | const isSufficient = this.isBalanceSufficientForOrder(order, balances.free[markets[index][quoteOrBase]], markets[index].limits);
79 | if (this.bot.config.logWarnings && !isSufficient) {
80 | helpers_1.log(`\x1b[33mBalance ${balances.free[markets[index][quoteOrBase]]} ${markets[index].quote} under market ${markets[index].symbol} MIN ${markets[index].limits[isBuy ? 'cost' : 'amount'].min} ${isBuy ? 'cost' : 'amount'} limit\x1b[0m`);
81 | }
82 | isDirAvailable[dirIndex] = isDirAvailable[dirIndex] && isSufficient;
83 | });
84 | if (isDirAvailable[dirIndex]) {
85 | this.availableDirections.push(this.DIRECTIONS_SEQUENCES[dirIndex]);
86 | }
87 | });
88 | return this.availableDirections.length > 0;
89 | }
90 | static getValidatedTripletsOnExchange(exchange, toWatch = [], showLoadingStatus = true, showLoadingErrors = false, checkBalances = false) {
91 | const validatedTriplets = [];
92 | let marketsNumber = Object.entries(exchange.markets).length;
93 | let i = 0;
94 | for (let marketA in exchange.markets) {
95 | if (showLoadingStatus) {
96 | console.clear();
97 | helpers_1.log(`Loading ${exchange.id} ArbitrageTriangleWithinExchange ${((i / marketsNumber) * 100).toFixed()}%`);
98 | }
99 | i++;
100 | if (toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketA].base) < 0 || toWatch.indexOf(exchange.markets[marketA].quote) < 0))
101 | continue;
102 | for (let marketB in exchange.markets) {
103 | if (toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketB].base) < 0 || toWatch.indexOf(exchange.markets[marketB].quote) < 0))
104 | continue;
105 | for (let marketC in exchange.markets) {
106 | if (toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketC].base) < 0 || toWatch.indexOf(exchange.markets[marketC].quote) < 0))
107 | continue;
108 | if (marketA === marketB || marketA === marketC || marketB === marketC) {
109 | continue;
110 | }
111 | const exchangeMarketA = exchange.markets[marketA];
112 | const exchangeMarketB = exchange.markets[marketB];
113 | const exchangeMarketC = exchange.markets[marketC];
114 | if (!exchangeMarketA.active || !exchangeMarketB.active || !exchangeMarketC.active) {
115 | continue;
116 | }
117 | try {
118 | if (ArbitrageTriangleWithinExchange.validateMarkets([
119 | exchange.markets[marketA],
120 | exchange.markets[marketB],
121 | exchange.markets[marketC]
122 | ])) {
123 | validatedTriplets.push([marketA, marketB, marketC]);
124 | }
125 | }
126 | catch (err) {
127 | if (showLoadingErrors) {
128 | helpers_1.log(`\x1b[33mLoading ${exchange.id} ArbitrageTriangleWithinExchange ${err.name} ${err.message}\x1b[0m`);
129 | }
130 | }
131 | }
132 | }
133 | }
134 | return validatedTriplets;
135 | }
136 | static throwValidationException(message) {
137 | throw helpers_1.validationException(this.ALGORITHM_TYPE, message);
138 | }
139 | validate(markets, balances) {
140 | if (this.validateMarkets) {
141 | ArbitrageTriangleWithinExchange.validateMarkets(markets);
142 | }
143 | if (!this.validateBalances(markets, balances)) {
144 | ArbitrageTriangleWithinExchange.throwValidationException(`Balances insufficient for ${markets[0].symbol} ${markets[1].symbol} ${markets[2].symbol}`);
145 | }
146 | }
147 | getOrderBooks() {
148 | return __awaiter(this, void 0, void 0, function* () {
149 | // log('getOrderBooks');
150 | this.bot.printProfileTime();
151 | yield Promise.all(this.marketsTriplet.map((market) => __awaiter(this, void 0, void 0, function* () {
152 | // log('getOrderBook ' + this.exchange.name);
153 | let orderbook = yield this.exchange.fetchL2OrderBook(market.symbol, this.bot.config.orderBookLimit);
154 | this.orderBooks[market.symbol] = { [constants_1.ASKS]: orderbook[constants_1.ASKS], [constants_1.BIDS]: orderbook[constants_1.BIDS] };
155 | this.bot.printProfileTime();
156 | })));
157 | });
158 | }
159 | getPrecision(market, of) {
160 | // exceptations
161 | if (this.exchange.id === 'bleutrade') {
162 | switch (of) {
163 | case 'quote':
164 | return market.info.DivisorDecimal ? market.info.DivisorDecimal : 8;
165 | case 'base':
166 | return market.info.DividendDecimal ? market.info.DividendDecimal : 8;
167 | default:
168 | return market.precision[of];
169 | }
170 | }
171 | return market.precision[of];
172 | }
173 | getQuantity(market, total, ordersbookSide) {
174 | let calcAmount = new bignumber_js_1.BigNumber(0);
175 | let orderbookIndex = 0;
176 | let price = 0;
177 | let order;
178 | let stop = false;
179 | do {
180 | order = this.orderBooks[market.symbol][ordersbookSide][orderbookIndex];
181 | if (!order) {
182 | break;
183 | }
184 | calcAmount = calcAmount.plus(order ? order[1] : 0);
185 | // console.log(orderbookIndex, order, total.toString(), calcAmount.toString(), calcAmount.times(order[0]).isGreaterThanOrEqualTo(total));
186 | if (calcAmount.times(order[0]).isGreaterThanOrEqualTo(total)) {
187 | calcAmount = new bignumber_js_1.BigNumber(total).dividedBy(order[0]);
188 | price = order[0];
189 | stop = true;
190 | }
191 | else {
192 | orderbookIndex = orderbookIndex + 1;
193 | }
194 | } while (!stop);
195 | // console.log(order, !order);
196 | if (!order) {
197 | return {
198 | quantity: new bignumber_js_1.BigNumber(Infinity),
199 | price: Infinity
200 | };
201 | }
202 | return {
203 | // total: new BigNumber(amount).times(price).precision(market.precision.quote),
204 | // @TODO: add rounding depends on buyOrSell
205 | quantity: new bignumber_js_1.BigNumber(calcAmount.toPrecision(this.getPrecision(market, ordersbookSide === 'asks' ? 'quote' : 'base'))),
206 | price: price
207 | };
208 | }
209 | onArbitrageTriangleWithinExchangeRun() {
210 | return __awaiter(this, void 0, void 0, function* () {
211 | this.bot.printProfileTime();
212 | return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
213 | // log(`onArbitrageTriangleWithinExchangeRun ${this.marketsTriplet.map(market => market.symbol).join(' ')}`)
214 | yield this.getOrderBooks();
215 | this.availableDirections.forEach((direction, directionIndex) => __awaiter(this, void 0, void 0, function* () {
216 | let success = false;
217 | do {
218 | let minAB, qunatityAB, totalAB, minAC, minBC, qunatityBC, totalBC, qunatityAC, totalAC, feesAB, feesBC, feesAC, result;
219 | let step = 1;
220 | let reachedMinimum = false;
221 | minAB = Math.max(this.marketsTriplet[0].limits.amount.min, this.marketsTriplet[2].limits.amount.min);
222 | const firstSide = direction.orders[0] === 'buy';
223 | do {
224 | if (this.bot.config.logAdditionalWarnings && step > 1) {
225 | console.log("\x1b[33m%s\x1b[0m", `MINIMUMS CORRECTION (${step}. times)`);
226 | }
227 | if (firstSide) {
228 | qunatityAB = new bignumber_js_1.BigNumber(minAB).multipliedBy(step + this.bot.config.feesRate); // [A], market A/B
229 | totalAB = this.getCost(this.marketsTriplet[0], qunatityAB, constants_1.bidsOrAsksByBuyOrSell[direction.orders[0]]); // [B], market A/B
230 | feesAB = this.fees(this.marketsTriplet[0], qunatityAB.toNumber(), totalAB.price, direction.orders[0]);
231 | qunatityBC = totalAB.total; // [B], market B/C
232 | totalBC = this.getCost(this.marketsTriplet[1], qunatityBC, constants_1.bidsOrAsksByBuyOrSell[direction.orders[1]]); // [C], market B/C
233 | feesBC = this.fees(this.marketsTriplet[1], qunatityBC.toNumber(), totalBC.price, direction.orders[1]);
234 | qunatityAC = new bignumber_js_1.BigNumber(minAB).multipliedBy(step).minus(feesAB.cost); // [A], market A/C
235 | totalAC = this.getCost(this.marketsTriplet[2], qunatityAC, constants_1.bidsOrAsksByBuyOrSell[direction.orders[2]]); // [C], market A/C
236 | feesAC = this.fees(this.marketsTriplet[2], qunatityAC.toNumber(), totalAC.price, direction.orders[2]);
237 | // results in C
238 | result = totalAC.total.minus(totalBC.total).minus(feesAC.cost);
239 | }
240 | else {
241 | qunatityAC = new bignumber_js_1.BigNumber(minAB).multipliedBy(step + this.bot.config.feesRate); // [A], market A/C
242 | totalAC = this.getCost(this.marketsTriplet[2], qunatityAC, constants_1.bidsOrAsksByBuyOrSell[direction.orders[2]]); // [C], market A/C
243 | feesAC = this.fees(this.marketsTriplet[2], qunatityAC.toNumber(), totalAC.price, direction.orders[2]);
244 | qunatityAB = new bignumber_js_1.BigNumber(minAB).multipliedBy(step).minus(feesAC.cost); // [A], market A/B
245 | totalAB = this.getCost(this.marketsTriplet[0], qunatityAB, constants_1.bidsOrAsksByBuyOrSell[direction.orders[0]]); // [B], market A/B
246 | feesAB = this.fees(this.marketsTriplet[0], qunatityAB.toNumber(), totalAB.price, direction.orders[0]);
247 | qunatityBC = totalAB.total.minus(feesAB.cost); // [B], market B/C
248 | totalBC = this.getCost(this.marketsTriplet[1], qunatityBC, constants_1.bidsOrAsksByBuyOrSell[direction.orders[1]]); // [C], market B/C
249 | feesBC = this.fees(this.marketsTriplet[1], qunatityBC.toNumber(), totalBC.price, direction.orders[1]);
250 | // results in C
251 | result = totalBC.total.minus(totalAC.total).minus(feesBC.cost);
252 | }
253 | step = step + 1;
254 | reachedMinimum = !(qunatityAB.isLessThan(this.marketsTriplet[0].limits.amount.min)
255 | || qunatityBC.isLessThan(this.marketsTriplet[1].limits.amount.min)
256 | || qunatityAC.isLessThan(this.marketsTriplet[2].limits.amount.min)
257 | || totalAB.total < this.marketsTriplet[0].limits.cost.min
258 | || totalBC.total < this.marketsTriplet[1].limits.cost.min
259 | || totalAC.total < this.marketsTriplet[2].limits.cost.min);
260 | } while (!reachedMinimum && step < this.minimumsCorrectionTries);
261 | // @TODO: add fees to AB or AC (depends on ordersDirection)
262 | this.bot.config.logAdditionalDetails && console.table({
263 | ' ': {
264 | 'direction (max depth)': '-',
265 | '1. price': '-',
266 | '1. amount': '-',
267 | 'min cost': '(by amount)',
268 | 'amount min': '(limit)',
269 | 'cost min': '(limit)',
270 | 'price min': '(limit)'
271 | },
272 | [`${this.marketsTriplet[0].symbol}`]: {
273 | 'direction (max depth)': `${direction.orders[0]} (${this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]].length} ${direction.ordersbookSide[0]})`,
274 | '1. price': this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]][0][0],
275 | '1. amount': this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]][0][1],
276 | 'min cost': this.getMinCost(this.marketsTriplet[0], direction.ordersbookSide[0]).total.toString(),
277 | 'amount min': this.marketsTriplet[0].limits.amount.min,
278 | 'cost min': this.marketsTriplet[0].limits.cost.min,
279 | 'price min': this.marketsTriplet[0].limits.price.min
280 | },
281 | [`${this.marketsTriplet[1].symbol}`]: {
282 | 'direction (max depth)': `${direction.orders[1]} (${this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]].length} ${direction.ordersbookSide[1]})`,
283 | '1. price': this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]][0][0],
284 | '1. amount': this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]][0][1],
285 | 'min cost': this.getMinCost(this.marketsTriplet[1], direction.ordersbookSide[1]).total.toString(),
286 | 'amount min': this.marketsTriplet[1].limits.amount.min,
287 | 'cost min': this.marketsTriplet[1].limits.cost.min,
288 | 'price min': this.marketsTriplet[1].limits.price.min
289 | },
290 | [`${this.marketsTriplet[2].symbol}`]: {
291 | 'direction (max depth)': `${direction.orders[2]} (${this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]].length} ${direction.ordersbookSide[2]})`,
292 | '1. price': this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]][0][0],
293 | '1. amount': this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]][0][1],
294 | 'min cost': this.getMinCost(this.marketsTriplet[2], direction.ordersbookSide[2]).total.toString(),
295 | 'amount min': this.marketsTriplet[2].limits.amount.min,
296 | 'cost min': this.marketsTriplet[2].limits.cost.min,
297 | 'price min': this.marketsTriplet[2].limits.price.min
298 | }
299 | });
300 | this.bot.config.logDetails && console.table({
301 | [this.marketsTriplet[0].symbol]: {
302 | direction: direction.orders[0],
303 | quantity: `${qunatityAB.toString()} ${this.marketsTriplet[0].base}`,
304 | ' ': 'for',
305 | total: `${totalAB.total.toPrecision(this.getPrecision(this.marketsTriplet[0], 'quote')).toString()} ${this.marketsTriplet[0].quote}`,
306 | fees: `${feesAB.cost} ${feesAB.currency}`,
307 | 'fees rate': feesAB.rate
308 | },
309 | [this.marketsTriplet[1].symbol]: {
310 | direction: direction.orders[1],
311 | quantity: `${qunatityBC.toString()} ${this.marketsTriplet[1].base}`,
312 | ' ': 'for',
313 | total: `${totalBC.total.toPrecision(this.getPrecision(this.marketsTriplet[1], 'quote')).toString()} ${this.marketsTriplet[1].quote}`,
314 | fees: `${feesBC.cost} ${feesBC.currency}`,
315 | 'fees rate': feesBC.rate
316 | },
317 | [this.marketsTriplet[2].symbol]: {
318 | direction: direction.orders[2],
319 | quantity: `${qunatityAC.toString()} ${this.marketsTriplet[2].base}`,
320 | ' ': 'for',
321 | total: `${totalAC.total.toPrecision(this.getPrecision(this.marketsTriplet[2], 'quote')).toString()} ${this.marketsTriplet[2].quote}`,
322 | fees: `${feesAC.cost} ${feesAC.currency}`,
323 | 'fees rate': feesAC.rate
324 | }
325 | });
326 | const resultString = `${result.toString()} ${this.marketsTriplet[1].quote}`.padStart(100, ' ');
327 | if (result.isGreaterThan(0)) {
328 | console.log("\x1b[42m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
329 | // @TODO: check balances, make order (then check again if there iss still arbitrage)
330 | // @TODO: check minimums again
331 | if (!reachedMinimum) {
332 | }
333 | else {
334 | // use await instead promise.all if proxies list is not configured
335 | // await Promise.all([
336 | // this.makeOrder(this.marketsTriplet[0].symbol, direction.orders[0], qunatityAB, totalAB.price),
337 | // this.makeOrder(this.marketsTriplet[1].symbol, direction.orders[1], qunatityBC, totalBC.price),
338 | // this.makeOrder(this.marketsTriplet[2].symbol, direction.orders[2], qunatityAC, totalAC.price)
339 | // ]).then((values) => {
340 | // console.log(values);
341 | // success = ;
342 | // });
343 | }
344 | }
345 | else if (!result.isEqualTo(0)) {
346 | console.log("\x1b[41m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
347 | }
348 | else {
349 | console.log("\x1b[44m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
350 | }
351 | success = false;
352 | } while (success);
353 | }));
354 | resolve(false);
355 | }));
356 | });
357 | }
358 | ;
359 | makeOrder(market, side, amount, price, additionalParams = {}) {
360 | return this.bot.makeOrder(this.exchange, market, side, amount, price, additionalParams);
361 | }
362 | getCost(market, amount, ordersbookSide) {
363 | let calcAmount = new bignumber_js_1.BigNumber(0);
364 | let orderbookIndex = 0;
365 | let price = 0;
366 | let order;
367 | do {
368 | order = this.orderBooks[market.symbol][ordersbookSide][orderbookIndex];
369 | if (!order) {
370 | break;
371 | }
372 | calcAmount = calcAmount.plus(order ? order[1] : 0);
373 | // console.log(orderbookIndex, order, amount.toString(), calcAmount.toString());
374 | if (calcAmount.isGreaterThanOrEqualTo(amount)) {
375 | calcAmount = amount;
376 | price = order[0];
377 | }
378 | else {
379 | orderbookIndex = orderbookIndex + 1;
380 | }
381 | } while (calcAmount !== amount);
382 | // console.log(order, !order);
383 | if (!order) {
384 | return {
385 | total: new bignumber_js_1.BigNumber(Infinity),
386 | price: Infinity
387 | };
388 | }
389 | // const isBuy = buyOrSellByBidsOrAsks[ordersbookSide] === BUY;
390 | // const quoteOrBase = isBuy ? 'base': 'quote';
391 | return {
392 | // total: new BigNumber(amount).times(price).precision(market.precision.quote),
393 | total: new bignumber_js_1.BigNumber(new bignumber_js_1.BigNumber(amount).times(price).toPrecision(this.getPrecision(market, 'quote'))),
394 | price: price
395 | };
396 | }
397 | getMinCost(market, ordersbookSide) {
398 | return this.getCost(market, new bignumber_js_1.BigNumber(market.limits.amount.min), ordersbookSide);
399 | }
400 | fees(market, amount, price, buyOrSell, orderType = 'limit') {
401 | if (!Number.isFinite(amount) || !Number.isFinite(price)) {
402 | return {
403 | 'cost': Infinity,
404 | };
405 | }
406 | const fees = this.exchange.calculate_fee(market.symbol, orderType, buyOrSell, amount, price, 'taker');
407 | if ((!fees.cost && this.bot.config.zeroesFeesCorrection) || this.bot.config.correctAllFees) {
408 | return this.calculateFeeFallback(fees, market, buyOrSell, amount, price);
409 | }
410 | return fees;
411 | }
412 | calculateFeeFallback(fees, market, side, amount, price, takerOrMaker = 'taker') {
413 | if (this.bot.config.logAdditionalWarnings) {
414 | console.log("\x1b[33m%s\x1b[0m", `FEES CORRECTION ${market.symbol} (to correct: ${fees.cost} ${fees.currency})`);
415 | }
416 | return Object.assign(Object.assign({}, fees), { cost: helpers_1.floatRound(amount * price * this.exchange.markets[market.symbol].taker, market['precision']['price'], this.bot.config.feesRoundType) });
417 | }
418 | }
419 | exports.default = ArbitrageTriangleWithinExchange;
420 | ArbitrageTriangleWithinExchange.ALGORITHM_TYPE = 'ArbitrageTriangleWithinExchange';
421 | //# sourceMappingURL=ArbitrageTriangleWithinExchange.js.map
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangleWithinExchange.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"ArbitrageTriangleWithinExchange.js","sourceRoot":"","sources":["../../src/algorithms/ArbitrageTriangleWithinExchange.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangularBetweenExchanges.d.ts:
--------------------------------------------------------------------------------
1 | import Algorithm from './Algorithm';
2 | export default class ArbitrageTriangularBetweenExchanges extends Algorithm {
3 | }
4 |
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangularBetweenExchanges.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const Algorithm_1 = __importDefault(require("./Algorithm"));
7 | class ArbitrageTriangularBetweenExchanges extends Algorithm_1.default {
8 | }
9 | exports.default = ArbitrageTriangularBetweenExchanges;
10 | //# sourceMappingURL=ArbitrageTriangularBetweenExchanges.js.map
--------------------------------------------------------------------------------
/dist/algorithms/ArbitrageTriangularBetweenExchanges.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"ArbitrageTriangularBetweenExchanges.js","sourceRoot":"","sources":["../../src/algorithms/ArbitrageTriangularBetweenExchanges.ts"],"names":[],"mappings":";;;;;AAAA,4DAAoC;AAEpC,MAAqB,mCAAoC,SAAQ,mBAAS;CAKzE;AALD,sDAKC"}
--------------------------------------------------------------------------------
/dist/common/config.d.ts:
--------------------------------------------------------------------------------
1 | import { BotConfig } from "../Bot";
2 | declare const _default: BotConfig;
3 | export default _default;
4 | export declare const config: (config: any) => BotConfig;
5 |
--------------------------------------------------------------------------------
/dist/common/config.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.config = void 0;
4 | const defaultConfig = require("../config.json");
5 | exports.default = Object.assign({}, defaultConfig);
6 | const config = (config) => (Object.assign(Object.assign({}, defaultConfig), config));
7 | exports.config = config;
8 | //# sourceMappingURL=config.js.map
--------------------------------------------------------------------------------
/dist/common/config.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/common/config.ts"],"names":[],"mappings":";;;AAAA,gDAAiD;AAIjD,kBAAe,kBACR,aAAa,CAEN,CAAA;AAEP,MAAM,MAAM,GAAG,CAAC,MAAW,EAAE,EAAE,CAAC,iCAChC,aAAa,GACb,MAAM,EAEE,CAAA;AAJF,QAAA,MAAM,UAIJ"}
--------------------------------------------------------------------------------
/dist/common/constants.d.ts:
--------------------------------------------------------------------------------
1 | export declare const BUY = "buy";
2 | export declare const SELL = "sell";
3 | export declare const ASKS = "asks";
4 | export declare const BIDS = "bids";
5 | export declare const buyOrSellByBidsOrAsks: {
6 | asks: string;
7 | bids: string;
8 | };
9 | export declare const bidsOrAsksByBuyOrSell: {
10 | buy: string;
11 | sell: string;
12 | };
13 |
--------------------------------------------------------------------------------
/dist/common/constants.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.bidsOrAsksByBuyOrSell = exports.buyOrSellByBidsOrAsks = exports.BIDS = exports.ASKS = exports.SELL = exports.BUY = void 0;
4 | exports.BUY = 'buy';
5 | exports.SELL = 'sell';
6 | exports.ASKS = 'asks';
7 | exports.BIDS = 'bids';
8 | exports.buyOrSellByBidsOrAsks = {
9 | [exports.ASKS]: exports.BUY,
10 | [exports.BIDS]: exports.SELL
11 | };
12 | exports.bidsOrAsksByBuyOrSell = {
13 | [exports.BUY]: exports.ASKS,
14 | [exports.SELL]: exports.BIDS
15 | };
16 | //# sourceMappingURL=constants.js.map
--------------------------------------------------------------------------------
/dist/common/constants.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":";;;AAEa,QAAA,GAAG,GAAG,KAAK,CAAC;AACZ,QAAA,IAAI,GAAG,MAAM,CAAC;AACd,QAAA,IAAI,GAAG,MAAM,CAAC;AACd,QAAA,IAAI,GAAG,MAAM,CAAC;AAEd,QAAA,qBAAqB,GAAG;IACpC,CAAC,YAAI,CAAC,EAAE,WAAG;IACX,CAAC,YAAI,CAAC,EAAE,YAAI;CACZ,CAAA;AAEY,QAAA,qBAAqB,GAAG;IACpC,CAAC,WAAG,CAAC,EAAE,YAAI;IACX,CAAC,YAAI,CAAC,EAAE,YAAI;CACZ,CAAA"}
--------------------------------------------------------------------------------
/dist/common/errors.d.ts:
--------------------------------------------------------------------------------
1 | import Bot from "../Bot";
2 | import { BaseError, Exchange } from 'ccxt';
3 | export default class BotErrorHandler {
4 | static handleError: (bot: Bot, err: BaseError, exchange: Exchange, market?: string) => void;
5 | }
6 |
--------------------------------------------------------------------------------
/dist/common/errors.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5 | }) : (function(o, m, k, k2) {
6 | if (k2 === undefined) k2 = k;
7 | o[k2] = m[k];
8 | }));
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
11 | }) : function(o, v) {
12 | o["default"] = v;
13 | });
14 | var __importStar = (this && this.__importStar) || function (mod) {
15 | if (mod && mod.__esModule) return mod;
16 | var result = {};
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 | __setModuleDefault(result, mod);
19 | return result;
20 | };
21 | Object.defineProperty(exports, "__esModule", { value: true });
22 | const ccxt_1 = __importStar(require("ccxt"));
23 | class BotErrorHandler {
24 | }
25 | exports.default = BotErrorHandler;
26 | BotErrorHandler.handleError = (bot, err, exchange, market = '') => {
27 | const changeProxy = bot.config.enableProxy && bot.config.changeProxyAfterAnyNetworkError && (err instanceof ccxt_1.default.RequestTimeout ||
28 | err instanceof ccxt_1.default.ExchangeNotAvailable ||
29 | err instanceof ccxt_1.default.NetworkError ||
30 | err instanceof ccxt_1.default.DDoSProtection);
31 | if (changeProxy) {
32 | bot.setExchangeProxy(exchange);
33 | }
34 | if (bot.config.logError) {
35 | // @TODO: format
36 | console.log(exchange.symbol, market, typeof err);
37 | if (!bot.config.logErrorDetails)
38 | console.log(err);
39 | return;
40 | }
41 | if (err instanceof ccxt_1.BaseError) {
42 | throw err;
43 | }
44 | };
45 | //# sourceMappingURL=errors.js.map
--------------------------------------------------------------------------------
/dist/common/errors.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/common/errors.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AACA,6CAAiD;AAEjD,MAAqB,eAAe;;AAApC,kCAwBC;AAvBU,2BAAW,GAAG,CAAC,GAAQ,EAAE,GAAc,EAAE,QAAkB,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE;IAC/E,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC,+BAA+B,IAAI,CACxF,GAAG,YAAY,cAAI,CAAC,cAAc;QAClC,GAAG,YAAY,cAAI,CAAC,oBAAoB;QACxC,GAAG,YAAY,cAAI,CAAC,YAAY;QAChC,GAAG,YAAY,cAAI,CAAC,cAAc,CACrC,CAAC;IAEF,IAAI,WAAW,EAAE;QACb,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;KAClC;IAED,IAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE;QACpB,gBAAgB;QAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC;QACjD,IAAG,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjD,OAAO;KACV;IAED,IAAI,GAAK,YAAW,gBAAS,EAAE;QAC3B,MAAM,GAAG,CAAC;KACb;AACL,CAAC,CAAA"}
--------------------------------------------------------------------------------
/dist/common/helpers.d.ts:
--------------------------------------------------------------------------------
1 | export declare const errorLogTemplate: (e: Error) => string;
2 | export declare function log(message: any, type?: 'log' | 'warn', run?: boolean): void;
3 | export declare const validationException: (algorithmType: string, message: string) => {
4 | message: string;
5 | name: string;
6 | };
7 | export declare const floatRound: (value: number, precision: number, type: 'ceil' | 'floor' | 'round') => number;
8 |
--------------------------------------------------------------------------------
/dist/common/helpers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.floatRound = exports.validationException = exports.log = exports.errorLogTemplate = void 0;
4 | const errorLogTemplate = (e) => `\x1b[31m${e.name} ${e.message}\x1b[0m`;
5 | exports.errorLogTemplate = errorLogTemplate;
6 | function log(message, type = 'log', run = true) {
7 | if (!run) {
8 | return;
9 | }
10 | console[type](message);
11 | }
12 | exports.log = log;
13 | const validationException = (algorithmType, message) => ({
14 | message: `\n\x1b[31m${message}\x1b[0m`,
15 | name: `\x1b[31m${algorithmType} Validation Exception${message ? ':' : ''}\x1b[0m`
16 | });
17 | exports.validationException = validationException;
18 | const floatRound = (value, precision, type) => {
19 | switch (type) {
20 | case 'ceil': return Math.ceil(value * Math.pow(10, precision)) / Math.pow(10, precision);
21 | case 'floor': return Math.floor(value * Math.pow(10, precision)) / Math.pow(10, precision);
22 | case 'round': return Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision);
23 | }
24 | };
25 | exports.floatRound = floatRound;
26 | //# sourceMappingURL=helpers.js.map
--------------------------------------------------------------------------------
/dist/common/helpers.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/common/helpers.ts"],"names":[],"mappings":";;;AAAO,MAAM,gBAAgB,GAAG,CAAC,CAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,SAAS,CAAC;AAAzE,QAAA,gBAAgB,oBAAyD;AAEtF,SAAgB,GAAG,CAAC,OAAO,EAAE,OAAuB,KAAK,EAAE,GAAG,GAAG,IAAI;IACpE,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO;KAAE;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;AACxB,CAAC;AAHD,kBAGC;AAEM,MAAM,mBAAmB,GAAG,CAAC,aAAqB,EAAE,OAAe,EAAE,EAAE,CAAC,CAAC;IAC7E,OAAO,EAAE,aAAa,OAAO,SAAS;IACtC,IAAI,EAAE,WAAW,aAAa,wBAAwB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,SAAS;CACnF,CAAC,CAAC;AAHU,QAAA,mBAAmB,uBAG7B;AAGI,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,SAAiB,EAAE,IAA+B,EAAE,EAAE;IAC7F,QAAO,IAAI,EAAC;QACR,KAAK,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QACxF,KAAK,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QAC1F,KAAK,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;KAC7F;AACJ,CAAC,CAAA;AANY,QAAA,UAAU,cAMtB"}
--------------------------------------------------------------------------------
/dist/common/interfaces.d.ts:
--------------------------------------------------------------------------------
1 | import { BidsOrAsks } from "./types";
2 | export interface Amount {
3 | symbol: string;
4 | amount: number;
5 | }
6 | export interface Order {
7 | price: number;
8 | amount: number;
9 | type: BidsOrAsks;
10 | }
11 | export interface ValidationException {
12 | message: string;
13 | name: string;
14 | }
15 |
--------------------------------------------------------------------------------
/dist/common/interfaces.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | //# sourceMappingURL=interfaces.js.map
--------------------------------------------------------------------------------
/dist/common/interfaces.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/common/interfaces.ts"],"names":[],"mappings":""}
--------------------------------------------------------------------------------
/dist/common/types.d.ts:
--------------------------------------------------------------------------------
1 | export declare type MarketOrBase = 'market' | 'base';
2 | export declare type BidsOrAsks = 'bids' | 'asks';
3 | export declare type OrderType = 'market' | 'limit';
4 | export declare type BuyOrSell = 'buy' | 'sell';
5 |
--------------------------------------------------------------------------------
/dist/common/types.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | //# sourceMappingURL=types.js.map
--------------------------------------------------------------------------------
/dist/common/types.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/common/types.ts"],"names":[],"mappings":""}
--------------------------------------------------------------------------------
/dist/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeOrders": false,
3 | "profile": false,
4 | "logDetails": false,
5 | "logAdditionalDetails": false,
6 | "logWarnings": false,
7 | "logAdditionalWarnings": false,
8 | "logError": false,
9 | "logErrorDetails": false,
10 | "exchangesToWatch": ["example"],
11 | "orderBookLimit": 1000,
12 | "exchangeOptions": {
13 | "example": {}
14 | },
15 | "defaultExchangeOptions": {
16 | "enableRateLimit": true,
17 | "proxy": false
18 | },
19 | "keys": {
20 | "example": {
21 | "apiKey": "123456789ABC",
22 | "secret": "123456789DEF"
23 | }
24 | },
25 | "currenciesToWatch": [],
26 | "feesRate": 0.001,
27 | "zeroesFeesCorrection": true,
28 | "correctAllFees": false,
29 | "feesRoundType": "ceil",
30 | "orderOptionsByExchange": {},
31 | "defaultOrderOptions": {},
32 | "parallelOrders": false,
33 | "enableProxy": false,
34 | "changeProxyAfterEveryOrder": false,
35 | "changeProxyAfterAnyNetworkError": false,
36 | "proxies": [],
37 | "telegram": {
38 | "token": "",
39 | "startPhrase": "",
40 | "stopPhrase": "",
41 | "chats": [],
42 | "logErrors": false
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/dist/config.local.example.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeOrders": false,
3 | "profile": false,
4 | "logDetails": true,
5 | "logAdditionalDetails": true,
6 | "logWarnings": false,
7 | "logAdditionalWarnings": false,
8 | "logError": false,
9 | "logErrorDetails": false,
10 | "exchangesToWatch": ["binance"],
11 | "orderBookLimit": 1000,
12 | "exchangeOptions": {
13 | "bleutrade": {},
14 | "binance": {}
15 | },
16 | "keys": {
17 | "bleutrade": {
18 | "apiKey": "",
19 | "secret": ""
20 | },
21 | "binance": {
22 | "apiKey": "",
23 | "secret": ""
24 | }
25 | },
26 | "currenciesToWatch": ["ETH", "BTC", "BNB"],
27 | "orderOptionsByExchange": {
28 | "binance": {
29 | "adjustForTimeDifference": true
30 | }
31 | },
32 | "defaultOrderOptions": {},
33 | "enableProxy": false,
34 | "changeProxyAfterEveryOrder": false,
35 | "changeProxyAfterAnyNetworkError": false,
36 | "parallelOrders": false,
37 | "proxies": [],
38 | "telegram": {
39 | "token": "",
40 | "startPhrase": "",
41 | "stopPhrase": "",
42 | "chats": [],
43 | "logErrors": false
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/dist/example.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/dist/example.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
13 | };
14 | Object.defineProperty(exports, "__esModule", { value: true });
15 | const ArbitrageTriangleWithinExchange_1 = __importDefault(require("./algorithms/ArbitrageTriangleWithinExchange"));
16 | const Bot_1 = __importDefault(require("./Bot"));
17 | const helpers_1 = require("./common/helpers");
18 | const config_1 = require("./common/config");
19 | const localConfig = require("./config.local.example.json");
20 | // ccxt.d.ts
21 | process
22 | .on('unhandledRejection', (reason, p) => {
23 | console.error(reason, '\x1b[34mUnhandled Rejection at Promise\x1b[0m', p);
24 | })
25 | .on('uncaughtException', err => {
26 | console.error(err, '\x1b[34mUncaught Exception thrown\x1b[0m');
27 | process.exit(1);
28 | });
29 | // add triggering by socket (additional lib per exchange, compatible with ccxt)
30 | // crawl over markets by default
31 | // @TODO: consider:
32 | // moving to new CPU worker (optionally behind robin rounded proxy) if there is no other already running with given exchanges, otherwise add to queue
33 | const bot = new Bot_1.default(config_1.config(localConfig));
34 | startBot();
35 | function startBot() {
36 | return __awaiter(this, void 0, void 0, function* () {
37 | bot.init().then(() => startArbitrageTriangleWithinExchangeAlgorithm(), err => helpers_1.log(helpers_1.errorLogTemplate(err)));
38 | bot.printProfileTime();
39 | });
40 | }
41 | function startArbitrageTriangleWithinExchangeAlgorithm() {
42 | Object.entries(bot.exchanges).forEach(([key, exchange]) => __awaiter(this, void 0, void 0, function* () {
43 | try {
44 | yield exchange.loadMarkets();
45 | const validatedTriplets = ArbitrageTriangleWithinExchange_1.default.getValidatedTripletsOnExchange(exchange, bot.config.currenciesToWatch);
46 | helpers_1.log(`\x1b[32mFound ${validatedTriplets.length} ArbitrageTriangleWithinExchange triplets on ${exchange.id}\x1b[0m`);
47 | console.table(validatedTriplets);
48 | bot.cycle(validatedTriplets, (params) => {
49 | return new ArbitrageTriangleWithinExchange_1.default(params);
50 | }, element => ({
51 | exchange: exchange,
52 | bot: bot,
53 | markets: [
54 | exchange.markets[element[0]],
55 | exchange.markets[element[1]],
56 | exchange.markets[element[2]]
57 | ],
58 | balances: bot.balances[key],
59 | validateMarkets: false
60 | }), () => {
61 | helpers_1.log(`\x1b[32mCycle ${exchange.id} ArbitrageTriangleWithinExchange\x1b[0m`);
62 | });
63 | }
64 | catch (err) {
65 | helpers_1.log(helpers_1.errorLogTemplate(err));
66 | bot.printProfileTime();
67 | }
68 | }));
69 | }
70 | // @TODO: add checking
71 | // {
72 | // ...bot.config.ignore,
73 | // ...bot.config.MARKET_EXCHANGE_KEY.ignore,
74 | // ...bot.config.MARKET_KEY.ignore,
75 | // ...bot.config.MARKET_EXCHANGE_KEY.MARKET_KEY.ignore
76 | // }
77 | //# sourceMappingURL=example.js.map
--------------------------------------------------------------------------------
/dist/example.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"example.js","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,mHAAsI;AACtI,gDAAwB;AACxB,8CAAyD;AACzD,4CAAyC;AACzC,2DAA4D;AAC5D,YAAY;AAEZ,OAAO;KACN,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACpC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,+CAA+C,EAAE,CAAC,CAAC,CAAC;AAC9E,CAAC,CAAC;KACD,EAAE,CAAC,mBAAmB,EAAE,GAAG,CAAC,EAAE;IAC3B,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,0CAA0C,CAAC,CAAC;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,gCAAgC;AAEhC,mBAAmB;AACnB,wJAAwJ;AAExJ,MAAM,GAAG,GAAG,IAAI,aAAG,CAAC,eAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AACzC,QAAQ,EAAE,CAAC;AAEX,SAAe,QAAQ;;QACnB,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CACX,GAAG,EAAE,CAAC,6CAA6C,EAAE,EACrD,GAAG,CAAC,EAAE,CAAC,aAAG,CAAC,0BAAgB,CAAC,GAAG,CAAC,CAAC,CACpC,CAAC;QACF,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC3B,CAAC;CAAA;AAED,SAAS,6CAA6C;IAClD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE;QAC5D,IAAI;YACA,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,iBAAiB,GAAG,yCAA+B,CAAC,8BAA8B,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAEjI,aAAG,CAAC,iBAAiB,iBAAiB,CAAC,MAAM,gDAAgD,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;YACnH,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAEhC,GAAG,CAAC,KAAK,CACL,iBAAiB,EACjB,CAAC,MAA6C,EAAE,EAAE;gBAC9C,OAAO,IAAI,yCAA+B,CAAC,MAAM,CAAC,CAAA;YACtD,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;gBACX,QAAQ,EAAE,QAAQ;gBAClB,GAAG,EAAE,GAAG;gBACR,OAAO,EAAE;oBACL,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAC5B,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAC5B,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;iBAC/B;gBACD,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAC3B,eAAe,EAAE,KAAK;aACzB,CAAC,EACF,GAAG,EAAE;gBACD,aAAG,CAAE,iBAAiB,QAAQ,CAAC,EAAE,yCAAyC,CAAC,CAAA;YAC/E,CAAC,CACJ,CAAC;SACL;QAAC,OAAO,GAAG,EAAE;YACV,aAAG,CAAC,0BAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,GAAG,CAAC,gBAAgB,EAAE,CAAC;SAC1B;IACL,CAAC,CAAA,CAAC,CAAC;AACP,CAAC;AAED,sBAAsB;AACtB,IAAI;AACJ,yBAAyB;AACzB,6CAA6C;AAC7C,oCAAoC;AACpC,uDAAuD;AACvD,IAAI"}
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./Bot";
2 | export * from "./common/config";
3 | export * from "./algorithms/Algorithm";
4 | export * from "./algorithms/ArbitrageBetweenExchanges";
5 | export * from "./algorithms/ArbitrageTriangleWithinExchange";
6 | export * from "./algorithms/ArbitrageTriangularBetweenExchanges";
7 | export * from "./common/constants";
8 | export * from "./common/helpers";
9 | export * from "./common/interfaces";
10 | export * from "./common/types";
11 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5 | }) : (function(o, m, k, k2) {
6 | if (k2 === undefined) k2 = k;
7 | o[k2] = m[k];
8 | }));
9 | var __exportStar = (this && this.__exportStar) || function(m, exports) {
10 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11 | };
12 | Object.defineProperty(exports, "__esModule", { value: true });
13 | __exportStar(require("./Bot"), exports);
14 | __exportStar(require("./common/config"), exports);
15 | __exportStar(require("./algorithms/Algorithm"), exports);
16 | __exportStar(require("./algorithms/ArbitrageBetweenExchanges"), exports);
17 | __exportStar(require("./algorithms/ArbitrageTriangleWithinExchange"), exports);
18 | __exportStar(require("./algorithms/ArbitrageTriangularBetweenExchanges"), exports);
19 | __exportStar(require("./common/constants"), exports);
20 | __exportStar(require("./common/helpers"), exports);
21 | __exportStar(require("./common/interfaces"), exports);
22 | __exportStar(require("./common/types"), exports);
23 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/dist/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,wCAAsB;AACtB,kDAAgC;AAChC,yDAAuC;AACvC,yEAAuD;AACvD,+EAA6D;AAC7D,mFAAiE;AACjE,qDAAmC;AACnC,mDAAiC;AACjC,sDAAoC;AACpC,iDAA+B"}
--------------------------------------------------------------------------------
/dist/misc/Telegram.d.ts:
--------------------------------------------------------------------------------
1 | export interface TelegramParams {
2 | token: string;
3 | startPhrase: string;
4 | stopPhrase: string;
5 | chats: string[];
6 | logErrors: boolean;
7 | }
8 | export default class Telegram {
9 | token: string;
10 | startPhrase: string;
11 | stopPhrase: string;
12 | chats: any[];
13 | telegramBot: any;
14 | logErrors: boolean;
15 | constructor(params: TelegramParams);
16 | init(): void;
17 | addChat: (id: any) => void;
18 | removeChat: (id: any) => void;
19 | sendMessage: (message: any) => void;
20 | }
21 |
--------------------------------------------------------------------------------
/dist/misc/Telegram.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | process.env.NTBA_FIX_319 = '1';
7 | const node_telegram_bot_api_1 = __importDefault(require("node-telegram-bot-api"));
8 | class Telegram {
9 | constructor(params) {
10 | this.token = '';
11 | this.startPhrase = 'start';
12 | this.stopPhrase = 'stop';
13 | this.chats = [];
14 | this.logErrors = false;
15 | this.addChat = (id) => {
16 | if (!this.token)
17 | return;
18 | if (this.chats.indexOf(id) >= 0)
19 | return;
20 | this.chats.push(id);
21 | };
22 | this.removeChat = (id) => {
23 | if (!this.token)
24 | return;
25 | const index = this.chats.indexOf(id);
26 | if (index < 0)
27 | return;
28 | this.chats.splice(index, 1);
29 | };
30 | this.sendMessage = (message) => {
31 | if (!this.token)
32 | return;
33 | if (this.chats.length === 0)
34 | return;
35 | this.chats.forEach(chat => {
36 | this.telegramBot
37 | .sendMessage(chat, message, { parse_mode: 'HTML' })
38 | .catch(error => {
39 | // @TODO: format
40 | if (this.logErrors)
41 | console.log(error);
42 | });
43 | });
44 | };
45 | const { token, startPhrase, stopPhrase, chats, logErrors } = Object.assign({}, params);
46 | this.token = token;
47 | this.startPhrase = startPhrase;
48 | this.stopPhrase = stopPhrase;
49 | this.chats = chats;
50 | this.logErrors = logErrors;
51 | if (!this.token) {
52 | // @TODO: format
53 | if (this.logErrors)
54 | console.log('Cannot start Telegram notifications without token');
55 | return;
56 | }
57 | this.init();
58 | }
59 | init() {
60 | this.telegramBot = new node_telegram_bot_api_1.default(this.token, { polling: true });
61 | this.telegramBot.onText(this.startPhrase, (msg) => {
62 | this.addChat(msg.chat.id);
63 | let chatId = msg.chat.id;
64 | this.telegramBot.sendMessage(chatId, 'Notifications started');
65 | });
66 | this.telegramBot.onText(this.stopPhrase, (msg) => {
67 | this.removeChat(msg.chat.id);
68 | let chatId = msg.chat.id;
69 | this.telegramBot.sendMessage(chatId, 'Notifications stopped');
70 | });
71 | this.telegramBot.on('message', (msg) => {
72 | let chatId = msg.chat.id;
73 | this.telegramBot.sendMessage(chatId, 'Enter start phrase to start. Enter stop phrase to stop notifications.');
74 | });
75 | }
76 | }
77 | exports.default = Telegram;
78 | //# sourceMappingURL=Telegram.js.map
--------------------------------------------------------------------------------
/dist/misc/Telegram.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Telegram.js","sourceRoot":"","sources":["../../src/misc/Telegram.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/docs/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/g-zwolinski/arbitrage-algorithms-framework/4cd146037db8ad64473f6f3727f154b78336910a/docs/example.png
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'node',
4 | };
--------------------------------------------------------------------------------
/misc/proxy.js:
--------------------------------------------------------------------------------
1 | let port = (process.argv.length > 2) ? parseInt (process.argv[2]) : 8080;
2 | let host = (process.argv.length > 3) ? parseInt (process.argv[3]) : process.env.HOST || '0.0.0.0';
3 | require('cors-anywhere').createServer().listen(port, host).listen(port, host, function() {
4 | console.log('Running CORS Anywhere on ' + host + ':' + port);
5 | });
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["src"],
3 | "ext": ".ts,.js",
4 | "ignore": [],
5 | "exec": "ts-node ./src/app.ts"
6 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arbitrage-algorithms-framework",
3 | "version": "0.0.3",
4 | "description": "working prototype typescript framework for making arbitrage bots on cryptocurrencies exchanges, on top of ccxt",
5 | "main": "dist/src/index.js",
6 | "types": "./dist/src/index.d.ts",
7 | "scripts": {
8 | "build": "rimraf ./dist && tsc",
9 | "start": "tsc && node dist/example.js",
10 | "start:dev": "nodemon",
11 | "run": "ts-node ./src/example.ts",
12 | "lint": "tslint -c tslint.json 'src/**/*.ts'",
13 | "lint:fix": "npm run lint -- --fix",
14 | "test": "npx jest"
15 | },
16 | "keywords": ["multiple exchanges", "bot", "algorithm", "arbitrage", "framework", "ccxt", "typescript", "ts", "cryptocurrency"],
17 | "homepage": "https://github.com/g-zwolinski/arbitrage-algorithms-framework#readme",
18 | "bugs": "https://github.com/g-zwolinski/arbitrage-algorithms-framework/issues",
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/g-zwolinski/arbitrage-algorithms-framework"
22 | },
23 | "author": "zvvolinski.g@gmail.com",
24 | "license": "ISC",
25 | "devDependencies": {
26 | "@babel/preset-typescript": "^7.12.7",
27 | "@types/jest": "^26.0.20",
28 | "@types/node": "^14.14.22",
29 | "@types/node-telegram-bot-api": "^0.51.1",
30 | "jest": "^26.6.3",
31 | "nodemon": "^2.0.7",
32 | "rimraf": "^3.0.2",
33 | "ts-jest": "^26.5.0",
34 | "ts-node": "^9.1.1",
35 | "tslint": "^6.1.3",
36 | "typescript": "^4.1.3"
37 | },
38 | "dependencies": {
39 | "bignumber.js": "^9.0.1",
40 | "ccxt": "^1.41.20",
41 | "node-telegram-bot-api": "^0.52.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Bot.ts:
--------------------------------------------------------------------------------
1 | import { errorLogTemplate, log, validationException } from './common/helpers';
2 | import ccxt, { Balances, Exchange } from 'ccxt';
3 | import Algorithm from './algorithms/Algorithm';
4 | import Telegram, { TelegramParams } from './misc/Telegram';
5 | import { BUY, SELL } from './common/constants';
6 |
7 | export interface BotConfig {
8 | keys: {
9 | [key: string /* exchange */]: {
10 | apiKey: string;
11 | secret: string;
12 | }
13 | };
14 | exchangesToWatch: string[];
15 | orderBookLimit: number;
16 | exchangeOptions: {
17 | [key: string /* exchange */]: any
18 | };
19 | defaultExchangeOptions: any;
20 | currenciesToWatch: string[];
21 |
22 | makeOrders: boolean;
23 | parallelOrders: boolean;
24 |
25 | profile: boolean;
26 | logDetails: boolean;
27 | logAdditionalDetails: boolean;
28 | logWarnings: boolean;
29 | logAdditionalWarnings: boolean;
30 | logError: boolean;
31 | logErrorDetails: boolean;
32 |
33 | // ccxt calculate_fees correction
34 | feesRate: number;
35 | zeroesFeesCorrection: boolean;
36 | correctAllFees: boolean;
37 | feesRoundType: 'ceil' | 'floor' |'round';
38 |
39 | orderOptionsByExchange: {
40 | [key: string /* exchange */]: any
41 | };
42 | defaultOrderOptions: any;
43 |
44 | enableProxy: boolean;
45 | changeProxyAfterEveryOrder: boolean;
46 | changeProxyAfterAnyNetworkError: boolean;
47 | proxies: string[];
48 |
49 | telegram?: TelegramParams
50 | }
51 |
52 | export default class Bot {
53 | config: BotConfig;
54 | exchanges: {
55 | [key: string]: Exchange
56 | } = {};
57 | balances: {
58 | [key: string]: Balances
59 | } = {};
60 | cycleIndex = 0;
61 | proxyIndex = 0;
62 |
63 | telegram: Telegram;
64 |
65 | constructor(config: BotConfig) {
66 | this.config = config;
67 | this.telegram = new Telegram(this.config.telegram);
68 | this.telegram.sendMessage('Bot started');
69 | }
70 |
71 | printProfileTime() {
72 | if (!this.config.profile) { return; }
73 | console.log("\x1b[47m\x1b[30m%s\x1b[0m", new Date().toLocaleString().padEnd(100, ' '));
74 | }
75 |
76 | async init() {
77 | const self = this;
78 | return new Promise(async (resolve, reject) => {
79 | await Promise.all (ccxt.exchanges.map ((exchange) => (async function () {
80 | if (self.config.exchangesToWatch.includes(exchange)) {
81 | self.validateExchange(exchange);
82 | self.exchanges[exchange] = new (ccxt)[exchange]({
83 | ...self.config.defaultExchangeOptions,
84 | ...self.config.exchangeOptions[exchange]
85 | });
86 | self.exchanges[exchange].apiKey = self.config.keys[exchange].apiKey;
87 | self.exchanges[exchange].secret = self.config.keys[exchange].secret;
88 |
89 | try {
90 | self.balances[exchange] = await self.fetchBalance(self.exchanges[exchange]);
91 | } catch (err) {
92 | log(errorLogTemplate(err));
93 | }
94 | }
95 | })()));
96 | resolve();
97 | })
98 | }
99 |
100 | async runAlgorithm(
101 | algorithm: Algorithm
102 | ) {
103 | return new Promise(resolve => {
104 | algorithm.run().then(res => resolve(res));
105 | })
106 | }
107 |
108 | async runAlgorithmOnIteratedElement(elementsArray, algorithm, paramsFromElement) {
109 | const element = elementsArray[this.cycleIndex];
110 | console.log('\x1b[45m%s\x1b[0m', `RUNNING: ${element}`.padEnd(100, ' '));
111 |
112 | let runningAlgorithm;
113 | try {
114 | runningAlgorithm = algorithm(paramsFromElement(element));
115 | } catch (err) {
116 | log(errorLogTemplate(err));
117 | }
118 |
119 | let result = runningAlgorithm ? await this.runAlgorithm(runningAlgorithm) : false;
120 | if (!result) {
121 | this.cycleIndex = this.cycleIndex + 1;
122 | }
123 | }
124 |
125 | cycle: (
126 | toIterate: any[],
127 | algorithm: (params) => Algorithm,
128 | paramsFromElement: (element: any) => any,
129 | onCycleRun: () => any
130 | ) => void = async (toIterate, algorithm, paramsFromElement, onCycleRun = () => null) => {
131 | this.cycleIndex = 0;
132 | const cycleMaxIndex = toIterate.length;
133 | onCycleRun();
134 |
135 | do {
136 | await this.runAlgorithmOnIteratedElement(toIterate, algorithm, paramsFromElement);
137 | } while (this.cycleIndex < cycleMaxIndex)
138 |
139 | process.nextTick(() => {
140 | this.cycle(toIterate, algorithm, paramsFromElement, onCycleRun)
141 | });
142 | }
143 |
144 | validateExchange(exchange: string) {
145 | // @TODO: add validaiton (check if exchange has every required method)
146 | if (!this.config.exchangesToWatch.includes(exchange)) {
147 | validationException('EXCHANGE', `${exchange} IS IGNORED`);
148 | }
149 | }
150 |
151 | async fetchBalanceAsync(exchange: Exchange) {
152 | let balance: Balances;
153 |
154 | try {
155 | balance = await exchange.fetchBalance();
156 | } catch (err) {
157 | log(errorLogTemplate(err));
158 | await exports.fetchBalance(exchange);
159 | }
160 | return balance;
161 | }
162 |
163 | fetchBalance(exchange: Exchange) {
164 | return exchange.fetchBalance();
165 | }
166 |
167 | setExchangeProxy(exchange: Exchange, index: number | null = null) {
168 | this.proxyIndex = index === null ? this.proxyIndex + 1 : index;
169 | this.proxyIndex = this.proxyIndex === this.config.proxies.length ? 0 : this.proxyIndex;
170 | exchange.proxy = this.config.proxies[this.proxyIndex];
171 | }
172 |
173 | makeOrder(exchange: Exchange, market, side, amount, price, additionalParams = {}) {
174 | // @wip
175 | if (!this.config.makeOrders) false;
176 | // @TODO: send Telegram notification after order
177 | // @TODO: setExchangeProxy if config.changeProxyAfterEveryOrder
178 | if (side === BUY) {
179 | return exchange.createLimitBuyOrder(market, amount, price, {
180 | ...additionalParams,
181 | ...this.config.defaultOrderOptions,
182 | ...this.config.orderOptionsByExchange[exchange.id]
183 | });
184 | }
185 | if (side === SELL) {
186 | return exchange.createLimitSellOrder(market, amount, price, {
187 | ...additionalParams,
188 | ...this.config.defaultOrderOptions,
189 | ...this.config.orderOptionsByExchange[exchange.id]
190 | });
191 | }
192 | return false;
193 | }
194 | }
--------------------------------------------------------------------------------
/src/algorithms/Algorithm.ts:
--------------------------------------------------------------------------------
1 | import { Balances, Market } from "ccxt";
2 | import Bot from "../Bot";
3 |
4 | export interface AlgorithmCommonParams {
5 | bot: Bot;
6 | markets: Market[];
7 | balances: Balances;
8 | logWarnings: boolean;
9 | }
10 |
11 | export default class Algorithm {
12 | onRun: (params: any) => Promise = () => new Promise(() => false);
13 |
14 | run = async (params?: any): Promise => {
15 | return await this.onRun(params);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/algorithms/ArbitrageBetweenExchanges.ts:
--------------------------------------------------------------------------------
1 | import Algorithm from './Algorithm';
2 |
3 | export default class ArbitrageBetweenExchanges extends Algorithm {
4 | // BUY C0 FOR C1 -> transfer C0 -> SELL C0 FOR C1 -> transfer C1
5 | // SELL C0 FOR C1 -> transfer C1 -> BUY C0 FOR C1 -> transfer C0
6 |
7 | // check markets on exchanges pair (if transfer costs are less than difference)
8 | }
--------------------------------------------------------------------------------
/src/algorithms/ArbitrageTriangleWithinExchange.ts:
--------------------------------------------------------------------------------
1 | import { Balances, Exchange, Market, MinMax } from 'ccxt';
2 | import Bot from '../Bot';
3 | import { BUY, SELL, ASKS, BIDS, bidsOrAsksByBuyOrSell } from '../common/constants';
4 | import { floatRound, log, validationException } from '../common/helpers';
5 | import { Amount } from '../common/interfaces';
6 | import { BidsOrAsks, BuyOrSell, OrderType } from '../common/types';
7 | import Algorithm from './Algorithm';
8 | import {AlgorithmCommonParams} from './Algorithm';
9 | import { BigNumber } from "bignumber.js";
10 |
11 | export interface ArbitrageTriangleWithinExchangeParams extends AlgorithmCommonParams {
12 | exchange: Exchange;
13 | validateMarkets: boolean;
14 | minimumsCorrectionTries?: number;
15 | }
16 |
17 | interface DIRECTIONS_SEQUENCE {
18 | orders: BuyOrSell[],
19 | ordersbookSide: BidsOrAsks[]
20 | }
21 |
22 | export default class ArbitrageTriangleWithinExchange extends Algorithm {
23 | static ALGORITHM_TYPE = 'ArbitrageTriangleWithinExchange';
24 | DIRECTIONS_SEQUENCES: DIRECTIONS_SEQUENCE[] = [
25 | {
26 | orders: [BUY, BUY, SELL],
27 | ordersbookSide: [ASKS, ASKS, BIDS]
28 | },
29 | {
30 | orders: [SELL, SELL, BUY],
31 | ordersbookSide: [BIDS, BIDS, ASKS]
32 | }
33 | ];
34 | availableDirections: DIRECTIONS_SEQUENCE[] = [];
35 | bot: Bot;
36 | exchange: Exchange;
37 | marketsTriplet: Market[];
38 | availableBalances: Balances;
39 | validateMarkets = true;
40 | orderBooks: {
41 | [key: string]: {
42 | bids: number[][],
43 | asks: number[][],
44 | }
45 | } = {};
46 | // every minimum amount in every symbol
47 | minimum: {amount: Amount[], cost: Amount[], market: string}[];
48 | minimumsCorrectionTries;
49 |
50 | constructor(params: ArbitrageTriangleWithinExchangeParams) {
51 | super();
52 |
53 | params.bot.printProfileTime();
54 |
55 | const {bot, markets, balances, validateMarkets, exchange, minimumsCorrectionTries} = {...params};
56 |
57 | this.bot = bot;
58 | this.exchange = exchange;
59 | this.validateMarkets = validateMarkets;
60 | this.minimumsCorrectionTries = minimumsCorrectionTries || 1000;
61 | this.validate(markets, balances);
62 | this.marketsTriplet = markets;
63 | this.availableBalances = balances;
64 | this.onRun = this.onArbitrageTriangleWithinExchangeRun;
65 |
66 | params.bot.printProfileTime();
67 | }
68 |
69 | static validateMarkets(markets: Market[]) {
70 | if (markets.length !== 3) {
71 | this.throwValidationException('InvalidMarketsLength (should be equal to 3)');
72 | }
73 |
74 | if (markets[0].quote !== markets[1].base) {
75 | this.throwValidationException('InvalidMarkets (first market quote should be secound market base)');
76 | }
77 |
78 | if (markets[1].quote !== markets[2].quote) {
79 | this.throwValidationException('InvalidMarkets (second market quote should be third market base)');
80 | }
81 |
82 | if (markets[2].base !== markets[0].base) {
83 | this.throwValidationException('InvalidMarkets (third market base should be first market base)');
84 | }
85 |
86 | return true;
87 | }
88 |
89 | isBalanceSufficientForOrder(order: BuyOrSell, balance: number, marketLimits: { amount: MinMax, price: MinMax, cost?: MinMax },) {
90 | return order === BUY
91 | ? balance >= (marketLimits.cost && marketLimits.cost.min ? marketLimits.cost.min : 0)
92 | : balance >= marketLimits.amount.min;
93 | }
94 |
95 | validateBalances(markets: Market[], balances: Balances) {
96 | // @TODO: check again after getting orders and price
97 | // log('validateBalances');
98 |
99 | let isDirAvailable = {
100 | 0: true,
101 | 1: true
102 | };
103 |
104 | [0, 1].forEach(dirIndex => {
105 | this.DIRECTIONS_SEQUENCES[dirIndex].orders.forEach((order: BuyOrSell, index) => {
106 | const isBuy = order === BUY;
107 | const quoteOrBase = isBuy ? 'quote': 'base'
108 | const isSufficient = this.isBalanceSufficientForOrder(
109 | order,
110 | balances.free[markets[index][quoteOrBase]],
111 | markets[index].limits
112 | );
113 |
114 | if(this.bot.config.logWarnings && !isSufficient) {
115 | log(
116 | `\x1b[33mBalance ${balances.free[markets[index][quoteOrBase]]} ${markets[index].quote} under market ${markets[index].symbol} MIN ${markets[index].limits[isBuy ? 'cost' : 'amount'].min} ${isBuy ? 'cost' : 'amount'} limit\x1b[0m`
117 | );
118 | }
119 |
120 | isDirAvailable[dirIndex] = isDirAvailable[dirIndex] && isSufficient;
121 | })
122 |
123 | if (isDirAvailable[dirIndex]) {
124 | this.availableDirections.push(this.DIRECTIONS_SEQUENCES[dirIndex]);
125 | }
126 | })
127 | return this.availableDirections.length > 0;
128 | }
129 |
130 | static getValidatedTripletsOnExchange(exchange: Exchange, toWatch: string[] = [], showLoadingStatus: boolean = true, showLoadingErrors: boolean = false, checkBalances = false) {
131 | const validatedTriplets: string[][] = [];
132 | let marketsNumber = Object.entries(exchange.markets).length;
133 | let i = 0;
134 |
135 | for (let marketA in exchange.markets) {
136 | if (showLoadingStatus) {
137 | console.clear();
138 | log(`Loading ${exchange.id} ArbitrageTriangleWithinExchange ${((i / marketsNumber) * 100).toFixed()}%`);
139 | }
140 | i++;
141 | if(toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketA].base) < 0 || toWatch.indexOf(exchange.markets[marketA].quote) < 0)) continue;
142 | for (let marketB in exchange.markets) {
143 | if(toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketB].base) < 0 || toWatch.indexOf(exchange.markets[marketB].quote) < 0)) continue;
144 | for (let marketC in exchange.markets) {
145 | if(toWatch.length > 0 && (toWatch.indexOf(exchange.markets[marketC].base) < 0 || toWatch.indexOf(exchange.markets[marketC].quote) < 0)) continue;
146 | if(marketA === marketB || marketA === marketC || marketB === marketC) { continue; }
147 |
148 | const exchangeMarketA = exchange.markets[marketA];
149 | const exchangeMarketB = exchange.markets[marketB];
150 | const exchangeMarketC = exchange.markets[marketC];
151 |
152 | if(!exchangeMarketA.active || !exchangeMarketB.active || !exchangeMarketC.active) { continue; }
153 |
154 | try {
155 | if (ArbitrageTriangleWithinExchange.validateMarkets([
156 | exchange.markets[marketA],
157 | exchange.markets[marketB],
158 | exchange.markets[marketC]
159 | ])) {
160 | validatedTriplets.push([marketA, marketB, marketC]);
161 | }
162 | } catch (err) {
163 | if (showLoadingErrors) {
164 | log(`\x1b[33mLoading ${exchange.id} ArbitrageTriangleWithinExchange ${err.name} ${err.message}\x1b[0m`);
165 | }
166 | }
167 | }
168 | }
169 | }
170 |
171 | return validatedTriplets;
172 | }
173 |
174 | static throwValidationException(message) {
175 | throw validationException(this.ALGORITHM_TYPE, message);
176 | }
177 |
178 | validate(markets: Market[], balances: Balances) {
179 | if (this.validateMarkets) {
180 | ArbitrageTriangleWithinExchange.validateMarkets(markets);
181 | }
182 | if (!this.validateBalances(markets, balances)) {
183 | ArbitrageTriangleWithinExchange.throwValidationException(
184 | `Balances insufficient for ${markets[0].symbol} ${markets[1].symbol} ${markets[2].symbol}`
185 | );
186 | }
187 | }
188 |
189 | async getOrderBooks(){
190 | // log('getOrderBooks');
191 | this.bot.printProfileTime();
192 | await Promise.all(this.marketsTriplet.map(async market => {
193 | // log('getOrderBook ' + this.exchange.name);
194 | let orderbook = await this.exchange.fetchL2OrderBook(market.symbol, this.bot.config.orderBookLimit)
195 | this.orderBooks[market.symbol] = {[ASKS]: orderbook[ASKS], [BIDS]: orderbook[BIDS]};
196 | this.bot.printProfileTime();
197 | }))
198 | }
199 |
200 | getPrecision(market: Market, of: string) {
201 | // exceptations
202 | if (this.exchange.id === 'bleutrade') {
203 | switch (of) {
204 | case 'quote':
205 | return market.info.DivisorDecimal ? market.info.DivisorDecimal : 8
206 | case 'base':
207 | return market.info.DividendDecimal ? market.info.DividendDecimal : 8
208 | default:
209 | return market.precision[of];
210 | }
211 | }
212 | return market.precision[of];
213 | }
214 |
215 | getQuantity(market: Market, total: BigNumber, ordersbookSide: BidsOrAsks): {quantity: BigNumber; price: number} {
216 | let calcAmount = new BigNumber(0);
217 | let orderbookIndex = 0;
218 | let price = 0;
219 | let order;
220 | let stop = false;
221 |
222 | do {
223 | order = this.orderBooks[market.symbol][ordersbookSide][orderbookIndex];
224 | if (!order) {break;}
225 | calcAmount = calcAmount.plus(order ? order[1] : 0);
226 | // console.log(orderbookIndex, order, total.toString(), calcAmount.toString(), calcAmount.times(order[0]).isGreaterThanOrEqualTo(total));
227 | if (calcAmount.times(order[0]).isGreaterThanOrEqualTo(total)) {
228 | calcAmount = new BigNumber(total).dividedBy(order[0]);
229 | price = order[0];
230 | stop = true;
231 | } else {
232 | orderbookIndex = orderbookIndex + 1;
233 | }
234 | } while (!stop)
235 |
236 | // console.log(order, !order);
237 |
238 | if (!order) {
239 | return {
240 | quantity: new BigNumber(Infinity),
241 | price: Infinity
242 | }
243 | }
244 |
245 | return {
246 | // total: new BigNumber(amount).times(price).precision(market.precision.quote),
247 | // @TODO: add rounding depends on buyOrSell
248 | quantity: new BigNumber(calcAmount.toPrecision(this.getPrecision(market, ordersbookSide === 'asks' ? 'quote': 'base'))),
249 | price: price
250 | };
251 | }
252 |
253 | async onArbitrageTriangleWithinExchangeRun() {
254 | this.bot.printProfileTime();
255 |
256 | return new Promise(async resolve => {
257 | // log(`onArbitrageTriangleWithinExchangeRun ${this.marketsTriplet.map(market => market.symbol).join(' ')}`)
258 | await this.getOrderBooks();
259 |
260 | this.availableDirections.forEach(async (direction, directionIndex) => {
261 | let success = false;
262 | do {
263 | let minAB, qunatityAB, totalAB, minAC, minBC, qunatityBC, totalBC, qunatityAC, totalAC, feesAB, feesBC, feesAC, result;
264 | let step = 1;
265 | let reachedMinimum = false;
266 | minAB = Math.max(this.marketsTriplet[0].limits.amount.min, this.marketsTriplet[2].limits.amount.min);
267 |
268 | const firstSide = direction.orders[0] === 'buy';
269 | do {
270 | if(this.bot.config.logAdditionalWarnings && step > 1) {
271 | console.log("\x1b[33m%s\x1b[0m", `MINIMUMS CORRECTION (${step}. times)`);
272 | }
273 |
274 | if(firstSide) {
275 | qunatityAB = new BigNumber(minAB).multipliedBy(step + this.bot.config.feesRate); // [A], market A/B
276 | totalAB = this.getCost(this.marketsTriplet[0], qunatityAB, bidsOrAsksByBuyOrSell[direction.orders[0]] as BidsOrAsks); // [B], market A/B
277 | feesAB = this.fees(this.marketsTriplet[0], qunatityAB.toNumber(), totalAB.price, direction.orders[0]);
278 |
279 | qunatityBC = totalAB.total; // [B], market B/C
280 | totalBC = this.getCost(this.marketsTriplet[1], qunatityBC, bidsOrAsksByBuyOrSell[direction.orders[1]] as BidsOrAsks); // [C], market B/C
281 | feesBC = this.fees(this.marketsTriplet[1], qunatityBC.toNumber(), totalBC.price, direction.orders[1]);
282 |
283 | qunatityAC = new BigNumber(minAB).multipliedBy(step).minus(feesAB.cost); // [A], market A/C
284 | totalAC = this.getCost(this.marketsTriplet[2], qunatityAC, bidsOrAsksByBuyOrSell[direction.orders[2]] as BidsOrAsks); // [C], market A/C
285 | feesAC = this.fees(this.marketsTriplet[2], qunatityAC.toNumber(), totalAC.price, direction.orders[2]);
286 |
287 | // results in C
288 | result = totalAC.total.minus(totalBC.total).minus(feesAC.cost);
289 | } else {
290 |
291 | qunatityAC = new BigNumber(minAB).multipliedBy(step + this.bot.config.feesRate); // [A], market A/C
292 | totalAC = this.getCost(this.marketsTriplet[2], qunatityAC, bidsOrAsksByBuyOrSell[direction.orders[2]] as BidsOrAsks); // [C], market A/C
293 | feesAC = this.fees(this.marketsTriplet[2], qunatityAC.toNumber(), totalAC.price, direction.orders[2]);
294 |
295 | qunatityAB = new BigNumber(minAB).multipliedBy(step).minus(feesAC.cost); // [A], market A/B
296 | totalAB = this.getCost(this.marketsTriplet[0], qunatityAB, bidsOrAsksByBuyOrSell[direction.orders[0]] as BidsOrAsks); // [B], market A/B
297 | feesAB = this.fees(this.marketsTriplet[0], qunatityAB.toNumber(), totalAB.price, direction.orders[0]);
298 |
299 | qunatityBC = totalAB.total.minus(feesAB.cost); // [B], market B/C
300 | totalBC = this.getCost(this.marketsTriplet[1], qunatityBC, bidsOrAsksByBuyOrSell[direction.orders[1]] as BidsOrAsks); // [C], market B/C
301 | feesBC = this.fees(this.marketsTriplet[1], qunatityBC.toNumber(), totalBC.price, direction.orders[1]);
302 |
303 | // results in C
304 | result = totalBC.total.minus(totalAC.total).minus(feesBC.cost);
305 | }
306 | step = step + 1;
307 |
308 | reachedMinimum = !(qunatityAB.isLessThan(this.marketsTriplet[0].limits.amount.min)
309 | || qunatityBC.isLessThan(this.marketsTriplet[1].limits.amount.min)
310 | || qunatityAC.isLessThan(this.marketsTriplet[2].limits.amount.min)
311 | || totalAB.total < this.marketsTriplet[0].limits.cost.min
312 | || totalBC.total < this.marketsTriplet[1].limits.cost.min
313 | || totalAC.total < this.marketsTriplet[2].limits.cost.min);
314 | } while(!reachedMinimum && step < this.minimumsCorrectionTries)
315 |
316 | // @TODO: add fees to AB or AC (depends on ordersDirection)
317 |
318 | this.bot.config.logAdditionalDetails && console.table({
319 | ' ': {
320 | 'direction (max depth)': '-',
321 | '1. price': '-',
322 | '1. amount': '-',
323 | 'min cost': '(by amount)',
324 | 'amount min': '(limit)',
325 | 'cost min': '(limit)',
326 | 'price min': '(limit)'
327 | },
328 | [`${this.marketsTriplet[0].symbol}`]: {
329 | 'direction (max depth)': `${direction.orders[0]} (${this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]].length} ${direction.ordersbookSide[0]})`,
330 | '1. price': this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]][0][0],
331 | '1. amount': this.orderBooks[this.marketsTriplet[0].symbol][direction.ordersbookSide[0]][0][1],
332 | 'min cost': this.getMinCost(this.marketsTriplet[0], direction.ordersbookSide[0]).total.toString(),
333 | 'amount min': this.marketsTriplet[0].limits.amount.min,
334 | 'cost min': this.marketsTriplet[0].limits.cost.min,
335 | 'price min': this.marketsTriplet[0].limits.price.min
336 | },
337 | [`${this.marketsTriplet[1].symbol}`]: {
338 | 'direction (max depth)': `${direction.orders[1]} (${this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]].length} ${direction.ordersbookSide[1]})`,
339 | '1. price': this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]][0][0],
340 | '1. amount': this.orderBooks[this.marketsTriplet[1].symbol][direction.ordersbookSide[1]][0][1],
341 | 'min cost': this.getMinCost(this.marketsTriplet[1], direction.ordersbookSide[1]).total.toString(),
342 | 'amount min': this.marketsTriplet[1].limits.amount.min,
343 | 'cost min': this.marketsTriplet[1].limits.cost.min,
344 | 'price min': this.marketsTriplet[1].limits.price.min
345 | },
346 | [`${this.marketsTriplet[2].symbol}`]: {
347 | 'direction (max depth)': `${direction.orders[2]} (${this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]].length} ${direction.ordersbookSide[2]})`,
348 | '1. price': this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]][0][0],
349 | '1. amount': this.orderBooks[this.marketsTriplet[2].symbol][direction.ordersbookSide[2]][0][1],
350 | 'min cost': this.getMinCost(this.marketsTriplet[2], direction.ordersbookSide[2]).total.toString(),
351 | 'amount min': this.marketsTriplet[2].limits.amount.min,
352 | 'cost min': this.marketsTriplet[2].limits.cost.min,
353 | 'price min': this.marketsTriplet[2].limits.price.min
354 | }
355 | });
356 |
357 | this.bot.config.logDetails && console.table({
358 | [this.marketsTriplet[0].symbol]: {
359 | direction: direction.orders[0],
360 | quantity: `${qunatityAB.toString()} ${this.marketsTriplet[0].base}`,
361 | ' ': 'for',
362 | total: `${totalAB.total.toPrecision(this.getPrecision(this.marketsTriplet[0], 'quote')).toString()} ${this.marketsTriplet[0].quote}`,
363 | fees: `${feesAB.cost} ${feesAB.currency}`,
364 | 'fees rate': feesAB.rate
365 | },
366 | [this.marketsTriplet[1].symbol]: {
367 | direction: direction.orders[1],
368 | quantity: `${qunatityBC.toString()} ${this.marketsTriplet[1].base}`,
369 | ' ': 'for',
370 | total: `${totalBC.total.toPrecision(this.getPrecision(this.marketsTriplet[1], 'quote')).toString()} ${this.marketsTriplet[1].quote}`,
371 | fees: `${feesBC.cost} ${feesBC.currency}`,
372 | 'fees rate': feesBC.rate
373 | },
374 | [this.marketsTriplet[2].symbol]: {
375 | direction: direction.orders[2],
376 | quantity: `${qunatityAC.toString()} ${this.marketsTriplet[2].base}`,
377 | ' ': 'for',
378 | total: `${totalAC.total.toPrecision(this.getPrecision(this.marketsTriplet[2], 'quote')).toString()} ${this.marketsTriplet[2].quote}`,
379 | fees: `${feesAC.cost} ${feesAC.currency}`,
380 | 'fees rate': feesAC.rate
381 | }
382 | })
383 |
384 | const resultString = `${result.toString()} ${this.marketsTriplet[1].quote}`.padStart(100, ' ');
385 | if(result.isGreaterThan(0)) {
386 | console.log("\x1b[42m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
387 | // @TODO: check balances, make order (then check again if there iss still arbitrage)
388 | // @TODO: check minimums again
389 |
390 | if (!reachedMinimum) {
391 |
392 | } else {
393 | // use await instead promise.all if proxies list is not configured
394 | // await Promise.all([
395 | // this.makeOrder(this.marketsTriplet[0].symbol, direction.orders[0], qunatityAB, totalAB.price),
396 | // this.makeOrder(this.marketsTriplet[1].symbol, direction.orders[1], qunatityBC, totalBC.price),
397 | // this.makeOrder(this.marketsTriplet[2].symbol, direction.orders[2], qunatityAC, totalAC.price)
398 | // ]).then((values) => {
399 | // console.log(values);
400 | // success = ;
401 | // });
402 | }
403 |
404 | } else if(!result.isEqualTo(0)) {
405 | console.log("\x1b[41m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
406 | } else {
407 | console.log("\x1b[44m\x1b[37m%s\x1b[0m\x1b[0m", resultString);
408 | }
409 |
410 | success = false;
411 | } while (success)
412 | })
413 | resolve(false);
414 | })
415 | };
416 |
417 | makeOrder(market, side, amount, price, additionalParams = {}) {
418 | return this.bot.makeOrder(this.exchange, market, side, amount, price, additionalParams);
419 | }
420 |
421 | getCost(market: Market, amount: BigNumber, ordersbookSide: BidsOrAsks): {total: BigNumber; price: number} {
422 | let calcAmount = new BigNumber(0);
423 | let orderbookIndex = 0;
424 | let price = 0;
425 | let order;
426 |
427 | do {
428 | order = this.orderBooks[market.symbol][ordersbookSide][orderbookIndex];
429 | if (!order) {break;}
430 | calcAmount = calcAmount.plus(order ? order[1] : 0);
431 | // console.log(orderbookIndex, order, amount.toString(), calcAmount.toString());
432 | if (calcAmount.isGreaterThanOrEqualTo(amount)) {
433 | calcAmount = amount;
434 | price = order[0];
435 | } else {
436 | orderbookIndex = orderbookIndex + 1;
437 | }
438 | } while (calcAmount !== amount)
439 |
440 | // console.log(order, !order);
441 |
442 | if (!order) {
443 | return {
444 | total: new BigNumber(Infinity),
445 | price: Infinity
446 | }
447 | }
448 |
449 | // const isBuy = buyOrSellByBidsOrAsks[ordersbookSide] === BUY;
450 | // const quoteOrBase = isBuy ? 'base': 'quote';
451 |
452 | return {
453 | // total: new BigNumber(amount).times(price).precision(market.precision.quote),
454 | total: new BigNumber(new BigNumber(amount).times(price).toPrecision(this.getPrecision(market, 'quote'))),
455 | price: price
456 | };
457 | }
458 |
459 | getMinCost(market: Market, ordersbookSide: BidsOrAsks) {
460 | return this.getCost(market, new BigNumber(market.limits.amount.min), ordersbookSide);
461 | }
462 |
463 | fees(market: Market, amount: number, price: number, buyOrSell: BuyOrSell, orderType: OrderType = 'limit') {
464 | if(!Number.isFinite(amount) || !Number.isFinite(price)) {
465 | return {
466 | 'cost': Infinity,
467 | };
468 | }
469 |
470 | const fees = this.exchange.calculate_fee(market.symbol, orderType, buyOrSell, amount, price, 'taker')
471 |
472 | if((!fees.cost && this.bot.config.zeroesFeesCorrection) || this.bot.config.correctAllFees) {
473 | return this.calculateFeeFallback(fees, market, buyOrSell, amount, price);
474 | }
475 | return fees;
476 | }
477 |
478 | calculateFeeFallback (fees, market, side, amount, price, takerOrMaker = 'taker') {
479 | if(this.bot.config.logAdditionalWarnings) {
480 | console.log("\x1b[33m%s\x1b[0m", `FEES CORRECTION ${market.symbol} (to correct: ${fees.cost} ${fees.currency})`);
481 | }
482 | return {
483 | ...fees,
484 | cost: floatRound(amount * price * this.exchange.markets[market.symbol].taker, market['precision']['price'], this.bot.config.feesRoundType)
485 | }
486 | }
487 |
488 | // check if balances ar bigger than mins
489 |
490 | // dry run check (with min amounts)
491 | // dry run check (with max amounts - balances or in ordersbook amount)
492 | // if there is profit in at least one coin, try optimize (bisection 'oscilate') results
493 |
494 | // operates[3] = [...mins]
495 | // increments[3] = mins.map(min => min / 2)
496 |
497 | // optimize amounts
498 |
499 | // do while overall proift is bigger
500 | // for markets
501 | // make order amount bigger
502 | // operate = operate + increment
503 | // check if new overall proift is bigger
504 | // if yes - true (till max)
505 | // if no
506 | // increment = increment/2 (till operate < min or increment < 10 ^ MARKET_PRECISION)
507 | }
--------------------------------------------------------------------------------
/src/algorithms/ArbitrageTriangularBetweenExchanges.ts:
--------------------------------------------------------------------------------
1 | import Algorithm from './Algorithm';
2 |
3 | export default class ArbitrageTriangularBetweenExchanges extends Algorithm {
4 | // BUY C0 FOR C1 -> SELL C0 FOR C2 -> transfer C2 -> BUY C0 FOR C2 -> transfer C0
5 | // SELL C0 FOR C1 -> BUY C2 FOR C1 -> transfer C2 -> BUY C0 FOR C2 -> transfer C0
6 |
7 | // check currency on exchanges pair (if transfer costs are less than difference)
8 | }
--------------------------------------------------------------------------------
/src/common/config.ts:
--------------------------------------------------------------------------------
1 | import defaultConfig = require("../config.json");
2 | // import localConfig = require("./config.local.json");
3 | import { BotConfig } from "../Bot";
4 |
5 | export default {
6 | ...defaultConfig,
7 | // ...(localConfig ?? {})
8 | } as BotConfig
9 |
10 | export const config = (config: any) => ({
11 | ...defaultConfig,
12 | ...config,
13 | // ...(localConfig ?? {})
14 | }) as BotConfig
--------------------------------------------------------------------------------
/src/common/constants.ts:
--------------------------------------------------------------------------------
1 | import { BidsOrAsks } from "./types";
2 |
3 | export const BUY = 'buy';
4 | export const SELL = 'sell';
5 | export const ASKS = 'asks';
6 | export const BIDS = 'bids';
7 |
8 | export const buyOrSellByBidsOrAsks = {
9 | [ASKS]: BUY,
10 | [BIDS]: SELL
11 | }
12 |
13 | export const bidsOrAsksByBuyOrSell = {
14 | [BUY]: ASKS,
15 | [SELL]: BIDS
16 | }
--------------------------------------------------------------------------------
/src/common/errors.ts:
--------------------------------------------------------------------------------
1 | import Bot from "../Bot";
2 | import ccxt, { BaseError, Exchange } from 'ccxt';
3 |
4 | export default class BotErrorHandler {
5 | static handleError = (bot: Bot, err: BaseError, exchange: Exchange, market = '') => {
6 | const changeProxy = bot.config.enableProxy && bot.config.changeProxyAfterAnyNetworkError && (
7 | err instanceof ccxt.RequestTimeout ||
8 | err instanceof ccxt.ExchangeNotAvailable ||
9 | err instanceof ccxt.NetworkError ||
10 | err instanceof ccxt.DDoSProtection
11 | );
12 |
13 | if (changeProxy) {
14 | bot.setExchangeProxy(exchange);
15 | }
16 |
17 | if(bot.config.logError) {
18 | // @TODO: format
19 | console.log(exchange.symbol, market, typeof err);
20 | if(!bot.config.logErrorDetails) console.log(err);
21 | return;
22 | }
23 |
24 | if (err !instanceof BaseError) {
25 | throw err;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/common/helpers.ts:
--------------------------------------------------------------------------------
1 | export const errorLogTemplate = (e: Error) => `\x1b[31m${e.name} ${e.message}\x1b[0m`;
2 |
3 | export function log(message, type: 'log' | 'warn' = 'log', run = true) {
4 | if (!run) { return; }
5 | console[type](message);
6 | }
7 |
8 | export const validationException = (algorithmType: string, message: string) => ({
9 | message: `\n\x1b[31m${message}\x1b[0m`,
10 | name: `\x1b[31m${algorithmType} Validation Exception${message ? ':' : ''}\x1b[0m`
11 | });
12 |
13 |
14 | export const floatRound = (value: number, precision: number, type: 'ceil' | 'floor' |'round') => {
15 | switch(type){
16 | case 'ceil': return Math.ceil(value * Math.pow(10, precision)) / Math.pow(10, precision)
17 | case 'floor': return Math.floor(value * Math.pow(10, precision)) / Math.pow(10, precision)
18 | case 'round': return Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision)
19 | }
20 | }
--------------------------------------------------------------------------------
/src/common/interfaces.ts:
--------------------------------------------------------------------------------
1 | import { BidsOrAsks } from "./types";
2 |
3 | export interface Amount {
4 | symbol: string;
5 | amount: number;
6 | }
7 |
8 | export interface Order {
9 | price: number;
10 | amount: number;
11 | type: BidsOrAsks;
12 | }
13 |
14 | export interface ValidationException {
15 | message: string;
16 | name: string;
17 | }
--------------------------------------------------------------------------------
/src/common/types.ts:
--------------------------------------------------------------------------------
1 | export type MarketOrBase = 'market' | 'base';
2 | export type BidsOrAsks = 'bids' | 'asks';
3 | export type OrderType = 'market' | 'limit';
4 | export type BuyOrSell = 'buy' | 'sell';
--------------------------------------------------------------------------------
/src/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeOrders": false,
3 | "profile": false,
4 | "logDetails": false,
5 | "logAdditionalDetails": false,
6 | "logWarnings": false,
7 | "logAdditionalWarnings": false,
8 | "logError": false,
9 | "logErrorDetails": false,
10 | "exchangesToWatch": ["example"],
11 | "orderBookLimit": 1000,
12 | "exchangeOptions": {
13 | "example": {
14 | }
15 | },
16 | "defaultExchangeOptions": {
17 | "enableRateLimit": true,
18 | "proxy": false
19 | },
20 | "keys": {
21 | "example": {
22 | "apiKey": "123456789ABC",
23 | "secret": "123456789DEF"
24 | }
25 | },
26 | "currenciesToWatch": [],
27 | "feesRate": 0.001,
28 | "zeroesFeesCorrection": true,
29 | "correctAllFees": false,
30 | "feesRoundType": "ceil",
31 | "orderOptionsByExchange": {},
32 | "defaultOrderOptions": {},
33 | "parallelOrders": false,
34 | "enableProxy": false,
35 | "changeProxyAfterEveryOrder": false,
36 | "changeProxyAfterAnyNetworkError": false,
37 | "proxies": [],
38 | "telegram": {
39 | "token": "",
40 | "startPhrase": "",
41 | "stopPhrase": "",
42 | "chats": [],
43 | "logErrors": false
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config.local.example.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeOrders": false,
3 | "profile": false,
4 | "logDetails": true,
5 | "logAdditionalDetails": true,
6 | "logWarnings": false,
7 | "logAdditionalWarnings": false,
8 | "logError": false,
9 | "logErrorDetails": false,
10 | "exchangesToWatch": ["binance"],
11 | "orderBookLimit": 1000,
12 | "exchangeOptions": {
13 | "bleutrade": {},
14 | "binance": {}
15 | },
16 | "keys": {
17 | "bleutrade": {
18 | "apiKey": "",
19 | "secret": ""
20 | },
21 | "binance": {
22 | "apiKey": "",
23 | "secret": ""
24 | }
25 | },
26 | "currenciesToWatch": ["ETH", "BTC", "BNB"],
27 | "orderOptionsByExchange": {
28 | "binance": {
29 | "adjustForTimeDifference": true
30 | }
31 | },
32 | "defaultOrderOptions": {},
33 | "enableProxy": false,
34 | "changeProxyAfterEveryOrder": false,
35 | "changeProxyAfterAnyNetworkError": false,
36 | "parallelOrders": false,
37 | "proxies": [],
38 | "telegram": {
39 | "token": "",
40 | "startPhrase": "",
41 | "stopPhrase": "",
42 | "chats": [],
43 | "logErrors": false
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/example.ts:
--------------------------------------------------------------------------------
1 | import ArbitrageTriangleWithinExchange, { ArbitrageTriangleWithinExchangeParams } from "./algorithms/ArbitrageTriangleWithinExchange";
2 | import Bot from "./Bot";
3 | import { errorLogTemplate, log } from "./common/helpers";
4 | import { config } from "./common/config";
5 | import localConfig = require("./config.local.example.json");
6 | // ccxt.d.ts
7 |
8 | process
9 | .on('unhandledRejection', (reason, p) => {
10 | console.error(reason, '\x1b[34mUnhandled Rejection at Promise\x1b[0m', p);
11 | })
12 | .on('uncaughtException', err => {
13 | console.error(err, '\x1b[34mUncaught Exception thrown\x1b[0m');
14 | process.exit(1);
15 | });
16 |
17 | // add triggering by socket (additional lib per exchange, compatible with ccxt)
18 | // crawl over markets by default
19 |
20 | // @TODO: consider:
21 | // moving to new CPU worker (optionally behind robin rounded proxy) if there is no other already running with given exchanges, otherwise add to queue
22 |
23 | const bot = new Bot(config(localConfig));
24 | startBot();
25 |
26 | async function startBot() {
27 | bot.init().then(
28 | () => startArbitrageTriangleWithinExchangeAlgorithm(),
29 | err => log(errorLogTemplate(err))
30 | );
31 | bot.printProfileTime();
32 | }
33 |
34 | function startArbitrageTriangleWithinExchangeAlgorithm() {
35 | Object.entries(bot.exchanges).forEach(async ([key, exchange]) => {
36 | try {
37 | await exchange.loadMarkets();
38 | const validatedTriplets = ArbitrageTriangleWithinExchange.getValidatedTripletsOnExchange(exchange, bot.config.currenciesToWatch);
39 |
40 | log(`\x1b[32mFound ${validatedTriplets.length} ArbitrageTriangleWithinExchange triplets on ${exchange.id}\x1b[0m`);
41 | console.table(validatedTriplets)
42 |
43 | bot.cycle(
44 | validatedTriplets,
45 | (params: ArbitrageTriangleWithinExchangeParams) => {
46 | return new ArbitrageTriangleWithinExchange(params)
47 | }, element => ({
48 | exchange: exchange,
49 | bot: bot,
50 | markets: [
51 | exchange.markets[element[0]],
52 | exchange.markets[element[1]],
53 | exchange.markets[element[2]]
54 | ],
55 | balances: bot.balances[key],
56 | validateMarkets: false
57 | }),
58 | () => {
59 | log( `\x1b[32mCycle ${exchange.id} ArbitrageTriangleWithinExchange\x1b[0m`)
60 | }
61 | );
62 | } catch (err) {
63 | log(errorLogTemplate(err));
64 | bot.printProfileTime();
65 | }
66 | });
67 | }
68 |
69 | // @TODO: add checking
70 | // {
71 | // ...bot.config.ignore,
72 | // ...bot.config.MARKET_EXCHANGE_KEY.ignore,
73 | // ...bot.config.MARKET_KEY.ignore,
74 | // ...bot.config.MARKET_EXCHANGE_KEY.MARKET_KEY.ignore
75 | // }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Algorithm from "./algorithms/Algorithm";
2 | export * from "./Bot";
3 | export * from "./common/config";
4 | export * from "./algorithms/Algorithm";
5 | export * from "./algorithms/ArbitrageBetweenExchanges";
6 | export * from "./algorithms/ArbitrageTriangleWithinExchange";
7 | export * from "./algorithms/ArbitrageTriangularBetweenExchanges";
8 | export * from "./common/constants";
9 | export * from "./common/helpers";
10 | export * from "./common/interfaces";
11 | export * from "./common/types";
--------------------------------------------------------------------------------
/src/misc/Telegram.ts:
--------------------------------------------------------------------------------
1 | process.env.NTBA_FIX_319 = '1';
2 | import TelegramBot from 'node-telegram-bot-api';
3 |
4 | export interface TelegramParams {
5 | token: string;
6 | startPhrase: string;
7 | stopPhrase: string;
8 | chats: string[];
9 | logErrors: boolean;
10 | }
11 |
12 | export default class Telegram {
13 | token = '';
14 | startPhrase = 'start';
15 | stopPhrase = 'stop';
16 | chats = [];
17 | telegramBot: any;
18 | logErrors = false;
19 |
20 | constructor(params: TelegramParams) {
21 | const {token, startPhrase, stopPhrase, chats, logErrors} = {...params};
22 |
23 | this.token = token;
24 | this.startPhrase = startPhrase;
25 | this.stopPhrase = stopPhrase;
26 | this.chats = chats;
27 | this.logErrors = logErrors;
28 |
29 | if(!this.token) {
30 | // @TODO: format
31 | if(this.logErrors) console.log('Cannot start Telegram notifications without token');
32 | return;
33 | }
34 |
35 | this.init();
36 | }
37 |
38 | init() {
39 | this.telegramBot = new TelegramBot(this.token, {polling: true});
40 |
41 | this.telegramBot.onText(this.startPhrase, (msg) => {
42 | this.addChat(msg.chat.id)
43 | let chatId = msg.chat.id
44 | this.telegramBot.sendMessage(chatId, 'Notifications started')
45 | })
46 |
47 | this.telegramBot.onText(this.stopPhrase, (msg) => {
48 | this.removeChat(msg.chat.id)
49 | let chatId = msg.chat.id
50 | this.telegramBot.sendMessage(chatId, 'Notifications stopped')
51 | })
52 |
53 | this.telegramBot.on('message', (msg) => {
54 | let chatId = msg.chat.id
55 | this.telegramBot.sendMessage(chatId, 'Enter start phrase to start. Enter stop phrase to stop notifications.')
56 | });
57 | }
58 |
59 | addChat = (id) => {
60 | if (!this.token) return;
61 | if (this.chats.indexOf(id) >= 0) return;
62 | this.chats.push(id);
63 | }
64 |
65 | removeChat = (id) => {
66 | if (!this.token) return;
67 | const index = this.chats.indexOf(id)
68 | if (index < 0) return;
69 | this.chats.splice(index, 1);
70 | }
71 |
72 | sendMessage = (message) => {
73 | if (!this.token) return;
74 | if (this.chats.length === 0) return;
75 | this.chats.forEach(chat => {
76 | this.telegramBot
77 | .sendMessage(chat, message, {parse_mode: 'HTML'})
78 | .catch(error => {
79 | // @TODO: format
80 | if(this.logErrors) console.log(error)
81 | })
82 | })
83 | }
84 | }
--------------------------------------------------------------------------------
/test/ArbitrageTriangleWithinExchange.spec.ts:
--------------------------------------------------------------------------------
1 | import { log } from "../src/common/helpers";
2 | import ArbitrageTriangleWithinExchange from "../src/algorithms/ArbitrageTriangleWithinExchange";
3 | import { defaultMarket } from "./common/helpers";
4 |
5 | describe("ArbitrageTriangleWithinExchange validateMarkets", () => {
6 | test("should fails with A/B A/B A/B markets given", () => {
7 | let failResults = false;
8 |
9 | try {
10 | failResults = ArbitrageTriangleWithinExchange.validateMarkets([
11 | {
12 | ...defaultMarket,
13 | base: 'a',
14 | quote: 'b'
15 | },{
16 | ...defaultMarket,
17 | base: 'a',
18 | quote: 'b'
19 | },{
20 | ...defaultMarket,
21 | base: 'a',
22 | quote: 'b'
23 | }
24 | ]);
25 | } catch (err) {
26 | log(`${err.name} ${err.message}`);
27 | }
28 | expect(failResults).toBe(false);
29 | })
30 |
31 | test("should passes with A/B B/C A/C markets given", () => {
32 | let passResults = false;
33 |
34 | try {
35 | passResults = ArbitrageTriangleWithinExchange.validateMarkets([
36 | {
37 | ...defaultMarket,
38 | base: 'a',
39 | quote: 'b'
40 | },{
41 | ...defaultMarket,
42 | base: 'b',
43 | quote: 'c'
44 | },{
45 | ...defaultMarket,
46 | base: 'a',
47 | quote: 'c'
48 | }
49 | ]);
50 | } catch (err) {
51 | log(`${err.name} ${err.message}`);
52 | }
53 |
54 | expect(passResults).toBe(true);
55 | });
56 | });
--------------------------------------------------------------------------------
/test/common/helpers.ts:
--------------------------------------------------------------------------------
1 | export const defaultMarket = {
2 | id: '',
3 | symbol: '',
4 | base: '',
5 | quote: '',
6 | baseId: '',
7 | quoteId: '',
8 | active: true,
9 | precision: { base: 8, quote: 8, amount: 8, price: 8 },
10 | limits: { amount: {min: 0, max: 0}, price: {min: 0, max: 0}, cost: {min: 0, max: 0} },
11 | tierBased: false,
12 | percentage: true,
13 | taker: 0.02,
14 | maker: 0.02,
15 | info: {}
16 | };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "esModuleInterop": true,
5 | "target": "es6",
6 | "moduleResolution": "node",
7 | "sourceMap": true,
8 | "outDir": "dist",
9 | "resolveJsonModule": true,
10 | "declaration": true
11 | },
12 | "lib": ["es2015"],
13 | "include": [
14 | "src/**/*.ts"
15 | ],
16 | "exclude": [
17 | // "src/example.ts"
18 | ]
19 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {},
7 | "rules": {
8 | "no-console": false
9 | },
10 | "rulesDirectory": []
11 | }
--------------------------------------------------------------------------------