• Announcement: Lua.org now officially recommends this forum as a meeting place for the Lua community

Indicore 3 Trading Strategy coding help (1 Viewer)

ami9192

Newcomer
Joined
Sep 21, 2020
Messages
1
Reaction score
0
Age
31
Location
United Kingdom
If someone could please help, that would be great! I'm fairly new to coding, but am trying to create a basic automated trading strategy in Indicore 3, which uses LUA script. Even if you don't know much about trading, you could probably still recognize where I've gone wrong in this code.

Basically I'm trying to automate a buy into a trade when price, is above the FastEMA, the FastEMA is above the MedEMA, and the MedEMA is above the SlowEMA. And a sell trade in the exactly opposite fashion. I'm then wanting to close the trade when, in a buy situation, the Fast EMA is no longer above the MedEMA, and for a sell, the FastEMA rises above the MedEMA.

Plus, if the currency is already in a trade, I don't want it to put another one on. Hence the idea around the HaveTrades function.

Please if you have any idea, give me a shout. Like I say I'm a beginner, however I feel like this is a pretty basic code.

EMA Example.png
Picture is to illustrate idea. Blue line is FastEMA, Green line is MedEMA, and Red line is SlowEMA.

Thanks all!

Code:
-- Indicator profile initialization routine
-- Defines indicator profile properties and indicator parameters
-- TODO: Add minimal and maximal value of numeric parameters and default color of the streams
function Init()
    strategy:name("3EMA test AMI");
    strategy:description("No description");
    strategy:requiredSource(core.Bar);
    strategy:type(core.both);

    strategy.parameters:addInteger("FastEMA", "Fast EMA", "No description", 5);
    strategy.parameters:addInteger("MedEMA", "Medium Ema", "No description", 20);
    strategy.parameters:addInteger("SlowEMA", "Slow EMA", "No description", 50);


    strategy.parameters:addGroup("Trading Parameters");
    strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false);
    strategy.parameters:setFlag("AllowTrade", core.FLAG_ALLOW_TRADE);
    strategy.parameters:addString("Account", "Account to trade on", "", "");
    strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT);
    strategy.parameters:addInteger("Amount", "Trade Amount in Lots", "", 1, 1, 100);
    strategy.parameters:addBoolean("SetLimit", "Set Limit Orders", "", false);
    strategy.parameters:addInteger("Limit", "Limit Order in pips", "", 30, 1, 10000);
    strategy.parameters:addBoolean("SetStop", "Set Stop Orders", "", false);
    strategy.parameters:addInteger("Stop", "Stop Order in pips", "", 30, 1, 10000);
    strategy.parameters:addBoolean("TrailingStop", "Trailing stop order", "", false);

    strategy.parameters:addGroup("Notification");
    strategy.parameters:addBoolean("ShowAlert", "Show Alert", "", true);
    strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false);
    strategy.parameters:addBoolean("RecurrentSound", "Recurrent Sound", "", false);
    strategy.parameters:addFile("SoundFile", "Sound File", "", "");
    strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND);
    strategy.parameters:addBoolean("SendEmail", "Send Email", "", false);
    strategy.parameters:addString("Email", "Email", "", "");
    strategy.parameters:setFlag("Email", core.FLAG_EMAIL);
end

-- strategy instance initialization routine
-- Processes strategy parameters and creates output streams
-- TODO: Calculate all constants, create instances all necessary indicators and load all required libraries
-- Parameters block
local FastEMA;
local MedEMA;
local SlowEMA;
local EnableStage1TimeRange;
local TimeRangeStart;
local TimeRangeEnd;
local gSource = nil; -- the source stream
local PlaySound;
local RecurrentSound;
local SoundFile;
local Email;
local SendEmail;
local AllowTrade;
local Account;
local Amount;
local BaseSize;
local SetLimit;
local Limit;
local SetStop;
local Stop;
local TrailingStop;
local Offer;
local CanClose;
--TODO: Add variable(s) for your indicator(s) if needed
local iEMAFast;
local iEMAMed;
local iEMASlow;

local PrevHigh;
local PrevLow;

-- Routine
function Prepare(nameOnly)
    FastEMA = instance.parameters.FastEMA;
    MedEMA = instance.parameters.MedEMA;
    SlowEMA = instance.parameters.SlowEMA;
    LookBack = instance.parameters.LookBack;
    EnableStage1TimeRange = instance.parameters.EnableStage1TimeRange;
    TimeRangeStart = instance.parameters.TimeRangeStart;
    TimeRangeEnd = instance.parameters.TimeRangeEnd;
    

    local name = profile:id() .. "(" .. instance.bid:instrument() .. ", " .. tostring(FastEMA) .. ", " .. tostring(MedEMA) .. ", " .. tostring(SlowEMA) .. ", " .. tostring(LookBack).. ", " .. tostring(EnableStage1TimeRange) .. ")";
    instance:name(name);

    if nameOnly then
        return ;
    end

    ShowAlert = instance.parameters.ShowAlert;

    PlaySound = instance.parameters.PlaySound;
    if PlaySound then
        RecurrentSound = instance.parameters.RecurrentSound;
        SoundFile = instance.parameters.SoundFile;
    else
        SoundFile = nil;
    end
    assert(not(PlaySound) or (PlaySound and SoundFile ~= ""), "Sound file must be specified");

    SendEmail = instance.parameters.SendEmail;
    if SendEmail then
        Email = instance.parameters.Email;
    else
        Email = nil;
    end
    assert(not(SendEmail) or (SendEmail and Email ~= ""), "E-mail address must be specified");


    AllowTrade = instance.parameters.AllowTrade;
    if AllowTrade then
        Account = instance.parameters.Account;
        Amount = instance.parameters.Amount;
        BaseSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), Account);
        Offer = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID;
        CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), Account);
        SetLimit = instance.parameters.SetLimit;
        Limit = instance.parameters.Limit * instance.bid:pipSize();
        SetStop = instance.parameters.SetStop;
        Stop = instance.parameters.Stop * instance.bid:pipSize();
        TrailingStop = instance.parameters.TrailingStop;
    end

    gSource = ExtSubscribe(1, nil, instance.parameters.TF, true, "bar");
    tSource = ExtSubscribe(2, nil, "t1", true, "bar");
    --TODO: Find indicator's profile, intialize parameters, and create indicator's instance (if needed)
    iEMAFast = core.indicators:create("EMA", gSource.close, FastEMA)
    iEMAMed = core.indicators:create("EMA", gSource.close, MedEMA)
    iEMASlow = core.indicators:create("EMA", gSource.close, SlowEMA)
