Percentage Price Change

Tutorial for creating the PercentagePriceChange custom command.

Introduction

In this tutorial you will learn how to implement your own PercentagePriceChange insurance. It will guide you through the process of creating custom commands from scratch.

This custom command will resemble the exact same functionality as the built-in command.

Creating new script

First off, we will start by creating a new script in the script editor.

Select the Command type and name it "Percentage Price Change".

Command and input parameters

Define command

Now, let's continue by splitting the script into sections and defining our command:

-- --------------------------------------
-- Command
-- --------------------------------------
DefineCommand(
   'PercentagePriceChange', -- Name
   'Custom insurance. Returns true if trade is allowed, otherwise false.' -- Description
   )

-- --------------------------------------
-- Parameters
-- --------------------------------------
   -- code here...

-- --------------------------------------
-- Logic
-- --------------------------------------
   -- code here...

-- --------------------------------------
-- Output
-- --------------------------------------
   -- code here...

Auto-complete will help you find and learn about commands as you type.

Define parameters

Easy start, no? Next, we need to have inputs for our command. There's two ways of doing this though:

  • DefineParameter()

    • Will only work via the command call.

  • Input()

    • Will only work via the user interface.

But in this guide, we will stick with DefineParameter(). We will be creating 3 parameters for our inputs. These are:

  • percentage

    • Parameter type: NumberType

    • Name: "percentage"

    • Description: "Minimum percentage change before a trade is allowed."

    • IsRequired: True

    • Default value: 0

    • Input suggestions: "Input"

    local percentage =
        DefineParameter(
            NumberType,
            'percentage',
            'Minimum percentage change before a trade is allowed.',
            true,
            0,
            'Input'
        )
  • targetPrice

    • Parameter type: NumberType

    • Name: "targetPrice"

    • Description: "The target price of the trade. Default is the current buy or sell price."

    • IsRequired: False

    • Default value: 0

    • Input suggestions: "ClosePrices, OpenPrices, BidPrices, AskPrices"

    local targetPrice =
        DefineParameter(
            NumberType,
            'targetPrice',
            'The target price of the trade. Default is the current buy or sell price.',
            false,
            0,
            'Number, Input, BidPrices, AskPrices'
        )
  • positionId

    • Parameter type: StringType

    • Name: "positionId"

    • Description: "Unique identifier. Used when the bot is trading multiple positions at once."

    • IsRequired: False

    • Default value: ""

    • Input suggestions: "SessionGet, Load"

    local positionId = 
        DefineParameter(
            StringType,
            'positionId',
            'Optional unique identifier. Required when the bot is trading multiple position at once.',
            false,
            '',
            'SessionGet, Load'
        )

Section code summary

Great! Now our script should look something like this:

-- --------------------------------------
-- Command
-- --------------------------------------
DefineCommand(
   'PercentagePriceChange', -- Name
   'Custom insurance. Returns true if trade is allowed, otherwise false.' -- Description
   )

-- --------------------------------------
-- Parameters
-- --------------------------------------
local percentage =
    DefineParameter(
        NumberType,
        'percentage',
        'Minimum percentage change before a trade is allowed.',
        true,
        0,
        'Input'
    )
 
local targetPrice =
    DefineParameter(
        NumberType,
        'targetPrice',
        'The target price of the trade. Default is the current buy or sell price.',
        false,
        0,
        'Number, Input, BidPrices, AskPrices'
    )

local positionId = 
    DefineParameter(
        StringType,
        'positionId',
        'Optional unique identifier. Required when the bot is trading multiple position at once.',
        false,
        '',
        'SessionGet, Load'
    )

-- --------------------------------------
-- Logic
-- --------------------------------------
   -- code here...

-- --------------------------------------
-- Output
-- --------------------------------------
   -- code here...

Adding logic

Internal variables

Right, so next we need to create the soul of this command. We will start off by defining some internal variables which we will use. What we need are position that is assigned with the position information and result which will be set to true if a trade would be allowed or otherwise false.

-- Internal variables
local position = PositionContainer(positionId) -- Position information
local result = false -- Our result value

The PositionContainer() returns an object with accessible properties. See: Commands -> Position Information -> PositionContainer

Simple optimization

Also we want to make sure that the input value percentage will always be a positive value.

percentage = Abs(percentage)

Now, I know the percentage is a required value, but what if it was set to zero? Wouldn't it be unnecessary to go through the entire logic if this was the case? To overcome that problem, we add a simple if-statement to only run our logic if the value is above zero.

