1.6.15 • Published 4 years ago

@cross.team/fti-sdk v1.6.15

Weekly downloads
-
License
ISC
Repository
github
Last release
4 years ago

fti-sdk

Franklin Templeton SDK used for backend interactivity

Overview

This SDK is used to standardize the way the FTI-Blotter app will interact with the different UI data sources.

Install

Install the package in your project directory with:

// with npm
npm install @cross.team/fti-sdk

// with yarn
yarn add @cross.team/fti-sdk

General Usage

import { ftidata } from '@cross.team/fti-sdk'

const desks = ftidata.desks.get()

desks
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })

Available ftidata Methods

ftidata.set({env, src}) ftidata.envs() ftidata.sources(env)

ftidata.desks.get() ftidata.desks.createInstance() ftidata.desk.get(initials) ftidata.desk.createInstance() ftidata.desk.set(data) ftidata.users.auth(user) ftidata.users.createAuthPayloadInstance() ftidata.orders.get(trader_ids, uid) ftidata.ordersNew.get(trader_ids, uid) ftidata.users.get(user)

ftidata.set({env, src})

Back to index

Sets the current environmen and source of data to use.

  • object { env: %ENVIRONMENT ID%, src: %SOURCE ID%}

Returns: none

Expected schema: none

ftidata.envs()

Back to index

Returns a list of environments available within a data source

  • No method parameter allowed

Returns: An array of {id, name}

ftidata.sources(env)

Back to index

Returns a list of sources available for a particular environment within a data source

  • string containing the id of an environment

Returns: An array of {id, name}

ftidata.desks.get()

Back to index

Returns a list of available desks

  • No method parameter allowed

Usage:

const desks = ftidata.desks.get()

desks
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })

Returns:

