Skip to content

Commit

Permalink
Account for fees
Browse files Browse the repository at this point in the history
  • Loading branch information
notVitaliy committed Nov 27, 2020
1 parent 1ec9dca commit bc96c6d
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 115 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"start:dev": "cross-env NODE_ENV=development nodemon -w ./src -e ts ./magic8bot.js",
"lint": "tslint -p ./tsconfig.json",
"lint:fix": "npm run lint -- --fix",
"test": "jest --detectOpenHandles --runInBand --forceExit"
"test": "jest --detectOpenHandles --runInBand --forceExit",
"test:watch": "jest --detectOpenHandles --runInBand --forceExit --watch"
},
"dependencies": {
"@magic8bot/event-bus": "^1.1.1",
Expand Down Expand Up @@ -85,4 +86,4 @@
"@m8bTypes": "dist/types/index.js",
"@util": "dist/util/index.js"
}
}
}
3 changes: 1 addition & 2 deletions src/core/strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Balances, Balance } from 'ccxt'
import { EventBusListener } from '@magic8bot/event-bus'

import { StrategyConf, Signal } from '@m8bTypes'
import { Signal } from '@m8bTypes'
import { eventBus, EVENT, StrategyConfig, Adjustment } from '@lib'

import { PeriodStore, WalletStore } from '@store'
Expand Down
195 changes: 102 additions & 93 deletions src/engine/order.spec.ts

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions src/engine/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class OrderEngine {
const price = this.exchangeProvider.priceToPrecision(exchange, symbol, rawPrice)

const purchasingPower = this.exchangeProvider.priceToPrecision(exchange, symbol, this.wallet.currency * strength)
const amount = this.exchangeProvider.amountToPrecision((purchasingPower / price) * 0.995)
const amount = this.exchangeProvider.amountToPrecision(purchasingPower / price)

// @todo(notVitaliy): Add support for market
const orderOpts: OrderOpts = { symbol, price, amount, type: 'limit', side: 'buy' }
Expand Down Expand Up @@ -129,6 +129,7 @@ export class OrderEngine {
const order = await this.exchangeProvider.checkOrder(exchange, id)

await this.updateOrder(order)

switch (order.status) {
case 'open':
return this.adjustOrder(id)
Expand All @@ -139,28 +140,30 @@ export class OrderEngine {
logger.info(`Order ${id} for ${symbol} on ${exchange} was canceled.`)
break
}

return this.orderStore.closeOpenOrder(this.storeOpts, id)
}

private async updateOrder(order: OrderWithTrades) {
logger.verbose(`update order`)
this.adjustWallet(order)

this.orderStore.updateOrder(this.storeOpts, order)
await this.orderStore.saveOrder(this.storeOpts, order)
}

private adjustWallet(order: OrderWithTrades) {
// @todo(notVitaliy): Adjust for fees
const openOrder = this.orderStore.getOpenOrder(this.storeOpts, order.id)
const adjustment = { asset: 0, currency: 0 }
logger.verbose(`adjust wallet`)

if (order.side === 'buy') {
adjustment.asset = order.filled - openOrder.filled
} else {
adjustment.currency = order.cost - openOrder.cost
}
const savedOrder = this.orderStore.getOpenOrder(this.storeOpts, order.id)
const adjustment = { asset: 0, currency: 0 }

if (order.side === 'buy') adjustment.asset = order.filled - savedOrder.filled
else adjustment.currency = order.cost - savedOrder.cost
if (adjustment.asset || adjustment.currency) this.emitWalletAdjustment({ ...adjustment, type: 'fillOrder' })

const savedFee = savedOrder.fee ? savedOrder.fee.cost : 0
if (order.fee && order.fee.cost) this.emitWalletAdjustment({ asset: 0, currency: 0 - (order.fee.cost - savedFee), type: 'fee' })
}

private async adjustOrder(id: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/exchange/exchange.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ExchangeAuth } from '@m8bTypes'
import ccxt, { Trade } from 'ccxt'
import { ExchangeWrapper } from './exchange.wrapper'
import { ExchangeErrorHandler } from './exchange.error'
import { wsServer, ExchangeConfig } from '@lib'
import { ExchangeConfig } from '@lib'
import { sleep, logger } from '@util'
import { ChaosXcg } from '../seed/chaos.exchange'

Expand Down
14 changes: 7 additions & 7 deletions src/exchange/exchange.wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

// const debug = { name: 'fetchTrades', req: { symbol, params }, res: res.length }
// logger.debug(JSON.stringify(debug))
// logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -47,7 +47,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

const debug = { name: 'fetchBalance', req: {}, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -57,7 +57,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

const debug = { name: 'fetchOrderBook', req: { symbol }, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -67,7 +67,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

const debug = { name: 'createOrder', req: { symbol, type, side, amount, price }, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -77,7 +77,7 @@ export class ExchangeWrapper {
const res: any = await this.bottleneck.schedule(fn)

const debug = { name: 'checkOrder', req: { orderId }, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -87,7 +87,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

const debug = { name: 'cancelOrder', req: { orderId }, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand All @@ -97,7 +97,7 @@ export class ExchangeWrapper {
const res = await this.bottleneck.schedule(fn)

const debug = { name: 'fetchTicker', req: { symbol }, res }
logger.debug(JSON.stringify(debug))
logger.silly(JSON.stringify(debug))

return res
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/db/db.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export type WalletCollection = Wallet & {
}

export type Adjustment = Wallet & {
type: 'user' | 'newOrder' | 'cancelOrder' | 'fillOrder'
type: 'user' | 'newOrder' | 'cancelOrder' | 'fillOrder' | 'fee'
}

export type AdjustmentCollection = Adjustment & {
Expand Down
2 changes: 2 additions & 0 deletions src/store/wallet.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export class WalletStore {
}

this.wallets.set(idStr, { asset: 0, currency: 0 })
if (!adjustment) return

await this.adjustWallet(storeOpts, adjustment)
}

Expand Down
14 changes: 14 additions & 0 deletions src/strategy/indicators/aroon-down.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { candles } from './spec.helper'
import { AroonDown } from './aroon-down'

describe('AroonDown', () => {
it('should calculate the lowest close', () => {
const lowest = AroonDown.getLowestPeriodIndex(candles)
expect(lowest).toBe(16)
})

it('should calculate correctly', () => {
const lowest = AroonDown.calculate(candles)
expect(lowest).toBe(36)
})
})
17 changes: 17 additions & 0 deletions src/strategy/indicators/aroon-down.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PeriodItem } from '@lib'

export class AroonDown {
public static calculate(allPeriods: PeriodItem[], length = 25) {
const usedPeriods = [...allPeriods]
usedPeriods.length = length

return ((length - this.getLowestPeriodIndex(usedPeriods)) / length) * 100
}

public static getLowestPeriodIndex(periods: PeriodItem[]) {
const mapped = periods.map(({ close }, idx) => ({ close, idx }))
const sorted = mapped.sort((a, b) => (a.close < b.close ? -1 : 1))

return sorted[0].idx
}
}
14 changes: 14 additions & 0 deletions src/strategy/indicators/aroon-up.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { candles } from './spec.helper'
import { AroonUp } from './aroon-up'

describe('AroonUp', () => {
it('should calculate the lowest low', () => {
const lowest = AroonUp.getHighestPeriodIndex(candles)
expect(lowest).toBe(58)
})

it('should calculate correctly', () => {
const lowest = AroonUp.calculate(candles)
expect(lowest).toBe(88)
})
})
17 changes: 17 additions & 0 deletions src/strategy/indicators/aroon-up.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PeriodItem } from '@lib'

export class AroonUp {
public static calculate(allPeriods: PeriodItem[], length = 25) {
const usedPeriods = [...allPeriods]
usedPeriods.length = length

return ((length - this.getHighestPeriodIndex(usedPeriods)) / length) * 100
}

public static getHighestPeriodIndex(periods: PeriodItem[]) {
const mapped = periods.map(({ close }, idx) => ({ close, idx }))
const sorted = mapped.sort((a, b) => (a.close > b.close ? -1 : 1))

return sorted[0].idx
}
}
2 changes: 2 additions & 0 deletions src/strategy/indicators/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './aroon-down'
export * from './aroon-up'
export * from './average'
export * from './cci'
export * from './cmf'
Expand Down
75 changes: 75 additions & 0 deletions src/strategy/strategies/aroon/aroon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { PeriodItem } from '@lib'
import { Signal } from '@m8bTypes'
import { AroonDown, AroonUp } from '../../indicators'
import { BaseStrategy, StrategyField } from '../base-strategy'
import { logger } from '../../../util'

export interface AroonOptions {
period?: string
}

export interface AroonPeriod {
up: number
down: number
}

export class Aroon extends BaseStrategy<AroonOptions, void> {
public static description = ''

public static fields: StrategyField[] = []

public options: AroonOptions = {}

private periods: AroonPeriod[] = [
{
up: null,
down: null,
},
]

constructor(exchange: string, symbol: string, options?: Partial<AroonOptions>) {
super('aroon', exchange, symbol)
this.options = { ...this.options, ...options }
}

public calculate(periods: PeriodItem[]) {
if (!periods.length) return

const up = AroonUp.calculate(periods, 60)
const down = AroonDown.calculate(periods, 60)

this.periods[0] = { up, down }
}

public onPeriod() {
const signal = this.getSignal()
const [{ up, down }] = this.periods

if (!this.isPreroll) logger.debug(`Aroon: up(${up}) down(${down})`)

return signal
}

private getSignal(): Signal {
if (this.shouldBuy()) return 'buy'
if (this.shouldSell()) return 'sell'

return null
}

private shouldBuy() {
const [{ up, down }] = this.periods

return up > 50 && down < 50
}

private shouldSell() {
const [{ up, down }] = this.periods

return up < 50 && down > 50
}

public newPeriod() {
this.periods.unshift({ up: null, down: null })
}
}
3 changes: 3 additions & 0 deletions src/strategy/strategies/aroon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Aroon } from './aroon'

export const strategy = Aroon

0 comments on commit bc96c6d

Please sign in to comment.