if percentage > 0 then
    -- Run logic
    -- ...
else
    -- Allow trade
    result = true
end

Great! This might not optimize much for this particular command, but it's for good practice.

Another way to optimize even further would be to use DefineIntervalOptimization().

Calculating result

Next we will want to separate Long and Short positions since they will have a slightly different calculations for determining the result. We can get this information from our position variable which we defined earlier. Let's do just that. First, we will check if the current position is Long (Bought) position and then calculate the result for it.

if position.isLong then
    result = AddPercentage(
        AverageEnterPrice(position.positionId),
        percentage) < targetPrice
end

Okay, so now we will have result set to true whenever the targetPrice is bigger than the entry price. We also increase the entry price value by the percentage variable.

But... Weren't the targetPrice defined as a "not required" parameter? Oh yes, it was. And its description even mentioned about buy and sell prices! If we would use the default value of this parameter - which is zero - we would never get any trades through. Don't worry, there's always a solution. We will check if the targetPrice is set to zero and if it is, we simply set it to something else. In this case, we set it as the current sell price (top bid price). We will also have to take the position's market into account - otherwise this would not work in unmanaged trading with multi-market positions.

if position.isLong then
    if targetPrice == 0 then
        targetPrice = CurrentPrice(position.market).bid
    end
    
    result = AddPercentage(
        AverageEnterPrice(position.positionId),
        percentage) < targetPrice
end

Nice! Now our targetPrice will never be zero and we don't even have to give it an input. Let's do the same thing for Short positions now. But instead of using the sell price (bid), we will use the buy price (ask) and flip the logic.

elseif position.isShort then
    if targetPrice == 0 then
        targetPrice = CurrentPrice(position.market).ask
    end
    
    result = SubPercentage(
        AverageEntryPrice(position.positionId),
        percentage) > targetPrice
end

Section code summary

Remember to save your work often! Saving wont only keep your work from disappearing, but will also compile the script and show possible errors as you go.

And that's that for the logic part. Simple, but effective!

Here's how the full script should look now:

-- --------------------------------------
-- Command
-- --------------------------------------
DefineCommand(
   'PercentagePriceChange', -- Name
   'Custom insurance. Returns true if trade is allowed, otherwise false.' -- Description
   )

-- --------------------------------------
-- Parameters
-- --------------------------------------
local percentage =
    DefineParameter(
        NumberType,
        'percentage',
        'Minimum percentage change before a trade is allowed.',
        true,
        0,
        'Input'
    )
 
local targetPrice =
    DefineParameter(
        NumberType,
        'targetPrice',
        'The target price of the trade. Default is the current buy or sell price.',
        false,
        0,
        'Number, Input, BidPrices, AskPrices'
    )

local positionId = 
    DefineParameter(
        StringType,
        'positionId',
        'Optional unique identifier. Required when the bot is trading multiple position at once.',
        false,
        '',
        'SessionGet, Load'
    )

-- --------------------------------------
-- Logic
-- --------------------------------------

-- Internal variables
local position = PositionContainer(positionId) -- Position information
local result = false -- Our result value

-- Make sure percentage is properly set
percentage = Abs(percentage)

if percentage > 0 then -- Simple optimization
    -- Run logic
    if position.isLong then -- Long position
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).bid
        end
        
        result = AddPercentage(
            AverageEnterPrice(position.positionId),
            percentage) < targetPrice
            
    elseif position.isShort then -- Short position
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).ask
        end
        
        result = SubPercentage(
            AverageEntryPrice(position.positionId),
            percentage) > targetPrice
    end
else
    -- Allow trade
    result = true
end

-- --------------------------------------
-- Output
-- --------------------------------------
   -- code here...

Command output

Okay, we are getting closer. In this last part all we will do is to define the output for the command. This is what the command will return when used. In our case, we will be returning the result variable as a parameter type BooleanType. I will be listing some output's suggestions that I think could be useful with our command.

There are plenty of other output types for you to use. You can find these by typing ParameterEnums in the editor or checking out the Cheat Sheet.

DefineOutput(
-- Output type:
    BooleanType,
-- Output value:
    result,
-- Description:
    'True if trade is allowed, otherwise false.',
-- Output's suggestions:
    'InsuranceContainer, IfElse, And, Or'
)

Awesome! Simple as that! Now you have yourself a fully functioning insurance command that you can play around and do whatever you want with it! Congratulations! Pat yourself on a shoulder - you've earned it!

Full script