{
    data: [ // returning data; empty if none found
        {
            id: 'unique identifies',
            cwalls: [ // chinese wall groups associated with this desk
                {
                    initials: 'chinese wall group initials'
                }
            ],
            initial: 'initials used to describe the desk',
            location: 'desk location',
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Expected schema:

{
    id: yup
        .string()
        .required()
        .default(''),
    cwalls: yup
        .array()
        .of(
            yup.object().shape({
            initials: yup.string(),
            }),
        )
        .default([]),
    initial: yup.string().default(''),
    location: yup.string().default(''),
}

ftidata.desks.createInstance()

Back to index

Returns a clean instance of the desks resource

  • No method parameter allowed

Returns: SEE ftidata.desks.get()

Expected schema: SEE ftidata.desks.get()

ftidata.desk.get(initials)

Back to index

Returns a specific desk based on the desk initials passed

  • initials: valid desk initial

Usage:

const desk = ftidata.desk.get("HK")

desk
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })

Returns:

{
    data: [ // returning data; empty if none found
        {
            id: 'unique identifies',
            cwalls: [ // chinese wall groups associated with this desk
                {
                    initials: 'chinese wall group initials'
                }
            ],
            initial: 'initials used to describe the desk',
            location: 'desk location',
            adv_percentage: '% ADV',
            auto_execute: 'AUTO EXECUTE',
            auto_place_broker_ems: 'AUTO PLACE BROKER/EMS',
            auto_place_compid: 'AUTO PLACE COMPID',
            enable: 'ENABLE',
            trader_ids: 'EXCLUDE TRADER IDS',
            low_touch_managers: 'LOW TOUCH MANAGERS',
            no_touch_managers: 'NO TOUCH MANAGERS',
            route_to_trader_id: 'ROUTE TO TRADER ID',
            time_in_force: 'TIMEINFORCE',
            upper_order_limit: 'UPPER ORDER LIMIT',
            exclude_comment_orders: 'EXCLUDE COMMENT ORDERS',
            exclude_limit_orders: 'EXCLUDE LIMIT ORDERS',
            currencies: 'CURRENCIES',
            cwalls: 'CW GROUPS',
            adv_name: 'ADV NAME',
            funds: 'FUNDS',
            alert_rule_id: 'ALERT RULEID',
            security_types: 'SECURITY TYPES',
            auto_placement_market_open_offset: 'AUTOPLACEMENTMARKETOPENOFFSET',
            auto_placement_market_close_offset: 'AUTOPLACEMENTMARKETCLOSEOFFSET',
            upper_share_limit: 'UPPER SHARE LIMIT',
            ignore_lunch_hours: 'IGNORELUNCHHOURS',
            spread_limit: 'SPREAD LIMIT',
            adv_time_minutes: 'ADV TIME MINUTES',
            adv_time_percentage: 'ADV TIME %',
            dynamic_volume_calculation_enabled: 'DYNAMIC VOLUME CALCULATION ENABLED'
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Expected schema:

{
    id: yup
        .string()
        .required()
        .default(''),
    cwalls: yup
        .array()
        .of(
            yup.object().shape({
            initials: yup.string(),
            }),
        )
        .default([]),
    initial: yup.string().default(''),
    location: yup.string().default(''),
    adv_percentage: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(100)
        .nullable()
        .default(null),
    auto_execute: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
    auto_place_broker_ems: yup
        .string()
        .required()
        .uppercase()
        .default(''),
    auto_place_compid: yup
        .string()
        .required()
        .uppercase()
        .default(''),
    enable: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
    trader_ids: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.string().default(null)
                    ))
                    .default([]),
            )
        .default([]),
    low_touch_managers: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.string().default(null)
                    ))
                    .default([]),
            )
        .default([]),
    no_touch_managers: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.string().default(null)
                    ))
                    .default([]),
            )
        .default([]),
    route_to_trader_id: yup
        .string()
        .required()
        .uppercase()
        .default(''),
    time_in_force: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(9)
        .nullable()
        .default(null),
    upper_order_limit: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(99999999999)
        .nullable()
        .default(null),
    exclude_comment_orders: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
    exclude_limit_orders: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
    currencies: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.string().default(null)
                    ))
                    .default([]),
            )
        .default([]),
    cwalls: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.string().default(null)
                    ))
                    .default([]),
            )
        )
        .default([]),
    adv_name: yup
        .string()
        .required()
        .default(''),
    funds: yup
        .object()
        .shape(
                include: yup
                    .boolean()
                    .nullable()
                    .default(null),
                values: yup
                    .array()
                    .of(yup.object().shape(
                        value: yup.number().default(null)
                    ))
                    .default([]),
            )
        .default([]),
    alert_rule_id: yup
        .string()
        .required()
        .default(''),
    security_types: yup
        .object()
        .shape(
            include: yup
                .boolean()
                .nullable()
                .default(null),
            values: yup
                .array()
                .of(yup.object().shape(
                    value: yup.string().default(null)
                ))
                .default([]),
        )
        .default([]),
    auto_placement_market_open_offset: yup
        .number()
        .required()
        .integer()
        .min(-999)
        .max(999)
        .nullable()
        .default(null),
    auto_placement_market_close_offset: yup
        .number()
        .required()
        .integer()
        .min(-999)
        .max(999)
        .nullable()
        .default(null),
    upper_share_limit: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(999999)
        .nullable()
        .default(null),
    ignore_lunch_hours: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
    spread_limit: yup
        .string()
        .required()
        .default(''),
    adv_time_minutes: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(999)
        .nullable()
        .default(null),
    adv_time_percentage: yup
        .number()
        .required()
        .integer()
        .min(0)
        .max(100)
        .nullable()
        .default(null),
    dynamic_volume_calculation_enabled: yup
        .boolean()
        .required()
        .nullable()
        .default(null),
}

ftidata.desk.createInstance()

Back to index

Returns a clean instance of the desk resource

  • No method parameter allowed

Returns: SEE ftidata.desk.get(initials)

Expected schema: SEE ftidata.desk.get(initials)

ftidata.desk.set(data)

Back to index

Sets updates to desk and desk_vpm data to endpoint

  • data: an instance of ftidata.desk which represents the base data structure for

Usage:

const data = ftidata.desk.createInstance()
const desk = ftidata.desk.set(data)

desk
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })

Returns:

  • If set was unsuccessful, data would be an empty array and the stackErrors array would contain the errors
  • If set was successful, data would contain an 'update successful' message and stackErrors array would be empty
{
    data: [ // returning message; empty if none found
        {
            matchCount: 1
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Expected schema:

Same as the desk.get schema

ftidata.users.auth(user)

Back to index

Authenticates using a user object

  • user: an instance of ftidata.user which represents the base data structure for a user

Usage:

const user = ftidata.users.createAuthPayloadInstance()
user.uid = 'blb'
user.password = 'enternow'
const users = ftidata.users.auth(user)

users
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })

Returns:

  • If auth is unsuccessful, data would be an empty array and the stackErrors array would contain the errors
  • If auth is was successful, data would contain an new user profile object from the endpointand stackErrors array would be empty
{
    data: [ // returning message; empty if none found
        {
            fullname: string, // fullname
            isHeadOfDesk: boolean, // is this a head of desk
            isLoginBlocked: boolean, // is this profile blocked
            isManager: boolean, // is this profile a manager
            isServiceAcct: boolean, // is this profile a service account
            isTrader: boolean, // is this profile a trader
            lg_cwgroup: string, // profile cwgroup
            password: string, // current password
            passwordNew: string, // used when you want to change the profile password
            uid: string, // user id
            uloc: string, // user location
            ustatus: string, // user status
            auth: { // auth info
                appPermissions: { // app permissions
                    blotterAccess: Enum (NONE, READ, FULL)
                },
                authMessage: string, // auhentication message
                authSuccess: boolean, // authentication success status
                daysUntilExpiration: number, // days until the session expires
                daysUntilExpireWarning: number, // days until the session expire warning
                fullname: string, // fullname
                passwordExpired: boolean // is the password expired
            }
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Expected schema:

{
uid: yup
    .string()
    .nullable()
    .default(null),
  fullname: yup
    .string()
    .nullable()
    .default(null),
  ustatus: yup
    .string()
    .nullable()
    .default(null),
  uloc: yup
    .string()
    .nullable()
    .default(null),
  lg_cwgroup: yup
    .string()
    .nullable()
    .default(null),
  isTrader: yup
    .boolean()
    .nullable()
    .default(null),
  isServiceAcct: yup
    .boolean()
    .nullable()
    .default(null),
  isLoginBlocked: yup
    .boolean()
    .nullable()
    .default(null),
  isManager: yup
    .boolean()
    .nullable()
    .default(null),
  isHeadOfDesk: yup
    .boolean()
    .nullable()
    .default(null),
  password: yup
    .string()
    .nullable()
    .default(null),
  passwordNew: yup
    .string()
    .nullable()
    .default(null),
  auth: yup.object().shape({
    appPermissions: yup.object().shape({
      blotterAccess: yup
        .string()
        .required()
        .oneOf(['NONE', 'FULL', 'READ'])
        .nullable()
        .default(null),
    }),
    authMessage: yup
      .string()
      .nullable()
      .default(null),
    authSuccess: yup
      .boolean()
      .nullable()
      .default(null),
    daysUntilExpiration: yup
      .number()
      .integer()
      .nullable()
      .default(null),
    daysUntilExpireWarning: yup
      .number()
      .integer()
      .nullable()
      .default(null),
    fullname: yup
      .string()
      .nullable()
      .default(null),
    passwordExpired: yup
      .boolean()
      .nullable()
      .default(null),
}

ftidata.users.createAuthPayloadInstance()

Back to index

Returns a clean instance of the user object. Only uid and password are required to attempt to authenticate

  • No method parameter allowed

Returns: SEE ftidata.users.auth()

Expected schema: SEE ftidata.users.auth()

ftidata.orders.get(trader_ids, uid)

Back to index

Returns block orders based on an array of trader ids

  • trader_ids: an Array of 3 char, uppercase string of trader ids
  • uid: the user id of the current logged in user

Usage:

const traders = ['bpr', 'sl2', 'lt1']
const uid = 'bpr'
const orders = ftidata.orders.get(traders, uid)
orders
.then(res => {
  console.log(res)
 })
 .catch(err => {
   console.log(err)
 })

Returns:

  • If get is was successful, data would contain an new order objects from the endpoint and stackErrors array would be empty
{
    data: [ // returning message; empty if none found
        {
            id: string, // unique id for the block of orders -> concat of seq + bs + pby fields
            avg: number, // avg -> aveExecPrice
            placed: number, // orders that have been placed -> balance
            progress:  number, // progress -> balance
            buy: string, //  (b)uy or (s)ell code -> bs
            currency_code: string, // currency code -> currcode
            symbol_price: number, // price for the symbol -> currprc
            company_name1: string, // company name 1 -> issue1
            company_name2: string, // company name 2 -> issue2
            nbr_of_orders: number, // the number of orders -> ordercount
            progress_orig: number, // the original number of orders -> OrigUnits
            trader_id: string, // the trader id -> pby
            symbol: string, // company symbol -> reuters
            total_amt: number, // total amount of the balance -> usdbalance
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Response schema:

{
  id: yup
    .string()
    .required()
    .default(''),
  avg: yup
    .number()
    .nullable()
    .default(null),
  placed: yup
    .number()
    .nullable()
    .default(null),
  progress: yup
    .number()
    .nullable()
    .default(null),
  buy: yup
    .string()
    .nullable()
    .default(null),
  currency_code: yup
    .string()
    .nullable()
    .default(null),
  symbol_price: yup
    .number()
    .nullable()
    .default(null),
  company_name1: yup
    .string()
    .nullable()
    .default(null),
  company_name2: yup
    .string()
    .nullable()
    .default(null),
  nbr_of_orders: yup
    .number()
    .nullable()
    .default(null),
  progress_orig: yup
    .number()
    .nullable()
    .default(null),
  trader_id: yup
    .string()
    .nullable()
    .default(null),
  symbol: yup
    .string()
    .nullable()
    .default(null),
  total_amt: yup
    .number()
    .nullable()
    .default(null),
}

ftidata.ordersNew.get(trader_ids, uid)

Back to index

Returns new orders based on an array of trader ids

  • trader_ids: an Array of 3 char, uppercase string of trader ids
  • uid: the user id of the current logged in user

Usage:

const traders = ['bpr', 'sl2', 'lt1']
const uid = 'bpr'
const orders = ftidata.ordersNew.get(traders, uid)
orders
.then(res => {
  console.log(res)
 })
 .catch(err => {
   console.log(err)
 })

Returns:

  • If get is was successful, data would contain an new order objects from the endpoint and stackErrors array would be empty
{
    data: [ // returning message; empty if none found
        {
            id: string, // unique id for the order -> txser
            trader_id:: string, // the trader id -> pby,
            currency_code: string, // currency code -> isscurr,
            symbol_price1: number, // price1 for the symbol -> currprc,
            symbol_price2: number, // price2 for the symbol -> usValue,
            buy: string, //  'buy' or 'sell' status -> bs,
            status_fix: string, // fix status -> tstatus,
            symbol: string, // company symbol -> reuters,,
            percentage_change: number, // percentage change -> *pctChg,
            company_name1: string, // company name 1 -> issue1
            avg: number, // avg -> averagePrice,
            units: number, // units or total units -> units,
            total_units: number, // units or total units -> units,

        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Response schema:

{
 id: yup
    .string()
    .required()
    .default(''),
  trader_id: yup
    .string()
    .nullable()
    .default(null),
  currency_code: yup
    .string()
    .nullable()
    .default(null),
  symbol_price1: yup
    .number()
    .nullable()
    .default(null),
  symbol_price2: yup
    .number()
    .nullable()
    .default(null),
  buy: yup
    .string()
    .nullable()
    .default(null),
  status_fix: yup
    .string()
    .nullable()
    .default(null),
  symbol: yup
    .string()
    .nullable()
    .default(null),
  percentage_change: yup
    .number()
    .nullable()
    .default(null),
  company_name1: yup
    .string()
    .nullable()
    .default(null),
  avg: yup
    .number()
    .nullable()
    .default(null),
  units: yup
    .number()
    .nullable()
    .default(null),
  total_units: yup
    .number()
    .nullable()
    .default(null),
}

ftidata.users.get(user)

Back to index

Returns a user profile if the user initials entered are correct

  • user: 3 char, uppercase string of a user's initial

Usage:

  const user = ftidata.users.get('BPR')
  user
    .then(res => {
      console.log(res)
    })
    .catch(err => {
      console.log(err)
    })

Returns:

  • If get is was successful, data would contain an new user objects from the endpoint and stackErrors array would be empty
{
    data: [ // returning message; empty if none found
        {
            fullname: string, // fullname
            isHeadOfDesk: boolean, // is this a head of desk
            isLoginBlocked: boolean, // is this profile blocked
            isManager: boolean, // is this profile a manager
            isServiceAcct: boolean, // is this profile a service account
            isTrader: boolean, // is this profile a trader
            lg_cwgroup: string, // profile cwgroup
            password: string, // current password
            passwordNew: string, // used when you want to change the profile password
            uid: string, // user id
            uloc: string, // user location
            ustatus: string, // user status
            auth: { // auth info
                appPermissions: { // app permissions
                    blotterAccess: Enum (NONE, READ, FULL)
                },
                authMessage: string, // auhentication message
                authSuccess: boolean, // authentication success status
                daysUntilExpiration: number, // days until the session expires
                daysUntilExpireWarning: number, // days until the session expire warning
                fullname: string, // fullname
                passwordExpired: boolean // is the password expired
            }
        }
    ],
    stackCalls: [ // group of call status objects
        {
            name: 'resource name',
            call: [ // call detail
                headers: {}, // call headers
                status: int(3), // call status code
                statusText: string // call status text
            ]

        }
    ],
    stackErrors: [ // group of error objects tat occurred during execution (based off of the yup ValidationError)
        {
            error: ['array of strings containing all errors that occurred'],
            inner: [ // Array of ValidationError
                message: 'error message',
                path: 'field pertaining to the error'
                stack: 'error stack'
                value: 'value in error'
            ],
            message: 'general error or total error count'

        }
    ]
}

Response schema:

{
uid: yup
    .string()
    .nullable()
    .default(null),
  fullname: yup
    .string()
    .nullable()
    .default(null),
  ustatus: yup
    .string()
    .nullable()
    .default(null),
  uloc: yup
    .string()
    .nullable()
    .default(null),
  lg_cwgroup: yup
    .string()
    .nullable()
    .default(null),
  isTrader: yup
    .boolean()
    .nullable()
    .default(null),
  isServiceAcct: yup
    .boolean()
    .nullable()
    .default(null),
  isLoginBlocked: yup
    .boolean()
    .nullable()
    .default(null),
  isManager: yup
    .boolean()
    .nullable()
    .default(null),
  isHeadOfDesk: yup
    .boolean()
    .nullable()
    .default(null),
  password: yup
    .string()
    .nullable()
    .default(null),
  passwordNew: yup
    .string()
    .nullable()
    .default(null),
  auth: yup.object().shape({
    appPermissions: yup.object().shape({
      blotterAccess: yup
        .string()
        .required()
        .oneOf(['NONE', 'FULL', 'READ'])
        .nullable()
        .default(null),
    }),
    authMessage: yup
      .string()
      .nullable()
      .default(null),
    authSuccess: yup
      .boolean()
      .nullable()
      .default(null),
    daysUntilExpiration: yup
      .number()
      .integer()
      .nullable()
      .default(null),
    daysUntilExpireWarning: yup
      .number()
      .integer()
      .nullable()
      .default(null),
    fullname: yup
      .string()
      .nullable()
      .default(null),
    passwordExpired: yup
      .boolean()
      .nullable()
      .default(null),
}

MORE METHODS FORTHCOMING

1.6.15

4 years ago

1.6.14

4 years ago

1.6.13

4 years ago

1.6.12

4 years ago

1.6.11

4 years ago

1.6.10

4 years ago

1.6.9

4 years ago

1.6.8

4 years ago

1.6.7

4 years ago

1.6.6

4 years ago

1.6.5

4 years ago

1.6.4

4 years ago

1.6.3

4 years ago

1.6.2

4 years ago

1.6.1

4 years ago

1.6.0

4 years ago

1.5.11

5 years ago

1.5.10

5 years ago

1.5.9

5 years ago

1.5.8

5 years ago

1.5.7

5 years ago

1.5.6

5 years ago

1.5.5

5 years ago

1.5.4

5 years ago

1.5.3

5 years ago

1.5.2

5 years ago

1.5.1

5 years ago

1.5.0

5 years ago

1.4.10

5 years ago

1.4.9

5 years ago

1.4.8

5 years ago

1.4.7

5 years ago

1.4.6

5 years ago

1.4.5

5 years ago

1.4.4

5 years ago

1.4.3

5 years ago

1.4.2

5 years ago

1.4.1

5 years ago

1.4.0

5 years ago

1.3.5

5 years ago

1.3.4

5 years ago

1.3.3

5 years ago

1.3.2

5 years ago

1.3.1

5 years ago

1.3.0

5 years ago

1.2.9

5 years ago

1.2.8

5 years ago

1.2.7

5 years ago

1.2.6

5 years ago

1.2.5

5 years ago

1.2.4

5 years ago

1.2.3

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.9

5 years ago

1.1.8

5 years ago

1.1.7

5 years ago

1.1.6

5 years ago

1.1.5

5 years ago

1.1.4

5 years ago

1.1.3

5 years ago

1.1.2

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.0

5 years ago