Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

♻️ Refactoring exchanges services #15

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
♻️ Updating exchanges interfaces
  • Loading branch information
thibaultyou committed Aug 7, 2021
commit b2ebd449d4a74f4903c013752db02e8b89d1e427
15 changes: 10 additions & 5 deletions src/entities/trade.entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
Matches,
ValidateIf
} from 'class-validator';
import { SIDES, Side } from '../constants/trading.constants';
import {
SIDES,
Side,
TRADING_MODES,
TradingMode
} from '../constants/trading.constants';

export class Trade {
@IsString()
Expand All @@ -19,10 +24,10 @@ export class Trade {
@IsOptional()
max?: string;

// @IsString()
// @IsIn(TRADING_MODES)
// @IsOptional()
// mode?: TradingMode;
@IsString()
@IsIn(TRADING_MODES)
@IsOptional()
mode?: TradingMode;

@IsString()
@Matches(/.*(PERP|USD).*/)
Expand Down
53 changes: 53 additions & 0 deletions src/interfaces/exchanges/base/base.exchange.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Exchange, Order, Ticker } from 'ccxt';
import { Account } from '../../../entities/account.entities';
import { Trade } from '../../../entities/trade.entities';
import { IMarket } from '../../market.interfaces';
import { IOrderOptions } from '../../trading.interfaces';
import { IBalance, ISession } from '../common.exchange.interfaces';

export interface IBaseExchange {
checkCredentials(account: Account, instance: Exchange): Promise<boolean>;

refreshSession(account: Account): Promise<ISession>;

getBalances(account: Account, instance?: Exchange): Promise<IBalance[]>;

getTicker(symbol: string): Promise<Ticker>;

getMarkets(): Promise<IMarket[]>;

getAvailableFunds(account: Account, ticker: Ticker): Promise<number>;

getOpenOrderOptions(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<IOrderOptions>;

openOrder(account: Account, trade: Trade): Promise<Order>;

getCloseOrderOptions(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<IOrderOptions>;

closeOrder(
account: Account,
trade: Trade,
ticker?: Ticker // can be preloaded in openOrder
): Promise<Order>;

handleOrderModes(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<boolean>;

handleMaxBudget(
account: Account,
ticker: Ticker,
trade: Trade,
balance: number
): Promise<void>;
}
26 changes: 26 additions & 0 deletions src/interfaces/exchanges/base/futures.exchange.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Exchange, Ticker } from 'ccxt';
import { Account } from '../../../entities/account.entities';
import { Trade } from '../../../entities/trade.entities';
import { FuturesPosition } from '../../../types/exchanges.types';

export interface IFuturesExchange {
handleReverseOrder(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<void>;

handleOverflow(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<boolean>;

fetchPositions(instance: Exchange): Promise<FuturesPosition[]>;

getPositions(account: Account): Promise<FuturesPosition[]>;

getTickerPosition(account: Account, ticker: Ticker): Promise<FuturesPosition>;

getTickerPositionSize(account: Account, ticker: Ticker): Promise<number>;
}
6 changes: 6 additions & 0 deletions src/interfaces/exchanges/base/spot.exchange.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Ticker } from 'ccxt';
import { Account } from '../../../entities/account.entities';

export interface ISpotExchange {
getTickerBalance(account: Account, ticker: Ticker): Promise<number>;
}
114 changes: 46 additions & 68 deletions src/services/exchanges/base/base.exchange.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ import {
} from '../../../utils/trading/conversion.utils';
import { getTickerPrice } from '../../../utils/trading/ticker.utils';
import { filterBalances } from '../../../utils/trading/balance.utils';
import { IBaseExchange } from '../../../interfaces/exchanges/base/base.exchange.interfaces';

export abstract class BaseExchangeService {
export abstract class BaseExchangeService implements IBaseExchange {
exchangeId: ExchangeId;
defaultExchange: Exchange;
sessions = new Map<string, ISession>(); // account id, exchange session
Expand All @@ -70,19 +71,7 @@ export abstract class BaseExchangeService {
this.defaultExchange = new ccxt[exchangeId]();
}

abstract getCloseOrderOptions(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<IOrderOptions>;

abstract handleReverseOrder(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<void>;

abstract handleOverflow(
abstract handleOrderModes(
account: Account,
ticker: Ticker,
trade: Trade
Expand All @@ -95,6 +84,12 @@ export abstract class BaseExchangeService {
balance: number
): Promise<void>;

abstract getCloseOrderOptions(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<IOrderOptions>;

checkCredentials = async (
account: Account,
instance: Exchange
Expand Down Expand Up @@ -220,23 +215,6 @@ export abstract class BaseExchangeService {
return availableFunds;
};

// handleOrderModes = async (
// account: Account,
// ticker: Ticker,
// trade: Trade
// ): Promise<boolean> => {
// const { mode } = trade;
// if (mode === TradingMode.Reverse) {
// await this.handleReverseOrder(account, ticker, trade);
// } else if (mode === TradingMode.Overflow) {
// const isOverflowing = await this.handleOverflow(account, ticker, trade);
// if (isOverflowing) {
// return false; // on overflow we only close position
// }
// }
// return true;
// };

getOpenOrderOptions = async (
account: Account,
ticker: Ticker,
Expand Down Expand Up @@ -280,48 +258,48 @@ export abstract class BaseExchangeService {
const accountId = getAccountId(account);
try {
const ticker = await this.getTicker(symbol);
// const isOrderAllowed = await this.handleOrderModes(
// account,
// ticker,
// trade
// );
// if (isOrderAllowed) {
// TODO refacto
// close on sell spot order
if (
getSide(direction) === Side.Sell &&
isSpotExchange(ticker, this.exchangeId)
) {
return await this.closeOrder(account, trade, ticker);
}
const { side, size } = await this.getOpenOrderOptions(
const isOrderAllowed = await this.handleOrderModes(
account,
ticker,
trade
);
const cost = getOrderCost(ticker, this.exchangeId, size);
const order: Order = await this.sessions
.get(accountId)
.exchange.createMarketOrder(symbol, side, size);
side === Side.Buy
? long(
OPEN_LONG_TRADE_SUCCESS(
this.exchangeId,
accountId,
symbol,
cost.toFixed(2)
)
)
: short(
OPEN_SHORT_TRADE_SUCCESS(
this.exchangeId,
accountId,
symbol,
cost.toFixed(2)
if (isOrderAllowed) {
// TODO refacto
// close on sell spot order
if (
getSide(direction) === Side.Sell &&
isSpotExchange(ticker, this.exchangeId)
) {
return await this.closeOrder(account, trade, ticker);
}
const { side, size } = await this.getOpenOrderOptions(
account,
ticker,
trade
);
const cost = getOrderCost(ticker, this.exchangeId, size);
const order: Order = await this.sessions
.get(accountId)
.exchange.createMarketOrder(symbol, side, size);
side === Side.Buy
? long(
OPEN_LONG_TRADE_SUCCESS(
this.exchangeId,
accountId,
symbol,
cost.toFixed(2)
)
)
);
return order;
// }
: short(
OPEN_SHORT_TRADE_SUCCESS(
this.exchangeId,
accountId,
symbol,
cost.toFixed(2)
)
);
return order;
}
} catch (err) {
error(OPEN_TRADE_ERROR(this.exchangeId, accountId, symbol), err);
throw new OpenPositionError(
Expand Down
36 changes: 35 additions & 1 deletion src/services/exchanges/base/futures.exchange.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Exchange, Ticker } from 'ccxt';
import { TradingMode } from '../../../constants/trading.constants';
import { Account } from '../../../entities/account.entities';
import { Trade } from '../../../entities/trade.entities';
import { PositionsFetchError } from '../../../errors/exchange.errors';
import {
NoOpenPositionError,
OpenPositionError
} from '../../../errors/trading.errors';
import { IFuturesExchange } from '../../../interfaces/exchanges/base/futures.exchange.interfaces';
import {
NO_CURRENT_POSITION,
POSITIONS_READ_ERROR,
Expand All @@ -25,7 +27,22 @@ import { getSide } from '../../../utils/trading/side.utils';
import { debug, error, info } from '../../logger.service';
import { BaseExchangeService } from './base.exchange.service';

export abstract class FuturesExchangeService extends BaseExchangeService {
export abstract class FuturesExchangeService
extends BaseExchangeService
implements IFuturesExchange
{
abstract handleReverseOrder(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<void>;

abstract handleOverflow(
account: Account,
ticker: Ticker,
trade: Trade
): Promise<boolean>;

abstract fetchPositions(instance: Exchange): Promise<FuturesPosition[]>;

getPositions = async (account: Account): Promise<FuturesPosition[]> => {
Expand Down Expand Up @@ -72,6 +89,23 @@ export abstract class FuturesExchangeService extends BaseExchangeService {
return getPositionSize(position, this.exchangeId);
};

handleOrderModes = async (
account: Account,
ticker: Ticker,
trade: Trade
): Promise<boolean> => {
const { mode } = trade;
if (mode === TradingMode.Reverse) {
await this.handleReverseOrder(account, ticker, trade);
} else if (mode === TradingMode.Overflow) {
const isOverflowing = await this.handleOverflow(account, ticker, trade);
if (isOverflowing) {
return false; // on overflow we only close position
}
}
return true;
};

handleMaxBudget = async (
account: Account,
ticker: Ticker,
Expand Down
Loading