Here are the full scripts of the tutorial; one with comments, one without.

With comments

-- --------------------------------------
-- Command
-- --------------------------------------
DefineCommand(
   'PercentagePriceChange', -- Name
   'Custom insurance. Returns true if trade is allowed, otherwise false.' -- Description
   )

-- --------------------------------------
-- Parameters
-- --------------------------------------
local percentage =
    DefineParameter(
        NumberType,
        'percentage',
        'Minimum percentage change before a trade is allowed.',
        true,
        0,
        'Input'
    )
 
local targetPrice =
    DefineParameter(
        NumberType,
        'targetPrice',
        'The target price of the trade. Default is the current buy or sell price.',
        false,
        0,
        'Number, Input, BidPrices, AskPrices'
    )

local positionId = 
    DefineParameter(
        StringType,
        'positionId',
        'Optional unique identifier. Required when the bot is trading multiple position at once.',
        false,
        '',
        'SessionGet, Load'
    )

-- --------------------------------------
-- Logic
-- --------------------------------------

-- Internal variables
local position = PositionContainer(positionId) -- Position information
local result = false -- Our result value

-- Make sure percentage is properly set
percentage = Abs(percentage)

if percentage > 0 then -- Simple optimization
    -- Run logic
    if position.isLong then -- Long position
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).bid
        end
        
        result = AddPercentage(
            AverageEnterPrice(position.positionId),
            percentage) < targetPrice
            
    elseif position.isShort then -- Short position
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).ask
        end
        
        result = SubPercentage(
            AverageEntryPrice(position.positionId),
            percentage) > targetPrice
    end
else
    -- Allow trade
    result = true
end

-- --------------------------------------
-- Output
-- --------------------------------------

DefineOutput(
-- Output type:
    BooleanType,
-- Output value:
    result,
-- Description:
    'True if trade is allowed, otherwise false.',
-- Output's suggestions:
    'InsuranceContainer, IfElse, And, Or'
)

Compact without comments

-- --------------------------------------
-- Command
-- --------------------------------------
DefineCommand('PercentagePriceChange', 'Custom insurance. Returns true if trade is allowed, otherwise false.')

-- --------------------------------------
-- Parameters
-- --------------------------------------
local percentage = DefineParameter(NumberType, 'percentage', 'Minimum percentage change before a trade is allowed.', true, 0, 'Input')
local targetPrice = DefineParameter(NumberType, 'targetPrice', 'The target price of the trade. Default is the current buy or sell price.', false, 0, 'Number, Input, BidPrices, AskPrices')
local positionId =  DefineParameter(StringType, 'positionId', 'Optional unique identifier. Required when the bot is trading multiple position at once.', false, '', 'SessionGet, Load')

-- --------------------------------------
-- Logic
-- --------------------------------------
local position = PositionContainer(positionId)
local result = false
percentage = Abs(percentage)

if percentage > 0 then
    if position.isLong then
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).bid
        end
        
        result = AddPercentage(AverageEnterPrice(position.positionId), percentage) < targetPrice
    elseif position.isShort then
        if targetPrice == 0 then
            targetPrice = CurrentPrice(position.market).ask
        end
        
        result = SubPercentage(AverageEntryPrice(position.positionId), percentage) > targetPrice
    end
else
    result = true
end

-- --------------------------------------
-- Output
-- --------------------------------------
DefineOutput(BooleanType, result, 'True if trade is allowed, otherwise false.', 'InsuranceContainer, IfElse, And, Or')

Testing the command

To test our custom command we will be creating a very, very basic scalper. It will use MFI and make a buy trade when MFI is below 20. This code snippet should be created and run on its own script, not in the insurance script.

Managed trading

if GetPositionDirection() != PositionLong then
    if MFI(HighPrices(), LowPrices(), ClosePrices(), GetVolume(), 20) < 20 then
        DoBuy()
    end
else
    if CC_PercentagePriceChange(0.5) then
        DoSell()
    end
end

Unmanaged trading

if GetPositionDirection() != PositionLong then
    if MFI(HighPrices(), LowPrices(), ClosePrices(), GetVolume(), 20) < 20 then
        Save("posId_1", NewGuid()) -- New position identifier
        PlaceBuyOrder(AskPrices(), 1, {positionId = Load("posId_1")})
    end
else
    if CC_PercentagePriceChange(0.5, BidPrices(), Load("posId_1")) then
        PlaceSellOrder(BidPrices(), 1, {positionId = Load("posId_1")})
    end
end

Last updated