Hammer example

Hammer is a simple algorithm, which tries to place order first in trading day. You can see example code below.

import { Job } from 'node-schedule'
import { AbstractTradeAlgorithm, CreateOrderOptions, InputTypes, AlgorithmRun } from '@tradeb0t/core'
import type { StubExchangeApi } from '../exchange'

export interface HammerInput {
  order: CreateOrderOptions
  date: Date
  seconds_before: number
}
export interface HammerState {
  send_date: Date
}
export interface HammerStopData {
  job: Job
}

export class HammerAlgorithm extends AbstractTradeAlgorithm<HammerInput, HammerState, HammerStopData, StubExchangeApi> {
  get name(): string {
    return 'hammer'
  }
  get description(): string {
    return 'Hammer algorithm sends predefined order at specified time. If order is rejected, it will be sent again until order is placed.'
  }
  get inputs(): InputTypes {
    return {
      order: 'OrderDetails',
      date: 'Date',
      seconds_before: 'number'
    }
  }

  async main(inputs: HammerInput) {
    const { order, seconds_before, date } = inputs

    const send_date = this.addSecondsToDate(new Date(date), -seconds_before)
    const algorithmRun = await this.commitStart(inputs, { send_date })

    try {
      this.initialize(algorithmRun, { send_date, order })
    } catch (e) {
      return this.commitError(algorithmRun.id, e as Error)
    }

    return algorithmRun
  }

  async resume(id: number) {
    const algorithmRun = await this.loadProgress(id)
    const { order } = algorithmRun.inputs
    const { send_date } = algorithmRun.state

    try {
      this.initialize(algorithmRun, { send_date, order })
    } catch (e) {
      return this.commitError(algorithmRun.id, e as Error)
    }

    return this.commitContinue(id)
  }

  async stop(id: number) {
    const stopData = this.stopState.get(id)
    if (!stopData) throw new Error(`Algorithm run with id:${id} was not found.`)
    stopData?.job?.cancel()
    return this.commitStop(id)
  }

  private initialize(
    algorithmRun: AlgorithmRun,
    opts: {
      send_date: Date
      order: CreateOrderOptions
    }
  ) {
    const { trader } = this
    const { send_date, order } = opts

    const job = trader.scheduleAction(async () => {
      await this.sendUntilNotRejected(order, algorithmRun.id)
      await this.commitFinish(algorithmRun.id)
    }, new Date(send_date))

    this.stopState.set(algorithmRun.id, { job })

    if (new Date(send_date) < new Date())
      throw new Error(`Hammer run with id:${algorithmRun.id} can't send orders in the past.`)
  }

  private async sendUntilNotRejected(order: CreateOrderOptions, run_id: number) {
    try {
      const status = await this.trader.sendOrder(order, this.name, run_id)
      if (status === 'rejected') await this.sendUntilNotRejected(order, run_id)
    } catch (e) {
      await this.commitError(run_id, e as Error)
    }
  }

  private addSecondsToDate(date: Date, seconds: number) {
    return new Date(date.getTime() + seconds * 1000)
  }
}