[FEBE] Execute

stable
By pshai in Other Published October 2020 👁 1,709 views 💬 0 comments

Description

Execute command for FEBE. More information about FEBE: https://www.haasscripts.com/blog/guides/first-ever-bot-engine-febe-for-haasscript/
HaasScript
DefineCommand('FEBE_Execute', 'Execute FEBE logic with parameters')

local p = DefineParameter(ListDynamicType, 'params', 'Parameters for the core logic', true, {})

-- setup trading commands
local cmd_enterLong, cmd_enterShort
local cmd_exitPosition = PlaceExitPositionOrder
if MarketType() == SpotTrading then
    cmd_enterLong = PlaceBuyOrder
    cmd_enterShort = PlaceSellOrder
else
    cmd_enterLong = PlaceExitShortOrder
    cmd_enterShort = PlaceExitLongOrder
end

-- position information
local pos = {
    dir = GetPositionDirection(),
    aep = GetPositionEnterPrice(),
    amt = GetPositionAmount()
}

-- price information
local cp = CurrentPrice()

-- supported modules array
local supported_modules = {
    's_orders',
    's_tp',
    's_sl',
    's_buy',
    's_sell',
}

-- init active modules
local active_modules = {}
for i = 1, #supported_modules do
    active_modules[ supported_modules[i] ] = false
end


-- log with prefix
local log = function(msg)
    Log('[FEBE] ' .. msg)
end

-- log warning with prefix
local warn = function(msg)
    LogWarning('[FEBE] ' .. msg)
end

-- log error with prefix
local error = function(msg)
    LogError('[FEBE] ' .. msg)
end


-- update order bundle (buy/sell entries)
local updateOrderBundle = function(isLong, id, settings)
    local active = settings.active

    for i = 1, settings.order_count do
        local name = id .. i
        local oid = Load(name .. 'oid', '')
        local place_price = Load(name .. 'pp', 0)
        local filled = Load(name .. 'f', false)

        if oid != '' then
            local order = OrderContainer(oid)
            
            if order.isOpen then
                local delta = -1
                local max_dist = active and settings.order_cancel_dist or 0
                
                -- only calculate delta if cancel distance is used
                if max_dist > 0 then
                    delta = isLong
                        and Delta(cp.close, place_price)
                        or Delta(place_price, cp.close)
                end
                
                if not active then
                    CancelOrder(oid)
                    Log('Deactivated '..name)
                elseif delta >= settings.order_cancel_dist then
                    CancelOrder(oid)
                    Log('Delta cancelled '..name..' (delta: '..Round(delta, 4)..' %)')
                end
            else
                oid = ''

                if order.filledAmount > 0 then
                    filled = true
                end
            end
        else
            if active then
                if not filled or settings.order_refill then
                    -- select trading command
                    local cmd = isLong and cmd_enterLong or cmd_enterShort

                    -- order settings
                    local order_settings = {
                        type = CC_FEBE_ParseOrderType(settings.order_type),
                        note = name,
                        timeout = settings.order_timeout
                    }

                    -- order price with spread
                    local offset = settings.order_spread * i
                    local price = isLong
                            and SubPerc(cp.close, offset)
                            or AddPerc(cp.close, offset)
                    
                    -- order size
                    local amount = settings.order_size

                    -- place order
                    oid = cmd(price, amount, order_settings)

                    -- save price for cancel threshold calcs
                    place_price = cp.close
                end
            else
                -- reset
                filled = false
                place_price = 0
            end
        end


        Save(name .. 'oid', oid)
        Save(name .. 'pp', place_price)
        Save(name .. 'f', filled)
    end
end