end


local Stage1Status = "Neutral"; -- possible values are "Neutral", "Buy", "Sell"

-- strategy calculation routine
-- TODO: Add your code for decision making
-- TODO: Update the instance of your indicator(s) if needed
function ExtUpdate(id, source, period)


    -- update indicators
    iEMAFast:update(core.UpdateLast);
    iEMAMed:update(core.UpdateLast);
    iEMASlow:update(core.UpdateLast);

    -- check for data
    --if not iEMAFast.DATA:hasData(period) or not iEMAMed.DATA:hasData(period) or not iEMAMed.DATA:hasData(period) then
        --core.host:trace("Not Enough Data. Checking Next Bar...")
        --return;
    --end

    -- close of bar, Stage 1, making sure EMAs are lined up.
    if not haveTrades() then
        
        -- buy setup, if Price > Fast > Med > Slow
        if gSource.close[period] > iEMAFast.DATA[period] and
            iEMAFast.DATA[period] > iEMAMed.DATA[period] and
            iEMAMed.DATA[period] > iEMASlow.DATA[period] then
            Status="Buy"
            enter("B")
        end
    
        
        -- sell setup, if Price < Fast < Med < Slow
        if gSource.close[period] < iEMAFast.DATA[period] and
            iEMAFast.DATA[period] < iEMAMed.DATA[period] and
            iEMAMed.DATA[period] < iEMASlow.DATA[period] then
            Status="Sell"
            enter("S")
        end
    
    end




end

-- open positions in direction BuySell
function enter(BuySell)

    local valuemap, success, msg;
    valuemap = core.valuemap();

    valuemap.OrderType = "OM";
    valuemap.OfferID = Offer;
    valuemap.AcctID = Account;
    valuemap.Quantity = Amount * BaseSize;
    valuemap.BuySell = BuySell;
    valuemap.GTC = "GTC";

    if SetLimit then
        -- set limit order
        valuemap.PegTypeLimit = "O";
        if BuySell == "B" then
           valuemap.PegPriceOffsetPipsLimit = Limit/instance.bid:pipSize();
        else
           valuemap.PegPriceOffsetPipsLimit = -Limit/instance.bid:pipSize();
        end
    end

    if SetStop then
        -- set stop order
        valuemap.PegTypeStop = "O";
        if BuySell == "B" then
           valuemap.PegPriceOffsetPipsStop = -Stop/instance.bid:pipSize();
        else
           valuemap.PegPriceOffsetPipsStop = Stop/instance.bid:pipSize();
        end
        
        if TrailingStop then
            valuemap.TrailStepStop = 1;
        end
    end

    if (not CanClose) and (StopLoss > 0 or TakeProfit > 0) then
        valuemap.EntryLimitStop = "Y"
    end
    
    success, msg = terminal:execute(100, valuemap);

    if not(success) then
        terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "open order failure: " .. msg, instance.bid:date(instance.bid:size() - 1));
        return false;
    end

    return true;
end
-- return true if trade is found (can check single side as well)
function haveTrades(BuySell)
    local enum, row;
    local found = false;
    enum = core.host:findTable("trades"):enumerator();
    row = enum:next();
    while (not found) and (row ~= nil) do
        if row.AccountID == Account and
           row.OfferID == Offer and
           (row.BS == BuySell or BuySell == nil) then
           found = true;
        end
        row = enum:next();
    end

    return found;
end

function close(CanClose)
    if HaveTrades and (Status == "Buy") and FastEMA<MedEMA then
        Status = "close"
      
    end
    
    if HaveTrades and (Status == "Sell") and FastEMA>MedEMA then
        Status = "close"
    
    end
end
 

Loggy

Newcomer
Joined
Dec 16, 2020
Messages
11
Reaction score
2
You are better posting this on the fxcodebase forum as this is specific to FXCM's Trading Station rather than a general Lua question. You also need to have an ExtAsyncOperationFinished function that processes the orders and events.

Before you try it, run it through luacheck and you will see a lot of unused and global variables so tidy it up first (remove strategy, instance, core etc)!

For example you are subscribing to the tick stream but tSource is never used while as id is never checked for a bar (id=1) ExtUpdate processes everything at every price change, ie tick, anyway.

Trading Station is quite good for small strategies and indicators but bigger ones really need direct access via an API.

BTW you only need semi-colons to separate commands on a single line, they are not needed at the end of each line!
 
Top