Skip to content

Order Form Layouts

Cris Mihalache edited this page Oct 23, 2018 · 2 revisions

To create a custom order form layout, provide a meta.getUIDef method on your AO which returns an object conforming to the spec outlined in this document. For an example, see the Iceberg UI definition, which will be used to explain the spec further below.

Top-level Fields

To begin with, the order form layout must have a name which will appear in the order type dropdown, and an optional customHelp key which will render as a 'Help' tooltip in the bottom-left corner of the form layout:

  label: 'Iceberg',
  customHelp: 'Iceberg allows you to place a large order on the market while ensuring only a small part of it is ever filled at once.\n\nBy enabling the \'Excess As Hidden\' option, it is possible to offer up the remainder as a hidden order, allowing for minimal market disruption when executing large trades.',
  connectionTimeout: 10000,
  actionTimeout: 10000,

The connectionTimeout is currently unused, but must be provided in order to render the HF footer in the Order Form signaling that an HF algo server instance is connected to the API. The actionTimeout controls how long the form will wait for a response from your algo server after submitting a preview/submit request.

Defining Form Sections

The form layout itself is composed of multiple sections, each of which can have multiple fields. Sections can be conditionally disabled, hidden, or collapsible, depending on the state of individual fields. Sections are defined within an array on the sections key. Each section must have a name serving as the section ID, an optional title which will render next to the section divider, and a rows array defining the fields in that section.

rows must be an array of arrays, with each internal array having two elements, representing two side-by-side form fields. If a row has only one field, the other element in the array should be null for consistent rendering. Take the Iceberg sections definition as an example:

  sections: [{
    title: '',
    name: 'general',
    rows: [
      ['orderType', 'price'],
      ['amount', 'excessAsHidden'],
      ['sliceAmount', 'sliceAmountPerc'],
      ['submitDelaySec', 'cancelDelaySec'],
      ['action', null]
    ]
  }],

For a more complex sections definition, see Accumulate/Distribute which includes conditional sections for configuring indicator price caps/offsets. Parts of it will be referenced below.

To mark a section as required, so it may not be disabled via a checkbox, set fixed: true on the section.

To control visibility, or disable a section, the disabled and visible keys are provided, which take a set of conditions evaluated against the fields in the layout. Both accept a map of [field]: [condition], in which all conditions must resolve to true for the section to be disabled or visible. Conditions are limited to:

  • neq - not equal
  • eq - equal
  • gt - greater than
  • gte - greater than or equal
  • lt - less than
  • lte - less than or equal

The conditions themselves must address explicit values, i.e. someField: { eq: 'some-value' }. For an example, see the 'Price Offset EMA' visible condition set for A/D:

    visible: {
      orderType: { eq: 'RELATIVE' },
      offsetType: { eq: 'EMA' }
    },

Defining Form Fields

Fields referenced by ID in sections must be defined within the fields map:

  fields: {
    excessAsHidden: {
      component: 'input.checkbox',
      label: 'Excess as hidden',
      default: true,
    },
    // ...

Each field has a component specifying how the field should render; the available components are:

  • input.number - for standard numeric fields
  • input.price - for order prices, renders with red/green circle icons for setting top bid/ask
  • input.amount - for order amounts, renders with red/green circle icons for setting max buy/sell amounts
  • input.percent - numeric input which divides by 100 before submitting
  • input.checkbox - boolean input
  • input.buysell - low-level input with red/green circle icons, should not be used in custom layouts
  • input.dropdown - dropdown input which renders a set of options
  • input.radio - renders multiple radio buttons from an array of options

Fields must have a label key which renders above the field in the layout. A default value may be specified via default. Visibility & disabled status can be controlled by conditions like in the form sections. Examples of each field follow:

input.number

    submitDelaySec: {
      component: 'input.number',
      label: 'Submit Delay (sec)',
      customHelp: 'Seconds to wait before submitting orders',
      default: 2
    },

input.price

    price: {
      component: 'input.price',
      label: 'Price $QUOTE',
      disabled: {
        orderType: { eq: 'MARKET' }
      }
    },

input.amount

    amount: {
      component: 'input.amount',
      label: 'Amount $BASE',
      customHelp: 'Total order amount, to be executed slice-by-slice',
      priceField: 'price'
    },

input.percent

    sliceAmountPerc: {
      component: 'input.percent',
      label: 'Slice Amount as %',
      customHelp: 'Takes priority over literal amount'
    },

input.checkbox

    excessAsHidden: {
      component: 'input.checkbox',
      label: 'Excess as hidden',
      default: true,
      customHelp: 'Create a hidden order for the non-slice amount'
    },

input.dropdown

    orderType: {
      component: 'input.dropdown',
      label: 'Order Type',
      default: 'LIMIT',
      options: {
        LIMIT: 'Limit',
        MARKET: 'Market'
      }
    },

input.radio

    action: {
      component: 'input.radio',
      label: 'Action',
      options: ['Buy', 'Sell'],
      inline: true,
      default: 'Buy'
    }

Defining Form Actions

Form layouts can define two of three possible actions, rendered as buttons at the end of the form. The available actions are buy, sell, and preview for algorithmic orders. The actions must be defined as an array on the layout spec:

  actions: ['preview', 'submit']

Uploading/Using Layouts

Once you've created an order form layout, it must be added to the api:bitfinex_algorithmic_orders user setting via a call to updateSettings using the node API:

return await rest.updateSettings({
  'api:bitfinex_algorithmic_orders': {
    'custom_ao': { /* JSON spec */ },
    'another_custom_ao': { /* ... */ }
  }
})

Once uploaded, the custom layout will be in Algorithmic Orders section of the Order Type dropdown. Generated broadcast payloads have the following structure:

{
  type: 'ucm-[action]-[orderType]-req',
  info: {
    action, // i.e. 'preview'
    data, // form data as entered by the user, key'd by field name (or collapsible section name)
    mid, // request ID
    orderType // layout ID as provided in the `api:bitfinex_algorithmic_orders` user settings map
  }
}

To act upon a payload, receive it via ws2.onNotification() and respond with the next consecutive mid and a *-res notification type to update the UI, i.e:

{
  type: 'ucm-preview-customOrderType-res',
  info: {
    mid: mid + 1, // see above
    orders: [{
      // preview orders
    }, {
      // ...
    }],

    notify: { // processed as a 'ucm-notify-ui' packet
      type: 'info',
      message: 'Successfully generated preview'
    }
  }
}

Further Examples

For more examples of order form layouts, see the layouts for the three algorithmic order types bundled with this library: