Skip to content

Commit

Permalink
Revert "major refactoring"
Browse files Browse the repository at this point in the history
This reverts commit b68dd72.
  • Loading branch information
bitrinjani committed Dec 5, 2017
1 parent b68dd72 commit 02171a0
Show file tree
Hide file tree
Showing 57 changed files with 224 additions and 392 deletions.
16 changes: 2 additions & 14 deletions src/AppRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,18 @@ import intl from './intl';
import 'reflect-metadata';
import container from './container';
import symbols from './symbols';
import { Arbitrager, QuoteAggregator, PositionService } from './types';
import { Arbitrager } from './type';
import { Container } from 'inversify';

export default class AppRoot {
private log = getLogger(this.constructor.name);
private quoteAggregator: QuoteAggregator;
private positionService: PositionService;
private log = getLogger('AppRoot');
private arbitrager: Arbitrager;

constructor(private readonly ioc: Container = container) { }

async start(): Promise<void> {
try {
this.log.info(intl.t('StartingTheService'));
this.quoteAggregator = this.ioc.get<QuoteAggregator>(symbols.QuoteAggregator);
await this.quoteAggregator.start();
this.positionService = this.ioc.get<PositionService>(symbols.PositionService);
await this.positionService.start();
this.arbitrager = this.ioc.get<Arbitrager>(symbols.Arbitrager);
await this.arbitrager.start();
this.log.info(intl.t('SuccessfullyStartedTheService'));
Expand All @@ -36,12 +30,6 @@ export default class AppRoot {
if (this.arbitrager) {
await this.arbitrager.stop();
}
if (this.positionService) {
await this.positionService.stop();
}
if (this.quoteAggregator) {
await this.quoteAggregator.stop();
}
this.log.info(intl.t('SuccessfullyStoppedTheService'));
} catch (ex) {
this.log.error(ex.message);
Expand Down
125 changes: 78 additions & 47 deletions src/ArbitragerImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,26 @@ import Order from './Order';
import {
BrokerAdapterRouter, ConfigStore, PositionService,
QuoteAggregator, SpreadAnalyzer, Arbitrager, SpreadAnalysisResult,
OrderType, QuoteSide, OrderSide, LimitCheckerFactory
} from './types';
OrderType, QuoteSide, OrderSide, OrderStatus, BrokerConfig
} from './type';
import intl from './intl';
import { padEnd, hr, delay, calculateCommission, findBrokerConfig } from './util';
import { padEnd, hr, delay, calculateCommission } from './util';
import Quote from './Quote';
import symbols from './symbols';
import { LOT_MIN_DECIMAL_PLACE } from './constants';

const t = s => intl.t(s);

@injectable()
export default class ArbitragerImpl implements Arbitrager {
private log = getLogger(this.constructor.name);
private log = getLogger('Arbitrager');
private activeOrders: Order[] = [];

constructor(
@inject(symbols.QuoteAggregator) private readonly quoteAggregator: QuoteAggregator,
@inject(symbols.ConfigStore) private readonly configStore: ConfigStore,
@inject(symbols.PositionService) private readonly positionService: PositionService,
@inject(symbols.BrokerAdapterRouter) private readonly brokerAdapterRouter: BrokerAdapterRouter,
@inject(symbols.SpreadAnalyzer) private readonly spreadAnalyzer: SpreadAnalyzer,
@inject(symbols.LimitCheckerFactory) private readonly limitCheckerFactory: LimitCheckerFactory
@inject(symbols.SpreadAnalyzer) private readonly spreadAnalyzer: SpreadAnalyzer
) { }

status: string = 'Init';
Expand All @@ -35,49 +33,77 @@ export default class ArbitragerImpl implements Arbitrager {
this.status = 'Starting';
this.log.info(t('StartingArbitrager'));
this.quoteAggregator.onQuoteUpdated = (quotes: Quote[]) => this.quoteUpdated(quotes);
await this.quoteAggregator.start();
await this.positionService.start();
this.status = 'Started';
this.log.info(t('StartedArbitrager'));
}

async stop(): Promise<void> {
this.status = 'Stopping';
this.log.info('Stopping Arbitrager...');
if (this.positionService) {
await this.positionService.stop();
}
if (this.quoteAggregator) {
await this.quoteAggregator.stop();
this.quoteAggregator.onQuoteUpdated = undefined;
}
this.log.info('Stopped Arbitrager.');
this.status = 'Stopped';
}

private async arbitrage(quotes: Quote[]): Promise<void> {
this.status = 'Arbitraging';
if (!this.positionService.isStarted) {
this.status = 'Waiting Position Service';
this.log.info(t('WaitingForPositionService'));
return;
}
this.log.info(t('LookingForOpportunity'));
const { config } = this.configStore;
let spreadAnalysisResult: SpreadAnalysisResult;
if (this.isMaxExposureBreached()) {
this.status = 'Max exposure breached';
this.log.error(t('NetExposureIsLargerThanMaxNetExposure'));
return;
}
let result: SpreadAnalysisResult;
try {
spreadAnalysisResult = await this.spreadAnalyzer.analyze(quotes, this.positionService.positionMap);
result = await this.spreadAnalyzer.analyze(quotes, this.positionService.positionMap);
} catch (ex) {
this.status = 'Spread analysis failed';
this.log.warn(t('FailedToGetASpreadAnalysisResult'), ex.message);
this.log.debug(ex.stack);
return;
}
this.printSnapshot(spreadAnalysisResult);
const limitChecker = this.limitCheckerFactory.create(spreadAnalysisResult);
const limitCheckResult = limitChecker.check();
if (!limitCheckResult.success) {
if (limitCheckResult.reason) {
this.status = limitCheckResult.reason;
}

this.printSnapshot(result);
const { bestBid, bestAsk, invertedSpread,
targetVolume, targetProfit } = result;
if (invertedSpread <= 0) {
this.status = 'Spread not inverted';
this.log.info(t('NoArbitrageOpportunitySpreadIsNotInverted'));
return;
}
const minTargetProfit = _.max([
config.minTargetProfit,
config.minTargetProfitPercent !== undefined ?
_.round((config.minTargetProfitPercent / 100) * _.mean([bestAsk.price, bestBid.price]) * targetVolume) :
0
]) as number;
if (targetProfit < minTargetProfit) {
this.status = 'Too small profit';
this.log.info(t('TargetProfitIsSmallerThanMinProfit'));
return;
}
if (config.demoMode) {
this.status = 'Demo mode';
this.log.info(t('ThisIsDemoModeNotSendingOrders'));
return;
}

this.log.info(t('FoundArbitrageOppotunity'));
try {
const { bestBid, bestAsk, targetVolume } = spreadAnalysisResult;
await Promise.all([
this.sendOrder(bestAsk, targetVolume, OrderType.Limit),
this.sendOrder(bestBid, targetVolume, OrderType.Limit)
]);
await this.sendOrder(bestAsk, targetVolume, OrderType.Limit);
await this.sendOrder(bestBid, targetVolume, OrderType.Limit);
this.status = 'Sent';
await this.checkOrderState();
} catch (ex) {
Expand All @@ -91,16 +117,20 @@ export default class ArbitragerImpl implements Arbitrager {
}

private printSnapshot(result: SpreadAnalysisResult) {
const columnWidth = 17;
this.log.info('%s: %s', padEnd(t('BestAsk'), columnWidth), result.bestAsk);
this.log.info('%s: %s', padEnd(t('BestBid'), columnWidth), result.bestBid);
this.log.info('%s: %s', padEnd(t('Spread'), columnWidth), -result.invertedSpread);
this.log.info('%s: %s', padEnd(t('AvailableVolume'), columnWidth), result.availableVolume);
this.log.info('%s: %s', padEnd(t('TargetVolume'), columnWidth), result.targetVolume);
this.log.info('%s: %s', padEnd(t('BestAsk'), 17), result.bestAsk);
this.log.info('%s: %s', padEnd(t('BestBid'), 17), result.bestBid);
this.log.info('%s: %s', padEnd(t('Spread'), 17), -result.invertedSpread);
this.log.info('%s: %s', padEnd(t('AvailableVolume'), 17), result.availableVolume);
this.log.info('%s: %s', padEnd(t('TargetVolume'), 17), result.targetVolume);
const midNotional = _.mean([result.bestAsk.price, result.bestBid.price]) * result.targetVolume;
const profitPercentAgainstNotional = _.round((result.targetProfit / midNotional) * 100, LOT_MIN_DECIMAL_PLACE);
this.log.info('%s: %s (%s%%)', padEnd(t('ExpectedProfit'), columnWidth),
result.targetProfit, profitPercentAgainstNotional);
const profitPercentAgainstNotional = _.round((result.targetProfit / midNotional) * 100, 2);
this.log.info('%s: %s (%s%%)', padEnd(t('ExpectedProfit'), 17), result.targetProfit, profitPercentAgainstNotional);
}

private isMaxExposureBreached(): boolean {
this.log.debug(`Net exposure ${this.positionService.netExposure}, ` +
`Max exposure ${this.configStore.config.maxNetExposure}`);
return Math.abs(this.positionService.netExposure) > this.configStore.config.maxNetExposure;
}

private async checkOrderState(): Promise<void> {
Expand All @@ -112,23 +142,21 @@ export default class ArbitragerImpl implements Arbitrager {
this.log.info(t('OrderCheckAttempt'), i);
this.log.info(t('CheckingIfBothLegsAreDoneOrNot'));
try {
await Promise.all([
this.brokerAdapterRouter.refresh(buyOrder),
this.brokerAdapterRouter.refresh(sellOrder)
]);
await this.brokerAdapterRouter.refresh(buyOrder);
await this.brokerAdapterRouter.refresh(sellOrder);
} catch (ex) {
this.log.warn(ex.message);
this.log.debug(ex.stack);
}

if (!buyOrder.filled) {
if (!this.isFilled(buyOrder)) {
this.log.warn(t('BuyLegIsNotFilledYetPendingSizeIs'), buyOrder.pendingSize);
}
if (!sellOrder.filled) {
if (!this.isFilled(sellOrder)) {
this.log.warn(t('SellLegIsNotFilledYetPendingSizeIs'), sellOrder.pendingSize);
}

if (buyOrder.filled && sellOrder.filled) {
if (this.isFilled(buyOrder) && this.isFilled(sellOrder)) {
this.status = 'Filled';
const commission = _([buyOrder, sellOrder]).sumBy(o => this.calculateFilledOrderCommission(o));
const profit = _.round(sellOrder.filledSize * sellOrder.averageFilledPrice -
Expand All @@ -146,24 +174,26 @@ export default class ArbitragerImpl implements Arbitrager {
if (i === config.maxRetryCount) {
this.status = 'MaxRetryCount breached';
this.log.warn(t('MaxRetryCountReachedCancellingThePendingOrders'));
const promises: Promise<void>[] = [];
if (!buyOrder.filled) {
promises.push(this.brokerAdapterRouter.cancel(buyOrder));
if (!this.isFilled(buyOrder)) {
await this.brokerAdapterRouter.cancel(buyOrder);
}
if (!sellOrder.filled) {
promises.push(this.brokerAdapterRouter.cancel(sellOrder));
if (!this.isFilled(sellOrder)) {
await this.brokerAdapterRouter.cancel(sellOrder);
}
await Promise.all(promises);
break;
}
}
}

private calculateFilledOrderCommission(order: Order): number {
const brokerConfig = findBrokerConfig(this.configStore.config, order.broker);
const brokerConfig = _.find(this.configStore.config.brokers, b => b.broker === order.broker) as BrokerConfig;
return calculateCommission(order.averageFilledPrice, order.filledSize, brokerConfig.commissionPercent);
}

private isFilled(order: Order): boolean {
return order.status === OrderStatus.Filled;
}

private async quoteUpdated(quotes: Quote[]): Promise<void> {
this.positionService.print();
this.log.info(hr(20) + 'ARBITRAGER' + hr(20));
Expand All @@ -173,7 +203,8 @@ export default class ArbitragerImpl implements Arbitrager {

private async sendOrder(quote: Quote, targetVolume: number, orderType: OrderType): Promise<void> {
this.log.info(t('SendingOrderTargettingQuote'), quote);
const brokerConfig = findBrokerConfig(this.configStore.config, quote.broker);
const brokerConfig = _.find(this.configStore.config.brokers,
x => x.broker === quote.broker) as BrokerConfig;
const orderSide = quote.side === QuoteSide.Ask ? OrderSide.Buy : OrderSide.Sell;
const { cashMarginType, leverageLevel } = brokerConfig;
const order = new Order(quote.broker, orderSide, targetVolume,
Expand Down
9 changes: 5 additions & 4 deletions src/Bitflyer/BrokerAdapterImpl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
BrokerAdapter, BrokerConfig, ConfigStore, OrderStatus,
OrderType, TimeInForce, OrderSide, CashMarginType, QuoteSide, Broker
} from '../types';
} from '../type';
import { getLogger } from '../logger';
import { injectable, inject } from 'inversify';
import symbols from '../symbols';
Expand All @@ -12,9 +12,9 @@ import BrokerApi from './BrokerApi';
import {
SendChildOrderResponse, ChildOrdersParam, SendChildOrderRequest,
ChildOrder, BoardResponse
} from './types';
} from './type';
import Execution from '../Execution';
import { eRound, findBrokerConfig } from '../util';
import { eRound } from '../util';

@injectable()
export default class BrokerAdapterImpl implements BrokerAdapter {
Expand All @@ -24,7 +24,8 @@ export default class BrokerAdapterImpl implements BrokerAdapter {
broker = Broker.Bitflyer;

constructor( @inject(symbols.ConfigStore) configStore: ConfigStore) {
this.config = findBrokerConfig(configStore.config, this.broker);
this.config = _.find(configStore.config.brokers,
(b: BrokerConfig) => b.broker === this.broker) as BrokerConfig;
this.brokerApi = new BrokerApi(this.config.key, this.config.secret);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Bitflyer/BrokerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
CancelChildOrderRequest, CancelChildOrderResponse, ChildOrdersParam,
ChildOrdersResponse, ExecutionsResponse, ExecutionsParam, Execution, BalanceResponse,
BoardResponse, ChildOrder, Balance
} from './types';
} from './type';
import * as querystring from 'querystring';

export default class BrokerApi {
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/BrokerAdapterRouterImpl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BrokerAdapterRouter, Broker, BrokerAdapter, BrokerMap } from './types';
import { BrokerAdapterRouter, Broker, BrokerAdapter, BrokerMap } from './type';
import { getLogger } from './logger';
import * as _ from 'lodash';
import Order from './Order';
Expand All @@ -8,7 +8,7 @@ import symbols from './symbols';

@injectable()
export default class BrokerAdapterRouterImpl implements BrokerAdapterRouter {
private log = getLogger(this.constructor.name);
private log = getLogger('BrokerAdapterRouter');
private brokerAdapterMap: BrokerMap<BrokerAdapter>;

constructor(@multiInject(symbols.BrokerAdapter) brokerAdapters: BrokerAdapter[]) {
Expand Down
2 changes: 1 addition & 1 deletion src/BrokerPosition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Broker } from './types';
import { Broker } from './type';
import * as _ from 'lodash';
import intl from './intl';
import { padStart, padEnd } from './util';
Expand Down
9 changes: 5 additions & 4 deletions src/Coincheck/BrokerAdapterImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import Execution from '../Execution';
import {
CashMarginType, ConfigStore, BrokerConfig, Broker,
BrokerAdapter, QuoteSide, OrderStatus, OrderSide
} from '../types';
import { OrderBooksResponse, NewOrderRequest, LeveragePosition } from './types';
} from '../type';
import { OrderBooksResponse, NewOrderRequest, LeveragePosition } from './type';
import { getBrokerOrderType } from './mapper';
import { eRound, almostEqual, findBrokerConfig } from '../util';
import { eRound, almostEqual } from '../util';

@injectable()
export default class BrokerAdapterImpl implements BrokerAdapter {
Expand All @@ -25,7 +25,8 @@ export default class BrokerAdapterImpl implements BrokerAdapter {
constructor(
@inject(symbols.ConfigStore) configStore: ConfigStore
) {
this.config = findBrokerConfig(configStore.config, this.broker);
this.config = _.find(configStore.config.brokers,
(b: BrokerConfig) => b.broker === this.broker) as BrokerConfig;
this.brokerApi = new BrokerApi(this.config.key, this.config.secret);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Coincheck/BrokerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
AccountsBalanceResponse, LeveragePositionsRequest, LeveragePositionsResponse,
LeveragePosition, OrderBooksResponse, NewOrderRequest, NewOrderResponse,
CancelOrderResponse, OpenOrdersResponse, TransactionsResponse, Pagination, Transaction
} from './types';
} from './type';

export default class BrokerApi {
key: string;
Expand Down
2 changes: 1 addition & 1 deletion src/Coincheck/mapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Order from '../Order';
import { CashMarginType, OrderSide, OrderType } from '../types';
import { CashMarginType, OrderSide, OrderType } from '../type';

export function getBrokerOrderType(order: Order): string {
switch (order.cashMarginType) {
Expand Down
File renamed without changes.
Loading

0 comments on commit 02171a0

Please sign in to comment.