-- update exit order
local updateExitOrder = function(id, note, price, type, active)
    local oid = Load(id .. 'oid', '')

    if oid == '' then
        if active then
            local settings = {
                type = CC_FEBE_ParseOrderType(type),
                note = note,
                timeout = 9999999
            }

            local isLong = pos.dir == PositionLong
            local cmd = isLong and cmd_enterShort or cmd_enterLong

            oid = cmd(price, pos.amt, settings)
        end
    else
        local order = OrderContainer(oid)

        if order.isOpen then
            if not active then
                CancelOrder(oid)
                Log('Deactivated '..note)
            
            elseif order.executedAmount != pos.amt then
                CancelOrder(oid)
                Log('Refreshing order size for '..note)

            elseif order.price != price then
                CancelOrder(oid)
                Log('Refreshing order price for '..note)
            end
        else
            oid = ''
        end
    end

    Save(id .. 'oid', oid)
end


-- pre-process parameters
local params = {}
local plen = #p
local key = ''
local value
for i = 1, plen do
    key = p[i][1]
    value = p[i][2]

    params[ key ] = {}

    -- build section parameters
    if value != nil then
        local vlen = #value
        for j = 1, vlen do
            params[ key ][ value[j][1] ] = value[j][2] 
        end
    end

    active_modules[ key ] = true

    Log( params[key] )
end

----------------------------  MODULES  ------------------------------
--    Module functionality should be placed below these lines!     --
---------------------------------------------------------------------

-- pre-define required variables
local orders = {}
local module = {}
module.name = supported_modules[1]
module.max_open = 'max_open'
module.size = 'size'
module.spread = 'spread'
module.cancel_dist = 'cancel_dist'

if active_modules[ module.name ] == true then
    log('Found order settings')

    local module_params = params[ module.name ]

    -- grab order settings
    orders.max_open     = module_params[ module.max_open ]
    orders.size         = module_params[ module.size ]
    orders.spread       = module_params[ module.spread ]
    orders.cancel_dist  = module_params[ module.cancel_dist ]

else
    -- setup 1 order and use main TradeAmount()
    orders.max_open = 1
    orders.size = TradeAmount()
    orders.spread = 0
    orders.cancel_dist = 0
end

---------------------------------------------------------------------
-- setup take-profit
module = {}
module.name = supported_modules[2]
module.percent = 'percent'
module.order_type = 'order_type'

if active_modules[ module.name ] == true then
    log('Found take-profit settings')

    local module_params = params[ module.name ]

    -- grab settings
    local tp = {}
    tp.percent          = module_params[ module.percent ]
    tp.order_type       = module_params[ module.order_type ]
    tp.active           = false
    tp.price            = 0

    -- if using market order, wait for trigger
    if tp.order_type == 2 then
        if TakeProfit(tp.percent) then
            tp.active = true
            tp.price = cp.close -- price isnt important for market orders
        end

    -- if using other than stop orders, we can place it immediately
    elseif tp.order_type <= 4 or tp.order_type >= 7 then
        -- place TP beforehand
        tp.active = true
        
        -- calculate trigger price
        if pos.dir == PositionLong then
            tp.price = AddPerc( pos.aep, tp.percent )
        elseif pos.dir == PositionShort then
            tp.price = SubPerc( pos.aep, tp.percent )
        end
    else
        error('Native Stop order types not supported for Take-Profit')
        tp.active = false
    end

    -- if we got a price, parse it properly
    if tp.price > 0 then
        tp.price = ParseTradePrice(PriceMarket(), tp.price)
    end

    tp.active = tp.active and pos.dir != NoPosition

    -- run
    updateExitOrder('tp', 'Take-Profit', tp.price, tp.order_type, tp.active)
end

---------------------------------------------------------------------
-- setup stop-loss
module.name = supported_modules[3]

if active_modules[ module.name ] == true then
    log('Found stop-loss settings')

    local module_params = params[ module.name ]

    -- grab settings
    local sl = {}
    sl.percent          = module_params[ module.percent ]
    sl.order_type       = module_params[ module.order_type ]
    sl.active           = false
    sl.price            = 0

    -- regular order types
    if sl.order_type <= 4 then
        -- wait until percent price change and place order
        if StopLoss(sl.percent) then
            sl.active = true
            
            if pos.dir == PositionLong then
                sl.price = cp.ask
            elseif pos.dir == PositionShort then
                sl.price = cp.bid
            end
        end

    -- native stop types
    elseif sl.order_type > 4 and sl.order_type < 7 then
        -- place SL beforehand when using native stops
        sl.active = true

        -- calculate trigger price
        if pos.dir == PositionLong then
            sl.price = SubPerc( pos.aep, sl.percent )
        elseif pos.dir == PositionShort then
            sl.price = AddPerc( pos.aep, sl.percent )
        end
    else
        error('Native Take-Profit order types not supported for Stop-Loss')
    end

    -- if we got a price, parse it properly
    if sl.price > 0 then
        sl.price = ParseTradePrice(PriceMarket(), sl.price)
    end

    sl.active = sl.active and pos.dir != NoPosition

    -- run
    updateExitOrder('sl', 'Stop-Loss', sl.price, sl.order_type, sl.active)
end

---------------------------------------------------------------------
-- setup and run buying
module = {}
module.name = supported_modules[4]
module.order_timeout = 'order_timeout'
module.order_refill = 'order_refill'
module.order_type = 'order_type'

if active_modules[ module.name ] == true then
    log('Found buy settings')

    local module_params = params[ module.name ]

    -- grab settings
    local buy = {}
    buy.active = true
    buy.order_timeout       = module_params[ module.order_timeout ]
    buy.order_refill        = module_params[ module.order_refill ]
    buy.order_type          = module_params[ module.order_type ]
    buy.order_count         = orders.max_open
    buy.order_size          = orders.size
    buy.order_spread        = orders.spread
    buy.order_cancel_dist   = orders.cancel_dist

    -- update orders with buy settings
    updateOrderBundle( true, 'buy', buy )
    
else
    -- grab settings
    local buy = {}
    buy.active = false
    buy.order_count         = orders.max_open

    -- cancel all open orders and reset
    updateOrderBundle( true, 'buy', buy )
end

---------------------------------------------------------------------
-- setup and run selling
module.name = supported_modules[5]

if active_modules[ module.name ] == true then
    log('Found sell settings')

    local module_params = params[ module.name ]

    -- grab settings
    local sell = {}
    sell.active = true
    sell.order_timeout       = module_params[ module.order_timeout ]
    sell.order_refill        = module_params[ module.order_refill ]
    sell.order_type          = module_params[ module.order_type ]
    sell.order_count         = orders.max_open
    sell.order_size          = orders.size
    sell.order_spread        = orders.spread
    sell.order_cancel_dist   = orders.cancel_dist

    -- update orders with sell settings
    updateOrderBundle( false, 'sell', sell )
    
else
    -- grab settings
    local sell = {}
    sell.active = false
    sell.order_count         = orders.max_open

    -- cancel all open orders and reset
    updateOrderBundle( false, 'sell', sell )
end


---------------------------------------------------------------------
-- plot position information on the chart
if pos.dir != NoPosition then
    local pc = PositionContainer()
    local id = pc.positionId
    local isLong = pos.dir == PositionLong

    -- position average entry price
    Plot(0, 'pos.aep', pos.aep, LineOptions({color = Cyan, id = id}))

    -- position size
    Plot(-2, 'pos.amt', pos.amt, LineOptions({color = isLong and Green or Red, id = id}))

    -- position profit
    Plot(-3, 'pos.profit', pc.profit, LineOptions({color = isLong and Green or Red, id = id}))

    -- position roi
    Plot(-3, 'pos.roi', pc.roi, LineOptions({color = isLong and Cyan or Fuchsia, id = id, side = LeftAxis}))

    -- zeron-lines for profit and roi
    PlotHorizontalLine(-3, '', White(50), 0, Dashed, RightAxis)
    PlotHorizontalLine(-3, '', White(50), 0, Dotted, LeftAxis)
end




---------------------------------------------------------------------
DefineOutput(VoidType)

0 Comments

Sign in to leave a comment.

No comments yet. Be the first!